Compare commits

..
8 Commits
Author SHA1 Message Date
Dave Horton c717470c5e version bump 2021-12-02 19:20:57 -05:00
Dave Horton f16598e144 Feature/sip refer (#18)
* support for sip refer to transfer an incoming call

* when sending REFER for call transfer, format Refer-To with carrier trunk if applicable

* better handling of e164 on Refer-To header
2021-11-20 11:41:12 -05:00
Dave Horton 8584020d4c add support for proxies that add X-Forwarded-For 2021-11-05 09:29:12 -04:00
Dave Horton 0b1b49bd59 Dockerfile 2021-11-04 12:57:15 -04:00
Dave Horton e22b2ae63f version bump 2021-11-03 13:51:50 -04:00
Dave Horton 2a5200673d bump version 2021-10-21 13:08:53 -04:00
Dave Horton 13ca308df7 bump version 2021-10-21 13:01:04 -04:00
Dave Horton 981788dc58 Feature/minimal media anchoring (#11)
* add support for sitting behind a sip proxy that adds X-Forwarded-For header

* feature: release media from freeswitch

* handle mute/unmute

* fix for relaying INFO

* support for relaying dtmf via SIP INFO to FS

* deps
2021-10-21 12:00:02 -04:00
7 changed files with 119 additions and 63 deletions
+2 -8
View File
@@ -1,16 +1,10 @@
FROM node:alpine as builder
RUN apk update && apk add --no-cache python make g++
FROM node:16
WORKDIR /opt/app/
COPY package.json ./
RUN npm install
RUN npm prune
FROM node:alpine as app
WORKDIR /opt/app
COPY . /opt/app
COPY --from=builder /opt/app/node_modules ./node_modules
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
CMD [ "npm", "start" ]
CMD [ "npm", "start" ]
+12 -1
View File
@@ -83,7 +83,18 @@ srf.locals = {...srf.locals,
retrieveSet
}
};
srf.locals.getFeatureServer = require('./lib/fs-tracking')(srf, logger);
const {
wasOriginatedFromCarrier,
getApplicationForDidAndCarrier,
getOutboundGatewayForRefer
} = require('./lib/db-utils')(srf, logger);
srf.locals = {
...srf.locals,
wasOriginatedFromCarrier,
getApplicationForDidAndCarrier,
getOutboundGatewayForRefer,
getFeatureServer: require('./lib/fs-tracking')(srf, logger)
};
const activeCallIds = srf.locals.activeCallIds;
const {
+35 -4
View File
@@ -1,7 +1,7 @@
const Emitter = require('events');
const {makeRtpEngineOpts, SdpWantsSrtp, makeCallCountKey} = require('./utils');
const {forwardInDialogRequests} = require('drachtio-fn-b2b-sugar');
const {parseUri, SipError} = require('drachtio-srf');
const {parseUri, stringifyUri, SipError} = require('drachtio-srf');
const debug = require('debug')('jambonz:sbc-inbound');
const MS_TEAMS_USER_AGENT = 'Microsoft.PSTNHub.SIPProxy';
const MS_TEAMS_SIP_ENDPOINT = 'sip.pstnhub.microsoft.com';
@@ -469,11 +469,42 @@ Duration=${payload.duration} `
try {
const referTo = req.getParsedHeader('Refer-To');
const uri = parseUri(referTo.uri);
this.logger.info({uri, referTo}, 'received REFER from feature server');
this.logger.info({uri, referTo, headers: req.headers}, 'received REFER from feature server');
const arr = /context-(.*)/.exec(uri.user);
if (!arr) {
this.logger.info(`invalid Refer-To header: ${referTo.uri}`);
return res.send(501);
/* call transfer requested */
const {gateway} = this.req.locals;
const referredBy = req.getParsedHeader('Referred-By');
if (!referredBy) return res.send(400);
const u = parseUri(referredBy.uri);
let selectedGateway = false;
let e164 = false;
if (gateway) {
/* host of Refer-to to an outbound gateway */
const gw = await this.srf.locals.getOutboundGatewayForRefer(gateway.voip_carrier_sid);
if (gw) {
selectedGateway = true;
e164 = gw.e164_leading_plus;
uri.host = gw.ipv4;
uri.port = gw.port;
}
}
if (!selectedGateway) {
uri.host = this.req.source_address;
uri.port = this.req.source_port;
}
if (e164 && !uri.user.startsWith('+')) {
uri.user = `+${uri.user}`;
}
const response = await this.uas.request({
method: 'REFER',
headers: {
'Refer-To': stringifyUri(uri),
'Referred-By': stringifyUri(u)
}
});
return res.send(response.status);
}
res.send(202);
+22 -1
View File
@@ -35,6 +35,13 @@ SELECT * FROM phone_numbers
WHERE number = ?
AND voip_carrier_sid = ?`;
const sqlSelectOutboundGatewayForCarrier = `
SELECT ipv4, port, e164_leading_plus
FROM sip_gateways sg, voip_carriers vc
WHERE sg.voip_carrier_sid = ?
AND sg.voip_carrier_sid = vc.voip_carrier_sid
AND outbound = 1`;
const gatewayMatchesSourceAddress = (source_address, gw) => {
if (32 === gw.netmask && gw.ipv4 === source_address) return true;
if (gw.netmask < 32) {
@@ -48,6 +55,19 @@ module.exports = (srf, logger) => {
const {pool} = srf.locals.dbHelpers;
const pp = pool.promise();
const getOutboundGatewayForRefer = async(voip_carrier_sid) => {
try {
const [r] = await pp.query(sqlSelectOutboundGatewayForCarrier, [voip_carrier_sid]);
if (0 === r.length) return null;
/* if multiple, prefer a DNS name */
const hasDns = r.find((row) => row.ipv4.match(/^[A-Za-z]/));
return hasDns || r[0];
} catch (err) {
logger.error({err}, 'getOutboundGatewayForRefer');
}
};
const getApplicationForDidAndCarrier = async(req, voip_carrier_sid) => {
const did = normalizeDID(req.calledNumber);
@@ -125,6 +145,7 @@ module.exports = (srf, logger) => {
return {
wasOriginatedFromCarrier,
getApplicationForDidAndCarrier
getApplicationForDidAndCarrier,
getOutboundGatewayForRefer
};
};
+4 -5
View File
@@ -68,15 +68,13 @@ module.exports = function(srf, logger) {
blacklistUnknownRealms: true,
emitter: new AuthOutcomeReporter(stats)
});
const {wasOriginatedFromCarrier, getApplicationForDidAndCarrier} = require('./db-utils')(srf, logger);
const initLocals = (req, res, next) => {
req.locals = req.locals || {};
/* check if forwarded by a proxy that applied an X-Forwarded-For Header */
if (req.has('X-Subspace-Forwarded-For')) {
const original_source_address = req.get('X-Subspace-Forwarded-For');
if (req.has('X-Forwarded-For') || req.has('X-Subspace-Forwarded-For')) {
const original_source_address = req.get('X-Forwarded-For') || req.get('X-Subspace-Forwarded-For');
logger.info({
callId: req.get('Call-ID'),
original_source_address,
@@ -113,7 +111,7 @@ module.exports = function(srf, logger) {
const identifyAccount = async(req, res, next) => {
try {
const {wasOriginatedFromCarrier, getApplicationForDidAndCarrier} = req.srf.locals;
const {fromCarrier, gateway, account_sid, application_sid, account} = await wasOriginatedFromCarrier(req);
/**
* calls come from 3 sources:
@@ -133,6 +131,7 @@ module.exports = function(srf, logger) {
req.locals = {
originator: 'trunk',
carrier: gateway.name,
gateway,
application_sid: sid || gateway.application_sid,
account_sid,
account,
+41 -41
View File
@@ -1,17 +1,17 @@
{
"name": "sbc-inbound",
"version": "0.3.6",
"version": "v0.6.7-rc8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "0.3.6",
"version": "v0.6.7-rc8",
"license": "MIT",
"dependencies": {
"@jambonz/db-helpers": "^0.6.12",
"@jambonz/http-authenticator": "^0.2.0",
"@jambonz/realtimedb-helpers": "^0.4.8",
"@jambonz/rtpengine-utils": "^0.1.14",
"@jambonz/rtpengine-utils": "^0.1.17",
"@jambonz/stats-collector": "^0.1.5",
"@jambonz/time-series": "^0.1.5",
"aws-sdk": "^2.848.0",
@@ -19,7 +19,7 @@
"cidr-matcher": "^2.1.1",
"debug": "^4.3.1",
"drachtio-fn-b2b-sugar": "0.0.12",
"drachtio-srf": "^4.4.55",
"drachtio-srf": "^4.4.59",
"express": "^4.17.1",
"pino": "^6.8.0",
"rtpengine-client": "^0.2.0",
@@ -1409,9 +1409,9 @@
}
},
"node_modules/drachtio-sip": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.6.0.tgz",
"integrity": "sha512-C8Y33rVpP0KwmZmBMxBjhbj58kktVFlzc+Od2g6TgOaqeEyF0JhwrHnech+iEtr2A2eKBlA85C9cCRh1+QpoRA==",
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.6.2.tgz",
"integrity": "sha512-BkiRZq3Yq2WVSGY3M7Hv4yX4dIW/o0/4xNMcm26IxT71YIRy07UtbQUHaMI3P2HfPu5zK6RoQW2MHrxPXtz6ZQ==",
"dependencies": {
"debug": "^4.3.1",
"eslint-plugin-promise": "^5.1.0",
@@ -1419,9 +1419,9 @@
}
},
"node_modules/drachtio-sip/node_modules/eslint-plugin-promise": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz",
"integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz",
"integrity": "sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA==",
"engines": {
"node": "^10.12.0 || >=12.0.0"
},
@@ -1430,15 +1430,15 @@
}
},
"node_modules/drachtio-srf": {
"version": "4.4.55",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.55.tgz",
"integrity": "sha512-wADXzcEdxD748iSK2KepD9LEiA+XW0nE2zNV89azKk0AafZGD0+DLMf1m8IOBnFE30H91pJI74Z8fO652+QB0A==",
"version": "4.4.59",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.59.tgz",
"integrity": "sha512-hrW9bZ8TZR9JQ3pqI+nyrI1eAzEOwHuvm1lNL1fbmZmRddKJzYdylkgVoyURs/OlT/nANy/M43GrQjcGP4psPw==",
"dependencies": {
"async": "^1.4.2",
"debug": "^3.2.7",
"delegates": "^0.1.0",
"deprecate": "^1.1.1",
"drachtio-sip": "^0.6.0",
"drachtio-sip": "^0.6.2",
"node-noop": "0.0.1",
"only": "0.0.2",
"sdp-transform": "^2.14.1",
@@ -2936,9 +2936,9 @@
}
},
"node_modules/json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
@@ -2966,17 +2966,17 @@
}
},
"node_modules/jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"engines": [
"node >=0.6.0"
],
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"dependencies": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"json-schema": "0.4.0",
"verror": "1.10.0"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/jwa": {
@@ -5850,9 +5850,9 @@
"requires": {}
},
"drachtio-sip": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.6.0.tgz",
"integrity": "sha512-C8Y33rVpP0KwmZmBMxBjhbj58kktVFlzc+Od2g6TgOaqeEyF0JhwrHnech+iEtr2A2eKBlA85C9cCRh1+QpoRA==",
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.6.2.tgz",
"integrity": "sha512-BkiRZq3Yq2WVSGY3M7Hv4yX4dIW/o0/4xNMcm26IxT71YIRy07UtbQUHaMI3P2HfPu5zK6RoQW2MHrxPXtz6ZQ==",
"requires": {
"debug": "^4.3.1",
"eslint-plugin-promise": "^5.1.0",
@@ -5860,23 +5860,23 @@
},
"dependencies": {
"eslint-plugin-promise": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz",
"integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz",
"integrity": "sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA==",
"requires": {}
}
}
},
"drachtio-srf": {
"version": "4.4.55",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.55.tgz",
"integrity": "sha512-wADXzcEdxD748iSK2KepD9LEiA+XW0nE2zNV89azKk0AafZGD0+DLMf1m8IOBnFE30H91pJI74Z8fO652+QB0A==",
"version": "4.4.59",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.59.tgz",
"integrity": "sha512-hrW9bZ8TZR9JQ3pqI+nyrI1eAzEOwHuvm1lNL1fbmZmRddKJzYdylkgVoyURs/OlT/nANy/M43GrQjcGP4psPw==",
"requires": {
"async": "^1.4.2",
"debug": "^3.2.7",
"delegates": "^0.1.0",
"deprecate": "^1.1.1",
"drachtio-sip": "^0.6.0",
"drachtio-sip": "^0.6.2",
"node-noop": "0.0.1",
"only": "0.0.2",
"sdp-transform": "^2.14.1",
@@ -7011,9 +7011,9 @@
}
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
},
"json-schema-traverse": {
"version": "0.4.1",
@@ -7035,13 +7035,13 @@
}
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"json-schema": "0.4.0",
"verror": "1.10.0"
}
},
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "sbc-inbound",
"version": "0.3.6",
"version": "v0.6.7-rc8",
"main": "app.js",
"engines": {
"node": ">= 10.16.0"
@@ -28,7 +28,7 @@
"@jambonz/db-helpers": "^0.6.12",
"@jambonz/http-authenticator": "^0.2.0",
"@jambonz/realtimedb-helpers": "^0.4.8",
"@jambonz/rtpengine-utils": "^0.1.14",
"@jambonz/rtpengine-utils": "^0.1.17",
"@jambonz/stats-collector": "^0.1.5",
"@jambonz/time-series": "^0.1.5",
"aws-sdk": "^2.848.0",
@@ -39,7 +39,7 @@
"cidr-matcher": "^2.1.1",
"debug": "^4.3.1",
"drachtio-fn-b2b-sugar": "0.0.12",
"drachtio-srf": "^4.4.55",
"drachtio-srf": "^4.4.59",
"pino": "^6.8.0",
"rtpengine-client": "^0.2.0"
},