mirror of
https://github.com/jambonz/sbc-inbound.git
synced 2026-10-04 02:04:22 +00:00
Compare commits
18
Commits
v0.9.0
...
v0.9.2-rc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6b1e58b87 | ||
|
|
524a556a7c | ||
|
|
b6d856c626 | ||
|
|
3454232314 | ||
|
|
a97cadc4b9 | ||
|
|
20755f456d | ||
|
|
fec4c42008 | ||
|
|
34cfe8ca9d | ||
|
|
55bcba05f6 | ||
|
|
c270c0df43 | ||
|
|
b848ab99e1 | ||
|
|
4153a141d7 | ||
|
|
e999b4e5e8 | ||
|
|
0e2285c4b8 | ||
|
|
f9ae466709 | ||
|
|
831005f7a1 | ||
|
|
a46ad934a8 | ||
|
|
d200799997 |
@@ -6,12 +6,15 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
- run: npm ci
|
||||
node-version: 20.x
|
||||
- run: npm install
|
||||
- run: npm run jslint
|
||||
- run: npm test
|
||||
|
||||
|
||||
- name: Install Docker Compose
|
||||
run: |
|
||||
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
docker-compose --version
|
||||
- run: npm test
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run jslint
|
||||
# npm audit fix || true
|
||||
# npm run jslint
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM --platform=linux/amd64 node:18.15-alpine3.16 as base
|
||||
FROM --platform=linux/amd64 node:20.13.0-alpine3.18 as base
|
||||
|
||||
RUN apk --update --no-cache add --virtual .builds-deps build-base python3
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ const {
|
||||
const StatsCollector = require('@jambonz/stats-collector');
|
||||
const CIDRMatcher = require('cidr-matcher');
|
||||
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 setNameRtp = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-rtp`;
|
||||
const rtpServers = [];
|
||||
@@ -55,7 +55,8 @@ const {
|
||||
lookupAccountBySid,
|
||||
lookupAccountCapacitiesBySid,
|
||||
queryCallLimits,
|
||||
lookupClientByAccountAndUsername
|
||||
lookupClientByAccountAndUsername,
|
||||
lookupSystemInformation
|
||||
} = require('@jambonz/db-helpers')({
|
||||
host: process.env.JAMBONES_MYSQL_HOST,
|
||||
port: process.env.JAMBONES_MYSQL_PORT || 3306,
|
||||
@@ -91,6 +92,7 @@ srf.locals = {...srf.locals,
|
||||
AlertType,
|
||||
activeCallIds: new Map(),
|
||||
getRtpEngine,
|
||||
privateNetworkCidr: process.env.PRIVATE_VOIP_NETWORK_CIDR || null,
|
||||
dbHelpers: {
|
||||
pool,
|
||||
ping,
|
||||
@@ -102,7 +104,8 @@ srf.locals = {...srf.locals,
|
||||
lookupAccountBySipRealm,
|
||||
lookupAccountCapacitiesBySid,
|
||||
queryCallLimits,
|
||||
lookupClientByAccountAndUsername
|
||||
lookupClientByAccountAndUsername,
|
||||
lookupSystemInformation
|
||||
},
|
||||
realtimeDbHelpers: {
|
||||
createSet,
|
||||
@@ -145,11 +148,28 @@ if (process.env.DRACHTIO_HOST && !process.env.K8S) {
|
||||
const matcher = new CIDRMatcher(cidrs);
|
||||
|
||||
srf.connect({host: process.env.DRACHTIO_HOST, port: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET });
|
||||
srf.on('connect', (err, hp) => {
|
||||
srf.on('connect', (err, hp, version, localHostports) => {
|
||||
if (err) return this.logger.error({err}, 'Error connecting to drachtio server');
|
||||
logger.info(`connected to drachtio listening on ${hp}`);
|
||||
let addedPrivateIp = false;
|
||||
logger.info(`connected to drachtio ${version} listening on ${hp}, local hostports: ${localHostports}`);
|
||||
|
||||
const hostports = hp.split(',');
|
||||
|
||||
if (localHostports) {
|
||||
const locals = localHostports.split(',');
|
||||
for (const hp of locals) {
|
||||
const arr = /^(.*)\/(.*):(\d+)$/.exec(hp);
|
||||
if (arr && 'tcp' === arr[1] && matcher.contains(arr[2])) {
|
||||
const hostport = `${arr[2]}:${arr[3]}`;
|
||||
logger.info(`adding sbc private address to redis: ${hostport}`);
|
||||
srf.locals.privateSipAddress = hostport;
|
||||
srf.locals.addToRedis = () => addToSet(setName, hostport);
|
||||
srf.locals.removeFromRedis = () => removeFromSet(setName, hostport);
|
||||
srf.locals.addToRedis();
|
||||
addedPrivateIp = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const hp of hostports) {
|
||||
const arr = /^(.*)\/(.*):(\d+)$/.exec(hp);
|
||||
if (arr && 'udp' === arr[1] && !matcher.contains(arr[2])) {
|
||||
@@ -157,7 +177,7 @@ if (process.env.DRACHTIO_HOST && !process.env.K8S) {
|
||||
srf.locals.sipAddress = arr[2];
|
||||
if (!process.env.SBC_ACCOUNT_SID) addSbcAddress(arr[2]);
|
||||
}
|
||||
else if (arr && 'tcp' === arr[1] && matcher.contains(arr[2])) {
|
||||
else if (!addedPrivateIp && arr && 'tcp' === arr[1] && matcher.contains(arr[2])) {
|
||||
const hostport = `${arr[2]}:${arr[3]}`;
|
||||
logger.info(`adding sbc private address to redis: ${hostport}`);
|
||||
srf.locals.privateSipAddress = hostport;
|
||||
@@ -166,6 +186,7 @@ if (process.env.DRACHTIO_HOST && !process.env.K8S) {
|
||||
srf.locals.addToRedis();
|
||||
}
|
||||
}
|
||||
srf.locals.sbcPublicIpAddress = parseHostPorts(logger, hostports, srf);
|
||||
});
|
||||
}
|
||||
else {
|
||||
@@ -173,13 +194,13 @@ else {
|
||||
logger.info(`listening in outbound mode on port ${process.env.DRACHTIO_PORT}`);
|
||||
});
|
||||
srf.listen({port: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET});
|
||||
srf.on('connect', (err, hp) => {
|
||||
srf.on('connect', (err, hp, version, localHostports) => {
|
||||
if (err) return this.logger.error({err}, 'Error connecting to drachtio server');
|
||||
logger.info(`connected to drachtio listening on ${hp}`);
|
||||
logger.info(`connected to drachtio ${version} listening on ${hp}, local hostports: ${localHostports}`);
|
||||
|
||||
if (process.env.K8S_FEATURE_SERVER_TRANSPORT === 'tcp') {
|
||||
const matcher = new CIDRMatcher(['192.168.0.0/24', '172.16.0.0/16', '10.0.0.0/8']);
|
||||
const hostports = hp.split(',');
|
||||
const hostports = localHostports ? localHostports.split(',') : hp.split(',');
|
||||
for (const hp of hostports) {
|
||||
const arr = /^(.*)\/(.*):(\d+)$/.exec(hp);
|
||||
if (arr && matcher.contains(arr[2])) {
|
||||
@@ -189,6 +210,7 @@ else {
|
||||
}
|
||||
}
|
||||
}
|
||||
srf.locals.sbcPublicIpAddress = parseHostPorts(logger, hp, srf);
|
||||
});
|
||||
}
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
@@ -254,10 +276,21 @@ if (process.env.K8S || process.env.HTTP_PORT) {
|
||||
});
|
||||
}
|
||||
if ('test' !== process.env.NODE_ENV) {
|
||||
/* update call stats periodically */
|
||||
setInterval(() => {
|
||||
/* update call stats periodically as well as definition of private network cidr */
|
||||
setInterval(async() => {
|
||||
stats.gauge('sbc.sip.calls.count', activeCallIds.size,
|
||||
['direction:inbound', `instance_id:${process.env.INSTANCE_ID || 0}`]);
|
||||
|
||||
const r = await lookupSystemInformation();
|
||||
if (r) {
|
||||
if (r.private_network_cidr !== srf.locals.privateNetworkCidr) {
|
||||
logger.info(`updating private network cidr from ${srf.locals.privateNetworkCidr} to ${r.private_network_cidr}`);
|
||||
srf.locals.privateNetworkCidr = r.private_network_cidr;
|
||||
}
|
||||
if (r.log_level) {
|
||||
logger.level = r.log_level;
|
||||
}
|
||||
}
|
||||
}, 20000);
|
||||
}
|
||||
|
||||
|
||||
+41
-11
@@ -166,6 +166,8 @@ class CallSession extends Emitter {
|
||||
const obj = parseUri(this.req.uri);
|
||||
let proxy, host, uri;
|
||||
|
||||
const scheme = obj.scheme;
|
||||
|
||||
// replace host part of uri if its an ipv4 address, leave it otherwise
|
||||
if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(obj.host)) {
|
||||
debug(`replacing host: was ${obj.host} is ${featureServer}`);
|
||||
@@ -176,8 +178,8 @@ class CallSession extends Emitter {
|
||||
host = obj.host;
|
||||
proxy = `sip:${featureServer}`;
|
||||
}
|
||||
if (obj.user) uri = `${obj.scheme}:${obj.user}@${host}`;
|
||||
else uri = `${obj.scheme}:${host}`;
|
||||
if (obj.user) uri = `${scheme}:${obj.user}@${host}`;
|
||||
else uri = `${scheme}:${host}`;
|
||||
this.logger.info(`uri will be: ${uri}, proxy ${proxy}`);
|
||||
|
||||
try {
|
||||
@@ -213,21 +215,39 @@ class CallSession extends Emitter {
|
||||
createSiprecBody(headers, response.sdp, this.xml.type, this.xml.content) :
|
||||
response.sdp;
|
||||
|
||||
const responseHeaders = {};
|
||||
if (this.req.locals.carrier) {
|
||||
Object.assign(headers, {
|
||||
'X-Originating-Carrier': this.req.locals.carrier,
|
||||
'X-Voip-Carrier-Sid': this.req.locals.voip_carrier_sid
|
||||
});
|
||||
}
|
||||
if (this.req.locals.msTeamsTenantFqdn) {
|
||||
|
||||
// set Contact header based on scenario, and transport protocol
|
||||
let responseHeaders = {};
|
||||
if (isPrivateVoipNetwork(this.req.source_address)) {
|
||||
this.contactHeader = `<${scheme}:${this.privateSipAddress};transport=${this.req.protocol}>`;
|
||||
responseHeaders = {
|
||||
...responseHeaders,
|
||||
'Contact': this.contactHeader
|
||||
};
|
||||
} else if (this.req.locals.msTeamsTenantFqdn) {
|
||||
Object.assign(headers, {'X-MS-Teams-Tenant-FQDN': this.req.locals.msTeamsTenantFqdn});
|
||||
|
||||
// for Microsoft Teams the Contact header must include the tenant FQDN
|
||||
Object.assign(responseHeaders, {
|
||||
this.contactHeader = `sip:${this.req.locals.msTeamsTenantFqdn}`;
|
||||
responseHeaders = {
|
||||
...responseHeaders,
|
||||
Allow: 'INVITE, ACK, OPTIONS, CANCEL, BYE, NOTIFY, UPDATE, PRACK',
|
||||
Contact: `sip:${this.req.locals.msTeamsTenantFqdn}`
|
||||
});
|
||||
Contact: this.contactHeader
|
||||
};
|
||||
}
|
||||
else {
|
||||
const hostport = this.srf.locals.sbcPublicIpAddress[this.req.protocol];
|
||||
this.contactHeader = `<${scheme}:${hostport};transport=${this.req.protocol}>`;
|
||||
responseHeaders = {
|
||||
...responseHeaders,
|
||||
'Contact': this.contactHeader
|
||||
};
|
||||
}
|
||||
if (this.req.locals.application_sid) {
|
||||
Object.assign(headers, {'X-Application-Sid': this.req.locals.application_sid});
|
||||
@@ -638,7 +658,12 @@ Duration=${payload.duration} `
|
||||
}
|
||||
else {
|
||||
this.logger.info('got a reINVITE with no SDP; just respond with our current offer');
|
||||
res.send(200, {body: dlg.local.sdp});
|
||||
res.send(200, {
|
||||
headers: {
|
||||
'Contact': this.contactHeader
|
||||
},
|
||||
body: dlg.local.sdp
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -691,7 +716,12 @@ Duration=${payload.duration} `
|
||||
res.send(488);
|
||||
throw new Error(`_onReinvite: rtpengine failed: ${JSON.stringify(response)}`);
|
||||
}
|
||||
res.send(200, {body: response.sdp});
|
||||
res.send(200, {
|
||||
headers: {
|
||||
'Contact': this.contactHeader
|
||||
},
|
||||
body: response.sdp
|
||||
});
|
||||
} catch (err) {
|
||||
res.send(err.status || 500);
|
||||
this.logger.error(err, 'Error handling reinvite');
|
||||
@@ -971,8 +1001,8 @@ Duration=${payload.duration} `
|
||||
const response = await this.uas.request({
|
||||
method: 'REFER',
|
||||
headers: {
|
||||
'Refer-To': stringifyUri(uri),
|
||||
'Referred-By': stringifyUri(u),
|
||||
'Refer-To': `<${stringifyUri(uri)}>`,
|
||||
'Referred-By': `<${stringifyUri(u)}>`,
|
||||
...customHeaders
|
||||
}
|
||||
});
|
||||
|
||||
+48
-15
@@ -7,7 +7,7 @@ const sqlSelectSPForAccount = 'SELECT service_provider_sid FROM accounts WHERE a
|
||||
|
||||
const sqlSelectAllCarriersForAccountByRealm =
|
||||
`SELECT sg.sip_gateway_sid, sg.voip_carrier_sid, vc.name, vc.account_sid,
|
||||
vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask
|
||||
vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask, sg.pad_crypto
|
||||
FROM sip_gateways sg, voip_carriers vc, accounts acc
|
||||
WHERE acc.sip_realm = ?
|
||||
AND vc.account_sid = acc.account_sid
|
||||
@@ -17,7 +17,7 @@ AND sg.voip_carrier_sid = vc.voip_carrier_sid`;
|
||||
|
||||
const sqlSelectAllCarriersForSPByRealm =
|
||||
`SELECT sg.sip_gateway_sid, sg.voip_carrier_sid, vc.name, vc.account_sid,
|
||||
vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask
|
||||
vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask, sg.pad_crypto
|
||||
FROM sip_gateways sg, voip_carriers vc, accounts acc
|
||||
WHERE acc.sip_realm = ?
|
||||
AND vc.service_provider_sid = acc.service_provider_sid
|
||||
@@ -28,7 +28,7 @@ AND sg.voip_carrier_sid = vc.voip_carrier_sid`;
|
||||
|
||||
const sqlSelectAllGatewaysForSP =
|
||||
`SELECT sg.sip_gateway_sid, sg.voip_carrier_sid, vc.name, vc.service_provider_sid,
|
||||
vc.account_sid, vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask
|
||||
vc.account_sid, vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask, sg.pad_crypto
|
||||
FROM sip_gateways sg, voip_carriers vc
|
||||
WHERE sg.voip_carrier_sid = vc.voip_carrier_sid
|
||||
AND vc.service_provider_sid IS NOT NULL
|
||||
@@ -37,7 +37,7 @@ AND sg.inbound = 1`;
|
||||
|
||||
const sqlCarriersForAccountBySid =
|
||||
`SELECT sg.sip_gateway_sid, sg.voip_carrier_sid, vc.name, vc.account_sid,
|
||||
vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask
|
||||
vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask, sg.pad_crypto
|
||||
FROM sip_gateways sg, voip_carriers vc, accounts acc
|
||||
WHERE acc.account_sid = ?
|
||||
AND vc.account_sid = acc.account_sid
|
||||
@@ -67,7 +67,7 @@ AND outbound = 1`;
|
||||
|
||||
const sqlSelectCarrierRequiringRegistration = `
|
||||
SELECT sg.sip_gateway_sid, sg.voip_carrier_sid, vc.name, vc.service_provider_sid, vc.account_sid,
|
||||
vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask
|
||||
vc.application_sid, sg.inbound, sg.outbound, sg.is_active, sg.ipv4, sg.netmask, sg.pad_crypto
|
||||
FROM sip_gateways sg, voip_carriers vc
|
||||
WHERE sg.voip_carrier_sid = vc.voip_carrier_sid
|
||||
AND vc.requires_register = 1
|
||||
@@ -173,21 +173,51 @@ module.exports = (srf, logger) => {
|
||||
const gw = gwAcc
|
||||
.concat(gwSP)
|
||||
.sort((a, b) => b.netmask - a.netmask);
|
||||
const selected = gw.find(gatewayMatchesSourceAddress.bind(null, logger, req.source_address));
|
||||
if (selected) {
|
||||
const gateways = gw.filter(gatewayMatchesSourceAddress.bind(null, logger, req.source_address));
|
||||
let voip_carriers = gateways.map((gw) => {
|
||||
return {
|
||||
voip_carrier_sid: gw.voip_carrier_sid,
|
||||
name: gw.name,
|
||||
service_provider_sid: gw.service_provider_sid,
|
||||
account_sid: gw.account_sid,
|
||||
application_sid: gw.application_sid
|
||||
};
|
||||
});
|
||||
/* remove duplicates, winnow down to voip_carriers, not gateways */
|
||||
if (voip_carriers.length > 1) {
|
||||
voip_carriers = [...new Set(voip_carriers.map(JSON.stringify))].map(JSON.parse);
|
||||
}
|
||||
if (voip_carriers.length) {
|
||||
/* we have one or more matches. Now check for one with a provisioned phone number matching the DID */
|
||||
const vc_sids = voip_carriers.map((m) => `'${m.voip_carrier_sid}'`).join(',');
|
||||
const sql =
|
||||
`SELECT application_sid FROM phone_numbers WHERE number = '${did}'
|
||||
AND voip_carrier_sid = '${selected.voip_carrier_sid}'
|
||||
`SELECT * FROM phone_numbers WHERE number = '${did}'
|
||||
AND voip_carrier_sid IN (${vc_sids})
|
||||
AND account_sid = '${a[0].account_sid}'`;
|
||||
logger.debug({selected, sql, did}, 'looking up DID');
|
||||
const [r] = await pp.query(sql);
|
||||
logger.debug({voip_carriers, sql, did}, 'looking up DID');
|
||||
|
||||
const [r] = await pp.query(sql);
|
||||
if (r.length > 1) {
|
||||
logger.info({r},
|
||||
'multiple carriers with the same gateway have the same number provisioned for the same account'
|
||||
+ ' -- cannot determine which one to use');
|
||||
return {
|
||||
fromCarrier: true,
|
||||
error: 'Multiple carriers with the same gateway are attempting to route the same number for this account'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* We have one or no routes for this phone number, carrier and account combination
|
||||
* Either take the matching gateway, or if no route has matched, the first gateway of the ones available.
|
||||
*/
|
||||
const gateway = r[0] ? gateways.find((m) => m.voip_carrier_sid === r[0]?.voip_carrier_sid) : gateways[0];
|
||||
return {
|
||||
fromCarrier: true,
|
||||
gateway: selected,
|
||||
gateway: gateway,
|
||||
service_provider_sid: a[0].service_provider_sid,
|
||||
account_sid: a[0].account_sid,
|
||||
application_sid: r[0]?.application_sid || selected.application_sid,
|
||||
application_sid: r[0]?.application_sid || gateway.application_sid,
|
||||
account: a[0]
|
||||
};
|
||||
}
|
||||
@@ -270,11 +300,14 @@ module.exports = (srf, logger) => {
|
||||
name: gw.name,
|
||||
service_provider_sid: gw.service_provider_sid,
|
||||
account_sid: gw.account_sid,
|
||||
application_sid: gw.application_sid
|
||||
application_sid: gw.application_sid,
|
||||
pad_crypto: gw.pad_crypto
|
||||
};
|
||||
});
|
||||
/* remove duplicates, winnow down to voip_carriers, not gateways */
|
||||
matches = [...new Set(matches.map(JSON.stringify))].map(JSON.parse);
|
||||
if (matches.length > 1) {
|
||||
matches = [...new Set(matches.map(JSON.stringify))].map(JSON.parse);
|
||||
}
|
||||
if (matches.length) {
|
||||
/* we have one or more matches. Now check for one with a provisioned phone number matching the DID */
|
||||
const vc_sids = matches.map((m) => `'${m.voip_carrier_sid}'`).join(',');
|
||||
|
||||
+4
-2
@@ -235,6 +235,8 @@ module.exports = function(srf, logger) {
|
||||
callId: req.get('Call-ID'),
|
||||
service_provider_sid: req.locals.service_provider_sid,
|
||||
account_sid: req.locals.account_sid
|
||||
}, {
|
||||
...(req.locals.account.enable_debug_log && {level: 'debug'})
|
||||
});
|
||||
|
||||
next();
|
||||
@@ -312,7 +314,7 @@ module.exports = function(srf, logger) {
|
||||
alert_type: AlertType.ACCOUNT_CALL_LIMIT,
|
||||
service_provider_sid: service_provider_sid,
|
||||
account_sid,
|
||||
count: calls
|
||||
count: account_limit
|
||||
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
|
||||
res.send(503, 'Max Account Calls In Progress', {
|
||||
headers: {
|
||||
@@ -327,7 +329,7 @@ module.exports = function(srf, logger) {
|
||||
writeAlerts({
|
||||
alert_type: AlertType.SP_CALL_LIMIT,
|
||||
service_provider_sid: service_provider_sid,
|
||||
count: callsSP
|
||||
count: sp_limit
|
||||
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
|
||||
res.send(503, 'Max Service Provider Calls In Progress', {
|
||||
headers: {
|
||||
|
||||
+60
-17
@@ -11,7 +11,7 @@ const isWSS = (req) => {
|
||||
|
||||
const getAppserver = (srf) => {
|
||||
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) {
|
||||
@@ -19,10 +19,13 @@ function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp, teams = false) {
|
||||
const srtpCopy = JSON.parse(JSON.stringify(srtpCharacteristics));
|
||||
const from = req.getParsedHeader('from');
|
||||
const srtpOpts = teams ? srtpCopy['teams'] : srtpCopy['default'];
|
||||
if ((req.locals.gateway?.pad_crypto || 0) > 0) {
|
||||
srtpOpts.flags.push('pad crypto');
|
||||
}
|
||||
const dstOpts = dstIsUsingSrtp ? srtpOpts : rtpCopy;
|
||||
const srcOpts = srcIsUsingSrtp ? srtpOpts : rtpCopy;
|
||||
|
||||
/* Allow Feature server to inject DTMF to both leg except call from Teams*/
|
||||
/* Allow Feature server to inject DTMF to both leg except call from Teams */
|
||||
if (!teams) {
|
||||
dstOpts.flags.push('inject DTMF');
|
||||
srcOpts.flags.push('inject DTMF');
|
||||
@@ -30,7 +33,8 @@ function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp, teams = false) {
|
||||
const common = {
|
||||
'call-id': req.get('Call-ID'),
|
||||
'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' } })
|
||||
};
|
||||
return {
|
||||
common,
|
||||
@@ -73,7 +77,7 @@ const equalsIgnoreOrder = (a, b) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const systemHealth = async(redisClient, ping, getCount) => {
|
||||
const systemHealth = async(redisClient, ping, getCount) => {
|
||||
await Promise.all([redisClient.ping(), ping()]);
|
||||
return getCount();
|
||||
};
|
||||
@@ -111,8 +115,8 @@ const createHealthCheckApp = (port, logger) => {
|
||||
};
|
||||
|
||||
const nudgeCallCounts = async(logger, sids, nudgeOperator, writers) => {
|
||||
const {service_provider_sid, account_sid, application_sid} = sids;
|
||||
const {writeCallCount, writeCallCountSP, writeCallCountApp} = writers;
|
||||
const { service_provider_sid, account_sid, application_sid } = sids;
|
||||
const { writeCallCount, writeCallCountSP, writeCallCountApp } = writers;
|
||||
const nudges = [];
|
||||
const writes = [];
|
||||
|
||||
@@ -144,28 +148,33 @@ const nudgeCallCounts = async(logger, sids, nudgeOperator, writers) => {
|
||||
const [callsSP, calls, callsApp] = await Promise.all(nudges);
|
||||
logger.debug({
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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 */
|
||||
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) {
|
||||
logger.error(err, 'error incrementing call counts');
|
||||
}
|
||||
|
||||
return {callsSP: null, calls: null, callsApp: null};
|
||||
return { callsSP: null, calls: null, callsApp: null };
|
||||
};
|
||||
|
||||
const roundTripTime = (startAt) => {
|
||||
@@ -196,13 +205,45 @@ const isMSTeamsCIDR = (ip) => {
|
||||
};
|
||||
|
||||
const isPrivateVoipNetwork = (ip) => {
|
||||
if (process.env.PRIVATE_VOIP_NETWORK_CIDR) {
|
||||
const matcher = new CIDRMatcher(process.env.PRIVATE_VOIP_NETWORK_CIDR.split(','));
|
||||
return matcher.contains(ip);
|
||||
const {srf, logger} = require('..');
|
||||
const {privateNetworkCidr} = srf.locals;
|
||||
if (privateNetworkCidr) {
|
||||
try {
|
||||
const matcher = new CIDRMatcher(privateNetworkCidr.split(','));
|
||||
return matcher.contains(ip);
|
||||
} catch (err) {
|
||||
logger.info({err, privateNetworkCidr},
|
||||
'Error checking private network CIDR, probably misconfigured must be a comma separated list of CIDRs');
|
||||
}
|
||||
}
|
||||
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 = {
|
||||
isWSS,
|
||||
SdpWantsSrtp,
|
||||
@@ -220,5 +261,7 @@ module.exports = {
|
||||
roundTripTime,
|
||||
parseConnectionIp,
|
||||
isMSTeamsCIDR,
|
||||
isPrivateVoipNetwork
|
||||
isPrivateVoipNetwork,
|
||||
parseHostPorts
|
||||
};
|
||||
|
||||
|
||||
Generated
+1603
-1499
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "sbc-inbound",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.2",
|
||||
"main": "app.js",
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
"node": ">= 18.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"sip",
|
||||
@@ -22,15 +22,16 @@
|
||||
"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/ ",
|
||||
"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": {
|
||||
"@jambonz/db-helpers": "^0.9.3",
|
||||
"@jambonz/db-helpers": "^0.9.7",
|
||||
"@jambonz/http-health-check": "^0.0.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.8.8",
|
||||
"@jambonz/rtpengine-utils": "^0.4.4",
|
||||
"@jambonz/siprec-client-utils": "^0.2.7",
|
||||
"@jambonz/stats-collector": "^0.1.9",
|
||||
"@jambonz/stats-collector": "^0.1.10",
|
||||
"@jambonz/time-series": "^0.2.8",
|
||||
"@jambonz/digest-utils": "^0.0.5",
|
||||
"@aws-sdk/client-sns": "^3.549.0",
|
||||
@@ -39,7 +40,7 @@
|
||||
"cidr-matcher": "^2.1.1",
|
||||
"debug": "^4.3.4",
|
||||
"drachtio-fn-b2b-sugar": "0.1.0",
|
||||
"drachtio-srf": "^4.5.31",
|
||||
"drachtio-srf": "^4.5.38",
|
||||
"express": "^4.19.2",
|
||||
"pino": "^8.20.0",
|
||||
"verify-aws-sns-signature": "^0.1.0",
|
||||
|
||||
+126
-7
@@ -14,14 +14,22 @@ DROP TABLE IF EXISTS beta_invite_codes;
|
||||
|
||||
DROP TABLE IF EXISTS call_routes;
|
||||
|
||||
DROP TABLE IF EXISTS clients;
|
||||
|
||||
DROP TABLE IF EXISTS dns_records;
|
||||
|
||||
DROP TABLE IF EXISTS lcr;
|
||||
|
||||
DROP TABLE IF EXISTS lcr_carrier_set_entry;
|
||||
|
||||
DROP TABLE IF EXISTS lcr_routes;
|
||||
|
||||
DROP TABLE IF EXISTS password_settings;
|
||||
|
||||
DROP TABLE IF EXISTS user_permissions;
|
||||
|
||||
DROP TABLE IF EXISTS permissions;
|
||||
|
||||
DROP TABLE IF EXISTS predefined_sip_gateways;
|
||||
|
||||
DROP TABLE IF EXISTS predefined_smpp_gateways;
|
||||
@@ -46,8 +54,12 @@ DROP TABLE IF EXISTS signup_history;
|
||||
|
||||
DROP TABLE IF EXISTS smpp_addresses;
|
||||
|
||||
DROP TABLE IF EXISTS google_custom_voices;
|
||||
|
||||
DROP TABLE IF EXISTS speech_credentials;
|
||||
|
||||
DROP TABLE IF EXISTS system_information;
|
||||
|
||||
DROP TABLE IF EXISTS users;
|
||||
|
||||
DROP TABLE IF EXISTS smpp_gateways;
|
||||
@@ -120,6 +132,19 @@ application_sid CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (call_route_sid)
|
||||
) COMMENT='a regex-based pattern match for call routing';
|
||||
|
||||
CREATE TABLE clients
|
||||
(
|
||||
client_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
account_sid CHAR(36) NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT 1,
|
||||
username VARCHAR(64),
|
||||
password VARCHAR(1024),
|
||||
allow_direct_app_calling BOOLEAN NOT NULL DEFAULT 1,
|
||||
allow_direct_queue_calling BOOLEAN NOT NULL DEFAULT 1,
|
||||
allow_direct_user_calling BOOLEAN NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (client_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE dns_records
|
||||
(
|
||||
dns_record_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
@@ -132,11 +157,23 @@ PRIMARY KEY (dns_record_sid)
|
||||
CREATE TABLE lcr_routes
|
||||
(
|
||||
lcr_route_sid CHAR(36),
|
||||
lcr_sid CHAR(36) NOT NULL,
|
||||
regex VARCHAR(32) NOT NULL COMMENT 'regex-based pattern match against dialed number, used for LCR routing of PSTN calls',
|
||||
description VARCHAR(1024),
|
||||
priority INTEGER NOT NULL UNIQUE COMMENT 'lower priority routes are attempted first',
|
||||
priority INTEGER NOT NULL COMMENT 'lower priority routes are attempted first',
|
||||
PRIMARY KEY (lcr_route_sid)
|
||||
) COMMENT='Least cost routing table';
|
||||
) COMMENT='An ordered list of digit patterns in an LCR table. The patterns are tested in sequence until one matches';
|
||||
|
||||
CREATE TABLE lcr
|
||||
(
|
||||
lcr_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
name VARCHAR(64) COMMENT 'User-assigned name for this LCR table',
|
||||
is_active BOOLEAN NOT NULL DEFAULT 1,
|
||||
default_carrier_set_entry_sid CHAR(36) COMMENT 'default carrier/route to use when no digit match based results are found.',
|
||||
service_provider_sid CHAR(36),
|
||||
account_sid CHAR(36),
|
||||
PRIMARY KEY (lcr_sid)
|
||||
) COMMENT='An LCR (least cost routing) table that is used by a service provider or account to make decisions about routing outbound calls when multiple carriers are available.';
|
||||
|
||||
CREATE TABLE password_settings
|
||||
(
|
||||
@@ -145,6 +182,14 @@ require_digit BOOLEAN NOT NULL DEFAULT false,
|
||||
require_special_character BOOLEAN NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
CREATE TABLE permissions
|
||||
(
|
||||
permission_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
name VARCHAR(32) NOT NULL UNIQUE ,
|
||||
description VARCHAR(255),
|
||||
PRIMARY KEY (permission_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE predefined_carriers
|
||||
(
|
||||
predefined_carrier_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
@@ -236,7 +281,10 @@ CREATE TABLE sbc_addresses
|
||||
sbc_address_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
ipv4 VARCHAR(255) NOT NULL,
|
||||
port INTEGER NOT NULL DEFAULT 5060,
|
||||
tls_port INTEGER,
|
||||
wss_port INTEGER,
|
||||
service_provider_sid CHAR(36),
|
||||
last_updated DATETIME,
|
||||
PRIMARY KEY (sbc_address_sid)
|
||||
);
|
||||
|
||||
@@ -292,9 +340,29 @@ last_tested DATETIME,
|
||||
tts_tested_ok BOOLEAN,
|
||||
stt_tested_ok BOOLEAN,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
label VARCHAR(64),
|
||||
PRIMARY KEY (speech_credential_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE google_custom_voices
|
||||
(
|
||||
google_custom_voice_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
speech_credential_sid CHAR(36) NOT NULL,
|
||||
model VARCHAR(512) NOT NULL,
|
||||
reported_usage ENUM('REPORTED_USAGE_UNSPECIFIED','REALTIME','OFFLINE') DEFAULT 'REALTIME',
|
||||
name VARCHAR(64) NOT NULL,
|
||||
PRIMARY KEY (google_custom_voice_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE system_information
|
||||
(
|
||||
domain_name VARCHAR(255),
|
||||
sip_domain_name VARCHAR(255),
|
||||
monitoring_domain_name VARCHAR(255),
|
||||
private_network_cidr VARCHAR(8192),
|
||||
log_level ENUM('info', 'debug') NOT NULL DEFAULT 'info'
|
||||
);
|
||||
|
||||
CREATE TABLE users
|
||||
(
|
||||
user_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
@@ -345,9 +413,18 @@ smpp_inbound_password VARCHAR(64),
|
||||
register_from_user VARCHAR(128),
|
||||
register_from_domain VARCHAR(255),
|
||||
register_public_ip_in_contact BOOLEAN NOT NULL DEFAULT false,
|
||||
register_status VARCHAR(4096),
|
||||
PRIMARY KEY (voip_carrier_sid)
|
||||
) COMMENT='A Carrier or customer PBX that can send or receive calls';
|
||||
|
||||
CREATE TABLE user_permissions
|
||||
(
|
||||
user_permissions_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
user_sid CHAR(36) NOT NULL,
|
||||
permission_sid CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (user_permissions_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE smpp_gateways
|
||||
(
|
||||
smpp_gateway_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
@@ -365,7 +442,7 @@ PRIMARY KEY (smpp_gateway_sid)
|
||||
CREATE TABLE phone_numbers
|
||||
(
|
||||
phone_number_sid CHAR(36) UNIQUE ,
|
||||
number VARCHAR(32) NOT NULL UNIQUE ,
|
||||
number VARCHAR(132) NOT NULL,
|
||||
voip_carrier_sid CHAR(36),
|
||||
account_sid CHAR(36),
|
||||
application_sid CHAR(36),
|
||||
@@ -378,11 +455,15 @@ CREATE TABLE sip_gateways
|
||||
sip_gateway_sid CHAR(36),
|
||||
ipv4 VARCHAR(128) NOT NULL COMMENT 'ip address or DNS name of the gateway. For gateways providing inbound calling service, ip address is required.',
|
||||
netmask INTEGER NOT NULL DEFAULT 32,
|
||||
port INTEGER NOT NULL DEFAULT 5060 COMMENT 'sip signaling port',
|
||||
port INTEGER COMMENT 'sip signaling port',
|
||||
inbound BOOLEAN NOT NULL COMMENT 'if true, whitelist this IP to allow inbound calls from the gateway',
|
||||
outbound BOOLEAN NOT NULL COMMENT 'if true, include in least-cost routing when placing calls to the PSTN',
|
||||
voip_carrier_sid CHAR(36) NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT 1,
|
||||
send_options_ping BOOLEAN NOT NULL DEFAULT 0,
|
||||
use_sips_scheme BOOLEAN NOT NULL DEFAULT 0,
|
||||
pad_crypto BOOLEAN NOT NULL DEFAULT 0,
|
||||
protocol ENUM('udp','tcp','tls', 'tls/srtp') DEFAULT 'udp' COMMENT 'Outbound call protocol',
|
||||
PRIMARY KEY (sip_gateway_sid)
|
||||
) COMMENT='A whitelisted sip gateway used for origination/termination';
|
||||
|
||||
@@ -415,12 +496,24 @@ account_sid CHAR(36) COMMENT 'account that this application belongs to (if null,
|
||||
call_hook_sid CHAR(36) COMMENT 'webhook to call for inbound calls ',
|
||||
call_status_hook_sid CHAR(36) COMMENT 'webhook to call for call status events',
|
||||
messaging_hook_sid CHAR(36) COMMENT 'webhook to call for inbound SMS/MMS ',
|
||||
app_json TEXT,
|
||||
speech_synthesis_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||
speech_synthesis_language VARCHAR(12) NOT NULL DEFAULT 'en-US',
|
||||
speech_synthesis_voice VARCHAR(64),
|
||||
speech_synthesis_voice VARCHAR(256),
|
||||
speech_synthesis_label VARCHAR(64),
|
||||
speech_recognizer_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||
speech_recognizer_language VARCHAR(64) NOT NULL DEFAULT 'en-US',
|
||||
speech_recognizer_label VARCHAR(64),
|
||||
use_for_fallback_speech BOOLEAN DEFAULT false,
|
||||
fallback_speech_synthesis_vendor VARCHAR(64),
|
||||
fallback_speech_synthesis_language VARCHAR(12),
|
||||
fallback_speech_synthesis_voice VARCHAR(256),
|
||||
fallback_speech_synthesis_label VARCHAR(64),
|
||||
fallback_speech_recognizer_vendor VARCHAR(64),
|
||||
fallback_speech_recognizer_language VARCHAR(64),
|
||||
fallback_speech_recognizer_label VARCHAR(64),
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
record_all_calls BOOLEAN NOT NULL DEFAULT false,
|
||||
PRIMARY KEY (application_sid)
|
||||
) COMMENT='A defined set of behaviors to be applied to phone calls ';
|
||||
|
||||
@@ -458,6 +551,10 @@ subspace_client_secret VARCHAR(255),
|
||||
subspace_sip_teleport_id VARCHAR(255),
|
||||
subspace_sip_teleport_destinations VARCHAR(255),
|
||||
siprec_hook_sid CHAR(36),
|
||||
record_all_calls BOOLEAN NOT NULL DEFAULT false,
|
||||
record_format VARCHAR(16) NOT NULL DEFAULT 'mp3',
|
||||
bucket_credential VARCHAR(8192) COMMENT 'credential used to authenticate with storage service',
|
||||
enable_debug_log BOOLEAN NOT NULL DEFAULT false,
|
||||
PRIMARY KEY (account_sid)
|
||||
) COMMENT='An enterprise that uses the platform for comm services';
|
||||
|
||||
@@ -478,9 +575,21 @@ ALTER TABLE call_routes ADD FOREIGN KEY account_sid_idxfk_3 (account_sid) REFERE
|
||||
|
||||
ALTER TABLE call_routes ADD FOREIGN KEY application_sid_idxfk (application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
CREATE INDEX client_sid_idx ON clients (client_sid);
|
||||
ALTER TABLE clients ADD CONSTRAINT account_sid_idxfk_13 FOREIGN KEY account_sid_idxfk_13 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX dns_record_sid_idx ON dns_records (dns_record_sid);
|
||||
ALTER TABLE dns_records ADD FOREIGN KEY account_sid_idxfk_4 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX lcr_sid_idx ON lcr_routes (lcr_sid);
|
||||
ALTER TABLE lcr_routes ADD FOREIGN KEY lcr_sid_idxfk (lcr_sid) REFERENCES lcr (lcr_sid);
|
||||
|
||||
CREATE INDEX lcr_sid_idx ON lcr (lcr_sid);
|
||||
ALTER TABLE lcr ADD FOREIGN KEY default_carrier_set_entry_sid_idxfk (default_carrier_set_entry_sid) REFERENCES lcr_carrier_set_entry (lcr_carrier_set_entry_sid);
|
||||
|
||||
CREATE INDEX service_provider_sid_idx ON lcr (service_provider_sid);
|
||||
CREATE INDEX account_sid_idx ON lcr (account_sid);
|
||||
CREATE INDEX permission_sid_idx ON permissions (permission_sid);
|
||||
CREATE INDEX predefined_carrier_sid_idx ON predefined_carriers (predefined_carrier_sid);
|
||||
CREATE INDEX predefined_sip_gateway_sid_idx ON predefined_sip_gateways (predefined_sip_gateway_sid);
|
||||
CREATE INDEX predefined_carrier_sid_idx ON predefined_sip_gateways (predefined_carrier_sid);
|
||||
@@ -533,8 +642,6 @@ CREATE INDEX smpp_address_sid_idx ON smpp_addresses (smpp_address_sid);
|
||||
CREATE INDEX service_provider_sid_idx ON smpp_addresses (service_provider_sid);
|
||||
ALTER TABLE smpp_addresses ADD FOREIGN KEY service_provider_sid_idxfk_4 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE UNIQUE INDEX speech_credentials_idx_1 ON speech_credentials (vendor,account_sid);
|
||||
|
||||
CREATE INDEX speech_credential_sid_idx ON speech_credentials (speech_credential_sid);
|
||||
CREATE INDEX service_provider_sid_idx ON speech_credentials (service_provider_sid);
|
||||
ALTER TABLE speech_credentials ADD FOREIGN KEY service_provider_sid_idxfk_5 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
@@ -542,6 +649,10 @@ ALTER TABLE speech_credentials ADD FOREIGN KEY service_provider_sid_idxfk_5 (ser
|
||||
CREATE INDEX account_sid_idx ON speech_credentials (account_sid);
|
||||
ALTER TABLE speech_credentials ADD FOREIGN KEY account_sid_idxfk_8 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX google_custom_voice_sid_idx ON google_custom_voices (google_custom_voice_sid);
|
||||
CREATE INDEX speech_credential_sid_idx ON google_custom_voices (speech_credential_sid);
|
||||
ALTER TABLE google_custom_voices ADD FOREIGN KEY speech_credential_sid_idxfk (speech_credential_sid) REFERENCES speech_credentials (speech_credential_sid) ON DELETE CASCADE;
|
||||
|
||||
CREATE INDEX user_sid_idx ON users (user_sid);
|
||||
CREATE INDEX email_idx ON users (email);
|
||||
CREATE INDEX phone_idx ON users (phone);
|
||||
@@ -561,10 +672,18 @@ ALTER TABLE voip_carriers ADD FOREIGN KEY service_provider_sid_idxfk_7 (service_
|
||||
|
||||
ALTER TABLE voip_carriers ADD FOREIGN KEY application_sid_idxfk_2 (application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
CREATE INDEX user_permissions_sid_idx ON user_permissions (user_permissions_sid);
|
||||
CREATE INDEX user_sid_idx ON user_permissions (user_sid);
|
||||
ALTER TABLE user_permissions ADD FOREIGN KEY user_sid_idxfk (user_sid) REFERENCES users (user_sid) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE user_permissions ADD FOREIGN KEY permission_sid_idxfk (permission_sid) REFERENCES permissions (permission_sid);
|
||||
|
||||
CREATE INDEX smpp_gateway_sid_idx ON smpp_gateways (smpp_gateway_sid);
|
||||
CREATE INDEX voip_carrier_sid_idx ON smpp_gateways (voip_carrier_sid);
|
||||
ALTER TABLE smpp_gateways ADD FOREIGN KEY voip_carrier_sid_idxfk (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
|
||||
CREATE UNIQUE INDEX phone_numbers_unique_idx_voip_carrier_number ON phone_numbers (number,voip_carrier_sid);
|
||||
|
||||
CREATE INDEX phone_number_sid_idx ON phone_numbers (phone_number_sid);
|
||||
CREATE INDEX number_idx ON phone_numbers (number);
|
||||
CREATE INDEX voip_carrier_sid_idx ON phone_numbers (voip_carrier_sid);
|
||||
|
||||
@@ -2,3 +2,4 @@ require('./docker_start');
|
||||
require('./create-test-db');
|
||||
require('./sip-tests');
|
||||
require('./docker_stop');
|
||||
require('./utils');
|
||||
|
||||
@@ -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