From 4dc71d5c6f90fc37c23f90cf5be4db61357a06cd Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Tue, 15 Sep 2020 11:50:00 -0400 Subject: [PATCH 01/16] Code cleanup --- mod/core/processor/request_handler.js | 2 -- mod/registrar/registrar.js | 7 ------- 2 files changed, 9 deletions(-) diff --git a/mod/core/processor/request_handler.js b/mod/core/processor/request_handler.js index ddf8f887b..aee2ba35c 100644 --- a/mod/core/processor/request_handler.js +++ b/mod/core/processor/request_handler.js @@ -15,7 +15,6 @@ const { configureProxyAuthorization, configureRequestURI, configureMaxForwards, - configureContact, configurePrivacy, configureRecordRoute, configureIdentity, @@ -27,7 +26,6 @@ const { RoutingType } = require('@routr/core/routing_type') const ObjectId = Java.type('org.bson.types.ObjectId') const Request = Java.type('javax.sip.message.Request') const Response = Java.type('javax.sip.message.Response') -const RouteHeader = Java.type('javax.sip.header.RouteHeader') const ViaHeader = Java.type('javax.sip.header.ViaHeader') const ToHeader = Java.type('javax.sip.header.ToHeader') const LogManager = Java.type('org.apache.logging.log4j.LogManager') diff --git a/mod/registrar/registrar.js b/mod/registrar/registrar.js index 7cfd75c59..52ad20142 100644 --- a/mod/registrar/registrar.js +++ b/mod/registrar/registrar.js @@ -5,21 +5,14 @@ const postal = require('postal') const AuthHelper = require('@routr/utils/auth_helper') const { Status } = require('@routr/core/status') -const isEmpty = require('@routr/utils/obj_util') -const getConfig = require('@routr/core/config_util') const RegistrarUtils = require('@routr/registrar/utils') const DSSelector = require('@routr/data_api/ds_selector') const AgentsAPI = require('@routr/data_api/agents_api') const PeersAPI = require('@routr/data_api/peers_api') -const ViaHeader = Java.type('javax.sip.header.ViaHeader') -const ContactHeader = Java.type('javax.sip.header.ContactHeader') const FromHeader = Java.type('javax.sip.header.FromHeader') -const ExpiresHeader = Java.type('javax.sip.header.ExpiresHeader') const AuthorizationHeader = Java.type('javax.sip.header.AuthorizationHeader') -const SipFactory = Java.type('javax.sip.SipFactory') const LogManager = Java.type('org.apache.logging.log4j.LogManager') -const addressFactory = SipFactory.getInstance().createAddressFactory() const LOG = LogManager.getLogger() From 726116639fe433c6ce2bf021f074be142a7870e5 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Tue, 15 Sep 2020 12:05:10 -0400 Subject: [PATCH 02/16] Code cleanup --- mod/core/server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/mod/core/server.js b/mod/core/server.js index 9719a56d4..2fe12f7a8 100644 --- a/mod/core/server.js +++ b/mod/core/server.js @@ -24,7 +24,6 @@ const System = Java.type('java.lang.System') const SipFactory = Java.type('javax.sip.SipFactory') const LogManager = Java.type('org.apache.logging.log4j.LogManager') const LogOutputStream = Java.type('io.routr.core.LogOutputStream') -const OutputStream = Java.type('java.io.OutputStream') const PrintStream = Java.type('java.io.PrintStream') const LOG = LogManager.getLogger() From 7e0c8544b35c85ec3ef083ddcf20cc542d2a5fa2 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Thu, 17 Sep 2020 20:51:18 -0400 Subject: [PATCH 03/16] Work on rtpengine support --- build.gradle | 1 + etc/schemas/config_schema.json | 8 +++ mod/core/config/config_defaults.js | 4 ++ mod/core/config/config_from_env.js | 2 + mod/core/processor/processor.js | 15 ++++-- mod/core/processor/processor_utils.js | 14 ++++++ mod/core/processor/request_processor.js | 20 +++++++- mod/core/processor/response_processor.js | 21 +++++++- mod/core/processor/test.js | 2 - mod/core/server.js | 2 + mod/data_api/domains.unit.test.js | 1 - mod/location/location.unit.test.js | 1 - mod/rtpengine/connector.js | 63 ++++++++++++++++++++++++ 13 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 mod/rtpengine/connector.js diff --git a/build.gradle b/build.gradle index 097e58b71..b42722bbe 100644 --- a/build.gradle +++ b/build.gradle @@ -53,6 +53,7 @@ dependencies { compile 'io.kotest:kotest-runner-junit5-jvm:4.0.3' compile 'io.kotest:kotest-assertions-core-jvm:4.0.3' compile 'io.kotest:kotest-property-jvm:4.0.3' + compile "com.mashape.unirest:unirest-java:1.4.9" implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.50' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' } diff --git a/etc/schemas/config_schema.json b/etc/schemas/config_schema.json index 60f143fc4..9fc8885df 100644 --- a/etc/schemas/config_schema.json +++ b/etc/schemas/config_schema.json @@ -58,6 +58,14 @@ "trustStorePassword": { "type": "string" } } }, + "ex_mediaEngine": { + "type": "object", + "properties": { + "provider": { "type": "string" }, + "port": { "type": "integer" }, + "host": { "type": "string" } + } + }, "dataSource": { "type": "object", "properties": { diff --git a/mod/core/config/config_defaults.js b/mod/core/config/config_defaults.js index d1eae7d70..e569e0e7e 100644 --- a/mod/core/config/config_defaults.js +++ b/mod/core/config/config_defaults.js @@ -23,6 +23,10 @@ module.exports = upSince => { dataSource: { provider: 'files_data_provider' }, + ex_mediaEngine: { + provider: 'rtpengine', + port: 22222 + }, registrarIntf: 'External', restService: { keyStore: 'etc/certs/api-cert.jks', diff --git a/mod/core/config/config_from_env.js b/mod/core/config/config_from_env.js index 95836aa63..dcb40c638 100644 --- a/mod/core/config/config_from_env.js +++ b/mod/core/config/config_from_env.js @@ -49,6 +49,8 @@ envsMap.set('LOG4J', '') envsMap.set('CONFIG_FILE', '') envsMap.set('SALT', '') envsMap.set('SALT_FILE', '') +envsMap.set('EX_MEDIA_ENGINE_HOST', '') +envsMap.set('EX_MEDIA_ENGINE_PORT', 22222) const boolVals = [ 'SECURITY_CONTEXT_DEBUGGING', diff --git a/mod/core/processor/processor.js b/mod/core/processor/processor.js index aae397feb..82291b7b1 100644 --- a/mod/core/processor/processor.js +++ b/mod/core/processor/processor.js @@ -5,6 +5,8 @@ const postal = require('postal') const RequestProcessor = require('@routr/core/processor/request_processor') const ResponseProcessor = require('@routr/core/processor/response_processor') +const CallIdHeader = Java.type('javax.sip.header.CallIdHeader') +const FromHeader = Java.type('javax.sip.header.FromHeader') const SipListener = Java.type('javax.sip.SipListener') const LogManager = Java.type('org.apache.logging.log4j.LogManager') const LOG = LogManager.getLogger() @@ -45,22 +47,29 @@ class Processor { channel: 'processor', topic: 'transaction.timeout', data: { - transactionId: transactionId, + transactionId, isServerTransaction: event.isServerTransaction() } }) }, processTransactionTerminated: event => { + const request = event.getServerTransaction().getRequest() + const callId = request.getHeader(CallIdHeader.NAME).getCallId() + const fromTag = request.getHeader(FromHeader.NAME).getTag() + const transactionId = event.isServerTransaction() ? event.getServerTransaction().getBranchId() : event.getClientTransaction().getBranchId() + postal.publish({ channel: 'processor', topic: 'transaction.terminated', data: { - transactionId: transactionId, - isServerTransaction: event.isServerTransaction() + transactionId, + isServerTransaction: event.isServerTransaction(), + callId, + fromTag } }) }, diff --git a/mod/core/processor/processor_utils.js b/mod/core/processor/processor_utils.js index b05462124..18542210d 100644 --- a/mod/core/processor/processor_utils.js +++ b/mod/core/processor/processor_utils.js @@ -6,10 +6,13 @@ const AuthHelper = require('@routr/utils/auth_helper') const { connectionException } = require('@routr/utils/exception_helpers') const ToHeader = Java.type('javax.sip.header.ToHeader') +const FromHeader = Java.type('javax.sip.header.FromHeader') const ContactHeader = Java.type('javax.sip.header.ContactHeader') const ExpiresHeader = Java.type('javax.sip.header.ExpiresHeader') const CSeqHeader = Java.type('javax.sip.header.CSeqHeader') const ViaHeader = Java.type('javax.sip.header.ViaHeader') +const ContentType = Java.type('javax.sip.header.ContentTypeHeader') +const CallIdHeader = Java.type('javax.sip.header.CallIdHeader') const Request = Java.type('javax.sip.message.Request') const Response = Java.type('javax.sip.message.Response') const AccountManager = Java.type( @@ -22,6 +25,15 @@ const SipFactory = Java.type('javax.sip.SipFactory') const headerFactory = SipFactory.getInstance().createHeaderFactory() const messageFactory = SipFactory.getInstance().createMessageFactory() +const extractRTPEngineParams = r => { + return { + sdp: String.fromCharCode.apply(null, r.getContent()), + 'call-id': r.getHeader(CallIdHeader.NAME).getCallId(), + 'from-tag': r.getHeader(FromHeader.NAME).getTag(), + 'to-tag': r.getHeader(ToHeader.NAME).getTag() + } +} +const hasSDP = r => r.getHeader(ContentType.NAME) != null const hasCodes = (r, c) => c.filter(code => r.getStatusCode() === code).length > 0 const isMethod = (r, m) => @@ -142,3 +154,5 @@ module.exports.isRegisterNok = isRegisterNok module.exports.isBehindNat = isBehindNat module.exports.handleAuthChallenge = handleAuthChallenge module.exports.getExpires = getExpires +module.exports.hasSDP = hasSDP +module.exports.extractRTPEngineParams = extractRTPEngineParams diff --git a/mod/core/processor/request_processor.js b/mod/core/processor/request_processor.js index 7b75dc3de..7fe9520b6 100644 --- a/mod/core/processor/request_processor.js +++ b/mod/core/processor/request_processor.js @@ -2,7 +2,11 @@ * @author Pedro Sanders * @since v1 */ -const { sendResponse } = require('@routr/core/processor/processor_utils') +const { + sendResponse, + hasSDP, + extractRTPEngineParams +} = require('@routr/core/processor/processor_utils') const RegisterHandler = require('@routr/core/processor/register_handler') const RegistryHandler = require('@routr/core/processor/registry_handler') const CancelHandler = require('@routr/core/processor/cancel_handler') @@ -13,7 +17,9 @@ const { RoutingType } = require('@routr/core/routing_type') const { RouteEntityType } = require('@routr/core/route_entity_type') const { Status } = require('@routr/core/status') const config = require('@routr/core/config_util')() +const RTPEngineConnector = require('@routr/rtpengine/connector') +const ContentTypeHeader = Java.type('javax.sip.header.ContentTypeHeader') const Request = Java.type('javax.sip.message.Request') const Response = Java.type('javax.sip.message.Response') const LogManager = Java.type('org.apache.logging.log4j.LogManager') @@ -29,7 +35,7 @@ class RequestProcessor { this.domainsAPI = dataAPIs.DomainsAPI } - process (event) { + async process (event) { const request = event.getRequest() let transaction = event.getServerTransaction() @@ -95,6 +101,16 @@ class RequestProcessor { new CancelHandler().doProcess(transaction) break default: + const isInviteOrAck = r => + r.getMethod() === Request.INVITE || r.getMethod() === Request.ACK + + if (isInviteOrAck(request) && hasSDP(request)) { + const obj = await RTPEngineConnector.offer( + extractRTPEngineParams(request) + ) + request.setContent(obj.sdp, request.getHeader(ContentTypeHeader.NAME)) + } + new RequestHandler(this.sipProvider, this.contextStorage).doProcess( transaction, request, diff --git a/mod/core/processor/response_processor.js b/mod/core/processor/response_processor.js index c4b0f6142..24ee67cf9 100644 --- a/mod/core/processor/response_processor.js +++ b/mod/core/processor/response_processor.js @@ -8,9 +8,14 @@ const { isStackJob, isTransactional, mustAuthenticate, - handleAuthChallenge + handleAuthChallenge, + hasSDP, + extractRTPEngineParams, + isOk } = require('@routr/core/processor/processor_utils') +const RTPEngineConnector = require('@routr/rtpengine/connector') const ViaHeader = Java.type('javax.sip.header.ViaHeader') +const ContentTypeHeader = Java.type('javax.sip.header.ContentTypeHeader') const SipFactory = Java.type('javax.sip.SipFactory') const LogManager = Java.type('org.apache.logging.log4j.LogManager') const LOG = LogManager.getLogger() @@ -41,7 +46,7 @@ class ResponseProcessor { this.sendResponse(event) } - sendResponse (event) { + async sendResponse (event) { const response = event.getResponse().clone() const viaHeader = response.getHeader(ViaHeader.NAME) const xReceivedHeader = headerFactory.createHeader( @@ -55,6 +60,18 @@ class ResponseProcessor { response.addHeader(xReceivedHeader) response.addHeader(xRPortHeader) response.removeFirst(ViaHeader.NAME) + + try { + if (isOk(response) && hasSDP(response)) { + const obj = await RTPEngineConnector.answer( + extractRTPEngineParams(response) + ) + response.setContent(obj.sdp, response.getHeader(ContentTypeHeader.NAME)) + } + } catch (e) { + console.log(e) + } + if (isTransactional(event)) { const context = this.contextStorage.findContext( event.getClientTransaction().getBranchId() diff --git a/mod/core/processor/test.js b/mod/core/processor/test.js index 869b58a21..4ff6fbb7b 100644 --- a/mod/core/processor/test.js +++ b/mod/core/processor/test.js @@ -81,8 +81,6 @@ describe('Core Processor Module', () => { }) it('Response utils', function (done) { - const CSeqHeader = Java.type('javax.sip.header.CSeqHeader') - const ViaHeader = Java.type('javax.sip.header.ViaHeader') const Request = Java.type('javax.sip.message.Request') const Response = Java.type('javax.sip.message.Response') diff --git a/mod/core/server.js b/mod/core/server.js index 2fe12f7a8..c5c9dfb4e 100644 --- a/mod/core/server.js +++ b/mod/core/server.js @@ -13,6 +13,7 @@ const Processor = require('@routr/core/processor/processor') const Locator = require('@routr/location/locator') const ContextStorage = require('@routr/core/context_storage') const showExternInfo = require('@routr/core/extern_info') +const RTPEngineConnector = require('@routr/rtpengine/connector') const config = require('@routr/core/config_util')() const properties = require('@routr/core/server_properties')(config) const ExceptionUtils = Java.type( @@ -49,6 +50,7 @@ class Server { this.dataAPIs = dataAPIs this.locator = new Locator() + new RTPEngineConnector() } buildSipProvider (sipStack, transport) { diff --git a/mod/data_api/domains.unit.test.js b/mod/data_api/domains.unit.test.js index 38998df3f..ba4e3d509 100644 --- a/mod/data_api/domains.unit.test.js +++ b/mod/data_api/domains.unit.test.js @@ -4,7 +4,6 @@ * * Unit Test for the "Domains API" */ -const APIBase = require('@routr/data_api/api_base') const DomainsAPI = require('@routr/data_api/domains_api') const TestUtils = require('@routr/data_api/test_utils') const chai = require('chai') diff --git a/mod/location/location.unit.test.js b/mod/location/location.unit.test.js index 5bc8196be..1cda075ce 100644 --- a/mod/location/location.unit.test.js +++ b/mod/location/location.unit.test.js @@ -5,7 +5,6 @@ * Unit Test for the configuration utility */ const chai = require('chai') -const sinon = require('sinon') const sinonChai = require('sinon-chai') chai.use(sinonChai) const expect = chai.expect diff --git a/mod/rtpengine/connector.js b/mod/rtpengine/connector.js new file mode 100644 index 000000000..06016022e --- /dev/null +++ b/mod/rtpengine/connector.js @@ -0,0 +1,63 @@ +/** + * Manage RTPEngine bindings + * + * @author Pedro Sanders + * @since v1 + */ +const postal = require('postal') +const config = require('@routr/core/config_util')() + +const LogManager = Java.type('org.apache.logging.log4j.LogManager') +const LOG = LogManager.getLogger() +const Unirest = Java.type('com.mashape.unirest.http.Unirest') +const rtpeBaseUrl = `http://${config.spec.ex_mediaEngine.host}:${ + config.spec.ex_mediaEngine.port +}/api` + +class RTPEngineConnector { + constructor () { + LOG.debug(`rtpengine.RTPEngineConnector connector is up`) + postal.subscribe({ + channel: 'processor', + topic: 'transaction.terminated', + callback: data => this.deleteCallBinding(data.callId, data.fromTag) + }) + } + + deleteCallBinding (callId, fromTag) { + return RTPEngineConnector.sendCmd( + `delete/${callId}`, + JSON.stringify({ + 'call-id': callId, + 'from-tag': fromTag + }) + ) + } + + static async offer (params) { + return await RTPEngineConnector.sendCmd('offer', params) + } + + static async answer (params) { + return await RTPEngineConnector.sendCmd('answer', params) + } + + static async sendCmd (cmd, params) { + try { + LOG.debug( + `rtpengine.RTPEngineConnector.${cmd} [call-id: ${params['call-id']}]` + ) + console.log(`xxxx => ${rtpeBaseUrl}/${cmd}`) + + const res = await Unirest.post(`${rtpeBaseUrl}/${cmd}`) + .header('Content-Type', 'application/json') + .body(JSON.stringify(params)) + .asString() + return JSON.parse(res.getBody()) + } catch (e) { + console.log(e) + } + } +} + +module.exports = RTPEngineConnector From d7354cf33fb4edf647af4f39d536f45373da4be9 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Thu, 17 Sep 2020 22:02:19 -0400 Subject: [PATCH 04/16] Minor corrections --- mod/core/config/config_from_env.js | 4 ++-- mod/rtpengine/connector.js | 19 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/mod/core/config/config_from_env.js b/mod/core/config/config_from_env.js index dcb40c638..0f7fa050c 100644 --- a/mod/core/config/config_from_env.js +++ b/mod/core/config/config_from_env.js @@ -45,12 +45,12 @@ envsMap.set( 'spec.securityContext.client.authType' ) envsMap.set('SECURITY_CONTEXT_DEBUGGING', 'spec.securityContext.debugging') +envsMap.set('EX_MEDIA_ENGINE_HOST', 'spec.ex_mediaEngine.host') +envsMap.set('EX_MEDIA_ENGINE_PORT', 'spec.ex_mediaEngine.port') envsMap.set('LOG4J', '') envsMap.set('CONFIG_FILE', '') envsMap.set('SALT', '') envsMap.set('SALT_FILE', '') -envsMap.set('EX_MEDIA_ENGINE_HOST', '') -envsMap.set('EX_MEDIA_ENGINE_PORT', 22222) const boolVals = [ 'SECURITY_CONTEXT_DEBUGGING', diff --git a/mod/rtpengine/connector.js b/mod/rtpengine/connector.js index 06016022e..e30c27102 100644 --- a/mod/rtpengine/connector.js +++ b/mod/rtpengine/connector.js @@ -17,21 +17,21 @@ const rtpeBaseUrl = `http://${config.spec.ex_mediaEngine.host}:${ class RTPEngineConnector { constructor () { LOG.debug(`rtpengine.RTPEngineConnector connector is up`) - postal.subscribe({ + + // This is not a good criteria to delete the binding. + // It should happend on a Bye or Cancel or Timeout + /*postal.subscribe({ channel: 'processor', topic: 'transaction.terminated', callback: data => this.deleteCallBinding(data.callId, data.fromTag) - }) + })*/ } deleteCallBinding (callId, fromTag) { - return RTPEngineConnector.sendCmd( - `delete/${callId}`, - JSON.stringify({ - 'call-id': callId, - 'from-tag': fromTag - }) - ) + return RTPEngineConnector.sendCmd(`delete/${callId}`, { + 'call-id': callId, + 'from-tag': fromTag + }) } static async offer (params) { @@ -47,7 +47,6 @@ class RTPEngineConnector { LOG.debug( `rtpengine.RTPEngineConnector.${cmd} [call-id: ${params['call-id']}]` ) - console.log(`xxxx => ${rtpeBaseUrl}/${cmd}`) const res = await Unirest.post(`${rtpeBaseUrl}/${cmd}`) .header('Content-Type', 'application/json') From 6675420fa49eb41c440ba87e796c57e013c369a3 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Fri, 25 Sep 2020 23:18:37 -0400 Subject: [PATCH 05/16] Refactored --- mod/core/processor/request_handler.js | 39 +++++++++++++++++---- mod/core/processor/request_processor.js | 19 +---------- mod/core/processor/response_processor.js | 15 ++++----- mod/core/processor/route_info.js | 1 - mod/registrar/utils.js | 6 +++- mod/rtpengine/connector.js | 43 ++++++++++++++++++++++-- mod/rtpengine/rtp_bridging_note.js | 10 ++++++ mod/rtpengine/utils.js | 23 +++++++++++++ 8 files changed, 118 insertions(+), 38 deletions(-) create mode 100644 mod/rtpengine/rtp_bridging_note.js create mode 100644 mod/rtpengine/utils.js diff --git a/mod/core/processor/request_handler.js b/mod/core/processor/request_handler.js index aee2ba35c..7c2c77987 100644 --- a/mod/core/processor/request_handler.js +++ b/mod/core/processor/request_handler.js @@ -3,9 +3,15 @@ * @since v1 */ const { connectionException } = require('@routr/utils/exception_helpers') -const { sendResponse } = require('@routr/core/processor/processor_utils') +const { + sendResponse, + hasSDP, + extractRTPEngineParams +} = require('@routr/core/processor/processor_utils') const { Status } = require('@routr/core/status') const config = require('@routr/core/config_util')() +const RTPEngineConnector = require('@routr/rtpengine/connector') +const ContentTypeHeader = Java.type('javax.sip.header.ContentTypeHeader') const postal = require('postal') const { @@ -22,6 +28,7 @@ const { configureCSeq, isInDialog } = require('@routr/core/processor/request_utils') +const { getBridgingNote } = require('@routr/rtpengine/utils') const { RoutingType } = require('@routr/core/routing_type') const ObjectId = Java.type('org.bson.types.ObjectId') const Request = Java.type('javax.sip.message.Request') @@ -31,6 +38,8 @@ const ToHeader = Java.type('javax.sip.header.ToHeader') const LogManager = Java.type('org.apache.logging.log4j.LogManager') const ConcurrentHashMap = Java.type('java.util.concurrent.ConcurrentHashMap') const requestStore = new ConcurrentHashMap() +const isInviteOrAck = r => + r.getMethod() === Request.INVITE || r.getMethod() === Request.ACK const LOG = LogManager.getLogger() @@ -94,7 +103,7 @@ class RequestHandler { } } - processRoute (transaction, request, route, routeInfo) { + async processRoute (transaction, request, route, routeInfo) { const transport = request .getHeader(ViaHeader.NAME) .getTransport() @@ -134,10 +143,20 @@ class RequestHandler { )}]` ) - this.sendRequest(transaction, request, requestOut) + let bridgingNote + if (isInviteOrAck(request) && hasSDP(request)) { + bridgingNote = getBridgingNote(request, route) + const obj = await RTPEngineConnector.offer( + bridgingNote, + extractRTPEngineParams(request) + ) + request.setContent(obj.sdp, request.getHeader(ContentTypeHeader.NAME)) + } + + this.sendRequest(transaction, request, requestOut, bridgingNote) } - sendRequest (serverTransaction, request, requestOut) { + sendRequest (serverTransaction, request, requestOut, bridgingNote) { // Does not need a transaction if (request.getMethod().equals(Request.ACK)) { return this.sipProvider.sendRequest(requestOut) @@ -160,14 +179,21 @@ class RequestHandler { request, requestOut, clientTransaction, - serverTransaction + serverTransaction, + bridgingNote ) } catch (e) { connectionException(e, requestOut.getRequestURI().getHost()) } } - saveContext (request, requestOut, clientTransaction, serverTransaction) { + saveContext ( + request, + requestOut, + clientTransaction, + serverTransaction, + bridgingNote + ) { // Transaction context const context = {} context.clientTransaction = clientTransaction @@ -175,6 +201,7 @@ class RequestHandler { context.method = request.getMethod() context.requestIn = request context.requestOut = requestOut + context.bridgingNote = bridgingNote this.contextStorage.addContext(context) } } diff --git a/mod/core/processor/request_processor.js b/mod/core/processor/request_processor.js index 7fe9520b6..ec093e7c2 100644 --- a/mod/core/processor/request_processor.js +++ b/mod/core/processor/request_processor.js @@ -2,11 +2,7 @@ * @author Pedro Sanders * @since v1 */ -const { - sendResponse, - hasSDP, - extractRTPEngineParams -} = require('@routr/core/processor/processor_utils') +const { sendResponse } = require('@routr/core/processor/processor_utils') const RegisterHandler = require('@routr/core/processor/register_handler') const RegistryHandler = require('@routr/core/processor/registry_handler') const CancelHandler = require('@routr/core/processor/cancel_handler') @@ -17,9 +13,6 @@ const { RoutingType } = require('@routr/core/routing_type') const { RouteEntityType } = require('@routr/core/route_entity_type') const { Status } = require('@routr/core/status') const config = require('@routr/core/config_util')() -const RTPEngineConnector = require('@routr/rtpengine/connector') - -const ContentTypeHeader = Java.type('javax.sip.header.ContentTypeHeader') const Request = Java.type('javax.sip.message.Request') const Response = Java.type('javax.sip.message.Response') const LogManager = Java.type('org.apache.logging.log4j.LogManager') @@ -101,16 +94,6 @@ class RequestProcessor { new CancelHandler().doProcess(transaction) break default: - const isInviteOrAck = r => - r.getMethod() === Request.INVITE || r.getMethod() === Request.ACK - - if (isInviteOrAck(request) && hasSDP(request)) { - const obj = await RTPEngineConnector.offer( - extractRTPEngineParams(request) - ) - request.setContent(obj.sdp, request.getHeader(ContentTypeHeader.NAME)) - } - new RequestHandler(this.sipProvider, this.contextStorage).doProcess( transaction, request, diff --git a/mod/core/processor/response_processor.js b/mod/core/processor/response_processor.js index 24ee67cf9..3734e1ea7 100644 --- a/mod/core/processor/response_processor.js +++ b/mod/core/processor/response_processor.js @@ -61,21 +61,18 @@ class ResponseProcessor { response.addHeader(xRPortHeader) response.removeFirst(ViaHeader.NAME) - try { + if (isTransactional(event)) { + const context = this.contextStorage.findContext( + event.getClientTransaction().getBranchId() + ) + if (isOk(response) && hasSDP(response)) { const obj = await RTPEngineConnector.answer( + context.bridgingNote, extractRTPEngineParams(response) ) response.setContent(obj.sdp, response.getHeader(ContentTypeHeader.NAME)) } - } catch (e) { - console.log(e) - } - - if (isTransactional(event)) { - const context = this.contextStorage.findContext( - event.getClientTransaction().getBranchId() - ) if (context && context.serverTransaction) { context.serverTransaction.sendResponse(response) diff --git a/mod/core/processor/route_info.js b/mod/core/processor/route_info.js index 3b7baf75b..5b099f198 100644 --- a/mod/core/processor/route_info.js +++ b/mod/core/processor/route_info.js @@ -17,7 +17,6 @@ class RouteInfo { constructor (request, dataAPIs) { const fromHeader = request.getHeader(FromHeader.NAME) const toHeader = request.getHeader(ToHeader.NAME) - const sipFactory = SipFactory.getInstance() this.request = request this._callerUser = fromHeader .getAddress() diff --git a/mod/registrar/utils.js b/mod/registrar/utils.js index b5eb3d5d8..aed18707e 100644 --- a/mod/registrar/utils.js +++ b/mod/registrar/utils.js @@ -66,7 +66,11 @@ class RegistrarUtils { contactURI: RegistrarUtils.getUpdatedContactURI(request, user), registeredOn: Date.now(), expires: getExpires(request), - nat: isBehindNat(request) + nat: isBehindNat(request), + transport: request + .getHeader(ViaHeader.NAME) + .getTransport() + .toLowerCase() } } diff --git a/mod/rtpengine/connector.js b/mod/rtpengine/connector.js index e30c27102..827509ed0 100644 --- a/mod/rtpengine/connector.js +++ b/mod/rtpengine/connector.js @@ -4,8 +4,10 @@ * @author Pedro Sanders * @since v1 */ +const merge = require('deepmerge') const postal = require('postal') const config = require('@routr/core/config_util')() +const RTPBridgingNote = require('@routr/rtpengine/rtp_bridging_note') const LogManager = Java.type('org.apache.logging.log4j.LogManager') const LOG = LogManager.getLogger() @@ -14,6 +16,23 @@ const rtpeBaseUrl = `http://${config.spec.ex_mediaEngine.host}:${ config.spec.ex_mediaEngine.port }/api` +const webToSip = { + 'transport protocol': 'RTP/AVP', + 'rtcp-mux': 'demux', + ICE: 'remove', + DTLS: 'off', + SDES: 'off' +} + +const sipToWeb = { + 'transport protocol': 'RTP/SAVPF', + 'rtcp-mux': 'offer', + ICE: 'force', + DTLS: 'active', + SDES: 'off', + flags: 'generate mid' +} + class RTPEngineConnector { constructor () { LOG.debug(`rtpengine.RTPEngineConnector connector is up`) @@ -34,11 +53,29 @@ class RTPEngineConnector { }) } - static async offer (params) { + static async offer (bridgingNote, params) { + LOG.debug( + `rtpengine.RTPEngineConnector.offer [bridging note: ${bridgingNote}]` + ) + if (bridgingNote === RTPBridgingNote.WEB_TO_SIP) { + merge(params, webToSip) + } else if (bridgingNote === RTPBridgingNote.SIP_TO_WEB) { + merge(params, sipToWeb) + } + return await RTPEngineConnector.sendCmd('offer', params) } - static async answer (params) { + static async answer (bridgingNote, params) { + LOG.debug( + `rtpengine.RTPEngineConnector.answer [bridging note: ${bridgingNote}]` + ) + if (bridgingNote === RTPBridgingNote.SIP_TO_WEB) { + merge(params, webToSip) + } else if (bridgingNote === RTPBridgingNote.WEB_TO_SIP) { + merge(params, sipToWeb) + } + return await RTPEngineConnector.sendCmd('answer', params) } @@ -54,7 +91,7 @@ class RTPEngineConnector { .asString() return JSON.parse(res.getBody()) } catch (e) { - console.log(e) + LOG.error(e) } } } diff --git a/mod/rtpengine/rtp_bridging_note.js b/mod/rtpengine/rtp_bridging_note.js new file mode 100644 index 000000000..e7b6312c7 --- /dev/null +++ b/mod/rtpengine/rtp_bridging_note.js @@ -0,0 +1,10 @@ +/** + * @author Pedro Sanders + * @since v1 + */ +module.exports.RTPBridgingNote = { + WEB_TO_WEB: 'WEB_TO_WEB', + WEB_TO_SIP: 'WEB_TO_SIP', + SIP_TO_SIP: 'SIP_TO_SIP', + SIP_TO_WEB: 'SIP_TO_WEB' +} diff --git a/mod/rtpengine/utils.js b/mod/rtpengine/utils.js new file mode 100644 index 000000000..5808ffe4a --- /dev/null +++ b/mod/rtpengine/utils.js @@ -0,0 +1,23 @@ +/** + * @author Pedro Sanders + * @since v1 + */ +const { RTPBridgingNote } = require('@routr/rtpengine/rtp_bridging_note') +const ViaHeader = Java.type('javax.sip.header.ViaHeader') +const isTransportWeb = t => t === 'ws' || t === 'wss' + +module.exports.getBridgingNote = (request, route) => { + const destTransport = route.transport.toLowerCase() + const srcTransport = request + .getHeader(ViaHeader.NAME) + .getTransport() + .toLowerCase() + if (isTransportWeb(srcTransport) && isTransportWeb(destTransport)) + return RTPBridgingNote.WEB_TO_WEB + if (isTransportWeb(srcTransport) && !isTransportWeb(destTransport)) + return RTPBridgingNote.WEB_TO_SIP + if (!isTransportWeb(srcTransport) && !isTransportWeb(destTransport)) + return RTPBridgingNote.SIP_TO_SIP + if (!isTransportWeb(srcTransport) && isTransportWeb(destTransport)) + return RTPBridgingNote.SIP_TO_WEB +} From 03bdf6c773b0714c407d41cf17cc973de25eb82e Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Sat, 26 Sep 2020 11:24:08 -0400 Subject: [PATCH 06/16] Improved rtp profile --- mod/core/processor/request_handler.js | 11 ++++--- mod/rtpengine/connector.js | 47 +++++++++++++++++++-------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/mod/core/processor/request_handler.js b/mod/core/processor/request_handler.js index 7c2c77987..fdd5a9be9 100644 --- a/mod/core/processor/request_handler.js +++ b/mod/core/processor/request_handler.js @@ -144,13 +144,16 @@ class RequestHandler { ) let bridgingNote - if (isInviteOrAck(request) && hasSDP(request)) { - bridgingNote = getBridgingNote(request, route) + if (isInviteOrAck(requestOut) && hasSDP(requestOut)) { + bridgingNote = getBridgingNote(requestOut, route) const obj = await RTPEngineConnector.offer( bridgingNote, - extractRTPEngineParams(request) + extractRTPEngineParams(requestOut) + ) + requestOut.setContent( + obj.sdp, + requestOut.getHeader(ContentTypeHeader.NAME) ) - request.setContent(obj.sdp, request.getHeader(ContentTypeHeader.NAME)) } this.sendRequest(transaction, request, requestOut, bridgingNote) diff --git a/mod/rtpengine/connector.js b/mod/rtpengine/connector.js index 827509ed0..b19a5c65d 100644 --- a/mod/rtpengine/connector.js +++ b/mod/rtpengine/connector.js @@ -7,7 +7,7 @@ const merge = require('deepmerge') const postal = require('postal') const config = require('@routr/core/config_util')() -const RTPBridgingNote = require('@routr/rtpengine/rtp_bridging_note') +const { RTPBridgingNote } = require('@routr/rtpengine/rtp_bridging_note') const LogManager = Java.type('org.apache.logging.log4j.LogManager') const LOG = LogManager.getLogger() @@ -16,21 +16,32 @@ const rtpeBaseUrl = `http://${config.spec.ex_mediaEngine.host}:${ config.spec.ex_mediaEngine.port }/api` +const webToWeb = { + ICE: 'force', + SDES: 'off', + flags: 'trust-address replace-origin replace-session-connection' +} + const webToSip = { - 'transport protocol': 'RTP/AVP', + 'transport-protocol': 'RTP/AVP', 'rtcp-mux': 'demux', ICE: 'remove', - DTLS: 'off', - SDES: 'off' + flags: 'trust-address replace-origin replace-session-connection' } const sipToWeb = { - 'transport protocol': 'RTP/SAVPF', + 'transport-protocol': 'UDP/TLS/RTP/SAVP', 'rtcp-mux': 'offer', ICE: 'force', - DTLS: 'active', SDES: 'off', - flags: 'generate mid' + flags: 'trust-address replace-origin replace-session-connection generate-mid' +} + +const sipToSip = { + 'transport-protocol': 'RTP/AVP', + 'rtcp-mux': 'demux', + ICE: 'remove', + flags: 'trust-address replace-origin replace-session-connection' } class RTPEngineConnector { @@ -57,10 +68,15 @@ class RTPEngineConnector { LOG.debug( `rtpengine.RTPEngineConnector.offer [bridging note: ${bridgingNote}]` ) - if (bridgingNote === RTPBridgingNote.WEB_TO_SIP) { - merge(params, webToSip) + + if (bridgingNote === RTPBridgingNote.WEB_TO_WEB) { + params = merge(params, webToWeb) + } else if (bridgingNote === RTPBridgingNote.WEB_TO_SIP) { + params = merge(params, webToSip) } else if (bridgingNote === RTPBridgingNote.SIP_TO_WEB) { - merge(params, sipToWeb) + params = merge(params, sipToWeb) + } else { + params = merge(params, sipToSip) } return await RTPEngineConnector.sendCmd('offer', params) @@ -70,10 +86,15 @@ class RTPEngineConnector { LOG.debug( `rtpengine.RTPEngineConnector.answer [bridging note: ${bridgingNote}]` ) - if (bridgingNote === RTPBridgingNote.SIP_TO_WEB) { - merge(params, webToSip) + + if (bridgingNote === RTPBridgingNote.WEB_TO_WEB) { + params = merge(params, webToWeb) + } else if (bridgingNote === RTPBridgingNote.SIP_TO_WEB) { + params = merge(params, webToSip) } else if (bridgingNote === RTPBridgingNote.WEB_TO_SIP) { - merge(params, sipToWeb) + params = merge(params, sipToWeb) + } else { + params = merge(params, sipToSip) } return await RTPEngineConnector.sendCmd('answer', params) From 7a60701b5f40d7095a4be1dfa8f8a4516dff93e0 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Sat, 26 Sep 2020 18:09:56 -0400 Subject: [PATCH 07/16] Fixed issue with outbound calling to gateways --- mod/rtpengine/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/rtpengine/utils.js b/mod/rtpengine/utils.js index 5808ffe4a..afe1c5394 100644 --- a/mod/rtpengine/utils.js +++ b/mod/rtpengine/utils.js @@ -7,7 +7,7 @@ const ViaHeader = Java.type('javax.sip.header.ViaHeader') const isTransportWeb = t => t === 'ws' || t === 'wss' module.exports.getBridgingNote = (request, route) => { - const destTransport = route.transport.toLowerCase() + const destTransport = !route.thruGw ? route.transport.toLowerCase() : '' const srcTransport = request .getHeader(ViaHeader.NAME) .getTransport() From 17e5aa09eae95fdda94d65d6ba5c8a9c155bc7c9 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Mon, 26 Oct 2020 14:29:42 -0400 Subject: [PATCH 08/16] Saving progress on rtpengine support --- mod/core/processor/request_handler.js | 11 ++++------- mod/core/processor/request_utils.js | 10 +++------- mod/location/locator.js | 1 - mod/location/route_loader.js | 2 -- mod/location/utils.js | 3 ++- mod/registry/registry.js | 7 +------ mod/registry/sip_listener.js | 2 +- 7 files changed, 11 insertions(+), 25 deletions(-) diff --git a/mod/core/processor/request_handler.js b/mod/core/processor/request_handler.js index fdd5a9be9..bfffa1c03 100644 --- a/mod/core/processor/request_handler.js +++ b/mod/core/processor/request_handler.js @@ -51,7 +51,7 @@ class RequestHandler { postal.subscribe({ channel: 'locator', topic: 'endpoint.find.reply', - callback: (data, envelope) => { + callback: data => { const requestInfo = requestStore.get(data.requestId) if (requestInfo === null) return @@ -104,11 +104,8 @@ class RequestHandler { } async processRoute (transaction, request, route, routeInfo) { - const transport = request - .getHeader(ViaHeader.NAME) - .getTransport() - .toLowerCase() - const lp = this.sipProvider.getListeningPoint(transport) + console.log('route => ', JSON.stringify(route)) + const lp = this.sipProvider.getListeningPoint(route.transport) const localAddr = { host: lp.getIPAddress().toString(), port: lp.getPort() } const advertisedAddr = getAdvertisedAddr(request, route, localAddr) @@ -116,7 +113,7 @@ class RequestHandler { let requestOut = configureMaxForwards(request) requestOut = configureProxyAuthorization(requestOut) requestOut = configureRoute(requestOut, localAddr) - requestOut = configureVia(requestOut, advertisedAddr) + requestOut = configureVia(requestOut, advertisedAddr, route) //requestOut = configureContact(requestOut) if (!isInDialog(request)) { diff --git a/mod/core/processor/request_utils.js b/mod/core/processor/request_utils.js index 14c299c0b..0118c6d96 100644 --- a/mod/core/processor/request_utils.js +++ b/mod/core/processor/request_utils.js @@ -116,19 +116,15 @@ const configureRoute = (request, localAddr) => { } return requestOut } -const configureVia = (request, advertisedAddr) => { - const requestOut = request.clone() - const transport = requestOut - .getHeader(ViaHeader.NAME) - .getTransport() - .toLowerCase() +const configureVia = (request, advertisedAddr, route) => { const viaHeader = headerFactory.createViaHeader( advertisedAddr.host, advertisedAddr.port, - transport, + route.transport, null ) viaHeader.setRPort() + const requestOut = request.clone() requestOut.addFirst(viaHeader) return requestOut } diff --git a/mod/location/locator.js b/mod/location/locator.js index a7cf1cfab..657840d3f 100644 --- a/mod/location/locator.js +++ b/mod/location/locator.js @@ -31,7 +31,6 @@ class Locator { addEndpoint (addressOfRecord, route) { // This must be done here before we convert contactURI into a string - const contactURI = LocatorUtils.aorAsString(route.contactURI) route.contactURI = route.contactURI.toString() LOG.debug( diff --git a/mod/location/route_loader.js b/mod/location/route_loader.js index e15407d4e..71d1af810 100644 --- a/mod/location/route_loader.js +++ b/mod/location/route_loader.js @@ -22,8 +22,6 @@ class RouteLoader { } getDomainEgressRoutes (domainsAPI, numbersAPI, gatewaysAPI) { - const SipFactory = Java.type('javax.sip.SipFactory') - const addressFactory = SipFactory.getInstance().createAddressFactory() const routes = new HashMap() const domains = domainsAPI.getDomains().data diff --git a/mod/location/utils.js b/mod/location/utils.js index 322d576f0..00b24052f 100644 --- a/mod/location/utils.js +++ b/mod/location/utils.js @@ -93,7 +93,8 @@ class LocatorUtils { gwHost: buildAddr(gateway.spec.host, gateway.spec.port), numberRef: number.metadata.ref, number: number.spec.location.telUrl.split(':')[1], - expires: -1 + expires: -1, + transport: gateway.spec.transport } if (domain) { route.rule = domain.spec.context.egressPolicy.rule diff --git a/mod/registry/registry.js b/mod/registry/registry.js index 09d016a4f..e8db709f6 100644 --- a/mod/registry/registry.js +++ b/mod/registry/registry.js @@ -17,12 +17,7 @@ const { protocolTransport, nearestInterface } = require('@routr/utils/misc_utils') -const { - isRegistered, - isExpired, - isStaticMode, - unregistered -} = require('@routr/registry/utils') +const { isExpired, unregistered } = require('@routr/registry/utils') const LogManager = Java.type('org.apache.logging.log4j.LogManager') const LOG = LogManager.getLogger() diff --git a/mod/registry/sip_listener.js b/mod/registry/sip_listener.js index b3205dae0..2f4831e88 100644 --- a/mod/registry/sip_listener.js +++ b/mod/registry/sip_listener.js @@ -28,9 +28,9 @@ function storeRegistry (store, gwRef, gwURI, expires) { host: gwURI.getHost(), ip: InetAddress.getByName(gwURI.getHost()).getHostAddress(), //expires: actualExpires, - expires: expires, registeredOn: Date.now(), gwRef: gwRef, + expires, gwURI: gwURI.toString() } store.withCollection('registry').put(gwURI.toString(), JSON.stringify(reg)) From b8edd7acdf52315b2fafdcca32c2c4d5484f5261 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Sat, 6 Mar 2021 20:16:50 -0500 Subject: [PATCH 09/16] Enabling tls,ws, and wss by default --- config/config.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/config.yml b/config/config.yml index 606abb512..2ea66ee28 100644 --- a/config/config.yml +++ b/config/config.yml @@ -5,3 +5,9 @@ spec: port: 5060 - protocol: udp port: 5060 + - protocol: tls + port: 5061 + - protocol: ws + port: 5062 + - protocol: wss + port: 5063 \ No newline at end of file From 83f3307f6eb9a382ae49f7132c9907b9aa7da0c5 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Sat, 6 Mar 2021 20:20:09 -0500 Subject: [PATCH 10/16] Minor fix --- mod/core/processor/request_handler.js | 104 ++++++++++++++------------ mod/location/locator.js | 2 +- package.json | 2 +- 3 files changed, 60 insertions(+), 48 deletions(-) diff --git a/mod/core/processor/request_handler.js b/mod/core/processor/request_handler.js index bfffa1c03..e815334b3 100644 --- a/mod/core/processor/request_handler.js +++ b/mod/core/processor/request_handler.js @@ -28,7 +28,7 @@ const { configureCSeq, isInDialog } = require('@routr/core/processor/request_utils') -const { getBridgingNote } = require('@routr/rtpengine/utils') +const { directionFromRequest } = require('@routr/rtpengine/utils') const { RoutingType } = require('@routr/core/routing_type') const ObjectId = Java.type('org.bson.types.ObjectId') const Request = Java.type('javax.sip.message.Request') @@ -104,56 +104,68 @@ class RequestHandler { } async processRoute (transaction, request, route, routeInfo) { - console.log('route => ', JSON.stringify(route)) - const lp = this.sipProvider.getListeningPoint(route.transport) - const localAddr = { host: lp.getIPAddress().toString(), port: lp.getPort() } - - const advertisedAddr = getAdvertisedAddr(request, route, localAddr) - - let requestOut = configureMaxForwards(request) - requestOut = configureProxyAuthorization(requestOut) - requestOut = configureRoute(requestOut, localAddr) - requestOut = configureVia(requestOut, advertisedAddr, route) - //requestOut = configureContact(requestOut) - - if (!isInDialog(request)) { - requestOut = configureRequestURI(requestOut, routeInfo, route) - requestOut = configurePrivacy(requestOut, routeInfo) - requestOut = configureIdentity(requestOut, route) - requestOut = configureXHeaders(requestOut, route) - requestOut = configureRecordRoute(requestOut, advertisedAddr, localAddr) - } + try { + // If the request autof dialog we wont have a route. Therefore, we get + // the transport from the request URI. + const transport = route + ? route.transport + : request.getRequestURI().getParameter('transport') + const lp = this.sipProvider.getListeningPoint(transport) + const localAddr = { + host: lp.getIPAddress().toString(), + port: lp.getPort() + } + const advertisedAddr = getAdvertisedAddr(request, route, localAddr) + + let requestOut = configureMaxForwards(request) + requestOut = configureProxyAuthorization(requestOut) + requestOut = configureRoute(requestOut, localAddr) + requestOut = configureVia(requestOut, advertisedAddr, transport) + //requestOut = configureContact(requestOut) + + if (!isInDialog(request)) { + requestOut = configureRequestURI(requestOut, routeInfo, route) + requestOut = configurePrivacy(requestOut, routeInfo) + requestOut = configureIdentity(requestOut, route) + requestOut = configureXHeaders(requestOut, route) + requestOut = configureRecordRoute(requestOut, advertisedAddr, localAddr) + } - if (routeInfo.getRoutingType() === RoutingType.DOMAIN_EGRESS_ROUTING) { - // XXX: Please document this situation :( - requestOut = configureCSeq(requestOut) - } + if (routeInfo.getRoutingType() === RoutingType.DOMAIN_EGRESS_ROUTING) { + // XXX: Please document this situation :( + requestOut = configureCSeq(requestOut) + } - LOG.debug( - `core.processor.RequestHandler.processRoute [advertised addr ${JSON.stringify( - advertisedAddr - )}]` - ) - LOG.debug( - `core.processor.RequestHandler.processRoute [route ${JSON.stringify( - route - )}]` - ) - - let bridgingNote - if (isInviteOrAck(requestOut) && hasSDP(requestOut)) { - bridgingNote = getBridgingNote(requestOut, route) - const obj = await RTPEngineConnector.offer( - bridgingNote, - extractRTPEngineParams(requestOut) + LOG.debug( + `core.processor.RequestHandler.processRoute [advertised addr ${JSON.stringify( + advertisedAddr + )}]` ) - requestOut.setContent( - obj.sdp, - requestOut.getHeader(ContentTypeHeader.NAME) + LOG.debug( + `core.processor.RequestHandler.processRoute [route ${JSON.stringify( + route + )}]` ) - } - this.sendRequest(transaction, request, requestOut, bridgingNote) + let bridgingNote + if (isInviteOrAck(request) && hasSDP(request)) { + // The note must be taken from the original request else it won't + // have the correct transport. + bridgingNote = directionFromRequest(request, route) + const obj = await RTPEngineConnector.offer( + bridgingNote, + extractRTPEngineParams(request) + ) + requestOut.setContent( + obj.sdp, + requestOut.getHeader(ContentTypeHeader.NAME) + ) + } + + this.sendRequest(transaction, request, requestOut, bridgingNote) + } catch (e) { + LOG.error(e) + } } sendRequest (serverTransaction, request, requestOut, bridgingNote) { diff --git a/mod/location/locator.js b/mod/location/locator.js index 407a5c335..3fc8a5567 100644 --- a/mod/location/locator.js +++ b/mod/location/locator.js @@ -76,8 +76,8 @@ class Locator { if (addressOfRecord.startsWith('tel:')) { return this.findEndpointByTelUrl(addressOfRecord) } else { - const tel = LocatorUtils.aorAsObj(addressOfRecord).getUser() try { + const tel = LocatorUtils.aorAsObj(addressOfRecord).getUser() const telE164 = phone(tel)[0] const response = this.findEndpointByTelUrl(`tel:${telE164}`) if (response.status === Status.OK) return response diff --git a/package.json b/package.json index 9404194ad..696212202 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "sinon-chai": "^3.5.0", "rewire": "^5.0.0", "sinon": "^9.0.1", - "webpack": "^4.11.1", + "webpack": "4.11.1", "webpack-command": "^0.2.0" }, "repository": { From 0d85f21cdb082e6278ee47f8f878c376efcbe7f7 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Sat, 6 Mar 2021 20:24:35 -0500 Subject: [PATCH 11/16] Fix media issues with sip.js --- mod/core/processor/response_processor.js | 84 ++++++++++++++++-------- mod/rtpengine/utils.js | 34 +++++++++- 2 files changed, 87 insertions(+), 31 deletions(-) diff --git a/mod/core/processor/response_processor.js b/mod/core/processor/response_processor.js index 3734e1ea7..3caf334da 100644 --- a/mod/core/processor/response_processor.js +++ b/mod/core/processor/response_processor.js @@ -20,6 +20,8 @@ const SipFactory = Java.type('javax.sip.SipFactory') const LogManager = Java.type('org.apache.logging.log4j.LogManager') const LOG = LogManager.getLogger() const headerFactory = SipFactory.getInstance().createHeaderFactory() +const { RTPBridgingNote } = require('@routr/rtpengine/rtp_bridging_note') +const { directionFromResponse } = require('@routr/rtpengine/utils') class ResponseProcessor { constructor (sipProvider, contextStorage) { @@ -47,43 +49,67 @@ class ResponseProcessor { } async sendResponse (event) { - const response = event.getResponse().clone() - const viaHeader = response.getHeader(ViaHeader.NAME) - const xReceivedHeader = headerFactory.createHeader( - 'X-Inf-Received', - viaHeader.getReceived() - ) - const xRPortHeader = headerFactory.createHeader( - 'X-Inf-RPort', - `${viaHeader.getRPort()}` - ) - response.addHeader(xReceivedHeader) - response.addHeader(xRPortHeader) - response.removeFirst(ViaHeader.NAME) - - if (isTransactional(event)) { - const context = this.contextStorage.findContext( - event.getClientTransaction().getBranchId() + try { + const response = event.getResponse().clone() + const bridgingNote = directionFromResponse(response) + const viaHeader = response.getHeader(ViaHeader.NAME) + const xReceivedHeader = headerFactory.createHeader( + 'X-Inf-Received', + viaHeader.getReceived() + ) + const xRPortHeader = headerFactory.createHeader( + 'X-Inf-RPort', + `${viaHeader.getRPort()}` ) + response.addHeader(xReceivedHeader) + response.addHeader(xRPortHeader) + response.removeFirst(ViaHeader.NAME) - if (isOk(response) && hasSDP(response)) { - const obj = await RTPEngineConnector.answer( - context.bridgingNote, - extractRTPEngineParams(response) + if (isTransactional(event)) { + const context = this.contextStorage.findContext( + event.getClientTransaction().getBranchId() ) - response.setContent(obj.sdp, response.getHeader(ContentTypeHeader.NAME)) - } - if (context && context.serverTransaction) { - context.serverTransaction.sendResponse(response) + // WARNINIG: We need to remove the SDP for response to WebRTC endpoints + // else we will get a Called with SDP without DTLS fingerprint + if ( + response.getStatusCode() === 183 && + bridgingNote === RTPBridgingNote.WEB_TO_SIP + ) + response.removeContent() + + if (isOk(response) && hasSDP(response)) { + const obj = await RTPEngineConnector.answer( + bridgingNote, + extractRTPEngineParams(response) + ) + + // WARNINIG: This patches an issue with RTPEngine where its not setting rtpmux + if (bridgingNote === RTPBridgingNote.WEB_TO_SIP) + obj.sdp = obj.sdp.replace( + 'a=setup:active', + 'a=setup:active\na=rtcp-mux' + ) + + response.setContent( + obj.sdp, + response.getHeader(ContentTypeHeader.NAME) + ) + } + + if (context && context.serverTransaction) { + context.serverTransaction.sendResponse(response) + } else if (response.getHeader(ViaHeader.NAME) !== null) { + this.sipProvider.sendResponse(response) + } } else if (response.getHeader(ViaHeader.NAME) !== null) { + // Could be a BYE due to Record-Route this.sipProvider.sendResponse(response) } - } else if (response.getHeader(ViaHeader.NAME) !== null) { - // Could be a BYE due to Record-Route - this.sipProvider.sendResponse(response) + LOG.debug(response) + } catch (e) { + LOG.error(e) } - LOG.debug(response) } } diff --git a/mod/rtpengine/utils.js b/mod/rtpengine/utils.js index afe1c5394..d2f2d1c88 100644 --- a/mod/rtpengine/utils.js +++ b/mod/rtpengine/utils.js @@ -6,18 +6,48 @@ const { RTPBridgingNote } = require('@routr/rtpengine/rtp_bridging_note') const ViaHeader = Java.type('javax.sip.header.ViaHeader') const isTransportWeb = t => t === 'ws' || t === 'wss' -module.exports.getBridgingNote = (request, route) => { - const destTransport = !route.thruGw ? route.transport.toLowerCase() : '' +module.exports.directionFromRequest = (request, route) => { + const destTransport = route.transport.toLowerCase() const srcTransport = request .getHeader(ViaHeader.NAME) .getTransport() .toLowerCase() + if (isTransportWeb(srcTransport) && isTransportWeb(destTransport)) return RTPBridgingNote.WEB_TO_WEB + if (isTransportWeb(srcTransport) && !isTransportWeb(destTransport)) return RTPBridgingNote.WEB_TO_SIP + if (!isTransportWeb(srcTransport) && !isTransportWeb(destTransport)) return RTPBridgingNote.SIP_TO_SIP + if (!isTransportWeb(srcTransport) && isTransportWeb(destTransport)) return RTPBridgingNote.SIP_TO_WEB } + +module.exports.directionFromResponse = response => { + const viaHeaders = response.getHeaders(ViaHeader.NAME) + if (viaHeaders.hasNext()) { + const viaHeaders = response.getHeaders(ViaHeader.NAME) + const srcTransport = viaHeaders + .next() + .getTransport() + .toLowerCase() + const destTransport = viaHeaders + .next() + .getTransport() + .toLowerCase() + + if (isTransportWeb(srcTransport) && isTransportWeb(destTransport)) + return RTPBridgingNote.WEB_TO_WEB + + if (isTransportWeb(srcTransport) && !isTransportWeb(destTransport)) + return RTPBridgingNote.SIP_TO_WEB + + if (!isTransportWeb(srcTransport) && isTransportWeb(destTransport)) + return RTPBridgingNote.WEB_TO_SIP + } + + return RTPBridgingNote.SIP_TO_SIP +} From 2d7a0875fb0e6512381c50f949106a0ca369070c Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Sun, 7 Mar 2021 12:11:55 -0500 Subject: [PATCH 12/16] Refactored --- mod/core/processor/request_utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/core/processor/request_utils.js b/mod/core/processor/request_utils.js index da2980fa7..08d455888 100644 --- a/mod/core/processor/request_utils.js +++ b/mod/core/processor/request_utils.js @@ -116,11 +116,11 @@ const configureRoute = (request, localAddr) => { } return requestOut } -const configureVia = (request, advertisedAddr, route) => { +const configureVia = (request, advertisedAddr, transport) => { const viaHeader = headerFactory.createViaHeader( advertisedAddr.host, advertisedAddr.port, - route.transport, + transport, null ) viaHeader.setRPort() From 32837cf20728044f9c73c786e166e36210d4149a Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Fri, 12 Mar 2021 09:28:45 -0500 Subject: [PATCH 13/16] Improved support for rfc5658 --- mod/core/processor/request_handler.js | 37 ++++++++++++++++------ mod/core/processor/request_utils.js | 44 ++++++++++++++++----------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/mod/core/processor/request_handler.js b/mod/core/processor/request_handler.js index e815334b3..2585c6982 100644 --- a/mod/core/processor/request_handler.js +++ b/mod/core/processor/request_handler.js @@ -105,22 +105,41 @@ class RequestHandler { async processRoute (transaction, request, route, routeInfo) { try { - // If the request autof dialog we wont have a route. Therefore, we get - // the transport from the request URI. - const transport = route + const lpTransport = request + .getHeader(ViaHeader.NAME) + .getTransport() + .toLowerCase() + const targetTransport = route ? route.transport - : request.getRequestURI().getParameter('transport') - const lp = this.sipProvider.getListeningPoint(transport) + : request + .getRequestURI() + .getParameter('transport') + .toLowerCase() + + const lp = this.sipProvider.getListeningPoint(lpTransport) const localAddr = { host: lp.getIPAddress().toString(), - port: lp.getPort() + port: lp.getPort(), + transport: lpTransport } - const advertisedAddr = getAdvertisedAddr(request, route, localAddr) + const advertisedAddr = getAdvertisedAddr( + request, + route, + localAddr, + targetTransport + ) + + LOG.debug( + `core.processor.RequestHandler.processRoute [targetTransport = ${targetTransport}]` + ) + LOG.debug( + `core.processor.RequestHandler.processRoute [lpTransport = ${lpTransport}]` + ) let requestOut = configureMaxForwards(request) requestOut = configureProxyAuthorization(requestOut) requestOut = configureRoute(requestOut, localAddr) - requestOut = configureVia(requestOut, advertisedAddr, transport) + requestOut = configureVia(requestOut, advertisedAddr, targetTransport) //requestOut = configureContact(requestOut) if (!isInDialog(request)) { @@ -128,7 +147,7 @@ class RequestHandler { requestOut = configurePrivacy(requestOut, routeInfo) requestOut = configureIdentity(requestOut, route) requestOut = configureXHeaders(requestOut, route) - requestOut = configureRecordRoute(requestOut, advertisedAddr, localAddr) + requestOut = configureRecordRoute(requestOut, localAddr, advertisedAddr) } if (routeInfo.getRoutingType() === RoutingType.DOMAIN_EGRESS_ROUTING) { diff --git a/mod/core/processor/request_utils.js b/mod/core/processor/request_utils.js index 08d455888..d57166328 100644 --- a/mod/core/processor/request_utils.js +++ b/mod/core/processor/request_utils.js @@ -34,7 +34,7 @@ const ownedAddresss = localAddr => } ] : [localAddr] -const getAdvertisedAddr = (request, route, localAddr) => { +const getAdvertisedAddr = (request, route, localAddr, targetTransport) => { // After the initial invite the route object will be null // and we need to the the target address from the request uri. // If the routing is type IDR the initial request uri will be a local @@ -44,9 +44,13 @@ const getAdvertisedAddr = (request, route, localAddr) => { ? LocatorUtils.aorAsObj(route.contactURI).getHost() : request.getRequestURI().getHost() const externAddr = config.spec.externAddr - return config.spec.externAddr && needsExternAddress(route, targetAddr) - ? { host: addrHost(externAddr), port: addrPort(externAddr, localAddr) } - : localAddr + return externAddr && needsExternAddress(route, targetAddr) + ? { + host: addrHost(externAddr), + port: addrPort(externAddr, localAddr), + transport: targetTransport + } + : { host: localAddr.host, port: localAddr.port, transport: targetTransport } } const getToUser = request => { const toHeader = request.getHeader(ToHeader.NAME) @@ -128,27 +132,31 @@ const configureVia = (request, advertisedAddr, transport) => { requestOut.addFirst(viaHeader) return requestOut } -const configureRecordRoute = (request, advertisedAddr, localAddr) => { + +// rfc5658 +const configureRecordRoute = (request, localAddr, advertisedAddr) => { const requestOut = request.clone() - if (config.spec.recordRoute) { + const viaHeader = request.getHeaders(ViaHeader.NAME).next() + const transport = viaHeader.getTransport().toLowerCase() + + if (config.spec.recordRoute || transport === 'ws' || transport === 'wss') { + // First we need the input interface from the top ViaHeader const p1 = addressFactory.createSipURI(null, localAddr.host) - p1.setLrParam() p1.setPort(localAddr.port) + p1.setTransportParam(localAddr.transport) + p1.setLrParam() const pa1 = addressFactory.createAddress(p1) const rr1 = headerFactory.createRecordRouteHeader(pa1) requestOut.addHeader(rr1) - if (config.spec.externAddr && isPublicAddress(advertisedAddr.host)) { - const p2 = addressFactory.createSipURI( - null, - addrHost(config.spec.externAddr) - ) - p2.setLrParam() - p2.setPort(addrPort(config.spec.externAddr, localAddr)) - const pa2 = addressFactory.createAddress(p2) - const rr2 = headerFactory.createRecordRouteHeader(pa2) - requestOut.addFirst(rr2) - } + // Then we get the advertisedAddr + const p2 = addressFactory.createSipURI(null, advertisedAddr.host) + p2.setLrParam() + p2.setTransportParam(advertisedAddr.transport) + p2.setPort(advertisedAddr.port) + const pa2 = addressFactory.createAddress(p2) + const rr2 = headerFactory.createRecordRouteHeader(pa2) + requestOut.addLast(rr2) } return requestOut } From df287e8f1b40fd6ab8a419cb3b2508d40391e557 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Sat, 13 Mar 2021 16:00:13 -0500 Subject: [PATCH 14/16] Refactored rtpengine configuration schema --- etc/schemas/config_schema.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/etc/schemas/config_schema.json b/etc/schemas/config_schema.json index 9fc8885df..092298084 100644 --- a/etc/schemas/config_schema.json +++ b/etc/schemas/config_schema.json @@ -58,13 +58,14 @@ "trustStorePassword": { "type": "string" } } }, - "ex_mediaEngine": { + "ex_rtpEngine": { "type": "object", "properties": { - "provider": { "type": "string" }, "port": { "type": "integer" }, - "host": { "type": "string" } - } + "host": { "type": "string" }, + "proto": { "type": "string" } + }, + "required": ["host"] }, "dataSource": { "type": "object", From 9470450c6284d2f989f9daa3681e55ca436d384b Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Sat, 13 Mar 2021 16:05:54 -0500 Subject: [PATCH 15/16] Added experimental support for rtpengine --- mod/core/config/config_defaults.js | 34 ++++++- mod/core/config/config_from_env.js | 6 +- mod/core/processor/request_handler.js | 19 +++- mod/core/processor/response_processor.js | 12 ++- mod/core/server.js | 3 +- mod/rtpengine/connector.js | 119 +++++++---------------- mod/rtpengine/ng_http_sender.js | 42 ++++++++ mod/rtpengine/utils.js | 21 ++++ package.json | 9 +- 9 files changed, 166 insertions(+), 99 deletions(-) create mode 100644 mod/rtpengine/ng_http_sender.js diff --git a/mod/core/config/config_defaults.js b/mod/core/config/config_defaults.js index e569e0e7e..67c89bb90 100644 --- a/mod/core/config/config_defaults.js +++ b/mod/core/config/config_defaults.js @@ -23,9 +23,37 @@ module.exports = upSince => { dataSource: { provider: 'files_data_provider' }, - ex_mediaEngine: { - provider: 'rtpengine', - port: 22222 + ex_rtpEngine: { + enabled: false, + proto: 'http', + port: 8080, + bridgeParams: { + webToWeb: { + ICE: 'force', + SDES: 'off', + flags: 'trust-address replace-origin replace-session-connection' + }, + webToSip: { + 'transport-protocol': 'RTP/AVP', + 'rtcp-mux': 'demux', + ICE: 'remove', + flags: 'trust-address replace-origin replace-session-connection' + }, + sipToWeb: { + 'transport-protocol': 'UDP/TLS/RTP/SAVP', + 'rtcp-mux': 'offer', + ICE: 'force', + SDES: 'off', + flags: + 'trust-address replace-origin replace-session-connection generate-mid' + }, + sipToSip: { + 'transport-protocol': 'RTP/AVP', + 'rtcp-mux': 'demux', + ICE: 'remove', + flags: 'trust-address replace-origin replace-session-connection' + } + } }, registrarIntf: 'External', restService: { diff --git a/mod/core/config/config_from_env.js b/mod/core/config/config_from_env.js index 0f7fa050c..6ed7059b2 100644 --- a/mod/core/config/config_from_env.js +++ b/mod/core/config/config_from_env.js @@ -45,8 +45,10 @@ envsMap.set( 'spec.securityContext.client.authType' ) envsMap.set('SECURITY_CONTEXT_DEBUGGING', 'spec.securityContext.debugging') -envsMap.set('EX_MEDIA_ENGINE_HOST', 'spec.ex_mediaEngine.host') -envsMap.set('EX_MEDIA_ENGINE_PORT', 'spec.ex_mediaEngine.port') +envsMap.set('EX_RTP_ENGINE_ENABLED', 'spec.ex_rtpEngine.enabled') +envsMap.set('EX_RTP_ENGINE_PROTO', 'spec.ex_rtpEngine.proto') +envsMap.set('EX_RTP_ENGINE_HOST', 'spec.ex_rtpEngine.host') +envsMap.set('EX_RTP_ENGINE_PORT', 'spec.ex_rtpEngine.port') envsMap.set('LOG4J', '') envsMap.set('CONFIG_FILE', '') envsMap.set('SALT', '') diff --git a/mod/core/processor/request_handler.js b/mod/core/processor/request_handler.js index 2585c6982..41cbde25f 100644 --- a/mod/core/processor/request_handler.js +++ b/mod/core/processor/request_handler.js @@ -12,6 +12,9 @@ const { Status } = require('@routr/core/status') const config = require('@routr/core/config_util')() const RTPEngineConnector = require('@routr/rtpengine/connector') const ContentTypeHeader = Java.type('javax.sip.header.ContentTypeHeader') +const CallIdHeader = Java.type('javax.sip.header.CallIdHeader') +const FromHeader = Java.type('javax.sip.header.FromHeader') +const Request = Java.type('javax.sip.message.Request') const postal = require('postal') const { @@ -47,6 +50,8 @@ class RequestHandler { constructor (sipProvider, contextStorage) { this.sipProvider = sipProvider this.contextStorage = contextStorage + if (config.spec.ex_rtpEngine.enabled) + this.rtpeConnector = new RTPEngineConnector(config.spec.ex_rtpEngine) postal.subscribe({ channel: 'locator', @@ -167,11 +172,15 @@ class RequestHandler { ) let bridgingNote - if (isInviteOrAck(request) && hasSDP(request)) { + if ( + config.spec.ex_rtpEngine.enabled && + isInviteOrAck(request) && + hasSDP(request) + ) { // The note must be taken from the original request else it won't // have the correct transport. bridgingNote = directionFromRequest(request, route) - const obj = await RTPEngineConnector.offer( + const obj = await this.rtpeConnector.offer( bridgingNote, extractRTPEngineParams(request) ) @@ -181,6 +190,12 @@ class RequestHandler { ) } + if (request.getMethod() === Request.BYE) { + const callId = request.getHeader(CallIdHeader.NAME).getCallId() + const fromTag = request.getHeader(FromHeader.NAME).getTag() + await this.rtpeConnector.delete(callId, fromTag) + } + this.sendRequest(transaction, request, requestOut, bridgingNote) } catch (e) { LOG.error(e) diff --git a/mod/core/processor/response_processor.js b/mod/core/processor/response_processor.js index 3caf334da..80bb559ee 100644 --- a/mod/core/processor/response_processor.js +++ b/mod/core/processor/response_processor.js @@ -22,12 +22,15 @@ const LOG = LogManager.getLogger() const headerFactory = SipFactory.getInstance().createHeaderFactory() const { RTPBridgingNote } = require('@routr/rtpengine/rtp_bridging_note') const { directionFromResponse } = require('@routr/rtpengine/utils') +const config = require('@routr/core/config_util')() class ResponseProcessor { constructor (sipProvider, contextStorage) { this.sipProvider = sipProvider this.contextStorage = contextStorage this.gatewaysAPI = new GatewaysAPI(DSSelector.getDS()) + if (config.spec.ex_rtpEngine.enabled) + this.rtpeConnector = new RTPEngineConnector(config.spec.ex_rtpEngine) } process (event) { @@ -73,13 +76,18 @@ class ResponseProcessor { // WARNINIG: We need to remove the SDP for response to WebRTC endpoints // else we will get a Called with SDP without DTLS fingerprint if ( + config.spec.ex_rtpEngine.enabled && response.getStatusCode() === 183 && bridgingNote === RTPBridgingNote.WEB_TO_SIP ) response.removeContent() - if (isOk(response) && hasSDP(response)) { - const obj = await RTPEngineConnector.answer( + if ( + config.spec.ex_rtpEngine.enabled && + isOk(response) && + hasSDP(response) + ) { + const obj = await this.rtpeConnector.answer( bridgingNote, extractRTPEngineParams(response) ) diff --git a/mod/core/server.js b/mod/core/server.js index c5c9dfb4e..8d9383edc 100644 --- a/mod/core/server.js +++ b/mod/core/server.js @@ -13,7 +13,6 @@ const Processor = require('@routr/core/processor/processor') const Locator = require('@routr/location/locator') const ContextStorage = require('@routr/core/context_storage') const showExternInfo = require('@routr/core/extern_info') -const RTPEngineConnector = require('@routr/rtpengine/connector') const config = require('@routr/core/config_util')() const properties = require('@routr/core/server_properties')(config) const ExceptionUtils = Java.type( @@ -50,7 +49,7 @@ class Server { this.dataAPIs = dataAPIs this.locator = new Locator() - new RTPEngineConnector() + //new RTPEngineConnector() } buildSipProvider (sipStack, transport) { diff --git a/mod/rtpengine/connector.js b/mod/rtpengine/connector.js index b19a5c65d..f2e5102d8 100644 --- a/mod/rtpengine/connector.js +++ b/mod/rtpengine/connector.js @@ -5,115 +5,66 @@ * @since v1 */ const merge = require('deepmerge') -const postal = require('postal') -const config = require('@routr/core/config_util')() const { RTPBridgingNote } = require('@routr/rtpengine/rtp_bridging_note') - const LogManager = Java.type('org.apache.logging.log4j.LogManager') const LOG = LogManager.getLogger() -const Unirest = Java.type('com.mashape.unirest.http.Unirest') -const rtpeBaseUrl = `http://${config.spec.ex_mediaEngine.host}:${ - config.spec.ex_mediaEngine.port -}/api` - -const webToWeb = { - ICE: 'force', - SDES: 'off', - flags: 'trust-address replace-origin replace-session-connection' -} - -const webToSip = { - 'transport-protocol': 'RTP/AVP', - 'rtcp-mux': 'demux', - ICE: 'remove', - flags: 'trust-address replace-origin replace-session-connection' -} - -const sipToWeb = { - 'transport-protocol': 'UDP/TLS/RTP/SAVP', - 'rtcp-mux': 'offer', - ICE: 'force', - SDES: 'off', - flags: 'trust-address replace-origin replace-session-connection generate-mid' -} - -const sipToSip = { - 'transport-protocol': 'RTP/AVP', - 'rtcp-mux': 'demux', - ICE: 'remove', - flags: 'trust-address replace-origin replace-session-connection' -} +const NGHttpSender = require('./ng_http_sender') +const postal = require('postal') class RTPEngineConnector { - constructor () { + constructor (config) { LOG.debug(`rtpengine.RTPEngineConnector connector is up`) - // This is not a good criteria to delete the binding. - // It should happend on a Bye or Cancel or Timeout + // This shouldn't be need because there is no dialog stablish + // The call will be removed afeter a timeout /*postal.subscribe({ channel: 'processor', - topic: 'transaction.terminated', - callback: data => this.deleteCallBinding(data.callId, data.fromTag) + topic: 'transaction.cancel', + callback: async(data) => { + await this.delete(data.callId, data.fromTag) + } })*/ + this.sender = new NGHttpSender( + `${config.proto}://${config.host}:${config.port}/ng` + ) + this.bridgeParams = config.bridgeParams } - deleteCallBinding (callId, fromTag) { - return RTPEngineConnector.sendCmd(`delete/${callId}`, { + getBridgingInfo (bridgingNote, offer = true) { + const bridgeParams = this.bridgeParams + switch (bridgingNote) { + case RTPBridgingNote.WEB_TO_WEB: + return bridgeParams.webToWeb + case RTPBridgingNote.WEB_TO_SIP: + return offer ? bridgeParams.webToSip : bridgeParams.sipToWeb + case RTPBridgingNote.SIP_TO_WEB: + return offer ? bridgeParams.sipToWeb : bridgeParams.webToSip + default: + return bridgeParams.sipToSip + } + } + + async delete (callId, fromTag) { + return await this.sender.sendCmd('delete', { 'call-id': callId, 'from-tag': fromTag }) } - static async offer (bridgingNote, params) { + async offer (bridgingNote, params) { LOG.debug( `rtpengine.RTPEngineConnector.offer [bridging note: ${bridgingNote}]` ) - - if (bridgingNote === RTPBridgingNote.WEB_TO_WEB) { - params = merge(params, webToWeb) - } else if (bridgingNote === RTPBridgingNote.WEB_TO_SIP) { - params = merge(params, webToSip) - } else if (bridgingNote === RTPBridgingNote.SIP_TO_WEB) { - params = merge(params, sipToWeb) - } else { - params = merge(params, sipToSip) - } - - return await RTPEngineConnector.sendCmd('offer', params) + const p = merge(params, this.getBridgingInfo(bridgingNote, true)) + return await this.sender.sendCmd('offer', p) } - static async answer (bridgingNote, params) { + async answer (bridgingNote, params) { LOG.debug( `rtpengine.RTPEngineConnector.answer [bridging note: ${bridgingNote}]` ) - - if (bridgingNote === RTPBridgingNote.WEB_TO_WEB) { - params = merge(params, webToWeb) - } else if (bridgingNote === RTPBridgingNote.SIP_TO_WEB) { - params = merge(params, webToSip) - } else if (bridgingNote === RTPBridgingNote.WEB_TO_SIP) { - params = merge(params, sipToWeb) - } else { - params = merge(params, sipToSip) - } - - return await RTPEngineConnector.sendCmd('answer', params) - } - - static async sendCmd (cmd, params) { - try { - LOG.debug( - `rtpengine.RTPEngineConnector.${cmd} [call-id: ${params['call-id']}]` - ) - - const res = await Unirest.post(`${rtpeBaseUrl}/${cmd}`) - .header('Content-Type', 'application/json') - .body(JSON.stringify(params)) - .asString() - return JSON.parse(res.getBody()) - } catch (e) { - LOG.error(e) - } + const p = merge(params, this.getBridgingInfo(bridgingNote, false)) + return await this.sender.sendCmd('answer', p) } } diff --git a/mod/rtpengine/ng_http_sender.js b/mod/rtpengine/ng_http_sender.js new file mode 100644 index 000000000..ec56a1a0d --- /dev/null +++ b/mod/rtpengine/ng_http_sender.js @@ -0,0 +1,42 @@ +const Unirest = Java.type('com.mashape.unirest.http.Unirest') +const LogManager = Java.type('org.apache.logging.log4j.LogManager') +const LOG = LogManager.getLogger() +const { benEncode, benDecode } = require('./utils') + +let cnt = 0 + +class NGHttpSender { + constructor (baseUrl) { + this.baseUrl = baseUrl + } + + async sendCmd (cmd, params) { + try { + LOG.debug( + `NGHttpSender.sendCmd cmd-${cmd} [call-id: ${params['call-id']}]` + ) + + params.command = cmd + + // The bencode id must be unique. So we add this value to avoid collition + cnt++ + + const res = await Unirest.post(`${this.baseUrl}`) + .header('Content-Type', 'application/x-rtpengine-ng') + .body(benEncode(params['call-id'] + cnt, params)) + .asString() + + LOG.debug( + `NGHttpSender.sendCmd cmd-${cmd} [results ${JSON.stringify( + benDecode(res.getBody()) + )}]` + ) + + return benDecode(res.getBody()).data + } catch (e) { + LOG.error(e) + } + } +} + +module.exports = NGHttpSender diff --git a/mod/rtpengine/utils.js b/mod/rtpengine/utils.js index d2f2d1c88..014c25aa5 100644 --- a/mod/rtpengine/utils.js +++ b/mod/rtpengine/utils.js @@ -4,7 +4,10 @@ */ const { RTPBridgingNote } = require('@routr/rtpengine/rtp_bridging_note') const ViaHeader = Java.type('javax.sip.header.ViaHeader') +const bencode = require('bencode') const isTransportWeb = t => t === 'ws' || t === 'wss' +const LogManager = Java.type('org.apache.logging.log4j.LogManager') +const LOG = LogManager.getLogger() module.exports.directionFromRequest = (request, route) => { const destTransport = route.transport.toLowerCase() @@ -51,3 +54,21 @@ module.exports.directionFromResponse = response => { return RTPBridgingNote.SIP_TO_SIP } + +module.exports.benDecode = msg => { + const m = msg.toString() + const idx = m.indexOf(' ') + + if (-1 !== idx) { + const id = m.substring(0, idx) + try { + const data = bencode.decode(Buffer.from(m.substring(idx + 1)), 'utf-8') + return { id, data } + } catch (err) { + LOG.error(err) + } + } +} + +module.exports.benEncode = (id, data) => + Buffer.from([id, bencode.encode(data)].join(' ')) diff --git a/package.json b/package.json index 696212202..e17f70b79 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ } }, "dependencies": { + "bencode": "^2.0.1", "deepmerge": "^4.2.2", "flat": "^5.0.0", "ip-utils": "^2.4.0", @@ -37,19 +38,19 @@ "xxhashjs": "^0.2.2" }, "devDependencies": { - "source-map-loader": "^0.2.4", - "awesome-typescript-loader": "^5.2.1", "@types/node": "^13.13.2", + "awesome-typescript-loader": "^5.2.1", + "chai": "^4.2.0", "cross-env": "^5.2.0", "husky": "^4.2.3", "jvm-npm": "^0.1.1", "mocha": "^6.2.0", "nyc": "^14.1.1", "prettier-standard": "^9.1.1", - "chai": "^4.2.0", - "sinon-chai": "^3.5.0", "rewire": "^5.0.0", "sinon": "^9.0.1", + "sinon-chai": "^3.5.0", + "source-map-loader": "^0.2.4", "webpack": "4.11.1", "webpack-command": "^0.2.0" }, From 82fe179e70e71a7968216d316918d388f2f1e108 Mon Sep 17 00:00:00 2001 From: Pedro Sanders Date: Sat, 13 Mar 2021 16:29:05 -0500 Subject: [PATCH 16/16] Merged changes to helm from ex_synth_reg --- .helm/Chart.yaml | 2 +- .helm/README.md | 1 + .helm/templates/deployment.yaml | 2 ++ .helm/templates/service.yml | 12 ++++++++++++ .helm/values.yaml | 6 ++++-- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.helm/Chart.yaml b/.helm/Chart.yaml index c5e9217d6..e345e231f 100644 --- a/.helm/Chart.yaml +++ b/.helm/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: routr description: Next-generation SIP Server type: application -version: 0.0.6 +version: 0.0.7 appVersion: 1.0.0-rc5 dependencies: - name: redis diff --git a/.helm/README.md b/.helm/README.md index b279e25c6..a56ed9cea 100644 --- a/.helm/README.md +++ b/.helm/README.md @@ -92,6 +92,7 @@ The following table lists the configurable parameters of the Routr chart and the | routr.localnets | Local networks in CIDR format. Use in combination with `externAddr` | [] | | routr.recordRoute | Stay within the signaling path | `false` | | routr.useToAsAOR | Uses the `To` header, instead of `Request-URI`, to locate endpoints | `false` | +| routr.patchRequestURI | Uses the user part of the `To` header to ammend the `Request-URI` if it doesn't have user| `false` | | routr.registrarIntf | `Internal` causes the server to use the IP and port it "sees"(received & rport) from a device attempting to register | `External` | | routr.accessControlList.deny | Deny incoming traffic from network list. Must be valid CIDR values | [] | | routr.accessControlList.allow | Allow incoming traffic from network list. Must be valid CIDR values | [] | diff --git a/.helm/templates/deployment.yaml b/.helm/templates/deployment.yaml index 2266cf71a..c3013e1fc 100644 --- a/.helm/templates/deployment.yaml +++ b/.helm/templates/deployment.yaml @@ -48,6 +48,8 @@ spec: value: {{ .Values.routr.recordRoute | quote }} - name: USE_TO_AS_AOR value: {{ .Values.routr.useToAsAOR | quote }} + - name: PATCH_REQUEST_URI + value: {{ .Values.routr.patchRequestURI | quote }} - name: REGISTRAR_INTF value: {{ .Values.routr.registrarIntf | quote }} - name: ACCESS_CONTROL_LIST_ALLOW diff --git a/.helm/templates/service.yml b/.helm/templates/service.yml index 6b3fe7ef6..e756d31ba 100644 --- a/.helm/templates/service.yml +++ b/.helm/templates/service.yml @@ -6,6 +6,10 @@ metadata: labels: {{- include ".helm2.labels" . | nindent 4 }} namespace: {{ .Release.Namespace }} +{{- with .Values.adminService.annotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} spec: type: {{ .Values.adminService.type }} ports: @@ -25,6 +29,10 @@ metadata: labels: {{- include ".helm2.labels" . | nindent 4 }} namespace: {{ .Release.Namespace }} +{{- with .Values.udpSignalingService.annotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} spec: type: {{ .Values.udpSignalingService.type }} {{- if ne .Values.udpSignalingService.type "ClusterIP" }} @@ -48,6 +56,10 @@ metadata: labels: {{- include ".helm2.labels" . | nindent 4 }} namespace: {{ .Release.Namespace }} +{{- with .Values.tcpSignalingService.annotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} spec: type: {{ .Values.tcpSignalingService.type }} {{- if ne .Values.tcpSignalingService.type "ClusterIP" }} diff --git a/.helm/values.yaml b/.helm/values.yaml index dc12d0c3b..6a77cfd2c 100644 --- a/.helm/values.yaml +++ b/.helm/values.yaml @@ -15,6 +15,7 @@ adminService: name: api type: ClusterIP port: 4567 + annotations: {} # Use this service to enable UDP access to the server's signaling # capabilities. Keep in mind that this will create the service for you @@ -25,6 +26,7 @@ udpSignalingService: name: sipudp type: ClusterIP port: 5060 + annotations: {} # Use this service to enable TCP access to the server's signaling # capabilities. Keep in mind that this will create the service for you @@ -37,6 +39,7 @@ tcpSignalingService: ports: - name: siptcp port: 5060 + annotations: {} # Routr internal configurations routr: @@ -46,6 +49,7 @@ routr: localnets: [] recordRoute: false useToAsAOR: false + patchRequestURI: false registrarIntf: External accessControlList: deny: [] @@ -80,8 +84,6 @@ redis: mountPath: /bitnami/redis size: 5Gi -# Omiting this for now - replicaCount: 1 nameOverride: "" fullnameOverride: ""