mirror of
https://github.com/jambonz/sbc-inbound.git
synced 2025-12-19 04:37:43 +00:00
Feat/improve parse host ports (#149)
* Move parseHostPorts to utils and cover with test suite * fix tabs * comment pre commit hook * add test * wip * remove only * fix 2 critical, 12 high vulnerabilities via npm audit fix --------- Co-authored-by: Markus Frindt <m.frindt@cognigy.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
#npm run jslint
|
# npm audit fix || true
|
||||||
|
# npm run jslint
|
||||||
|
|||||||
52
app.js
52
app.js
@@ -37,7 +37,7 @@ const {
|
|||||||
const StatsCollector = require('@jambonz/stats-collector');
|
const StatsCollector = require('@jambonz/stats-collector');
|
||||||
const CIDRMatcher = require('cidr-matcher');
|
const CIDRMatcher = require('cidr-matcher');
|
||||||
const stats = new StatsCollector(logger);
|
const stats = new StatsCollector(logger);
|
||||||
const {equalsIgnoreOrder, createHealthCheckApp, systemHealth} = require('./lib/utils');
|
const {equalsIgnoreOrder, createHealthCheckApp, systemHealth, parseHostPorts} = require('./lib/utils');
|
||||||
const {LifeCycleEvents} = require('./lib/constants');
|
const {LifeCycleEvents} = require('./lib/constants');
|
||||||
const setNameRtp = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-rtp`;
|
const setNameRtp = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-rtp`;
|
||||||
const rtpServers = [];
|
const rtpServers = [];
|
||||||
@@ -183,7 +183,7 @@ if (process.env.DRACHTIO_HOST && !process.env.K8S) {
|
|||||||
srf.locals.addToRedis();
|
srf.locals.addToRedis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
srf.locals.sbcPublicIpAddress = parseHostPorts(hostports);
|
srf.locals.sbcPublicIpAddress = parseHostPorts(logger, hostports, srf);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -207,7 +207,7 @@ else {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
srf.locals.sbcPublicIpAddress = parseHostPorts(hp.split(','));
|
srf.locals.sbcPublicIpAddress = parseHostPorts(logger, hp, srf);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (process.env.NODE_ENV === 'test') {
|
if (process.env.NODE_ENV === 'test') {
|
||||||
@@ -364,50 +364,4 @@ function handle(removeFromSet, setName, signal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseHostPorts = (hostports) => {
|
|
||||||
let obj = {};
|
|
||||||
logger.info({hostports}, 'sip endpoints');
|
|
||||||
for (const hp of hostports) {
|
|
||||||
const arr = /^(.*)\/(.*):(\d+)$/.exec(hp);
|
|
||||||
if (arr) {
|
|
||||||
const ipv4 = arr[2];
|
|
||||||
const port = arr[3];
|
|
||||||
switch (arr[1]) {
|
|
||||||
case 'udp':
|
|
||||||
obj = {
|
|
||||||
...obj,
|
|
||||||
udp: `${ipv4}:${port}`
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'tcp':
|
|
||||||
obj = {
|
|
||||||
...obj,
|
|
||||||
tcp: `${ipv4}:${port}`
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'tls':
|
|
||||||
obj = {
|
|
||||||
...obj,
|
|
||||||
tls: `${ipv4}:${port}`
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'wss':
|
|
||||||
obj = {
|
|
||||||
...obj,
|
|
||||||
wss: `${ipv4}:${port}`
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!obj.tls) {
|
|
||||||
obj.tls = `${srf.locals.sipAddress}:5061`;
|
|
||||||
}
|
|
||||||
if (!obj.tcp) {
|
|
||||||
obj.tcp = `${srf.locals.sipAddress}:5060`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info({obj}, 'sip endpoints');
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {srf, logger};
|
module.exports = {srf, logger};
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ class CallSession extends Emitter {
|
|||||||
// set Contact header based on scenario, and transport protocol
|
// set Contact header based on scenario, and transport protocol
|
||||||
let responseHeaders = {};
|
let responseHeaders = {};
|
||||||
if (isPrivateVoipNetwork(this.req.source_address)) {
|
if (isPrivateVoipNetwork(this.req.source_address)) {
|
||||||
this.contactHeader = `<${scheme}:${this.privateSipAddress}>;transport=${this.req.protocol}`;
|
this.contactHeader = `<${scheme}:${this.privateSipAddress};transport=${this.req.protocol}>`;
|
||||||
responseHeaders = {
|
responseHeaders = {
|
||||||
...responseHeaders,
|
...responseHeaders,
|
||||||
'Contact': this.contactHeader
|
'Contact': this.contactHeader
|
||||||
@@ -243,7 +243,7 @@ class CallSession extends Emitter {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const hostport = this.srf.locals.sbcPublicIpAddress[this.req.protocol];
|
const hostport = this.srf.locals.sbcPublicIpAddress[this.req.protocol];
|
||||||
this.contactHeader = `<${scheme}:${hostport}>;transport=${this.req.protocol}`;
|
this.contactHeader = `<${scheme}:${hostport};transport=${this.req.protocol}>`;
|
||||||
responseHeaders = {
|
responseHeaders = {
|
||||||
...responseHeaders,
|
...responseHeaders,
|
||||||
'Contact': this.contactHeader
|
'Contact': this.contactHeader
|
||||||
|
|||||||
58
lib/utils.js
58
lib/utils.js
@@ -11,7 +11,7 @@ const isWSS = (req) => {
|
|||||||
|
|
||||||
const getAppserver = (srf) => {
|
const getAppserver = (srf) => {
|
||||||
const len = srf.locals.featureServers.length;
|
const len = srf.locals.featureServers.length;
|
||||||
return srf.locals.featureServers[ idx++ % len];
|
return srf.locals.featureServers[idx++ % len];
|
||||||
};
|
};
|
||||||
|
|
||||||
function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp, teams = false) {
|
function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp, teams = false) {
|
||||||
@@ -31,7 +31,7 @@ function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp, teams = false) {
|
|||||||
'call-id': req.get('Call-ID'),
|
'call-id': req.get('Call-ID'),
|
||||||
'replace': ['origin', 'session-connection'],
|
'replace': ['origin', 'session-connection'],
|
||||||
'record call': process.env.JAMBONES_RECORD_ALL_CALLS ? 'yes' : 'no',
|
'record call': process.env.JAMBONES_RECORD_ALL_CALLS ? 'yes' : 'no',
|
||||||
...(process.env.JAMBONES_ACCEPT_G729 && { codec: { mask: 'g729', transcode: 'pcmu'}})
|
...(process.env.JAMBONES_ACCEPT_G729 && { codec: { mask: 'g729', transcode: 'pcmu' } })
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
common,
|
common,
|
||||||
@@ -74,7 +74,7 @@ const equalsIgnoreOrder = (a, b) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const systemHealth = async(redisClient, ping, getCount) => {
|
const systemHealth = async(redisClient, ping, getCount) => {
|
||||||
await Promise.all([redisClient.ping(), ping()]);
|
await Promise.all([redisClient.ping(), ping()]);
|
||||||
return getCount();
|
return getCount();
|
||||||
};
|
};
|
||||||
@@ -112,8 +112,8 @@ const createHealthCheckApp = (port, logger) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nudgeCallCounts = async(logger, sids, nudgeOperator, writers) => {
|
const nudgeCallCounts = async(logger, sids, nudgeOperator, writers) => {
|
||||||
const {service_provider_sid, account_sid, application_sid} = sids;
|
const { service_provider_sid, account_sid, application_sid } = sids;
|
||||||
const {writeCallCount, writeCallCountSP, writeCallCountApp} = writers;
|
const { writeCallCount, writeCallCountSP, writeCallCountApp } = writers;
|
||||||
const nudges = [];
|
const nudges = [];
|
||||||
const writes = [];
|
const writes = [];
|
||||||
|
|
||||||
@@ -145,28 +145,33 @@ const nudgeCallCounts = async(logger, sids, nudgeOperator, writers) => {
|
|||||||
const [callsSP, calls, callsApp] = await Promise.all(nudges);
|
const [callsSP, calls, callsApp] = await Promise.all(nudges);
|
||||||
logger.debug({
|
logger.debug({
|
||||||
calls, callsSP, callsApp,
|
calls, callsSP, callsApp,
|
||||||
service_provider_sid, account_sid, application_sid}, 'call counts after adjustment');
|
service_provider_sid, account_sid, application_sid
|
||||||
|
}, 'call counts after adjustment');
|
||||||
if (process.env.JAMBONES_TRACK_SP_CALLS) {
|
if (process.env.JAMBONES_TRACK_SP_CALLS) {
|
||||||
writes.push(writeCallCountSP({service_provider_sid, calls_in_progress: callsSP}));
|
writes.push(writeCallCountSP({ service_provider_sid, calls_in_progress: callsSP }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.JAMBONES_TRACK_ACCOUNT_CALLS || process.env.JAMBONES_HOSTING) {
|
if (process.env.JAMBONES_TRACK_ACCOUNT_CALLS || process.env.JAMBONES_HOSTING) {
|
||||||
writes.push(writeCallCount({service_provider_sid, account_sid, calls_in_progress: calls}));
|
writes.push(writeCallCount({ service_provider_sid, account_sid, calls_in_progress: calls }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.JAMBONES_TRACK_APP_CALLS && application_sid) {
|
if (process.env.JAMBONES_TRACK_APP_CALLS && application_sid) {
|
||||||
writes.push(writeCallCountApp({service_provider_sid, account_sid, application_sid, calls_in_progress: callsApp}));
|
writes.push(writeCallCountApp({
|
||||||
|
service_provider_sid,
|
||||||
|
account_sid, application_sid,
|
||||||
|
calls_in_progress: callsApp
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write the call counts to the database */
|
/* write the call counts to the database */
|
||||||
Promise.all(writes).catch((err) => logger.error({err}, 'Error writing call counts'));
|
Promise.all(writes).catch((err) => logger.error({ err }, 'Error writing call counts'));
|
||||||
|
|
||||||
return {callsSP, calls, callsApp};
|
return { callsSP, calls, callsApp };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err, 'error incrementing call counts');
|
logger.error(err, 'error incrementing call counts');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {callsSP: null, calls: null, callsApp: null};
|
return { callsSP: null, calls: null, callsApp: null };
|
||||||
};
|
};
|
||||||
|
|
||||||
const roundTripTime = (startAt) => {
|
const roundTripTime = (startAt) => {
|
||||||
@@ -204,6 +209,31 @@ const isPrivateVoipNetwork = (ip) => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hostports can be a string or an array of hostports
|
||||||
|
*/
|
||||||
|
const parseHostPorts = (logger, hostports, srf) => {
|
||||||
|
typeof hostports === 'string' && (hostports = hostports.split(','));
|
||||||
|
const obj = {};
|
||||||
|
for (const hp of hostports) {
|
||||||
|
const [, protocol, ipv4, port] = hp.match(/^(.*)\/(.*):(\d+)$/);
|
||||||
|
if (protocol && ipv4 && port) {
|
||||||
|
obj[protocol] = `${ipv4}:${port}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!obj.tls) {
|
||||||
|
obj.tls = `${srf.locals.sipAddress}:5061`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj.tcp) {
|
||||||
|
obj.tcp = `${srf.locals.sipAddress}:5060`;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info({ obj }, 'sip endpoints');
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
isWSS,
|
isWSS,
|
||||||
SdpWantsSrtp,
|
SdpWantsSrtp,
|
||||||
@@ -221,5 +251,7 @@ module.exports = {
|
|||||||
roundTripTime,
|
roundTripTime,
|
||||||
parseConnectionIp,
|
parseConnectionIp,
|
||||||
isMSTeamsCIDR,
|
isMSTeamsCIDR,
|
||||||
isPrivateVoipNetwork
|
isPrivateVoipNetwork,
|
||||||
|
parseHostPorts
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
2863
package-lock.json
generated
2863
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -22,10 +22,11 @@
|
|||||||
"start": "node app",
|
"start": "node app",
|
||||||
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONES_NETWORK_CIDR='127.0.0.1/32' JAMBONES_HOSTING=1 JWT_SECRET=foobarbazzle SBC_ACCOUNT_SID=ed649e33-e771-403a-8c99-1780eabbc803 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 JAMBONES_FEATURE_SERVERS=172.38.0.11 node test/ ",
|
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONES_NETWORK_CIDR='127.0.0.1/32' JAMBONES_HOSTING=1 JWT_SECRET=foobarbazzle SBC_ACCOUNT_SID=ed649e33-e771-403a-8c99-1780eabbc803 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 JAMBONES_FEATURE_SERVERS=172.38.0.11 node test/ ",
|
||||||
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
|
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
|
||||||
"jslint": "eslint app.js lib"
|
"jslint": "eslint app.js lib",
|
||||||
|
"jslint:fix": "npm run jslint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jambonz/db-helpers": "^0.9.3",
|
"@jambonz/db-helpers": "^0.9.4",
|
||||||
"@jambonz/http-health-check": "^0.0.1",
|
"@jambonz/http-health-check": "^0.0.1",
|
||||||
"@jambonz/realtimedb-helpers": "^0.8.8",
|
"@jambonz/realtimedb-helpers": "^0.8.8",
|
||||||
"@jambonz/rtpengine-utils": "^0.4.4",
|
"@jambonz/rtpengine-utils": "^0.4.4",
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ require('./docker_start');
|
|||||||
require('./create-test-db');
|
require('./create-test-db');
|
||||||
require('./sip-tests');
|
require('./sip-tests');
|
||||||
require('./docker_stop');
|
require('./docker_stop');
|
||||||
|
require('./utils');
|
||||||
|
|||||||
89
test/utils.js
Normal file
89
test/utils.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
const test = require('tape');
|
||||||
|
const { parseHostPorts } = require('../lib/utils');
|
||||||
|
const { parseUri } = require('drachtio-srf');
|
||||||
|
|
||||||
|
process.on('unhandledRejection', (reason, p) => {
|
||||||
|
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||||
|
});
|
||||||
|
|
||||||
|
const hostports = "tls/3.70.141.74:5061,wss/3.70.141.74:8443,tcp/3.70.141.74:5060,udp/3.70.141.74:5060";
|
||||||
|
const hostportsNoTls = "wss/3.70.141.74:8443,tcp/3.70.141.74:5060,udp/3.70.141.74:5060";
|
||||||
|
const logger = { info: (args) => console.log(args) };
|
||||||
|
|
||||||
|
const srf = {
|
||||||
|
locals: {
|
||||||
|
sipAddress: '127.0.0.1'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test('utils tests - parseHostPorts', async (t) => {
|
||||||
|
try {
|
||||||
|
let obj = parseHostPorts(logger, hostports, srf);
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
tls: '3.70.141.74:5061',
|
||||||
|
wss: '3.70.141.74:8443',
|
||||||
|
tcp: '3.70.141.74:5060',
|
||||||
|
udp: '3.70.141.74:5060'
|
||||||
|
};
|
||||||
|
|
||||||
|
t.ok(obj.tls === expected.tls, 'sip endpoint tls');
|
||||||
|
t.ok(obj.wss === expected.wss, 'sip endpoint wss');
|
||||||
|
t.ok(obj.tcp === expected.tcp, 'sip endpoint tcp');
|
||||||
|
t.ok(obj.udp === expected.udp, 'sip endpoint udp');
|
||||||
|
|
||||||
|
obj = parseHostPorts(logger, hostportsNoTls.split(','), srf);
|
||||||
|
|
||||||
|
t.ok(obj.tls === '127.0.0.1:5061', 'sip endpoint tls');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`error received: ${err}`);
|
||||||
|
t.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('utils tests - parse URI user', async (t) => {
|
||||||
|
try {
|
||||||
|
let invalidUri = "sip:@202660.tenios.com";
|
||||||
|
const req = {
|
||||||
|
getParsedHeader: () => ({ uri: invalidUri })
|
||||||
|
};
|
||||||
|
|
||||||
|
const referTo = req.getParsedHeader('Refer-To');
|
||||||
|
let uri = parseUri(referTo.uri);
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
family: "ipv4",
|
||||||
|
scheme: "sip",
|
||||||
|
user: "",
|
||||||
|
password: undefined,
|
||||||
|
host: "202660.tenios.com",
|
||||||
|
port: NaN,
|
||||||
|
params: {},
|
||||||
|
headers: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.ok(uri.family === expected.family, 'family eq ipv4');
|
||||||
|
t.ok(uri.scheme === expected.scheme, 'scheme eq sip');
|
||||||
|
t.ok(uri.password === expected.password, 'pw eq undefined');
|
||||||
|
t.ok(uri.host === expected.host, 'host eq 202660.tenios.com');
|
||||||
|
t.ok(uri.user === "", 'user eq empty string');
|
||||||
|
t.ok(isNaN(uri.port), 'port eq NaN');
|
||||||
|
t.ok(typeof uri.params === 'object', 'params eq object');
|
||||||
|
t.ok(typeof uri.headers === 'object', 'headers eq object');
|
||||||
|
|
||||||
|
invalidUri = "<sip:@202660.tenios.com>";
|
||||||
|
uri = parseUri(invalidUri);
|
||||||
|
/* TODO: uri can be undefined - check these conditions in call-session */
|
||||||
|
t.ok(uri === undefined, 'uri is undefined');
|
||||||
|
|
||||||
|
const validUri = "<sip:+49221578952870@202660.tenios.com>";
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`error received: ${err}`);
|
||||||
|
t.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user