Compare commits

..
18 Commits
Author SHA1 Message Date
Dave Horton 2266b80e73 release drachtio connection on final outbound failure 2022-03-18 14:34:30 -04:00
Snyk bot 4c3d6ddf0c fix: Dockerfile to reduce vulnerabilities (#35)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DEBIAN11-GNUTLS28-2419151
- https://snyk.io/vuln/SNYK-DEBIAN11-OPENSSL-2388380
- https://snyk.io/vuln/SNYK-DEBIAN11-OPENSSL-2426309
- https://snyk.io/vuln/SNYK-DEBIAN11-UTILLINUX-2401081
- https://snyk.io/vuln/SNYK-DEBIAN11-UTILLINUX-2401081
2022-03-18 07:55:10 -04:00
Dave Horton 3d1bcb23f4 bump version 2022-03-08 20:16:19 -05:00
Dave Horton 98ecfa20aa add support for redis auth 2022-03-08 09:30:44 -05:00
Dave Horton 56efe50aec forward incoming REFER to FS (#34) 2022-03-05 15:21:49 -05:00
Dave Horton 891d0ff38b use registered contact as uri when sending to user (#32) 2022-02-23 15:09:18 -05:00
Dave Horton 78d6cb5f22 use registered contact as uri when sending to user (#31) 2022-02-17 21:35:19 -05:00
Dave Horton 23255a71db added pre-commit hook for linting 2022-02-14 13:18:46 -05:00
Dave Horton 4cb34ad784 regression handling 302 redirect 2022-02-14 12:11:32 -05:00
Dave Horton f096a31a95 bump version 2022-02-09 15:43:50 -05:00
Dave Horton f671ef56bb update to latest @jambonz/realtimedb-helpers with support for redis username / password auth 2022-02-09 15:16:03 -05:00
Dave Horton 3a2fcf5976 update deps 2022-02-09 08:24:43 -05:00
Dave Horton dd85ec762d merge support for X-Override-To feature back into main branch 2022-02-01 12:30:34 -05:00
Dave Horton 61ec935ab8 0.7.2 version 2022-01-28 09:13:25 -05:00
Snyk bot cd7c425f7e fix: Dockerfile to reduce vulnerabilities (#30)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-1296898
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-2340908
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-2340908
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-2340922
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-2340922
2022-01-28 08:03:40 -05:00
Dave Horton e9b8cd595a Feature/rtpengine locate by dns (#29)
* k8s: query dns for rtpengine endpoints

* use udp now in k8s

* catch error on rtpengine failure

* specify node version in gh actions
2022-01-23 18:04:15 -05:00
Dave Horton fc7cdae22a Feature/rtpengine locate by dns (#28)
* k8s: query dns for rtpengine endpoints

* use udp now in k8s

* catch error on rtpengine failure
2022-01-23 17:32:51 -05:00
Dave Horton 2ca44c4f5a healthcheck only in k8s 2022-01-22 21:43:18 -05:00
8 changed files with 2046 additions and 363 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
node-version: 14.x
- run: npm ci
- run: npm run jslint
- run: npm test
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run jslint
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:current-slim
FROM node:17.7.1-slim
WORKDIR /opt/app/
COPY package.json ./
RUN npm install
+44 -26
View File
@@ -11,6 +11,7 @@ assert.ok(process.env.JAMBONES_NETWORK_CIDR || process.env.K8S, 'missing JAMBONE
const Srf = require('drachtio-srf');
const srf = new Srf('sbc-outbound');
const CIDRMatcher = require('cidr-matcher');
const {pingMsTeamsGateways, equalsIgnoreOrder} = require('./lib/utils');
const opts = Object.assign({
timestamp: () => {return `, "time": "${new Date().toISOString()}"`;}
}, {level: process.env.JAMBONES_LOGLEVEL || 'info'});
@@ -90,7 +91,7 @@ const {initLocals, checkLimits, route} = require('./lib/middleware')(srf, logger
const {getRtpEngine, setRtpEngines} = require('@jambonz/rtpengine-utils')([], logger, {
emitter: stats,
dtmfListenPort: process.env.DTMF_LISTEN_PORT || 22225,
protocol: process.env.RTPENGINE_NG_PROTOCOL || (process.env.K8S ? 'ws' : 'udp')
protocol: 'udp'
});
srf.locals.getRtpEngine = getRtpEngine;
@@ -136,40 +137,59 @@ srf.invite((req, res) => {
session.connect();
});
const PORT = process.env.HTTP_PORT || 3000;
const getCount = () => activeCallIds.size;
const healthCheck = require('@jambonz/http-health-check');
healthCheck({port: PORT, logger, path: '/', fn: getCount});
if (process.env.K8S) {
const PORT = process.env.HTTP_PORT || 3000;
const getCount = () => activeCallIds.size;
const healthCheck = require('@jambonz/http-health-check');
healthCheck({port: PORT, logger, path: '/', fn: getCount});
}
/* update call stats periodically */
setInterval(() => {
stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound']);
}, 5000);
if ('test' !== process.env.NODE_ENV) {
/* update call stats periodically */
setInterval(() => {
stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound']);
}, 5000);
}
const arrayCompare = (a, b) => {
if (a.length !== b.length) return false;
const uniqueValues = new Set([...a, ...b]);
for (const v of uniqueValues) {
const aCount = a.filter((e) => e === v).length;
const bCount = b.filter((e) => e === v).length;
if (aCount !== bCount) return false;
}
return true;
const lookupRtpServiceEndpoints = (lookup, serviceName) => {
logger.debug(`dns lookup for ${serviceName}..`);
lookup(serviceName, {family: 4, all: true}, (err, addresses) => {
if (err) {
logger.error({err}, `Error looking up ${serviceName}`);
return;
}
logger.debug({addresses, rtpServers}, `dns lookup for ${serviceName} returned`);
const addrs = addresses.map((a) => a.address);
if (!equalsIgnoreOrder(addrs, rtpServers)) {
rtpServers.length = 0;
Array.prototype.push.apply(rtpServers, addrs);
logger.info({rtpServers}, 'rtpserver endpoints have been updated');
setRtpEngines(rtpServers.map((a) => `${a}:${process.env.RTPENGINE_PORT || 22222}`));
}
});
};
const serviceName = process.env.JAMBONES_RTPENGINES || process.env.K8S_RTPENGINE_SERVICE_NAME;
if (serviceName) {
logger.info(`rtpengine(s) will be found at: ${serviceName}`);
setRtpEngines([serviceName]);
if (process.env.K8S_RTPENGINE_SERVICE_NAME) {
/* poll dns for endpoints every so often */
const arr = /^(.*):(\d+)$/.exec(process.env.K8S_RTPENGINE_SERVICE_NAME);
const svc = arr[1];
logger.info(`rtpengine(s) will be found at dns name: ${svc}`);
const {lookup} = require('dns');
lookupRtpServiceEndpoints(lookup, svc);
setInterval(lookupRtpServiceEndpoints.bind(null, lookup, svc), process.env.RTPENGINE_DNS_POLL_INTERVAL || 10000);
}
else if (process.env.JAMBONES_RTPENGINES) {
/* static list of rtpengines */
setRtpEngines([process.env.JAMBONES_RTPENGINES]);
}
else {
/* update rtpengines periodically */
/* poll redis periodically for rtpengines that have registered via OPTIONS ping */
const getActiveRtpServers = async() => {
try {
const set = await retrieveSet(setNameRtp);
const newArray = Array.from(set);
logger.debug({newArray, rtpServers}, 'getActiveRtpServers');
if (!arrayCompare(newArray, rtpServers)) {
if (!equalsIgnoreOrder(newArray, rtpServers)) {
logger.info({newArray}, 'resetting active rtpengines');
setRtpEngines(newArray.map((a) => `${a}:${process.env.RTPENGINE_PORT || 22222}`));
rtpServers.length = 0;
@@ -179,14 +199,12 @@ else {
logger.error({err}, 'Error setting new rtpengines');
}
};
setInterval(() => {
getActiveRtpServers();
}, 30000);
getActiveRtpServers();
}
const {pingMsTeamsGateways} = require('./lib/utils');
pingMsTeamsGateways(logger, srf);
module.exports = {srf};
+33 -6
View File
@@ -124,14 +124,19 @@ class CallSession extends Emitter {
};
if (this.req.locals.registration) {
debug(`sending call to user ${JSON.stringify(this.req.locals.registration)}`);
debug(`sending call to registered user ${JSON.stringify(this.req.locals.registration)}`);
const contact = this.req.locals.registration.contact;
if (contact.includes('transport=ws')) {
uris = [contact];
let destUri = contact;
if (this.req.has('X-Override-To')) {
const dest = this.req.get('X-Override-To');
const uri = parseUri(contact);
uri.user = dest;
destUri = stringifyUri(uri);
this.logger.info(`overriding destination user with ${dest}, so final uri is ${destUri}`);
}
else {
uris = [destUri];
if (!contact.includes('transport=ws')) {
proxy = this.req.locals.registration.proxy;
uris = [this.req.uri];
}
}
else if (this.req.locals.target === 'forward') {
@@ -372,7 +377,9 @@ class CallSession extends Emitter {
debug(`got final outdial error: ${err}`);
if (!passFailure) this.res.send(status);
this.emit('failed');
this.rtpEngineResource.destroy();
this.rtpEngineResource.destroy()
.catch((err) => this.logger.info({err}, 'Error destroying rtpe after failure'));
this.srf.endSession(this.req);
const tags = ['accepted:no', `sipStatus:${status}`];
this.stats.increment('sbc.originations', tags);
@@ -449,6 +456,7 @@ class CallSession extends Emitter {
uac.on('modify', this._onReinvite.bind(this, uac));
uas.on('refer', this._onFeatureServerTransfer.bind(this, uas));
uac.on('refer', this._onRefer.bind(this, uac));
uas.on('info', this._onInfo.bind(this, uas));
uac.on('info', this._onInfo.bind(this, uac));
@@ -457,6 +465,23 @@ class CallSession extends Emitter {
forwardInDialogRequests(uac, ['notify', 'options', 'message']);
}
async _onRefer(dlg, req, res) {
/* REFER coming in from a sip device, forward to feature server */
try {
const response = await dlg.other.request({
method: 'REFER',
headers: {
'Refer-To': req.get('Refer-To'),
'Referred-By': req.get('Referred-By'),
'User-Agent': req.get('User-Agent')
}
});
res.send(response.status, response.reason);
} catch (err) {
this.logger.error({err}, 'CallSession:_onRefer: error handling incoming REFER');
}
}
async _onDTMF(dlg, payload) {
this.logger.info({payload}, '_onDTMF');
try {
@@ -518,6 +543,7 @@ Duration=${payload.duration} `
}
else {
sdp = await dlg.other.modify(response.sdp);
this.logger.info({sdp}, 'CallSession:_onReinvite: got sdp from 200 OK to invite we sent');
}
opts = {
...this.rtpEngineOpts.common,
@@ -531,6 +557,7 @@ Duration=${payload.duration} `
res.send(488);
throw new Error(`_onReinvite: rtpengine failed: ${JSON.stringify(response)}`);
}
this.logger.info({sdp: response.sdp}, 'CallSession:_onReinvite: sending back upstream');
res.send(200, {body: response.sdp});
} catch (err) {
this.logger.error(err, 'Error handling reinvite');
+13 -1
View File
@@ -73,9 +73,21 @@ const pingMsTeamsGateways = (logger, srf) => {
const makeCallCountKey = (sid) => `${sid}:outcalls`;
const equalsIgnoreOrder = (a, b) => {
if (a.length !== b.length) return false;
const uniqueValues = new Set([...a, ...b]);
for (const v of uniqueValues) {
const aCount = a.filter((e) => e === v).length;
const bCount = b.filter((e) => e === v).length;
if (aCount !== bCount) return false;
}
return true;
};
module.exports = {
makeRtpEngineOpts,
selectHostPort,
pingMsTeamsGateways,
makeCallCountKey
makeCallCountKey,
equalsIgnoreOrder
};
+1943 -322
View File
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -1,9 +1,9 @@
{
"name": "sbc-outbound",
"version": "v0.7.1",
"version": "v0.7.4",
"main": "app.js",
"engines": {
"node": ">= 8.10.0"
"node": ">= 12.0.0"
},
"keywords": [
"sip",
@@ -27,17 +27,18 @@
"jslint": "eslint app.js lib"
},
"dependencies": {
"@jambonz/db-helpers": "^0.6.16",
"@jambonz/db-helpers": "^0.6.17",
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/mw-registrar": "0.2.1",
"@jambonz/realtimedb-helpers": "^0.4.9",
"@jambonz/rtpengine-utils": "^0.2.2",
"@jambonz/mw-registrar": "0.2.2",
"@jambonz/realtimedb-helpers": "^0.4.24",
"@jambonz/rtpengine-utils": "^0.3.1",
"@jambonz/stats-collector": "^0.1.6",
"@jambonz/time-series": "^0.1.6",
"cidr-matcher": "^2.1.1",
"debug": "^4.3.3",
"drachtio-fn-b2b-sugar": "^0.0.12",
"drachtio-srf": "^4.4.59",
"husky": "^7.0.4",
"pino": "^7.4.1"
},
"devDependencies": {