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
This commit is contained in:
Dave Horton
2021-11-20 11:41:12 -05:00
committed by GitHub
parent 8584020d4c
commit f16598e144
6 changed files with 93 additions and 31 deletions
+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
};
};
+2 -3
View File
@@ -68,8 +68,6 @@ 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 || {};
@@ -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,
+21 -21
View File
@@ -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",
@@ -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",
+1 -1
View File
@@ -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"
},