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:
Markus Frindt
2024-08-01 08:41:49 -04:00
committed by GitHub
co-authored by Markus Frindt
parent b848ab99e1
commit c270c0df43
8 changed files with 1618 additions and 1457 deletions
+1
View File
@@ -1,4 +1,5 @@
#!/bin/sh #!/bin/sh
. "$(dirname "$0")/_/husky.sh" . "$(dirname "$0")/_/husky.sh"
# npm audit fix || true
# npm run jslint # npm run jslint
+3 -49
View File
@@ -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};
+2 -2
View File
@@ -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
+35 -3
View File
@@ -145,7 +145,8 @@ 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 }));
} }
@@ -155,7 +156,11 @@ const nudgeCallCounts = async(logger, sids, nudgeOperator, writers) => {
} }
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 */
@@ -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
}; };
+1473 -1390
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -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",
+1
View File
@@ -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
View 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);
}
});