Compare commits

..
12 Commits
13 changed files with 627 additions and 804 deletions
+4 -4
View File
@@ -14,19 +14,19 @@ const opts = Object.assign({
timestamp: () => {return `, "time": "${new Date().toISOString()}"`;}
}, {level: process.env.JAMBONES_LOGLEVEL || 'info'});
const logger = require('pino')(opts);
const StatsCollector = require('jambonz-stats-collector');
const StatsCollector = require('@jambonz/stats-collector');
const stats = srf.locals.stats = new StatsCollector(logger);
const {route, setLogger} = require('./lib/middleware');
const CallSession = require('./lib/call-session');
const {performLcr, lookupAllTeamsFQDNs} = require('@jambonz/db-helpers')({
const {performLcr, lookupAllTeamsFQDNs, lookupAccountBySipRealm} = require('@jambonz/db-helpers')({
host: process.env.JAMBONES_MYSQL_HOST,
user: process.env.JAMBONES_MYSQL_USER,
password: process.env.JAMBONES_MYSQL_PASSWORD,
database: process.env.JAMBONES_MYSQL_DATABASE,
connectionLimit: process.env.JAMBONES_MYSQL_CONNECTION_LIMIT || 10
}, logger);
srf.locals.dbHelpers = {performLcr, lookupAllTeamsFQDNs};
const {getRtpEngine} = require('jambonz-rtpengine-utils')(process.env.JAMBONES_RTPENGINES.split(','), logger, {
srf.locals.dbHelpers = {performLcr, lookupAllTeamsFQDNs, lookupAccountBySipRealm};
const {getRtpEngine} = require('@jambonz/rtpengine-utils')(process.env.JAMBONES_RTPENGINES.split(','), logger, {
emitter: srf.locals.stats
});
srf.locals.getRtpEngine = getRtpEngine;
+10 -12
View File
@@ -93,17 +93,15 @@ class CallSession extends Emitter {
else {
debug('calling lcr');
try {
// strip leading plus sign
/**
* We normalize the called number by removing a leading + before sending it to LCR..
* but LCR will return us an array of sip uris, with leading + for carriers that require it
*/
const routableNumber = this.req.calledNumber.startsWith('+') ?
this.req.calledNumber.slice(1) :
this.req.calledNumber;
uris = await this.performLcr(routableNumber);
if (!uris || uris.length === 0) throw new Error('no routes found');
uris = uris.map((uri) => {
this.logger.debug(`uri: ${uri}`);
const arr = /^(.*):(\d+)$/.exec(uri);
return arr ? `sip:${routableNumber}@${arr[1]}:${arr[2]}` : uri;
});
} catch (err) {
debug(err);
this.logger.error(err, 'Error performing lcr');
@@ -116,7 +114,7 @@ class CallSession extends Emitter {
// rtpengine 'offer'
debug('sending offer command to rtpengine');
const response = await this.offer(this.rtpEngineOpts.offer);
const response = await this.offer(this.rtpEngineOpts.offer, {sdp: this.req.body});
debug(`response from rtpengine to offer ${JSON.stringify(response)}`);
this.logger.debug({offer: this.rtpEngineOpts.offer, response}, 'initial offer to rtpengine');
if ('ok' !== response.result) {
@@ -128,13 +126,15 @@ class CallSession extends Emitter {
let earlyMedia = false;
while (uris.length) {
const uri = uris.shift();
const passFailure = 0 === uris.length; // only a single target
debug(`sending INVITE to ${uri} via ${proxy})`);
this.logger.info(`sending INVITE to ${uri}`);
try {
const {uas, uac} = await this.srf.createB2BUA(this.req, this.res, uri, {
proxy,
passFailure: false,
proxyRequestHeaders: ['all', '-X-MS-Teams-FQDN', '-X-MS-Teams-Tenant-FQDN', '-Allow'],
passFailure,
proxyRequestHeaders: ['all', '-X-MS-Teams-FQDN', '-X-MS-Teams-Tenant-FQDN', '-Allow',
'-Session-Expires', 'Min-SE'],
proxyResponseHeaders: ['all', '-Allow'],
headers,
localSdpB: response.sdp,
@@ -173,7 +173,7 @@ class CallSession extends Emitter {
if (err instanceof SipError) this.logger.info(`final call failure ${err.status}`);
else this.logger.error(err, 'unexpected call failure');
debug(`got final outdial error: ${err}`);
this.res.send(err.status || 500);
if (!passFailure) this.res.send(err.status || 500);
this.emit('failed');
this.rtpEngineResource.destroy();
const tags = ['accepted:no', `sipStatus:${err.status || 500}`];
@@ -213,8 +213,6 @@ class CallSession extends Emitter {
uac.on('modify', this._onNetworkReinvite.bind(this, uac));
uas.on('refer', this._onFeatureServerTransfer.bind(this, uas));
uac.on('hold', () => this.logger.info('invite on hold from network side'));
uac.on('unhold', () => this.logger.info('invite off hold from network side'));
// default forwarding of other request types
+12 -3
View File
@@ -22,6 +22,7 @@ function route(opts) {
const registrar = new Registrar(opts);
return async(req, res, next) => {
const logger = req.locals.logger;
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);
if (!uri.user || !uri.host) {
@@ -68,10 +69,18 @@ function route(opts) {
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(`returning 404 to unregistered user in valid domain: ${req.uri}`);
res.send(404);
return;
}
}
}
else if (!isLocalUri(uri.host, req) /*|| !dotDecimalHost */) {
// TODO: need to try to forward calls to a DNS host name
// so long as that domain name is not associated with an account or SP in our DB
if (!dotDecimalHost || !isLocalUri(uri.host, req)) {
// 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();
+1
View File
@@ -10,6 +10,7 @@ function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp, teams = false) {
common,
offer: Object.assign(
{'sdp': req.body, 'replace': ['origin', 'session-connection']},
{'direction': [ 'private', 'public']},
common,
dstIsUsingSrtp ? srtpOpts : rtpCharacteristics),
answer: Object.assign(
+540 -715
View File
File diff suppressed because it is too large Load Diff
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "sbc-outbound",
"version": "0.3.2",
"version": "0.4.3",
"main": "app.js",
"engines": {
"node": ">= 8.10.0"
@@ -27,21 +27,21 @@
"jslint": "eslint app.js lib"
},
"dependencies": {
"@jambonz/db-helpers": "^0.4.2",
"@jambonz/rtpengine-utils": "^0.1.7",
"@jambonz/stats-collector": "0.0.4",
"debug": "^4.1.1",
"drachtio-fn-b2b-sugar": "^0.0.12",
"drachtio-srf": "^4.4.33",
"jambonz-rtpengine-utils": "0.1.1",
"@jambonz/db-helpers": "^0.3.8",
"jambonz-mw-registrar": "^0.1.2",
"jambonz-stats-collector": "0.0.3",
"pino": "^5.14.0",
"rtpengine-client": "^0.0.8"
"drachtio-srf": "^4.4.37",
"jambonz-mw-registrar": "^0.1.3",
"pino": "^6.5.1",
"rtpengine-client": "^0.1.1"
},
"devDependencies": {
"blue-tape": "^1.0.0",
"eslint": "^6.8.0",
"eslint": "^7.7.0",
"eslint-plugin-promise": "^4.2.1",
"nyc": "^15.0.1",
"nyc": "^15.1.0",
"tap-dot": "^2.0.0",
"tap-spec": "^5.0.0"
}
+31 -28
View File
@@ -1,6 +1,6 @@
/* SQLEditor (MySQL (2))*/
SET FOREIGN_KEY_CHECKS = 0;
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS call_routes;
@@ -8,10 +8,10 @@ DROP TABLE IF EXISTS lcr_carrier_set_entry;
DROP TABLE IF EXISTS lcr_routes;
DROP TABLE IF EXISTS ms_teams_tenants;
DROP TABLE IF EXISTS api_keys;
DROP TABLE IF EXISTS ms_teams_tenants;
DROP TABLE IF EXISTS sbc_addresses;
DROP TABLE IF EXISTS users;
@@ -22,16 +22,14 @@ DROP TABLE IF EXISTS sip_gateways;
DROP TABLE IF EXISTS voip_carriers;
DROP TABLE IF EXISTS applications;
DROP TABLE IF EXISTS accounts;
DROP TABLE IF EXISTS applications;
DROP TABLE IF EXISTS service_providers;
DROP TABLE IF EXISTS webhooks;
SET FOREIGN_KEY_CHECKS = 1;
CREATE TABLE call_routes
(
call_route_sid CHAR(36) NOT NULL UNIQUE ,
@@ -51,26 +49,28 @@ priority INTEGER NOT NULL UNIQUE COMMENT 'lower priority routes are attempted f
PRIMARY KEY (lcr_route_sid)
) COMMENT='Least cost routing table';
CREATE TABLE ms_teams_tenants
(
ms_teams_tenant_sid CHAR(36) NOT NULL UNIQUE ,
service_provider_sid CHAR(36) NOT NULL,
account_sid CHAR(36),
application_sid CHAR(36),
tenant_fqdn VARCHAR(255) NOT NULL UNIQUE ,
PRIMARY KEY (ms_teams_tenant_sid)
) COMMENT='A Microsoft Teams customer tenant';
CREATE TABLE api_keys
(
api_key_sid CHAR(36) NOT NULL UNIQUE ,
token CHAR(36) NOT NULL UNIQUE ,
account_sid CHAR(36),
service_provider_sid CHAR(36),
expires_at TIMESTAMP,
expires_at TIMESTAMP NULL,
last_used TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (api_key_sid)
) ENGINE=InnoDB COMMENT='An authorization token that is used to access the REST api';
CREATE TABLE ms_teams_tenants
(
ms_teams_tenant_sid CHAR(36) NOT NULL UNIQUE ,
service_provider_sid CHAR(36) NOT NULL,
account_sid CHAR(36) NOT NULL,
application_sid CHAR(36),
tenant_fqdn VARCHAR(255) NOT NULL UNIQUE ,
PRIMARY KEY (ms_teams_tenant_sid)
) COMMENT='A Microsoft Teams customer tenant';
CREATE TABLE sbc_addresses
(
sbc_address_sid CHAR(36) NOT NULL UNIQUE ,
@@ -97,6 +97,7 @@ name VARCHAR(64) NOT NULL UNIQUE ,
description VARCHAR(255),
account_sid CHAR(36) COMMENT 'if provided, indicates this entity represents a customer PBX that is associated with a specific account',
application_sid CHAR(36) COMMENT 'If provided, all incoming calls from this source will be routed to the associated application',
e164_leading_plus BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (voip_carrier_sid)
) ENGINE=InnoDB COMMENT='A Carrier or customer PBX that can send or receive calls';
@@ -185,21 +186,21 @@ ALTER TABLE call_routes ADD FOREIGN KEY account_sid_idxfk (account_sid) REFERENC
ALTER TABLE call_routes ADD FOREIGN KEY application_sid_idxfk (application_sid) REFERENCES applications (application_sid);
CREATE INDEX ms_teams_tenant_sid_idx ON ms_teams_tenants (ms_teams_tenant_sid);
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY service_provider_sid_idxfk (service_provider_sid) REFERENCES service_providers (service_provider_sid);
CREATE INDEX api_key_sid_idx ON api_keys (api_key_sid);
CREATE INDEX account_sid_idx ON api_keys (account_sid);
ALTER TABLE api_keys ADD FOREIGN KEY account_sid_idxfk_1 (account_sid) REFERENCES accounts (account_sid);
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY account_sid_idxfk_1 (account_sid) REFERENCES accounts (account_sid);
CREATE INDEX service_provider_sid_idx ON api_keys (service_provider_sid);
ALTER TABLE api_keys ADD FOREIGN KEY service_provider_sid_idxfk (service_provider_sid) REFERENCES service_providers (service_provider_sid);
CREATE INDEX ms_teams_tenant_sid_idx ON ms_teams_tenants (ms_teams_tenant_sid);
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY service_provider_sid_idxfk_1 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY account_sid_idxfk_2 (account_sid) REFERENCES accounts (account_sid);
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY application_sid_idxfk_1 (application_sid) REFERENCES applications (application_sid);
CREATE INDEX tenant_fqdn_idx ON ms_teams_tenants (tenant_fqdn);
CREATE INDEX api_key_sid_idx ON api_keys (api_key_sid);
CREATE INDEX account_sid_idx ON api_keys (account_sid);
ALTER TABLE api_keys ADD FOREIGN KEY account_sid_idxfk_2 (account_sid) REFERENCES accounts (account_sid);
CREATE INDEX service_provider_sid_idx ON api_keys (service_provider_sid);
ALTER TABLE api_keys ADD FOREIGN KEY service_provider_sid_idxfk_1 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
CREATE INDEX sbc_addresses_idx_host_port ON sbc_addresses (ipv4,port);
CREATE INDEX sbc_address_sid_idx ON sbc_addresses (sbc_address_sid);
@@ -254,3 +255,5 @@ ALTER TABLE accounts ADD FOREIGN KEY service_provider_sid_idxfk_3 (service_provi
ALTER TABLE accounts ADD FOREIGN KEY registration_hook_sid_idxfk_1 (registration_hook_sid) REFERENCES webhooks (webhook_sid);
ALTER TABLE accounts ADD FOREIGN KEY device_calling_application_sid_idxfk (device_calling_application_sid) REFERENCES applications (application_sid);
SET FOREIGN_KEY_CHECKS=1;
+1
View File
@@ -27,6 +27,7 @@ services:
rtpengine:
image: drachtio/rtpengine:latest
command: rtpengine --interface private/172.39.0.12 --interface public/172.39.0.12!172.39.0.12
ports:
- "12222:22222/udp"
networks:
+2 -2
View File
@@ -25,10 +25,10 @@
<send retrans="500" start_txn="invite">
<![CDATA[
INVITE sip:16173333456@jambonz.org SIP/2.0
INVITE sip:16173333456@127.0.0.1 SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:16173333456@jambonz.org>
To: <sip:16173333456@127.0.0.1>
Call-ID: [call_id]
CSeq: 1 INVITE
Contact: sip:sipp@[local_ip]:[local_port]
@@ -6,10 +6,10 @@
<send retrans="500">
<![CDATA[
INVITE sip:16173333456@jambonz.org SIP/2.0
INVITE sip:16173333456@127.0.0.1 SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:16173333456@jambonz.org>
To: <sip:16173333456@127.0.0.1>
Call-ID: [call_id]
CSeq: 1 INVITE
Contact: sip:sipp@[local_ip]:[local_port]
@@ -43,10 +43,10 @@
<send>
<![CDATA[
ACK sip:sip:+16173333456@jambonz.org SIP/2.0
ACK sip:sip:+16173333456@127.0.0.1 SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:sip:+16173333456@jambonz.org>[peer_tag_param]
To: <sip:sip:+16173333456@127.0.0.1>[peer_tag_param]
Call-ID: [call_id]
CSeq: 1 ACK
Subject: uac-pcap-carrier-success
@@ -122,10 +122,10 @@
<send retrans="500">
<![CDATA[
BYE sip:sip:+16173333456@jambonz.org SIP/2.0
BYE sip:sip:+16173333456@127.0.0.1 SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:sip:+16173333456@jambonz.org>[peer_tag_param]
To: <sip:sip:+16173333456@127.0.0.1>[peer_tag_param]
Call-ID: [call_id]
CSeq: 3 BYE
Max-Forwards: 70
+6 -6
View File
@@ -6,10 +6,10 @@
<send retrans="500">
<![CDATA[
INVITE sip:16173333456@jambonz.org SIP/2.0
INVITE sip:16173333456@127.0.0.1 SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:16173333456@jambonz.org>
To: <sip:16173333456@127.0.0.1>
Call-ID: [call_id]
CSeq: 1 INVITE
Contact: sip:sipp@[local_ip]:[local_port]
@@ -43,10 +43,10 @@
<send>
<![CDATA[
ACK sip:sip:+16173333456@jambonz.org SIP/2.0
ACK sip:sip:+16173333456@127.0.0.1 SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:sip:+16173333456@jambonz.org>[peer_tag_param]
To: <sip:sip:+16173333456@127.0.0.1>[peer_tag_param]
Call-ID: [call_id]
CSeq: 1 ACK
Subject: uac-pcap-carrier-success
@@ -69,10 +69,10 @@
<send retrans="500">
<![CDATA[
BYE sip:sip:+16173333456@jambonz.org SIP/2.0
BYE sip:sip:+16173333456@127.0.0.1 SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:sip:+16173333456@jambonz.org>[peer_tag_param]
To: <sip:sip:+16173333456@127.0.0.1>[peer_tag_param]
Call-ID: [call_id]
CSeq: 2 BYE
Max-Forwards: 70
+4 -4
View File
@@ -26,10 +26,10 @@
<send retrans="500">
<![CDATA[
INVITE sip:jane@jambonz.org SIP/2.0
INVITE sip:jane@sip.example.com SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:jane@jambonz.org>
To: <sip:jane@sip.example.com>
Call-ID: [call_id]
CSeq: 1 INVITE
Contact: sip:sipp@[local_ip]:[local_port]
@@ -62,10 +62,10 @@
<send>
<![CDATA[
ACK sip:sip:jane@jambonz.org SIP/2.0
ACK sip:sip:jane@sip.example.com SIP/2.0
[last_Via]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:sip:jane@jambonz.org>[peer_tag_param]
To: <sip:sip:jane@sip.example.com>[peer_tag_param]
Call-ID: [call_id]
CSeq: 1 ACK
Subject: uac-pcap-device-success
-14
View File
@@ -21,20 +21,6 @@ test('sbc-outbound tests', async(t) => {
try {
await connect(srf);
// for now, the docker image for sbc-registrar needs a mysql database..
// TODO: figure out how to add that into the docker network and build the schema there
/* send a registration from 172.39.0.31 */
//await sippUac('uac-register-auth-success.xml', {ip: '172.39.0.31', data_file: 'good_user.csv'});
//t.pass('sip user/device registered over udp');
/* now start a sipp service waiting on 172.39.0.31 for an INVITE */
//const p = sippUac('uas.xml', {ip: '172.39.0.31'});
/* now send an outbound call from the appserver targeted to the user that registered from 172.39.0.31 */
//await sippUac('uac-pcap-device-success.xml');
//await p;
//t.pass('successfully completed outbound call to the registered user/devices');
/* call to unregistered user */
await sippUac('uac-pcap-device-404.xml');