support for routing based on X-Jambonz-Routing header

This commit is contained in:
Dave Horton
2022-01-08 13:01:02 -05:00
parent 9d228d4805
commit fa7de71650
15 changed files with 87 additions and 41 deletions

12
app.js
View File

@@ -45,7 +45,14 @@ const {
database: process.env.JAMBONES_MYSQL_DATABASE,
connectionLimit: process.env.JAMBONES_MYSQL_CONNECTION_LIMIT || 10
}, logger);
const {createHash, retrieveHash, incrKey, decrKey, retrieveSet} = require('@jambonz/realtimedb-helpers')({
const {
createHash,
retrieveHash,
incrKey,
decrKey,
retrieveSet,
isMemberOfSet
} = require('@jambonz/realtimedb-helpers')({
host: process.env.JAMBONES_REDIS_HOST || 'localhost',
port: process.env.JAMBONES_REDIS_PORT || 6379
}, logger);
@@ -78,7 +85,8 @@ srf.locals = {...srf.locals,
createHash,
retrieveHash,
incrKey,
decrKey
decrKey,
isMemberOfSet
}
};
const {initLocals, checkLimits, route} = require('./lib/middleware')(srf, logger, {

View File

@@ -277,9 +277,23 @@ class CallSession extends Emitter {
const {uas, uac} = await this.srf.createB2BUA(this.req, this.res, uri, {
proxy,
passFailure,
proxyRequestHeaders: ['all', '-X-MS-Teams-FQDN', '-X-MS-Teams-Tenant-FQDN', 'X-CID', '-Allow',
'-Session-Expires', '-X-Requested-Carrier-Sid', 'Min-SE'],
proxyResponseHeaders: ['all', '-Allow', '-Session-Expires'],
proxyRequestHeaders: [
'all',
'-X-MS-Teams-FQDN',
'-X-MS-Teams-Tenant-FQDN',
'X-CID',
'-Allow',
'-Session-Expires',
'-X-Requested-Carrier-Sid',
'-X-Jambonz-Routing',
'-X-Jambonz-FS-UUID',
'Min-SE'
],
proxyResponseHeaders: [
'all',
'-Allow',
'-Session-Expires'
],
headers: hdrs,
responseHeaders,
auth: gw ? gw.auth : undefined,

View File

@@ -2,15 +2,10 @@ const debug = require('debug')('jambonz:sbc-outbound');
const parseUri = require('drachtio-srf').parseUri;
const Registrar = require('@jambonz/mw-registrar');
const {selectHostPort, makeCallCountKey} = require('./utils');
const isLocalUri = (host, req) => {
debug({hostport: req.server.hostport}, `is ${host} local?`);
return req.server.hostport.includes(host);
};
const FS_UUID_SET_NAME = 'fsUUIDs';
module.exports = (srf, logger, opts) => {
const {incrKey, decrKey} = srf.locals.realtimeDbHelpers;
const {incrKey, decrKey, isMemberOfSet} = srf.locals.realtimeDbHelpers;
const {stats} = srf.locals;
const registrar = new Registrar(opts);
const {
@@ -34,6 +29,36 @@ module.exports = (srf, logger, opts) => {
return req.srf.endSession(req);
}
/* must come from a valid FS */
if (!req.has('X-Jambonz-Routing')) {
logger.info({msg: req.msg}, 'missing X-Jambonz-Routing header');
res.send(403, {
headers: {
'X-Reason': 'missing required jambonz headers'
}
});
return req.srf.endSession(req);
}
if (process.env.K8S) {
/* for K8S we do not use JAMBONES_CIDR so we must validate the sender by uuid FS creates */
const fsUUID = req.get('X-Jambonz-FS-UUID');
try {
const exists = await isMemberOfSet(FS_UUID_SET_NAME, fsUUID);
if (!exists || !fsUUID) {
res.send(403, {
headers: {
'X-Reason': `missing or invalid FS-UUID ${fsUUID}`
}
});
return req.srf.endSession(req);
}
} catch (err) {
res.send(500);
return req.srf.endSession(req);
}
}
stats.increment('sbc.invites', ['direction:outbound']);
req.on('cancel', () => {
@@ -122,6 +147,7 @@ module.exports = (srf, logger, opts) => {
const {lookupAccountBySipRealm} = req.srf.locals.dbHelpers;
logger.info(`received outbound INVITE to ${req.uri} from server at ${req.server.hostport}`);
const uri = parseUri(req.uri);
const desiredRouting = req.get('X-Jambonz-Routing');
if (!uri || !uri.user || !uri.host) {
logger.info({uri: req.uri}, 'invalid request-uri on outbound call, rejecting');
@@ -132,21 +158,15 @@ module.exports = (srf, logger, opts) => {
});
return req.srf.endSession(req);
}
const aor = `${uri.user}@${uri.host}`;
let reg;
const dotDecimalHost = /^[0-9\.]+$/.test(uri.host);
debug(`received outbound INVITE to ${req.calledNumber} from server at ${req.server.hostport}`);
if (req.has('X-MS-Teams-FQDN') && req.has('X-MS-Teams-Tenant-FQDN')) {
if ('teams' === desiredRouting) {
logger.debug('This is a call to ms teams');
req.locals.target = 'teams';
return next();
}
else if (!dotDecimalHost) {
// uri host is not a dot-decimal address, so try to look up user
logger.debug(`searching for registered user ${aor}`);
reg = await registrar.query(aor);
else if ('user' === desiredRouting) {
const aor = `${uri.user}@${uri.host}`;
const reg = await registrar.query(aor);
if (reg) {
// user is registered..find out which sbc is handling it
// us => we can put the call through
@@ -161,35 +181,28 @@ module.exports = (srf, logger, opts) => {
}
req.locals.registration = reg;
req.locals.target = 'user';
return next();
}
else {
// if the sip domain is one of ours return 404
const account = await lookupAccountBySipRealm(uri.host);
if (account) {
logger.info({host: uri.host, account}, `returning 404 to unregistered user in valid domain: ${req.uri}`);
res.send(404);
return req.srf.endSession(req);
}
else {
logger.info({host: uri.host, account}, `returning 404 to user in invalid domain: ${req.uri}`);
}
res.send(404);
return req.srf.endSession(req);
}
}
if (!dotDecimalHost || !isLocalUri(uri.host, req)) {
else if ('sip' === desiredRouting) {
// call that needs to be forwarded to a sip endpoint
logger.info(`forwarding call to sip endpoint ${req.uri}`);
req.locals.target = 'forward';
return next();
}
// if the called number is digits only (after possible leading plus sign), do lcr
if (!/^\d+$/.test(req.calledNumber.slice(1))) {
debug(`unable to route call to ${aor}; no registered user found`);
logger.info(`unable to route call to ${aor}; no registered user found`);
res.send(404);
return req.srf.endSession(req);
else if ('phone' === desiredRouting) {
debug('sending call to LCR');
req.locals.target = 'lcr';
}
debug('sending call to LCR');
req.locals.target = 'lcr';
next();
};

View File

@@ -118,7 +118,7 @@ services:
ipv4_address: 172.39.0.24
influxdb:
image: influxdb:1.8-alpine
image: influxdb:1.8
ports:
- "8086:8086"
networks:

View File

@@ -36,6 +36,7 @@
Subject: uac-pcap-carrier-success
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
X-Jambonz-Routing: phone
Content-Type: application/sdp
Content-Length: [len]
@@ -103,4 +104,3 @@
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
</scenario>

View File

@@ -34,6 +34,7 @@
Contact: sip:sipp@[local_ip]:[local_port]
Max-Forwards: 70
Subject: uac-device-unknown-user
X-Jambonz-Routing: user
Content-Type: application/sdp
Content-Length: [len]

View File

@@ -34,6 +34,7 @@
Contact: sip:sipp@[local_ip]:[local_port]
Max-Forwards: 70
Subject: uac-device-unknown-user
X-Jambonz-Routing: user
Content-Type: application/sdp
Content-Length: [len]

View File

@@ -34,6 +34,7 @@
Contact: sip:sipp@[local_ip]:[local_port]
Max-Forwards: 70
Subject: uac-device-unknown-user
X-Jambonz-Routing: user
Content-Type: application/sdp
Content-Length: [len]

View File

@@ -16,6 +16,7 @@
Max-Forwards: 70
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
X-Call-Sid: ff49e33-e771-403a-8c99-1780eabbc803
X-Jambonz-Routing: phone
Subject: uac-pcap-carrier-fail-limits
Content-Type: application/sdp
Content-Length: [len]

View File

@@ -17,6 +17,7 @@
Subject: uac-pcap-carrier-success
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
X-Jambonz-Routing: phone
Content-Type: application/sdp
Content-Length: [len]

View File

@@ -16,6 +16,7 @@
Max-Forwards: 70
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
X-Jambonz-Routing: phone
Subject: uac-pcap-carrier-success
Content-Type: application/sdp
Content-Length: [len]

View File

@@ -37,6 +37,7 @@
Subject: uac-pcap-device-success
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
X-Call-Sid: ff649e33-e771-403a-8c99-1780eabbc803
X-Jambonz-Routing: user
Content-Type: application/sdp
Content-Length: [len]

View File

@@ -34,6 +34,7 @@
Contact: sip:sipp@[local_ip]:[local_port]
Max-Forwards: 70
Subject: uac-pcap-device-success
X-Jambonz-Routing: user
Content-Type: application/sdp
Content-Length: [len]

View File

@@ -35,6 +35,7 @@
Contact: sip:sipp@[local_ip]:[local_port]
Max-Forwards: 70
Subject: uac-pcap-device-success
X-Jambonz-Routing: user
Content-Type: application/sdp
Content-Length: [len]

View File

@@ -37,6 +37,7 @@
Subject: uac-sip-uri-auth-success
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
X-Jambonz-Routing: sip
Content-Type: application/sdp
Content-Length: [len]
@@ -90,6 +91,7 @@
Subject: uac-sip-uri-auth-success
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
X-Jambonz-Routing: sip
Content-Type: application/sdp
Content-Length: [len]
@@ -126,7 +128,7 @@
To: <sip:sip:john@jambonz.org>[peer_tag_param]
[last_Call-ID:]
CSeq: 2 ACK
Subject: uac-sip-uri-auth-success
Subject:
Content-Length: 0
]]>