mirror of
https://github.com/jambonz/sbc-outbound.git
synced 2026-10-04 02:04:19 +00:00
Compare commits
226
Commits
v0.6.4
...
v0.9.5-rc15
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd0d360561 | ||
|
|
32d82ed67d | ||
|
|
c17f27ab2c | ||
|
|
2fc570f731 | ||
|
|
c87f831868 | ||
|
|
616228bf09 | ||
|
|
55fef10f0e | ||
|
|
9357920f76 | ||
|
|
6771cbdefc | ||
|
|
e422c2ed9c | ||
|
|
fd5e2f1a6c | ||
|
|
5cc37265a9 | ||
|
|
1a9c438321 | ||
|
|
fb62fd9bf4 | ||
|
|
e02e0fa8c1 | ||
|
|
dd198105e3 | ||
|
|
a34491a356 | ||
|
|
3a467921a6 | ||
|
|
9cb75a0b6b | ||
|
|
28dae50202 | ||
|
|
0b45e766b9 | ||
|
|
7380457b5a | ||
|
|
9feb6f3c8f | ||
|
|
c449feeb9c | ||
|
|
b2abe9891e | ||
|
|
a89b54b19e | ||
|
|
ab0f3ed2b5 | ||
|
|
d8ae824559 | ||
|
|
334db6f84f | ||
|
|
c762accce8 | ||
|
|
c6c63e26da | ||
|
|
916d577b75 | ||
|
|
b976a62a60 | ||
|
|
5e2369e5e8 | ||
|
|
41528db630 | ||
|
|
c22104fbe2 | ||
|
|
3701e20295 | ||
|
|
821275cb67 | ||
|
|
f5ac51a8f7 | ||
|
|
0cf90f37b8 | ||
|
|
62fb716236 | ||
|
|
b8fdf9f429 | ||
|
|
503aa73bc6 | ||
|
|
a7406ddb8a | ||
|
|
bf1c670b40 | ||
|
|
b21eaad1ff | ||
|
|
c671fbdbd0 | ||
|
|
73e3779eb1 | ||
|
|
9ad7dc76c7 | ||
|
|
1baf7fa824 | ||
|
|
d2807c69ed | ||
|
|
3e6dc35e86 | ||
|
|
16a4709b7d | ||
|
|
01adb5cbf0 | ||
|
|
49ba872f51 | ||
|
|
591b8f2f8a | ||
|
|
16ac56e50e | ||
|
|
9412790366 | ||
|
|
453b27d6e1 | ||
|
|
4ba3d5b2b0 | ||
|
|
bc3e2334ed | ||
|
|
d70a01578d | ||
|
|
eb0de5b0ee | ||
|
|
d684fbb59b | ||
|
|
d86d03c792 | ||
|
|
b93e220fef | ||
|
|
43ee944023 | ||
|
|
5a6a9b34d3 | ||
|
|
1b215ed77b | ||
|
|
0b141c55bf | ||
|
|
f7bd1f2bf5 | ||
|
|
454986edcd | ||
|
|
114d34def4 | ||
|
|
6cf59732fb | ||
|
|
7f2bce927d | ||
|
|
41eaa4286f | ||
|
|
9fe731ab65 | ||
|
|
0977dd16b3 | ||
|
|
33971ed6d8 | ||
|
|
6200f2b50a | ||
|
|
7e13c5559f | ||
|
|
4343b01505 | ||
|
|
81d7a9f1e6 | ||
|
|
695e44da29 | ||
|
|
9b1a70d4a5 | ||
|
|
db38269a77 | ||
|
|
337c24df41 | ||
|
|
554cdbcacf | ||
|
|
d50272d7da | ||
|
|
b2c4199c04 | ||
|
|
f79f29bd7e | ||
|
|
738429a46f | ||
|
|
9015ac4e13 | ||
|
|
750294eab2 | ||
|
|
76558cc5c9 | ||
|
|
d604440907 | ||
|
|
039a60c1b7 | ||
|
|
e00379d119 | ||
|
|
5174e140a2 | ||
|
|
7923ececaf | ||
|
|
66974c467d | ||
|
|
95bd061e2d | ||
|
|
2f53c0bb4f | ||
|
|
f03308b11d | ||
|
|
7c91ac9d68 | ||
|
|
fcffa1041c | ||
|
|
a6d5b25e40 | ||
|
|
5cba07ee44 | ||
|
|
579e21b5bf | ||
|
|
8864ab1430 | ||
|
|
2187b03ca6 | ||
|
|
0808eeeda4 | ||
|
|
f841e88bef | ||
|
|
b7873654b8 | ||
|
|
b6f9f214c3 | ||
|
|
dbb93732bf | ||
|
|
a553105e55 | ||
|
|
36b2c752da | ||
|
|
f6c0ee6c0d | ||
|
|
96daad8ea1 | ||
|
|
b68edf425d | ||
|
|
6d44c6c986 | ||
|
|
6d593cbc7d | ||
|
|
35b15c1f2c | ||
|
|
7f1d1d61db | ||
|
|
20c17fd723 | ||
|
|
da6fa1da1b | ||
|
|
f74dff3b59 | ||
|
|
d9c4e01c36 | ||
|
|
3d902c65a4 | ||
|
|
d641504797 | ||
|
|
b871812a70 | ||
|
|
e123a2ef88 | ||
|
|
775e63518a | ||
|
|
307cf9bd65 | ||
|
|
f48ca4821e | ||
|
|
43ed2bafe1 | ||
|
|
5ee041bdee | ||
|
|
5bbe6a8752 | ||
|
|
2e5d609bab | ||
|
|
8f598bf7b0 | ||
|
|
c7d717b3ee | ||
|
|
e9209b37ca | ||
|
|
3b98dc6ec2 | ||
|
|
1c84dd799c | ||
|
|
4338ae9411 | ||
|
|
e17e7dbddd | ||
|
|
254479e289 | ||
|
|
a10a311dcb | ||
|
|
806cb89c37 | ||
|
|
fffa2748d1 | ||
|
|
76625c7596 | ||
|
|
3b0f7ff6eb | ||
|
|
33c75acc9e | ||
|
|
bae9ef7638 | ||
|
|
4ed4b38301 | ||
|
|
6b6f89264f | ||
|
|
f0e0fba2f1 | ||
|
|
290723f234 | ||
|
|
5a14aa807a | ||
|
|
2505a36db6 | ||
|
|
90818206f5 | ||
|
|
1de4db6ebc | ||
|
|
7d2125788f | ||
|
|
3b83c1bda8 | ||
|
|
f352bf885c | ||
|
|
08a4f5defb | ||
|
|
87df38110b | ||
|
|
06e370fa59 | ||
|
|
e4ed2cea26 | ||
|
|
0c8967acdb | ||
|
|
1dfeed3ac8 | ||
|
|
d10bda2926 | ||
|
|
3938773738 | ||
|
|
f881002943 | ||
|
|
2266b80e73 | ||
|
|
4c3d6ddf0c | ||
|
|
3d1bcb23f4 | ||
|
|
98ecfa20aa | ||
|
|
56efe50aec | ||
|
|
891d0ff38b | ||
|
|
78d6cb5f22 | ||
|
|
23255a71db | ||
|
|
4cb34ad784 | ||
|
|
f096a31a95 | ||
|
|
f671ef56bb | ||
|
|
3a2fcf5976 | ||
|
|
dd85ec762d | ||
|
|
61ec935ab8 | ||
|
|
cd7c425f7e | ||
|
|
e9b8cd595a | ||
|
|
fc7cdae22a | ||
|
|
2ca44c4f5a | ||
|
|
50ff35c582 | ||
|
|
a318b07f61 | ||
|
|
be354cde10 | ||
|
|
594e8983e8 | ||
|
|
13f7d583df | ||
|
|
6085452469 | ||
|
|
64ac043656 | ||
|
|
7e9d48ee5b | ||
|
|
fa7de71650 | ||
|
|
9d228d4805 | ||
|
|
607fb3d127 | ||
|
|
8da9c4ffa1 | ||
|
|
97131be582 | ||
|
|
3144cfd9c9 | ||
|
|
a1810e577a | ||
|
|
bd6c39d9ac | ||
|
|
24a974f48d | ||
|
|
1dfad575b9 | ||
|
|
dc30075665 | ||
|
|
e2bcc16568 | ||
|
|
a9ee51d671 | ||
|
|
9e45de261c | ||
|
|
5eb209354a | ||
|
|
4c3c167fe0 | ||
|
|
a018b03310 | ||
|
|
a61740556d | ||
|
|
07f3a55ec3 | ||
|
|
2ea516be8a | ||
|
|
d9d2fa9918 | ||
|
|
16573632fc | ||
|
|
d0f1f16d9a | ||
|
|
98886cfdbf | ||
|
|
2ec513b1b0 |
+1
-1
@@ -8,7 +8,7 @@
|
||||
"jsx": false,
|
||||
"modules": false
|
||||
},
|
||||
"ecmaVersion": 2018
|
||||
"ecmaVersion": 2020
|
||||
},
|
||||
"plugins": ["promise"],
|
||||
"rules": {
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 12
|
||||
node-version: 20.x
|
||||
- run: npm ci
|
||||
- run: npm run jslint
|
||||
- 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
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
push:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push'
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: prepare tag
|
||||
id: prepare_tag
|
||||
run: |
|
||||
IMAGE_ID=jambonz/sbc-outbound
|
||||
|
||||
# Strip git ref prefix from version
|
||||
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
||||
|
||||
# Strip "v" prefix from tag name
|
||||
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
|
||||
|
||||
# Use Docker `latest` tag convention
|
||||
[ "$VERSION" == "main" ] && VERSION=latest
|
||||
|
||||
echo IMAGE_ID=$IMAGE_ID
|
||||
echo VERSION=$VERSION
|
||||
|
||||
echo "image_id=$IMAGE_ID" >> $GITHUB_OUTPUT
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.prepare_tag.outputs.image_id }}:${{ steps.prepare_tag.outputs.version }}
|
||||
build-args: |
|
||||
GITHUB_REPOSITORY=$GITHUB_REPOSITORY
|
||||
GITHUB_REF=$GITHUB_REF
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run jslint
|
||||
+18
-10
@@ -1,15 +1,23 @@
|
||||
FROM node:alpine as builder
|
||||
WORKDIR /opt/app/
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
RUN npm prune --production
|
||||
FROM --platform=linux/amd64 node:18.15-alpine3.16 as base
|
||||
|
||||
FROM node:alpine as app
|
||||
WORKDIR /opt/app
|
||||
COPY . /opt/app
|
||||
COPY --from=builder /opt/app/node_modules ./node_modules
|
||||
RUN apk --update --no-cache add --virtual .builds-deps build-base python3
|
||||
|
||||
WORKDIR /opt/app/
|
||||
|
||||
FROM base as build
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
|
||||
FROM base
|
||||
|
||||
COPY --from=build /opt/app /opt/app/
|
||||
|
||||
ARG NODE_ENV
|
||||
|
||||
ENV NODE_ENV $NODE_ENV
|
||||
|
||||
CMD [ "npm", "start" ]
|
||||
CMD [ "node", "app.js" ]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 jambonz
|
||||
Copyright (c) 2018-2024 FirstFive8, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# sbc-outbound 
|
||||
|
||||
This application provides a part of the SBC (Session Border Controller) functionality of jambonz. It handles outbound INVITE requests from the cpaas application server that is going to carrier sip trunks or registered sip users/devices, including webrtc applications.
|
||||
This application provides a part of the SBC (Session Border Controller) functionality of jambonz platfrom. It handles outbound INVITE requests from the cpaas application server that is going to carrier sip trunks or registered sip users/devices, including webrtc applications.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -11,22 +11,26 @@ Configuration is provided via environment variables:
|
||||
|DRACHTIO_HOST| ip address of drachtio server (typically '127.0.0.1')|yes|
|
||||
|DRACHTIO_PORT| listening port of drachtio server for control connections (typically 9022)|yes|
|
||||
|DRACHTIO_SECRET| shared secret|yes|
|
||||
|ENABLE_METRICS| if 1, metrics will be generated|no|
|
||||
|HTTP_PORT| tcp port listen port |no|
|
||||
|JAMBONES_LOGLEVEL| log level for application, 'info' or 'debug'|no|
|
||||
|JAMBONES_MYSQL_HOST| mysql host|yes|
|
||||
|JAMBONES_MYSQL_PORT| mysql port |no|
|
||||
|JAMBONES_MYSQL_USER| mysql username|yes|
|
||||
|JAMBONES_MYSQL_PASSWORD| mysql password|yes|
|
||||
|JAMBONES_MYSQL_DATABASE| mysql data|yes|
|
||||
|JAMBONES_MYSQL_CONNECTION_LIMIT| mysql connection limit |no|
|
||||
|DTMF_LISTEN_PORT| DTMF listening port |no|
|
||||
|JAMBONES_NG_PROTOCOL| rtpengine NG protocol |no|
|
||||
|RTPENGINE_PORT| rtpengine port |no|
|
||||
|JAMBONES_CLUSTER_ID| cluster id |no|
|
||||
|JAMBONES_NETWORK_CIDR| CIDR of private network that feature server is running in (e.g. '172.31.0.0/16')|yes|
|
||||
|JAMBONES_REDIS_HOST| redis host|yes|
|
||||
|JAMBONES_REDIS_PORT|redis port|yes|
|
||||
|JAMBONES_REDIS_PORT|redis port|no|
|
||||
|JAMBONES_RTPENGINES| commans-separated list of ip:ng-port for rtpengines (e.g. '172.31.32.10:22222')|yes|
|
||||
|JAMBONES_SBCS| list of IP addresses (on the internal network) of SBCs, comma-separated|yes|
|
||||
|STATS_HOST| ip address of metrics host (usually '127.0.0.1' since telegraf is installed locally|no|
|
||||
|STATS_PORT| listening port for metrics host|no|
|
||||
|STATS_PROTOCOL| 'tcp' or 'udp'|no|
|
||||
|STATS_TELEGRAF| if 1, metrics will be generated in telegraf format|no|
|
||||
|JAMBONES_TIME_SERIES_HOST| influxdb host |yes|
|
||||
|JAMBONES_RECORD_ALL_CALLS| enable auto record calls |no|
|
||||
|K8S| service running as kubernetes service |no|
|
||||
|K8S_RTPENGINE_SERVICE_NAME| rtpengine service name(required for K8S) |no|
|
||||
|
||||
### running under pm2
|
||||
Typically, this application runs under [pm2](https://pm2.io) using an [ecosystem.config.js](https://pm2.keymetrics.io/docs/usage/application-declaration/) file similar to this:
|
||||
@@ -58,17 +62,13 @@ module.exports = {
|
||||
JAMBONES_MYSQL_CONNECTION_LIMIT: 10,
|
||||
JAMBONES_REDIS_HOST: 'jambonz.zzzzzzz.0001.usw1.cache.amazonaws.com',
|
||||
JAMBONES_REDIS_PORT: 6379,
|
||||
ENABLE_METRICS: 1,
|
||||
STATS_HOST: '127.0.0.1',
|
||||
STATS_PORT: 8125,
|
||||
STATS_PROTOCOL: 'tcp',
|
||||
STATS_TELEGRAF: 1,
|
||||
JAMBONES_TIME_SERIES_HOST: '172.31.32.11',
|
||||
JAMBONES_NETWORK_CIDR: '172.31.0.0/16'
|
||||
}
|
||||
}]
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
#### Running the test suite
|
||||
To run the included test suite, you will need to have docker installed on your laptop.
|
||||
```
|
||||
|
||||
@@ -3,17 +3,29 @@ assert.ok(process.env.JAMBONES_MYSQL_HOST &&
|
||||
process.env.JAMBONES_MYSQL_USER &&
|
||||
process.env.JAMBONES_MYSQL_PASSWORD &&
|
||||
process.env.JAMBONES_MYSQL_DATABASE, 'missing JAMBONES_MYSQL_XXX env vars');
|
||||
assert.ok(process.env.JAMBONES_REDIS_HOST, 'missing JAMBONES_REDIS_HOST env var');
|
||||
if (process.env.JAMBONES_REDIS_SENTINELS) {
|
||||
assert.ok(process.env.JAMBONES_REDIS_SENTINEL_MASTER_NAME,
|
||||
'missing JAMBONES_REDIS_SENTINEL_MASTER_NAME env var, JAMBONES_REDIS_SENTINEL_PASSWORD env var is optional');
|
||||
} else {
|
||||
assert.ok(process.env.JAMBONES_REDIS_HOST, 'missing JAMBONES_REDIS_HOST env var');
|
||||
}
|
||||
assert.ok(process.env.DRACHTIO_PORT || process.env.DRACHTIO_HOST, 'missing DRACHTIO_PORT env var');
|
||||
assert.ok(process.env.DRACHTIO_SECRET, 'missing DRACHTIO_SECRET env var');
|
||||
assert.ok(process.env.JAMBONES_NETWORK_CIDR || process.env.K8S, 'missing JAMBONES_NETWORK_CIDR env var');
|
||||
assert.ok(process.env.JAMBONES_TIME_SERIES_HOST, 'missing JAMBONES_TIME_SERIES_HOST env var');
|
||||
|
||||
const Srf = require('drachtio-srf');
|
||||
const srf = new Srf('sbc-outbound');
|
||||
const CIDRMatcher = require('cidr-matcher');
|
||||
const {equalsIgnoreOrder, pingMsTeamsGateways, createHealthCheckApp, systemHealth} = require('./lib/utils');
|
||||
const opts = Object.assign({
|
||||
timestamp: () => {return `, "time": "${new Date().toISOString()}"`;}
|
||||
}, {level: process.env.JAMBONES_LOGLEVEL || 'info'});
|
||||
const logger = require('pino')(opts);
|
||||
const {
|
||||
writeCallCount,
|
||||
writeCallCountSP,
|
||||
writeCallCountApp,
|
||||
writeCdrs,
|
||||
queryCdrs,
|
||||
writeAlerts,
|
||||
@@ -29,64 +41,117 @@ const CallSession = require('./lib/call-session');
|
||||
const setNameRtp = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-rtp`;
|
||||
const rtpServers = [];
|
||||
const {
|
||||
performLcr,
|
||||
ping,
|
||||
lookupOutboundCarrierForAccount,
|
||||
lookupAllTeamsFQDNs,
|
||||
lookupAccountBySipRealm,
|
||||
lookupAccountBySid,
|
||||
lookupAccountCapacitiesBySid
|
||||
lookupAccountCapacitiesBySid,
|
||||
lookupSipGatewaysByCarrier,
|
||||
lookupCarrierBySid,
|
||||
queryCallLimits,
|
||||
lookupCarrierByAccountLcr,
|
||||
lookupSystemInformation
|
||||
} = require('@jambonz/db-helpers')({
|
||||
host: process.env.JAMBONES_MYSQL_HOST,
|
||||
port: process.env.JAMBONES_MYSQL_PORT || 3306,
|
||||
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);
|
||||
const {createHash, retrieveHash, incrKey, decrKey, retrieveSet} = require('@jambonz/realtimedb-helpers')({
|
||||
host: process.env.JAMBONES_REDIS_HOST || 'localhost',
|
||||
port: process.env.JAMBONES_REDIS_PORT || 6379
|
||||
}, logger);
|
||||
const {
|
||||
client: redisClient,
|
||||
createHash,
|
||||
retrieveHash,
|
||||
incrKey,
|
||||
decrKey,
|
||||
retrieveSet,
|
||||
isMemberOfSet,
|
||||
addKey,
|
||||
deleteKey,
|
||||
retrieveKey
|
||||
} = require('@jambonz/realtimedb-helpers')({}, logger);
|
||||
|
||||
const activeCallIds = new Map();
|
||||
const Emitter = require('events');
|
||||
const idleEmitter = new Emitter();
|
||||
|
||||
srf.locals = {...srf.locals,
|
||||
stats,
|
||||
writeCallCount,
|
||||
writeCallCountSP,
|
||||
writeCallCountApp,
|
||||
writeCdrs,
|
||||
writeAlerts,
|
||||
AlertType,
|
||||
queryCdrs,
|
||||
activeCallIds,
|
||||
idleEmitter,
|
||||
privateNetworkCidr: process.env.PRIVATE_VOIP_NETWORK_CIDR || null,
|
||||
dbHelpers: {
|
||||
performLcr,
|
||||
ping,
|
||||
lookupOutboundCarrierForAccount,
|
||||
lookupAllTeamsFQDNs,
|
||||
lookupAccountBySipRealm,
|
||||
lookupAccountBySid,
|
||||
lookupAccountCapacitiesBySid
|
||||
lookupAccountCapacitiesBySid,
|
||||
lookupSipGatewaysByCarrier,
|
||||
lookupCarrierBySid,
|
||||
queryCallLimits,
|
||||
lookupCarrierByAccountLcr,
|
||||
lookupSystemInformation
|
||||
},
|
||||
realtimeDbHelpers: {
|
||||
client: redisClient,
|
||||
addKey,
|
||||
deleteKey,
|
||||
retrieveKey,
|
||||
createHash,
|
||||
retrieveHash,
|
||||
incrKey,
|
||||
decrKey
|
||||
decrKey,
|
||||
isMemberOfSet
|
||||
}
|
||||
};
|
||||
const {initLocals, checkLimits, route} = require('./lib/middleware')(srf, logger, {
|
||||
host: process.env.JAMBONES_REDIS_HOST,
|
||||
port: process.env.JAMBONES_REDIS_PORT || 6379
|
||||
const {initLocals, checkLimits, route} = require('./lib/middleware')(srf, logger, redisClient);
|
||||
const ngProtocol = process.env.JAMBONES_NG_PROTOCOL || 'udp';
|
||||
const ngPort = process.env.RTPENGINE_PORT || ('udp' === ngProtocol ? 22222 : 8080);
|
||||
const {getRtpEngine, setRtpEngines} = require('@jambonz/rtpengine-utils')([], logger, {
|
||||
//emitter: stats,
|
||||
dtmfListenPort: process.env.DTMF_LISTEN_PORT || 22225,
|
||||
protocol: ngProtocol
|
||||
});
|
||||
const {getRtpEngine, setRtpEngines} = require('@jambonz/rtpengine-utils')([], logger, {emitter: stats});
|
||||
srf.locals.getRtpEngine = getRtpEngine;
|
||||
|
||||
if (process.env.DRACHTIO_HOST) {
|
||||
if (process.env.DRACHTIO_HOST && !process.env.K8S) {
|
||||
const cidrs = process.env.JAMBONES_NETWORK_CIDR
|
||||
.split(',')
|
||||
.map((s) => s.trim());
|
||||
logger.info({cidrs}, 'internal network CIDRs');
|
||||
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) => {
|
||||
logger.info(`connected to drachtio listening on ${hp}`);
|
||||
const last = hp.split(',').pop();
|
||||
const arr = /^(.*)\/(.*):(\d+)$/.exec(last);
|
||||
logger.info(`connected to drachtio listening on ${hp}: adding ${arr[2]} to sbc_addresses table`);
|
||||
srf.locals.sipAddress = arr[2];
|
||||
|
||||
const hostports = hp.split(',');
|
||||
for (const hp of hostports) {
|
||||
const arr = /^(.*)\/(.*):(\d+)$/.exec(hp);
|
||||
if (arr && 'udp' === arr[1] && !matcher.contains(arr[2])) {
|
||||
logger.info(`sbc public address: ${arr[2]}`);
|
||||
srf.locals.sipAddress = arr[2];
|
||||
}
|
||||
else if (arr && 'tcp' === arr[1] && matcher.contains(arr[2])) {
|
||||
const hostport = `${arr[2]}:${arr[3]}`;
|
||||
logger.info(`sbc private address: ${hostport}`);
|
||||
srf.locals.privateSipAddress = hostport;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
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});
|
||||
}
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
@@ -101,35 +166,92 @@ srf.invite((req, res) => {
|
||||
session.connect();
|
||||
});
|
||||
|
||||
/* update call stats periodically */
|
||||
setInterval(() => {
|
||||
stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound']);
|
||||
}, 5000);
|
||||
if (process.env.K8S || process.env.HTTP_PORT) {
|
||||
const PORT = process.env.HTTP_PORT || 3000;
|
||||
const healthCheck = require('@jambonz/http-health-check');
|
||||
|
||||
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 getCount = () => srf.locals.activeCallIds.size;
|
||||
|
||||
createHealthCheckApp(PORT, logger)
|
||||
.then((app) => {
|
||||
healthCheck({
|
||||
app,
|
||||
logger,
|
||||
path: '/',
|
||||
fn: getCount
|
||||
});
|
||||
healthCheck({
|
||||
app,
|
||||
logger,
|
||||
path: '/system-health',
|
||||
fn: systemHealth.bind(null, redisClient, ping, getCount)
|
||||
});
|
||||
return;
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error({err}, 'Error creating health check server');
|
||||
});
|
||||
}
|
||||
if ('test' !== process.env.NODE_ENV) {
|
||||
/* update call stats periodically as well as definition of private network cidr */
|
||||
setInterval(async() => {
|
||||
stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound',
|
||||
`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;
|
||||
}
|
||||
// Update system log level
|
||||
if (r.log_level) {
|
||||
logger.level = r.log_level;
|
||||
}
|
||||
}
|
||||
}, 20000);
|
||||
}
|
||||
|
||||
const lookupRtpServiceEndpoints = (lookup, 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}:${ngPort}`));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* update rtpengines periodically */
|
||||
if (process.env.JAMBONES_RTPENGINES) {
|
||||
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 {
|
||||
/* 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}`));
|
||||
setRtpEngines(newArray.map((a) => `${a}:${ngPort}`));
|
||||
rtpServers.length = 0;
|
||||
Array.prototype.push.apply(rtpServers, newArray);
|
||||
}
|
||||
@@ -137,14 +259,28 @@ else {
|
||||
logger.error({err}, 'Error setting new rtpengines');
|
||||
}
|
||||
};
|
||||
|
||||
setInterval(() => {
|
||||
getActiveRtpServers();
|
||||
}, 30000);
|
||||
getActiveRtpServers();
|
||||
}
|
||||
|
||||
const {pingMsTeamsGateways} = require('./lib/utils');
|
||||
pingMsTeamsGateways(logger, srf);
|
||||
|
||||
module.exports = {srf};
|
||||
process.on('SIGUSR2', handle.bind(null));
|
||||
process.on('SIGTERM', handle.bind(null));
|
||||
|
||||
function handle(signal) {
|
||||
logger.info(`got signal ${signal}`);
|
||||
if (process.env.K8S) {
|
||||
if (0 === activeCallIds.size) {
|
||||
logger.info('exiting immediately since we have no calls in progress');
|
||||
process.exit(0);
|
||||
}
|
||||
else {
|
||||
idleEmitter.once('idle', () => process.exit(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {srf, logger};
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env node
|
||||
const bent = require('bent');
|
||||
const getJSON = bent('json');
|
||||
const PORT = process.env.HTTP_PORT || 3000;
|
||||
|
||||
const sleep = (ms) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
(async function() {
|
||||
|
||||
try {
|
||||
do {
|
||||
const obj = await getJSON(`http://127.0.0.1:${PORT}/`);
|
||||
const {calls} = obj;
|
||||
if (calls === 0) {
|
||||
console.log('no calls on the system, we can exit');
|
||||
process.exit(0);
|
||||
}
|
||||
else {
|
||||
console.log(`waiting for ${calls} to exit..`);
|
||||
}
|
||||
await sleep(10000);
|
||||
} while (1);
|
||||
} catch (err) {
|
||||
console.error(err, 'Error querying health endpoint');
|
||||
process.exit(-1);
|
||||
}
|
||||
})();
|
||||
@@ -3,5 +3,6 @@
|
||||
"DTLS": "off",
|
||||
"SDES": "off",
|
||||
"ICE": "remove",
|
||||
"flags": ["port latching"],
|
||||
"rtcp-mux": ["demux"]
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
"transport-protocol": "UDP/TLS/RTP/SAVPF",
|
||||
"ICE": "force",
|
||||
"SDES": "off",
|
||||
"flags": ["generate mid", "SDES-no", "media handover"],
|
||||
"flags": ["generate mid", "SDES-no", "port latching"],
|
||||
"rtcp-mux": ["require"]
|
||||
},
|
||||
"teams": {
|
||||
"transport-protocol": "RTP/SAVP",
|
||||
"ICE": "force",
|
||||
"flags": ["generate mid", "media handover",
|
||||
"flags": ["generate mid",
|
||||
"SDES-no-AES_CM_128_HMAC_SHA1_32",
|
||||
"SDES-no-F8_128_HMAC_SHA1_80",
|
||||
"SDES-no-F8_128_HMAC_SHA1_32",
|
||||
|
||||
+928
-134
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"MediaPath": {
|
||||
"NoMedia": "no-media",
|
||||
"PartialMedia": "partial-media",
|
||||
"FullMedia": "full-media"
|
||||
}
|
||||
}
|
||||
+173
-77
@@ -1,36 +1,71 @@
|
||||
const debug = require('debug')('jambonz:sbc-outbound');
|
||||
const parseUri = require('drachtio-srf').parseUri;
|
||||
const Registrar = require('@jambonz/mw-registrar');
|
||||
const {selectHostPort, makeCallCountKey} = require('./utils');
|
||||
const { nudgeCallCounts} = require('./utils');
|
||||
const FS_UUID_SET_NAME = 'fsUUIDs';
|
||||
|
||||
const isLocalUri = (host, req) => {
|
||||
debug({hostport: req.server.hostport}, `is ${host} local?`);
|
||||
return req.server.hostport.includes(host);
|
||||
};
|
||||
|
||||
|
||||
module.exports = (srf, logger, opts) => {
|
||||
const {incrKey, decrKey} = srf.locals.realtimeDbHelpers;
|
||||
module.exports = (srf, logger, redisClient) => {
|
||||
const {incrKey, decrKey, isMemberOfSet} = srf.locals.realtimeDbHelpers;
|
||||
const {stats} = srf.locals;
|
||||
const registrar = new Registrar(opts);
|
||||
const registrar = new Registrar(logger, redisClient);
|
||||
const {
|
||||
lookupAccountCapacitiesBySid,
|
||||
lookupAccountBySid
|
||||
lookupAccountBySid,
|
||||
queryCallLimits
|
||||
} = srf.locals.dbHelpers;
|
||||
|
||||
const initLocals = async(req, res, next) => {
|
||||
req.locals = req.locals || {};
|
||||
const callId = req.get('Call-ID');
|
||||
req.locals.nudge = 0;
|
||||
req.locals.callId = callId;
|
||||
req.locals.account_sid = req.get('X-Account-Sid');
|
||||
req.locals.logger = logger.child({callId, account_sid: req.locals.account_sid});
|
||||
req.locals.application_sid = req.get('X-Application-Sid');
|
||||
req.locals.record_all_calls = req.get('X-Record-All-Calls');
|
||||
const traceId = req.locals.trace_id = req.get('X-Trace-ID');
|
||||
req.locals.logger = logger.child({
|
||||
callId,
|
||||
traceId,
|
||||
account_sid:
|
||||
req.locals.account_sid});
|
||||
|
||||
if (!req.locals.account_sid) {
|
||||
logger.info('missing X-Account-Sid on outbound call');
|
||||
return res.send(403, {
|
||||
res.send(403, {
|
||||
headers: {
|
||||
'X-Reason': 'missing X-Account-Sid'
|
||||
}
|
||||
});
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
|
||||
/* must come from a valid FS */
|
||||
if (!req.has('X-Jambonz-Routing')) {
|
||||
logger.info({msg: req.msg}, 'missing X-Jambonz-Routing header');
|
||||
res.send(403, {
|
||||
headers: {
|
||||
'X-Reason': 'missing required jambonz headers'
|
||||
}
|
||||
});
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
if (process.env.K8S) {
|
||||
/* for K8S we do not use JAMBONES_CIDR so we must validate the sender by uuid FS creates */
|
||||
const fsUUID = req.get('X-Jambonz-FS-UUID');
|
||||
try {
|
||||
const exists = await isMemberOfSet(FS_UUID_SET_NAME, fsUUID);
|
||||
if (!exists || !fsUUID) {
|
||||
res.send(403, {
|
||||
headers: {
|
||||
'X-Reason': `missing or invalid FS-UUID ${fsUUID}`
|
||||
}
|
||||
});
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
} catch (err) {
|
||||
res.send(500);
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
}
|
||||
|
||||
stats.increment('sbc.invites', ['direction:outbound']);
|
||||
@@ -40,39 +75,47 @@ module.exports = (srf, logger, opts) => {
|
||||
req.canceled = true;
|
||||
const tags = ['canceled:yes', 'sipStatus:487'];
|
||||
if (req.locals.originator) tags.push(`originator:${req.locals.originator}`);
|
||||
stats.increment('sbc.terminations', tags);
|
||||
stats.increment('sbc.origination', tags);
|
||||
});
|
||||
|
||||
try {
|
||||
req.locals.account = await lookupAccountBySid(req.locals.account_sid);
|
||||
const account = await lookupAccountBySid(req.locals.account_sid);
|
||||
req.locals.account = account;
|
||||
if (account.enable_debug_log) {
|
||||
req.locals.logger.level = 'debug';
|
||||
}
|
||||
req.locals.service_provider_sid = req.locals.account.service_provider_sid;
|
||||
} catch (err) {
|
||||
req.locals.logger.error({err}, `Error looking up account sid ${req.locals.account_sid}`);
|
||||
return res.send(500);
|
||||
res.send(500);
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
const checkLimits = async(req, res, next) => {
|
||||
const {logger, account_sid} = req.locals;
|
||||
const {writeAlerts, AlertType} = req.srf.locals;
|
||||
const {logger, account_sid, service_provider_sid, application_sid} = req.locals;
|
||||
const trackingOn = process.env.JAMBONES_TRACK_ACCOUNT_CALLS ||
|
||||
process.env.JAMBONES_TRACK_SP_CALLS ||
|
||||
process.env.JAMBONES_TRACK_APP_CALLS;
|
||||
if (!process.env.JAMBONES_HOSTING && !trackingOn) {
|
||||
logger.debug('tracking is off, skipping call limit checks');
|
||||
return next(); // skip
|
||||
}
|
||||
|
||||
const {writeCallCount, writeCallCountSP, writeCallCountApp, writeAlerts, AlertType} = req.srf.locals;
|
||||
|
||||
const key = makeCallCountKey(account_sid);
|
||||
try {
|
||||
/* increment the call count */
|
||||
const calls = await incrKey(key);
|
||||
debug(`checkLimits: call count is now ${calls}`);
|
||||
|
||||
/* decrement count if INVITE is later rejected */
|
||||
res.once('end', ({status}) => {
|
||||
res.once('end', async({status}) => {
|
||||
if (status > 200) {
|
||||
debug('checkLimits: decrementing call count due to rejection');
|
||||
decrKey(key)
|
||||
.then((count) => {
|
||||
logger.debug({key}, `after rejection there are ${count} active calls for this account`);
|
||||
debug({key}, `after rejection there are ${count} active calls for this account`);
|
||||
return;
|
||||
})
|
||||
.catch((err) => logger.error({err}, 'checkLimits: decrKey err'));
|
||||
nudgeCallCounts(req, 'failure', {
|
||||
service_provider_sid,
|
||||
account_sid,
|
||||
application_sid,
|
||||
callId: req.locals.callId
|
||||
}, decrKey, {writeCallCountSP, writeCallCount, writeCallCountApp})
|
||||
.catch((err) => logger.error(err, 'Error decrementing call counts'));
|
||||
const tags = ['accepted:no', `sipStatus:${status}`];
|
||||
stats.increment('sbc.originations', tags);
|
||||
}
|
||||
@@ -82,6 +125,14 @@ module.exports = (srf, logger, opts) => {
|
||||
}
|
||||
});
|
||||
|
||||
/* increment the call count */
|
||||
const {callsSP, calls} = await nudgeCallCounts(req, 'init', {
|
||||
service_provider_sid,
|
||||
account_sid,
|
||||
application_sid,
|
||||
callId: req.locals.callId
|
||||
}, incrKey, {writeCallCountSP, writeCallCount, writeCallCountApp});
|
||||
|
||||
/* compare to account's limit, though avoid db hit when call count is low */
|
||||
const minLimit = process.env.MIN_CALL_LIMIT ?
|
||||
parseInt(process.env.MIN_CALL_LIMIT) :
|
||||
@@ -90,19 +141,59 @@ module.exports = (srf, logger, opts) => {
|
||||
|
||||
const capacities = await lookupAccountCapacitiesBySid(account_sid);
|
||||
const limit = capacities.find((c) => c.category == 'voice_call_session');
|
||||
if (!limit) throw new Error('no account_capacities found');
|
||||
const limit_sessions = limit.quantity;
|
||||
if (limit) {
|
||||
const limit_sessions = limit.quantity;
|
||||
|
||||
if (calls > limit_sessions) {
|
||||
debug(`checkLimits: limits exceeded: call count ${calls}, limit ${limit_sessions}`);
|
||||
logger.info({calls, limit_sessions}, 'checkLimits: limits exceeded');
|
||||
writeAlerts({
|
||||
alert_type: AlertType.CALL_LIMIT,
|
||||
account_sid,
|
||||
count: limit_sessions
|
||||
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
|
||||
|
||||
res.send(503, 'Maximum Calls In Progress');
|
||||
if (calls > limit_sessions) {
|
||||
logger.info({calls, limit_sessions}, 'checkLimits: limits exceeded');
|
||||
writeAlerts({
|
||||
alert_type: AlertType.ACCOUNT_CALL_LIMIT,
|
||||
service_provider_sid,
|
||||
account_sid,
|
||||
count: limit_sessions
|
||||
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
|
||||
res.send(503, 'Maximum Calls In Progress');
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
}
|
||||
else if (trackingOn) {
|
||||
const {account_limit, sp_limit} = await queryCallLimits(service_provider_sid, account_sid);
|
||||
if (process.env.JAMBONES_TRACK_ACCOUNT_CALLS && account_limit > 0 && calls > account_limit) {
|
||||
logger.info({calls, account_limit}, 'checkLimits: account limits exceeded');
|
||||
writeAlerts({
|
||||
alert_type: AlertType.ACCOUNT_CALL_LIMIT,
|
||||
service_provider_sid: service_provider_sid,
|
||||
account_sid,
|
||||
count: calls
|
||||
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
|
||||
res.send(503, 'Max Account Calls In Progress', {
|
||||
headers: {
|
||||
'X-Account-Sid': account_sid,
|
||||
'X-Call-Limit': account_limit
|
||||
}
|
||||
});
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
if (!account_limit && !sp_limit && process.env.JAMBONES_HOSTING) {
|
||||
logger.info(`checkLimits: no active subscription found for account ${account_sid}, rejecting call`);
|
||||
res.send(503, 'No Active Subscription');
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
if (process.env.JAMBONES_TRACK_SP_CALLS && sp_limit > 0 && callsSP > sp_limit) {
|
||||
logger.info({callsSP, sp_limit}, 'checkLimits: service provider limits exceeded');
|
||||
writeAlerts({
|
||||
alert_type: AlertType.SP_CALL_LIMIT,
|
||||
service_provider_sid: service_provider_sid,
|
||||
count: callsSP
|
||||
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
|
||||
res.send(503, 'Max Service Provider Calls In Progress', {
|
||||
headers: {
|
||||
'X-Service-Provider-Sid': service_provider_sid,
|
||||
'X-Call-Limit': sp_limit
|
||||
}
|
||||
});
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
}
|
||||
next();
|
||||
} catch (err) {
|
||||
@@ -116,31 +207,26 @@ module.exports = (srf, logger, opts) => {
|
||||
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) {
|
||||
const desiredRouting = req.get('X-Jambonz-Routing');
|
||||
const validUri = uri && uri.user && uri.host;
|
||||
if (['user', 'sip'].includes(desiredRouting) && !validUri) {
|
||||
logger.info({uri: req.uri}, 'invalid request-uri on outbound call, rejecting');
|
||||
res.send(404, {
|
||||
res.send(400, {
|
||||
headers: {
|
||||
'X-Reason': 'invalid request-uri'
|
||||
}
|
||||
});
|
||||
return;
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
const aor = `${uri.user}@${uri.host}`;
|
||||
let reg;
|
||||
const dotDecimalHost = /^[0-9\.]+$/.test(uri.host);
|
||||
|
||||
debug(`received outbound INVITE to ${req.calledNumber} from server at ${req.server.hostport}`);
|
||||
|
||||
if (req.has('X-MS-Teams-FQDN') && req.has('X-MS-Teams-Tenant-FQDN')) {
|
||||
if ('teams' === desiredRouting) {
|
||||
logger.debug('This is a call to ms teams');
|
||||
req.locals.target = 'teams';
|
||||
return next();
|
||||
}
|
||||
else if (!dotDecimalHost) {
|
||||
// uri host is not a dot-decimal address, so try to look up user
|
||||
logger.debug(`searching for registered user ${aor}`);
|
||||
reg = await registrar.query(aor);
|
||||
else if ('user' === desiredRouting) {
|
||||
const aor = `${uri.user}@${uri.host}`;
|
||||
const reg = await registrar.query(aor);
|
||||
if (reg) {
|
||||
// user is registered..find out which sbc is handling it
|
||||
// us => we can put the call through
|
||||
@@ -148,41 +234,51 @@ module.exports = (srf, logger, opts) => {
|
||||
logger.info({details: reg}, `sending call to registered user ${aor}`);
|
||||
if (req.server.hostport !== reg.sbcAddress) {
|
||||
/* redirect to the correct SBC where this user is connected */
|
||||
const proxyAddress = selectHostPort(reg.sbcAddress, 'tcp');
|
||||
const redirectUri = `<sip:${proxyAddress[1]}>`;
|
||||
logger.info(`redirecting call to SBC at ${redirectUri}`);
|
||||
const proxyAddress = reg.privateSbcAddress.split(':');
|
||||
const redirectUri = `<sip:${proxyAddress[0]}>`;
|
||||
logger.info({
|
||||
myHostPort: req.server.hostport,
|
||||
registeredHostPort: reg.sbcAddress,
|
||||
}, `redirecting call to SBC at ${redirectUri}`);
|
||||
return res.send(302, {headers: {Contact: redirectUri}});
|
||||
}
|
||||
req.locals.registration = reg;
|
||||
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({host: uri.host, account}, `returning 404 to unregistered user in valid domain: ${req.uri}`);
|
||||
res.send(404);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
logger.info({host: uri.host, account}, `returning 404 to user in invalid domain: ${req.uri}`);
|
||||
}
|
||||
res.send(404);
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
}
|
||||
if (!dotDecimalHost || !isLocalUri(uri.host, req)) {
|
||||
else if ('sip' === desiredRouting) {
|
||||
// call that needs to be forwarded to a sip endpoint
|
||||
logger.info(`forwarding call to sip endpoint ${req.uri}`);
|
||||
|
||||
// check if the domain is one of ours
|
||||
if (process.env.JAMBONES_LOCAL_SIP_DOMAINS) {
|
||||
const allowedDomains = process.env.JAMBONES_LOCAL_SIP_DOMAINS.split(',');
|
||||
const domain = uri.host;
|
||||
const isLoop = allowedDomains.some((allowed) =>
|
||||
domain === allowed.trim() || domain.endsWith(`.${allowed.trim()}`)
|
||||
);
|
||||
if (isLoop) {
|
||||
logger.info({ host: domain }, `returning 482 Loop Detected for attempt to send to: ${req.uri}`);
|
||||
res.send(482, 'Loop Detected');
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
}
|
||||
req.locals.target = 'forward';
|
||||
return next();
|
||||
} else if ('phone' === desiredRouting) {
|
||||
debug('sending call to LCR');
|
||||
req.locals.target = 'lcr';
|
||||
}
|
||||
|
||||
// if the called number is digits only (after possible leading plus sign) and long enough, do lcr
|
||||
if (!/^\d+$/.test(req.calledNumber.slice(1)) || req.calledNumber.length < 8) {
|
||||
debug(`unable to route call to ${aor}; no registered user found`);
|
||||
logger.info(`unable to route call to ${aor}; no registered user found`);
|
||||
return res.send(404);
|
||||
}
|
||||
|
||||
debug('sending call to LCR');
|
||||
req.locals.target = 'lcr';
|
||||
next();
|
||||
};
|
||||
|
||||
|
||||
+295
-8
@@ -1,15 +1,45 @@
|
||||
const rtpCharacteristics = require('../data/rtp-transcoding');
|
||||
const srtpCharacteristics = require('../data/srtp-transcoding');
|
||||
const debug = require('debug')('jambonz:sbc-outbound');
|
||||
const CIDRMatcher = require('cidr-matcher');
|
||||
const dns = require('dns');
|
||||
const sdpTransform = require('sdp-transform');
|
||||
|
||||
function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp, teams = false) {
|
||||
function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp, padCrypto, teams) {
|
||||
const from = req.getParsedHeader('from');
|
||||
const srtpOpts = teams ? srtpCharacteristics['teams'] : srtpCharacteristics['default'];
|
||||
const dstOpts = dstIsUsingSrtp ? srtpOpts : rtpCharacteristics;
|
||||
const srcOpts = srcIsUsingSrtp ? srtpOpts : rtpCharacteristics;
|
||||
const rtpCopy = JSON.parse(JSON.stringify(rtpCharacteristics));
|
||||
const srtpCopy = JSON.parse(JSON.stringify(srtpCharacteristics));
|
||||
|
||||
if (padCrypto) {
|
||||
srtpCopy['default'].flags.push('SDES-pad');
|
||||
srtpCopy['teams'].flags.push('SDES-pad');
|
||||
}
|
||||
|
||||
const srtpOpts = teams ? srtpCopy['teams'] : srtpCopy['default'];
|
||||
const dstOpts = dstIsUsingSrtp ? srtpOpts : rtpCopy;
|
||||
const srcOpts = srcIsUsingSrtp ? srtpOpts : rtpCopy;
|
||||
|
||||
/** Allow feature server to send DTMF to the call excepts call from/to teams */
|
||||
if (!teams) {
|
||||
if (!dstOpts.flags.includes('inject DTMF')) {
|
||||
dstOpts.flags.push('inject DTMF');
|
||||
}
|
||||
if (!srcOpts.flags.includes('inject DTMF')) {
|
||||
srcOpts.flags.push('inject DTMF');
|
||||
}
|
||||
}
|
||||
|
||||
/** By default, and for backwards compatibility, use media handover
|
||||
* set env var to true to use strict source instead (needed for rtpbleed vulnerability)
|
||||
*/
|
||||
const enableStrictSource = !!process.env.RTPENGINE_ENABLE_STRICT_SOURCE;
|
||||
dstOpts.flags.push(enableStrictSource ? 'strict source' : 'media handover');
|
||||
srcOpts.flags.push(enableStrictSource ? 'strict source' : 'media handover');
|
||||
|
||||
const common = {
|
||||
'call-id': req.get('Call-ID'),
|
||||
'replace': ['origin', 'session-connection']
|
||||
'replace': ['origin', 'session-connection'],
|
||||
'record call': process.env.JAMBONES_RECORD_ALL_CALLS ? 'yes' : 'no'
|
||||
};
|
||||
return {
|
||||
common,
|
||||
@@ -19,7 +49,11 @@ function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp, teams = false) {
|
||||
},
|
||||
uac: {
|
||||
tag: null,
|
||||
mediaOpts: dstOpts
|
||||
mediaOpts: {
|
||||
...dstOpts,
|
||||
...(process.env.JAMBONES_CODEC_OFFER_WITH_ORDER &&
|
||||
{ codec: { offer: process.env.JAMBONES_CODEC_OFFER_WITH_ORDER.split(','), strip: 'all' } }),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -71,11 +105,264 @@ const pingMsTeamsGateways = (logger, srf) => {
|
||||
});
|
||||
};
|
||||
|
||||
const makeCallCountKey = (sid) => `${sid}:outcalls`;
|
||||
const makeAccountCallCountKey = (sid) => `outcalls:account:${sid}`;
|
||||
const makeSPCallCountKey = (sid) => `outcalls:sp:${sid}`;
|
||||
const makeAppCallCountKey = (sid) => `outcalls:app:${sid}`;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
const systemHealth = async(redisClient, ping, getCount) => {
|
||||
await Promise.all([redisClient.ping(), ping()]);
|
||||
return getCount();
|
||||
};
|
||||
|
||||
const createHealthCheckApp = (port, logger) => {
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(express.json());
|
||||
|
||||
return new Promise((resolve) => {
|
||||
app.listen(port, () => {
|
||||
logger.info(`Health check server started at http://localhost:${port}`);
|
||||
resolve(app);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* nudgeCallCounts - increment or decrement call counts in redis
|
||||
*
|
||||
* current nudge value
|
||||
* -----------------------------------------
|
||||
* why | -1 | 0 | 1 |
|
||||
* -----------------------------------------
|
||||
* init | no-op | +1 | N/A |
|
||||
* failure | N/A | -1 | -1 |
|
||||
* complete| N/A | N/A | -1 |
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
const shouldNudge = (why, req) => {
|
||||
const {nudge, logger} = req.locals;
|
||||
let modifyCount = false;
|
||||
const originalNudge = nudge;
|
||||
|
||||
switch (why) {
|
||||
case 'init':
|
||||
if (nudge === 0) {
|
||||
// normal case: new call, increment call count
|
||||
req.locals.nudge = 1;
|
||||
modifyCount = true;
|
||||
}
|
||||
else if (nudge === -1) {
|
||||
// extremely quick cancel, don't increment call count
|
||||
req.locals.nudge = 0;
|
||||
}
|
||||
else {
|
||||
logger.info(`shouldNudge: unexpected nudge value ${nudge} for ${why}`);
|
||||
}
|
||||
break;
|
||||
case 'failure':
|
||||
if (nudge === 1) {
|
||||
// normal case of call failed for any reason, decrement call count
|
||||
req.locals.nudge = 0;
|
||||
modifyCount = true;
|
||||
}
|
||||
else if (nudge === 0) {
|
||||
// very quick failure dont decrement call count
|
||||
req.locals.nudge = -1;
|
||||
}
|
||||
else {
|
||||
logger.info(`shouldNudge: unexpected nudge value ${nudge} for ${why}`);
|
||||
}
|
||||
break;
|
||||
case 'complete':
|
||||
if (nudge === 1) {
|
||||
// normal case of call completed, decrement call count
|
||||
req.locals.nudge = 0;
|
||||
modifyCount = true;
|
||||
}
|
||||
else {
|
||||
logger.info(`shouldNudge: unexpected nudge value ${nudge} for ${why}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.info(`shouldNudge: unexpected why value ${why}`);
|
||||
break;
|
||||
}
|
||||
|
||||
logger.info(`shouldNudge: '${why}': updating count: ${modifyCount}, nudge: ${originalNudge} -> ${req.locals.nudge}`);
|
||||
return modifyCount;
|
||||
};
|
||||
|
||||
const nudgeCallCounts = async(req, why, sids, nudgeOperator, writers) => {
|
||||
const {logger} = req.locals;
|
||||
const {service_provider_sid, account_sid, application_sid, callId} = sids;
|
||||
const {writeCallCount, writeCallCountSP, writeCallCountApp} = writers;
|
||||
const nudges = [];
|
||||
const writes = [];
|
||||
|
||||
if (!shouldNudge(why, req)) {
|
||||
return {callsSP: null, calls: null, callsApp: null};
|
||||
}
|
||||
|
||||
if (process.env.JAMBONES_DEBUG_CALL_COUNTS) {
|
||||
const {srf} = require('..');
|
||||
const {addKey, deleteKey} = srf.locals.realtimeDbHelpers;
|
||||
|
||||
if (why === 'init') {
|
||||
// save for 3 days
|
||||
await addKey(`debug:outcalls:${account_sid}:${callId}`, new Date().toISOString(), 259200);
|
||||
}
|
||||
else {
|
||||
await deleteKey(`debug:outcalls:${account_sid}:${callId}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.JAMBONES_TRACK_SP_CALLS) {
|
||||
const key = makeSPCallCountKey(service_provider_sid);
|
||||
nudges.push(nudgeOperator(key));
|
||||
}
|
||||
else {
|
||||
nudges.push(() => Promise.resolve(null));
|
||||
}
|
||||
|
||||
if (process.env.JAMBONES_TRACK_ACCOUNT_CALLS || process.env.JAMBONES_HOSTING) {
|
||||
const key = makeAccountCallCountKey(account_sid);
|
||||
nudges.push(nudgeOperator(key));
|
||||
}
|
||||
else {
|
||||
nudges.push(() => Promise.resolve(null));
|
||||
}
|
||||
|
||||
if (process.env.JAMBONES_TRACK_APP_CALLS && application_sid) {
|
||||
const key = makeAppCallCountKey(application_sid);
|
||||
nudges.push(nudgeOperator(key));
|
||||
}
|
||||
else {
|
||||
nudges.push(() => Promise.resolve(null));
|
||||
}
|
||||
|
||||
try {
|
||||
const [callsSP, calls, callsApp] = await Promise.all(nudges);
|
||||
logger.debug({
|
||||
calls, callsSP, callsApp,
|
||||
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}));
|
||||
}
|
||||
|
||||
if (process.env.JAMBONES_TRACK_ACCOUNT_CALLS || process.env.JAMBONES_HOSTING) {
|
||||
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}));
|
||||
}
|
||||
|
||||
/* write the call counts to the database */
|
||||
Promise.all(writes).catch((err) => logger.error({err}, 'Error writing call counts'));
|
||||
|
||||
return {callsSP, calls, callsApp};
|
||||
} catch (err) {
|
||||
logger.error(err, 'error incrementing call counts');
|
||||
}
|
||||
|
||||
return {callsSP: null, calls: null, callsApp: null};
|
||||
};
|
||||
|
||||
const isPrivateVoipNetwork = async(uri) => {
|
||||
const {srf, logger} = require('..');
|
||||
const {privateNetworkCidr} = srf.locals;
|
||||
|
||||
if (privateNetworkCidr) {
|
||||
try {
|
||||
const matcher = new CIDRMatcher(privateNetworkCidr.split(','));
|
||||
const arr = /sips?:.*@(.*?)(:\d+)?(;.*)?$/.exec(uri);
|
||||
if (arr) {
|
||||
const input = arr[1];
|
||||
let addresses;
|
||||
if (input.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)) {
|
||||
addresses = [input];
|
||||
} else {
|
||||
addresses = await dns.resolve4(input);
|
||||
}
|
||||
for (const ip of addresses) {
|
||||
if (matcher.contains(ip)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.info({err, privateNetworkCidr},
|
||||
'Error checking private network CIDR, probably misconfigured must be a comma separated list of CIDRs');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
function makeBlacklistGatewayKey(key) {
|
||||
return `blacklist-sip-gateway:${key}`;
|
||||
}
|
||||
|
||||
async function isBlackListedSipGateway(client, logger, sip_gateway_sid) {
|
||||
try {
|
||||
return await client.exists(makeBlacklistGatewayKey(sip_gateway_sid)) === 1;
|
||||
} catch (err) {
|
||||
logger.error({err}, `isBlackListedSipGateway: error while checking blacklist for ${sip_gateway_sid}`);
|
||||
}
|
||||
}
|
||||
|
||||
const makeFullMediaReleaseKey = (callId) => {
|
||||
return `b_sdp:${callId}`;
|
||||
};
|
||||
const makePartnerFullMediaReleaseKey = (callId) => {
|
||||
return `a_sdp:${callId}`;
|
||||
};
|
||||
|
||||
function isValidDomainOrIP(input) {
|
||||
const domainRegex = /^(?!:\/\/)([a-zA-Z0-9.-]+)(:\d+)?$/;
|
||||
// eslint-disable-next-line max-len
|
||||
const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(:\d+)?$/;
|
||||
|
||||
if (domainRegex.test(input) || ipRegex.test(input)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Invalid input
|
||||
}
|
||||
const removeVideoSdp = (sdp) => {
|
||||
const parsedSdp = sdpTransform.parse(sdp);
|
||||
// Filter out video media sections, keeping only non-video media
|
||||
parsedSdp.media = parsedSdp.media.filter((media) => media.type !== 'video');
|
||||
return sdpTransform.write(parsedSdp);
|
||||
};
|
||||
module.exports = {
|
||||
makeRtpEngineOpts,
|
||||
selectHostPort,
|
||||
pingMsTeamsGateways,
|
||||
makeCallCountKey
|
||||
makeAccountCallCountKey,
|
||||
makeSPCallCountKey,
|
||||
equalsIgnoreOrder,
|
||||
systemHealth,
|
||||
createHealthCheckApp,
|
||||
nudgeCallCounts,
|
||||
isPrivateVoipNetwork,
|
||||
isBlackListedSipGateway,
|
||||
makeFullMediaReleaseKey,
|
||||
makePartnerFullMediaReleaseKey,
|
||||
isValidDomainOrIP,
|
||||
removeVideoSdp
|
||||
};
|
||||
|
||||
Generated
+2822
-5347
File diff suppressed because it is too large
Load Diff
+24
-17
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "sbc-outbound",
|
||||
"version": "0.4.6",
|
||||
"version": "0.9.5",
|
||||
"main": "app.js",
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
"node": ">= 18.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"sip",
|
||||
@@ -22,26 +22,33 @@
|
||||
"description": "jambonz session border controller application for outbound calls",
|
||||
"scripts": {
|
||||
"start": "node app",
|
||||
"test": "NODE_ENV=test JAMBONZ_HOSTING=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_TIME_SERIES_HOST=127.0.0.1 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 node test/ ",
|
||||
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONES_HOSTING=1 JAMBONES_NETWORK_CIDR=127.0.0.1/32 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_TIME_SERIES_HOST=127.0.0.1 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 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 --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jambonz/db-helpers": "^0.6.12",
|
||||
"@jambonz/mw-registrar": "0.2.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.3",
|
||||
"@jambonz/rtpengine-utils": "^0.1.12",
|
||||
"@jambonz/stats-collector": "^0.1.5",
|
||||
"@jambonz/time-series": "^0.1.5",
|
||||
"debug": "^4.3.1",
|
||||
"drachtio-fn-b2b-sugar": "^0.0.12",
|
||||
"drachtio-srf": "^4.4.49",
|
||||
"pino": "^6.11.2"
|
||||
"@babel/helpers": "^7.26.10",
|
||||
"@jambonz/db-helpers": "^0.9.12",
|
||||
"@jambonz/http-health-check": "^0.0.1",
|
||||
"@jambonz/mw-registrar": "0.2.7",
|
||||
"@jambonz/realtimedb-helpers": "^0.8.13",
|
||||
"@jambonz/rtpengine-utils": "^0.4.4",
|
||||
"@jambonz/siprec-client-utils": "^0.2.10",
|
||||
"@jambonz/stats-collector": "^0.1.10",
|
||||
"@jambonz/time-series": "^0.2.10",
|
||||
"cidr-matcher": "^2.1.1",
|
||||
"debug": "^4.3.4",
|
||||
"drachtio-fn-b2b-sugar": "^0.2.1",
|
||||
"drachtio-srf": "^5.0.12",
|
||||
"express": "^4.19.2",
|
||||
"pino": "^8.20.0",
|
||||
"sdp-transform": "^2.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.18.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"bent": "^7.3.12",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"nyc": "^15.1.0",
|
||||
"tape": "^5.2.2"
|
||||
"tape": "^5.7.5"
|
||||
}
|
||||
}
|
||||
|
||||
+220
-26
@@ -4,6 +4,8 @@ SET FOREIGN_KEY_CHECKS=0;
|
||||
|
||||
DROP TABLE IF EXISTS account_static_ips;
|
||||
|
||||
DROP TABLE IF EXISTS account_limits;
|
||||
|
||||
DROP TABLE IF EXISTS account_products;
|
||||
|
||||
DROP TABLE IF EXISTS account_subscriptions;
|
||||
@@ -12,32 +14,52 @@ 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;
|
||||
|
||||
DROP TABLE IF EXISTS predefined_carriers;
|
||||
|
||||
DROP TABLE IF EXISTS account_offers;
|
||||
|
||||
DROP TABLE IF EXISTS products;
|
||||
|
||||
DROP TABLE IF EXISTS schema_version;
|
||||
|
||||
DROP TABLE IF EXISTS api_keys;
|
||||
|
||||
DROP TABLE IF EXISTS sbc_addresses;
|
||||
|
||||
DROP TABLE IF EXISTS ms_teams_tenants;
|
||||
|
||||
DROP TABLE IF EXISTS service_provider_limits;
|
||||
|
||||
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;
|
||||
@@ -65,6 +87,15 @@ private_ipv4 VARBINARY(16) NOT NULL UNIQUE ,
|
||||
PRIMARY KEY (account_static_ip_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE account_limits
|
||||
(
|
||||
account_limits_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
account_sid CHAR(36) NOT NULL,
|
||||
category ENUM('api_rate','voice_call_session', 'device','voice_call_minutes','voice_call_session_license', 'voice_call_minutes_license') NOT NULL,
|
||||
quantity INTEGER NOT NULL,
|
||||
PRIMARY KEY (account_limits_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE account_subscriptions
|
||||
(
|
||||
account_subscription_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
@@ -101,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 ,
|
||||
@@ -113,11 +157,38 @@ 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
|
||||
(
|
||||
min_password_length INTEGER NOT NULL DEFAULT 8,
|
||||
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
|
||||
(
|
||||
@@ -148,6 +219,20 @@ predefined_carrier_sid CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (predefined_sip_gateway_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE predefined_smpp_gateways
|
||||
(
|
||||
predefined_smpp_gateway_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
ipv4 VARCHAR(128) NOT NULL COMMENT 'ip address or DNS name of the gateway. ',
|
||||
port INTEGER NOT NULL DEFAULT 2775 COMMENT 'smpp signaling port',
|
||||
inbound BOOLEAN NOT NULL COMMENT 'if true, whitelist this IP to allow inbound SMS from the gateway',
|
||||
outbound BOOLEAN NOT NULL COMMENT 'i',
|
||||
netmask INTEGER NOT NULL DEFAULT 32,
|
||||
is_primary BOOLEAN NOT NULL DEFAULT 1,
|
||||
use_tls BOOLEAN DEFAULT 0,
|
||||
predefined_carrier_sid CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (predefined_smpp_gateway_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE products
|
||||
(
|
||||
product_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
@@ -174,6 +259,11 @@ stripe_product_id VARCHAR(56) NOT NULL,
|
||||
PRIMARY KEY (account_offer_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE schema_version
|
||||
(
|
||||
version VARCHAR(16)
|
||||
);
|
||||
|
||||
CREATE TABLE api_keys
|
||||
(
|
||||
api_key_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
@@ -191,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)
|
||||
);
|
||||
|
||||
@@ -205,6 +298,15 @@ tenant_fqdn VARCHAR(255) NOT NULL UNIQUE ,
|
||||
PRIMARY KEY (ms_teams_tenant_sid)
|
||||
) COMMENT='A Microsoft Teams customer tenant';
|
||||
|
||||
CREATE TABLE service_provider_limits
|
||||
(
|
||||
service_provider_limits_sid CHAR(36) NOT NULL UNIQUE ,
|
||||
service_provider_sid CHAR(36) NOT NULL,
|
||||
category ENUM('api_rate','voice_call_session', 'device','voice_call_minutes','voice_call_session_license', 'voice_call_minutes_license') NOT NULL,
|
||||
quantity INTEGER NOT NULL,
|
||||
PRIMARY KEY (service_provider_limits_sid)
|
||||
);
|
||||
|
||||
CREATE TABLE signup_history
|
||||
(
|
||||
email VARCHAR(255) NOT NULL,
|
||||
@@ -238,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 ,
|
||||
@@ -260,6 +382,7 @@ email_activation_code VARCHAR(16),
|
||||
email_validated BOOLEAN NOT NULL DEFAULT false,
|
||||
phone_validated BOOLEAN NOT NULL DEFAULT false,
|
||||
email_content_opt_out BOOLEAN NOT NULL DEFAULT false,
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
PRIMARY KEY (user_sid)
|
||||
);
|
||||
|
||||
@@ -287,9 +410,21 @@ smpp_password VARCHAR(64),
|
||||
smpp_enquire_link_interval INTEGER DEFAULT 0,
|
||||
smpp_inbound_system_id VARCHAR(255),
|
||||
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 ,
|
||||
@@ -307,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),
|
||||
@@ -320,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';
|
||||
|
||||
@@ -357,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 ';
|
||||
|
||||
@@ -395,6 +546,15 @@ disable_cdrs BOOLEAN NOT NULL DEFAULT 0,
|
||||
trial_end_date DATETIME,
|
||||
deactivated_reason VARCHAR(255),
|
||||
device_to_call_ratio INTEGER NOT NULL DEFAULT 5,
|
||||
subspace_client_id VARCHAR(255),
|
||||
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';
|
||||
|
||||
@@ -402,24 +562,43 @@ CREATE INDEX account_static_ip_sid_idx ON account_static_ips (account_static_ip_
|
||||
CREATE INDEX account_sid_idx ON account_static_ips (account_sid);
|
||||
ALTER TABLE account_static_ips ADD FOREIGN KEY account_sid_idxfk (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX account_sid_idx ON account_limits (account_sid);
|
||||
ALTER TABLE account_limits ADD FOREIGN KEY account_sid_idxfk_1 (account_sid) REFERENCES accounts (account_sid) ON DELETE CASCADE;
|
||||
|
||||
CREATE INDEX account_subscription_sid_idx ON account_subscriptions (account_subscription_sid);
|
||||
CREATE INDEX account_sid_idx ON account_subscriptions (account_sid);
|
||||
ALTER TABLE account_subscriptions ADD FOREIGN KEY account_sid_idxfk_1 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE account_subscriptions ADD FOREIGN KEY account_sid_idxfk_2 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX invite_code_idx ON beta_invite_codes (invite_code);
|
||||
CREATE INDEX call_route_sid_idx ON call_routes (call_route_sid);
|
||||
ALTER TABLE call_routes ADD FOREIGN KEY account_sid_idxfk_2 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE call_routes ADD FOREIGN KEY account_sid_idxfk_3 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
ALTER TABLE call_routes ADD FOREIGN KEY application_sid_idxfk (application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
CREATE INDEX dns_record_sid_idx ON dns_records (dns_record_sid);
|
||||
ALTER TABLE dns_records ADD FOREIGN KEY account_sid_idxfk_3 (account_sid) REFERENCES accounts (account_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);
|
||||
ALTER TABLE predefined_sip_gateways ADD FOREIGN KEY predefined_carrier_sid_idxfk (predefined_carrier_sid) REFERENCES predefined_carriers (predefined_carrier_sid);
|
||||
|
||||
CREATE INDEX predefined_smpp_gateway_sid_idx ON predefined_smpp_gateways (predefined_smpp_gateway_sid);
|
||||
CREATE INDEX predefined_carrier_sid_idx ON predefined_smpp_gateways (predefined_carrier_sid);
|
||||
ALTER TABLE predefined_smpp_gateways ADD FOREIGN KEY predefined_carrier_sid_idxfk_1 (predefined_carrier_sid) REFERENCES predefined_carriers (predefined_carrier_sid);
|
||||
|
||||
CREATE INDEX product_sid_idx ON products (product_sid);
|
||||
CREATE INDEX account_product_sid_idx ON account_products (account_product_sid);
|
||||
CREATE INDEX account_subscription_sid_idx ON account_products (account_subscription_sid);
|
||||
@@ -429,14 +608,14 @@ ALTER TABLE account_products ADD FOREIGN KEY product_sid_idxfk (product_sid) REF
|
||||
|
||||
CREATE INDEX account_offer_sid_idx ON account_offers (account_offer_sid);
|
||||
CREATE INDEX account_sid_idx ON account_offers (account_sid);
|
||||
ALTER TABLE account_offers ADD FOREIGN KEY account_sid_idxfk_4 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE account_offers ADD FOREIGN KEY account_sid_idxfk_5 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX product_sid_idx ON account_offers (product_sid);
|
||||
ALTER TABLE account_offers ADD FOREIGN KEY product_sid_idxfk_1 (product_sid) REFERENCES products (product_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_5 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE api_keys ADD FOREIGN KEY account_sid_idxfk_6 (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);
|
||||
@@ -450,59 +629,72 @@ ALTER TABLE sbc_addresses ADD FOREIGN KEY service_provider_sid_idxfk_1 (service_
|
||||
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_2 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY account_sid_idxfk_6 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY account_sid_idxfk_7 (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 service_provider_sid_idx ON service_provider_limits (service_provider_sid);
|
||||
ALTER TABLE service_provider_limits ADD FOREIGN KEY service_provider_sid_idxfk_3 (service_provider_sid) REFERENCES service_providers (service_provider_sid) ON DELETE CASCADE;
|
||||
|
||||
CREATE INDEX email_idx ON signup_history (email);
|
||||
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_3 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE UNIQUE INDEX speech_credentials_idx_1 ON speech_credentials (vendor,account_sid);
|
||||
ALTER TABLE smpp_addresses ADD FOREIGN KEY service_provider_sid_idxfk_4 (service_provider_sid) REFERENCES service_providers (service_provider_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_4 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
ALTER TABLE speech_credentials ADD FOREIGN KEY service_provider_sid_idxfk_5 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE INDEX account_sid_idx ON speech_credentials (account_sid);
|
||||
ALTER TABLE speech_credentials ADD FOREIGN KEY account_sid_idxfk_7 (account_sid) REFERENCES accounts (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);
|
||||
CREATE INDEX account_sid_idx ON users (account_sid);
|
||||
ALTER TABLE users ADD FOREIGN KEY account_sid_idxfk_8 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE users ADD FOREIGN KEY account_sid_idxfk_9 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX service_provider_sid_idx ON users (service_provider_sid);
|
||||
ALTER TABLE users ADD FOREIGN KEY service_provider_sid_idxfk_5 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
ALTER TABLE users ADD FOREIGN KEY service_provider_sid_idxfk_6 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE INDEX email_activation_code_idx ON users (email_activation_code);
|
||||
CREATE INDEX voip_carrier_sid_idx ON voip_carriers (voip_carrier_sid);
|
||||
CREATE INDEX account_sid_idx ON voip_carriers (account_sid);
|
||||
ALTER TABLE voip_carriers ADD FOREIGN KEY account_sid_idxfk_9 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE voip_carriers ADD FOREIGN KEY account_sid_idxfk_10 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
CREATE INDEX service_provider_sid_idx ON voip_carriers (service_provider_sid);
|
||||
ALTER TABLE voip_carriers ADD FOREIGN KEY service_provider_sid_idxfk_6 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
ALTER TABLE voip_carriers ADD FOREIGN KEY service_provider_sid_idxfk_7 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
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);
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY voip_carrier_sid_idxfk_1 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
|
||||
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY account_sid_idxfk_10 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY account_sid_idxfk_11 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY application_sid_idxfk_3 (application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
CREATE INDEX service_provider_sid_idx ON phone_numbers (service_provider_sid);
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY service_provider_sid_idxfk_7 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
ALTER TABLE phone_numbers ADD FOREIGN KEY service_provider_sid_idxfk_8 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE INDEX sip_gateway_idx_hostport ON sip_gateways (ipv4,port);
|
||||
|
||||
@@ -518,10 +710,10 @@ CREATE UNIQUE INDEX applications_idx_name ON applications (account_sid,name);
|
||||
|
||||
CREATE INDEX application_sid_idx ON applications (application_sid);
|
||||
CREATE INDEX service_provider_sid_idx ON applications (service_provider_sid);
|
||||
ALTER TABLE applications ADD FOREIGN KEY service_provider_sid_idxfk_8 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
ALTER TABLE applications ADD FOREIGN KEY service_provider_sid_idxfk_9 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE INDEX account_sid_idx ON applications (account_sid);
|
||||
ALTER TABLE applications ADD FOREIGN KEY account_sid_idxfk_11 (account_sid) REFERENCES accounts (account_sid);
|
||||
ALTER TABLE applications ADD FOREIGN KEY account_sid_idxfk_12 (account_sid) REFERENCES accounts (account_sid);
|
||||
|
||||
ALTER TABLE applications ADD FOREIGN KEY call_hook_sid_idxfk (call_hook_sid) REFERENCES webhooks (webhook_sid);
|
||||
|
||||
@@ -537,7 +729,7 @@ ALTER TABLE service_providers ADD FOREIGN KEY registration_hook_sid_idxfk (regis
|
||||
CREATE INDEX account_sid_idx ON accounts (account_sid);
|
||||
CREATE INDEX sip_realm_idx ON accounts (sip_realm);
|
||||
CREATE INDEX service_provider_sid_idx ON accounts (service_provider_sid);
|
||||
ALTER TABLE accounts ADD FOREIGN KEY service_provider_sid_idxfk_9 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
ALTER TABLE accounts ADD FOREIGN KEY service_provider_sid_idxfk_10 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
ALTER TABLE accounts ADD FOREIGN KEY registration_hook_sid_idxfk_1 (registration_hook_sid) REFERENCES webhooks (webhook_sid);
|
||||
|
||||
@@ -545,4 +737,6 @@ ALTER TABLE accounts ADD FOREIGN KEY queue_event_hook_sid_idxfk (queue_event_hoo
|
||||
|
||||
ALTER TABLE accounts ADD FOREIGN KEY device_calling_application_sid_idxfk (device_calling_application_sid) REFERENCES applications (application_sid);
|
||||
|
||||
ALTER TABLE accounts ADD FOREIGN KEY siprec_hook_sid_idxfk (siprec_hook_sid) REFERENCES applications (application_sid);
|
||||
|
||||
SET FOREIGN_KEY_CHECKS=1;
|
||||
|
||||
@@ -27,11 +27,23 @@ insert into voip_carriers (voip_carrier_sid, name, service_provider_sid) values
|
||||
insert into sip_gateways (sip_gateway_sid, voip_carrier_sid, ipv4, inbound, outbound)
|
||||
values ('e71519ff-4494-4c98-a06a-324e2712d94b', '1d8ef351-062a-4487-94f8-7698d5a40d24', '172.39.0.21', true, true);
|
||||
|
||||
-- lcr route: first try eastco (503) then westco (200)
|
||||
insert into lcr_routes(lcr_route_sid, regex, priority) values ('0eba4204-b036-4388-8f47-724c4cfb3d4e', '.*', 1);
|
||||
-- lcr route: default eastco, but 1617 matches westco (503)
|
||||
insert into lcr(lcr_sid, account_sid)
|
||||
values('c0b0b6a0-0b0a-4b0b-8b0b-0b0b0b0b0b0b', 'ed649e33-e771-403a-8c99-1780eabbc803');
|
||||
|
||||
insert into lcr_routes(lcr_route_sid, lcr_sid, regex, priority)
|
||||
values ('9eba4204-b036-4388-8f47-724c4cfb3d4e', 'c0b0b6a0-0b0a-4b0b-8b0b-0b0b0b0b0b0b', '.*', 999);
|
||||
|
||||
insert into lcr_carrier_set_entry(lcr_carrier_set_entry_sid, lcr_route_sid, voip_carrier_sid, priority)
|
||||
values ('b677a7b5-bec6-4045-ae4a-a67a5ccb3448', '0eba4204-b036-4388-8f47-724c4cfb3d4e', '1d8ef351-062a-4487-94f8-7698d5a40d24', 1);
|
||||
values ('13e344a0-8a4c-4f95-8a19-ccbfc7ab053e', '9eba4204-b036-4388-8f47-724c4cfb3d4e', '1d8ef351-062a-4487-94f8-7698d5a40d24', 1);
|
||||
|
||||
-- attach default route to lcr
|
||||
update lcr set default_carrier_set_entry_sid = '13e344a0-8a4c-4f95-8a19-ccbfc7ab053e';
|
||||
|
||||
-- add a route based on digit match
|
||||
insert into lcr_routes(lcr_route_sid, lcr_sid, regex, priority)
|
||||
values ('3eba4204-b036-4388-8f47-724c4cfb3d4e', 'c0b0b6a0-0b0a-4b0b-8b0b-0b0b0b0b0b0b', '1617', 1);
|
||||
|
||||
insert into lcr_carrier_set_entry(lcr_carrier_set_entry_sid, lcr_route_sid, voip_carrier_sid, priority)
|
||||
values ('13e344a0-8a4c-4f95-8a19-ccbfc7ab053e', '0eba4204-b036-4388-8f47-724c4cfb3d4e', '287c1452-620d-4195-9f19-c9814ef90d78', 2);
|
||||
values ('b677a7b5-bec6-4045-ae4a-a67a5ccb3448', '3eba4204-b036-4388-8f47-724c4cfb3d4e', '287c1452-620d-4195-9f19-c9814ef90d78', 1);
|
||||
|
||||
|
||||
@@ -17,28 +17,7 @@ values ('f4e1848d-3ff8-40eb-b9c1-30e1ef053f94','ed649e33-e771-403a-8c99-1780eabb
|
||||
insert into account_products(account_product_sid, account_subscription_sid, product_sid,quantity)
|
||||
values ('f23ff996-6534-4aba-8666-4b347391eca2', 'f4e1848d-3ff8-40eb-b9c1-30e1ef053f94', 'c4403cdb-8e75-4b27-9726-7d8315e3216d', 10);
|
||||
|
||||
-- "good" carrier - "westco" at 172.39.0.20
|
||||
insert into voip_carriers (voip_carrier_sid, name, service_provider_sid) values ('287c1452-620d-4195-9f19-c9814ef90d78', 'westco', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0');
|
||||
insert into sip_gateways (sip_gateway_sid, voip_carrier_sid, ipv4, inbound, outbound)
|
||||
values ('124a5339-c62c-4075-9e19-f4de70a96597', '287c1452-620d-4195-9f19-c9814ef90d78', '172.39.0.20', true, true);
|
||||
|
||||
-- "bad" carrier - "eastco" at 172.39.0.21 (returns 503)
|
||||
insert into voip_carriers (voip_carrier_sid, name, service_provider_sid) values ('1d8ef351-062a-4487-94f8-7698d5a40d24', 'eastco', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0');
|
||||
insert into sip_gateways (sip_gateway_sid, voip_carrier_sid, ipv4, inbound, outbound)
|
||||
values ('e71519ff-4494-4c98-a06a-324e2712d94b', '1d8ef351-062a-4487-94f8-7698d5a40d24', '172.39.0.21', true, true);
|
||||
|
||||
-- "bad" carrier - "northco" at 172.39.0.22 (returns 100 Trying and never answers)
|
||||
insert into voip_carriers (voip_carrier_sid, name, service_provider_sid) values ('7b4b9c56-4d81-4f31-9d70-62cd7d82193b', 'northco', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0');
|
||||
insert into sip_gateways (sip_gateway_sid, voip_carrier_sid, ipv4, inbound, outbound)
|
||||
values ('fcbb2109-582c-4d55-b38a-b6bc7cc4be73', '7b4b9c56-4d81-4f31-9d70-62cd7d82193b', '172.39.0.22', true, true);
|
||||
|
||||
-- lcr route: first try eastco (503) then northco (cancel), then westco (200)
|
||||
insert into lcr_routes(lcr_route_sid, regex, priority) values ('0eba4204-b036-4388-8f47-724c4cfb3d4e', '.*', 1);
|
||||
|
||||
insert into lcr_carrier_set_entry(lcr_carrier_set_entry_sid, lcr_route_sid, voip_carrier_sid, priority)
|
||||
values ('b677a7b5-bec6-4045-ae4a-a67a5ccb3448', '0eba4204-b036-4388-8f47-724c4cfb3d4e', '1d8ef351-062a-4487-94f8-7698d5a40d24', 1);
|
||||
insert into lcr_carrier_set_entry(lcr_carrier_set_entry_sid, lcr_route_sid, voip_carrier_sid, priority)
|
||||
values ('b417ce66-a805-46b3-b296-697a6c2ca249', '0eba4204-b036-4388-8f47-724c4cfb3d4e', '7b4b9c56-4d81-4f31-9d70-62cd7d82193b', 2);
|
||||
insert into lcr_carrier_set_entry(lcr_carrier_set_entry_sid, lcr_route_sid, voip_carrier_sid, priority)
|
||||
values ('13e344a0-8a4c-4f95-8a19-ccbfc7ab053e', '0eba4204-b036-4388-8f47-724c4cfb3d4e', '287c1452-620d-4195-9f19-c9814ef90d78', 3);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ networks:
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
platform: linux/x86_64
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
@@ -23,7 +24,7 @@ services:
|
||||
ipv4_address: 172.39.0.2
|
||||
|
||||
sbc:
|
||||
image: drachtio/drachtio-server:latest
|
||||
image: drachtio/drachtio-server:0.8.26
|
||||
command: drachtio --contact "sip:*;transport=udp" --loglevel debug --sofia-loglevel 9
|
||||
ports:
|
||||
- "9060:9022/tcp"
|
||||
@@ -118,7 +119,7 @@ services:
|
||||
ipv4_address: 172.39.0.24
|
||||
|
||||
influxdb:
|
||||
image: influxdb:1.8-alpine
|
||||
image: influxdb:1.8
|
||||
ports:
|
||||
- "8086:8086"
|
||||
networks:
|
||||
|
||||
@@ -5,3 +5,7 @@ DRACHTIO_SECRET=cymru
|
||||
JAMBONES_REDIS_HOST=172.39.0.11
|
||||
JAMBONES_REDIS_PORT=6379
|
||||
JAMBONES_LOGLEVEL=info
|
||||
JAMBONES_MYSQL_HOST=172.39.0.2
|
||||
JAMBONES_MYSQL_USER=jambones_test
|
||||
JAMBONES_MYSQL_PASSWORD=jambones_test
|
||||
JAMBONES_MYSQL_DATABASE=jambones_test
|
||||
@@ -33,9 +33,10 @@
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-pcap-carrier-success
|
||||
Subject: uac-cancel
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: phone
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
@@ -59,7 +60,7 @@
|
||||
|
||||
<recv response="100" response_txn="invite"/>
|
||||
|
||||
<pause milliseconds="3000"/>
|
||||
<pause milliseconds="1000"/>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
@@ -103,4 +104,3 @@
|
||||
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
|
||||
|
||||
</scenario>
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-device-unknown-user
|
||||
X-Jambonz-Routing: user
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-device-unknown-user
|
||||
X-Jambonz-Routing: user
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-device-unknown-user
|
||||
X-Jambonz-Routing: user
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<scenario name="UAC with media">
|
||||
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
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@127.0.0.1>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: ff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: phone
|
||||
Subject: uac-pcap-carrier-fail-limits
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[local_ip_type] [local_ip]
|
||||
t=0 0
|
||||
m=audio [auto_media_port] RTP/AVP 8 101
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-11,16
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="603" rtd="true" crlf="true">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:sip:+16173333456@127.0.0.1 SIP/2.0
|
||||
[last_Via]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
|
||||
To: <sip:sip:+16173333456@127.0.0.1>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Subject: uac-pcap-carrier-fail-blacklist
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
|
||||
</scenario>
|
||||
@@ -16,6 +16,7 @@
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: ff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: phone
|
||||
Subject: uac-pcap-carrier-fail-limits
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<scenario name="UAC with media">
|
||||
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16173333456@sbc-sip:5060 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@sbc-sip:5060>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: phone
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[local_ip_type] [local_ip]
|
||||
t=0 0
|
||||
m=audio [auto_media_port] RTP/AVP 8 101
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-11,16
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="200" rtd="true" crlf="true">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:sip:+16173333456@sbc-sip:5060 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@sbc-sip:5060>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<!-- Play a pre-recorded PCAP file (RTP stream) -->
|
||||
<nop>
|
||||
<action>
|
||||
<exec play_pcap_audio="pcap/g711a.pcap"/>
|
||||
</action>
|
||||
</nop>
|
||||
|
||||
<!-- Pause briefly -->
|
||||
<pause milliseconds="2000"/>
|
||||
|
||||
<!-- The 'crlf' option inserts a blank line in the statistics report. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
BYE sip:sip:+16173333456@sbc-sip:5060 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@sbc-sip:5060>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 2 BYE
|
||||
Max-Forwards: 70
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200" crlf="true">
|
||||
</recv>
|
||||
|
||||
</scenario>
|
||||
@@ -17,6 +17,7 @@
|
||||
Subject: uac-pcap-carrier-success
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: phone
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: phone
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
Subject: uac-pcap-device-success
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: ff649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: user
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-pcap-device-success
|
||||
X-Jambonz-Routing: user
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-pcap-device-success
|
||||
X-Jambonz-Routing: user
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<scenario name="UAC with media">
|
||||
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
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@127.0.0.1>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: phone
|
||||
X-Voip-Carrier-Sid: 287c1452-620d-4195-9f19-c9814ef90d78
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[local_ip_type] [local_ip]
|
||||
t=0 0
|
||||
m=audio [auto_media_port] RTP/AVP 8 101
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-11,16
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="200" rtd="true" crlf="true">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
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@127.0.0.1>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<!-- Play a pre-recorded PCAP file (RTP stream) -->
|
||||
<nop>
|
||||
<action>
|
||||
<exec play_pcap_audio="pcap/g711a.pcap"/>
|
||||
</action>
|
||||
</nop>
|
||||
|
||||
<!-- Pause briefly -->
|
||||
<pause milliseconds="2000"/>
|
||||
|
||||
<!-- The 'crlf' option inserts a blank line in the statistics report. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
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@127.0.0.1>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 2 BYE
|
||||
Max-Forwards: 70
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200" crlf="true">
|
||||
</recv>
|
||||
|
||||
</scenario>
|
||||
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<scenario name="UAC with media">
|
||||
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:16173333456@172.39.0.20 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@172.39.0.20>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: sip
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[local_ip_type] [local_ip]
|
||||
t=0 0
|
||||
m=audio [auto_media_port] RTP/AVP 8 101
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-11,16
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="200" rtd="true" crlf="true">
|
||||
</recv>
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
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@127.0.0.1>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<!-- Play a pre-recorded PCAP file (RTP stream) -->
|
||||
<nop>
|
||||
<action>
|
||||
<exec play_pcap_audio="pcap/g711a.pcap"/>
|
||||
</action>
|
||||
</nop>
|
||||
|
||||
<!-- Pause briefly -->
|
||||
<pause milliseconds="2000"/>
|
||||
|
||||
<!-- The 'crlf' option inserts a blank line in the statistics report. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
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@127.0.0.1>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 2 BYE
|
||||
Max-Forwards: 70
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200" crlf="true">
|
||||
</recv>
|
||||
|
||||
</scenario>
|
||||
@@ -37,6 +37,7 @@
|
||||
Subject: uac-sip-uri-auth-success
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: sip
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
@@ -90,6 +91,7 @@
|
||||
Subject: uac-sip-uri-auth-success
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: sip
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
@@ -126,7 +128,7 @@
|
||||
To: <sip:sip:john@jambonz.org>[peer_tag_param]
|
||||
[last_Call-ID:]
|
||||
CSeq: 2 ACK
|
||||
Subject: uac-sip-uri-auth-success
|
||||
Subject:
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<!-- This program is free software; you can redistribute it and/or -->
|
||||
<!-- modify it under the terms of the GNU General Public License as -->
|
||||
<!-- published by the Free Software Foundation; either version 2 of the -->
|
||||
<!-- License, or (at your option) any later version. -->
|
||||
<!-- -->
|
||||
<!-- This program is distributed in the hope that it will be useful, -->
|
||||
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
|
||||
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
|
||||
<!-- GNU General Public License for more details. -->
|
||||
<!-- -->
|
||||
<!-- You should have received a copy of the GNU General Public License -->
|
||||
<!-- along with this program; if not, write to the -->
|
||||
<!-- Free Software Foundation, Inc., -->
|
||||
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
|
||||
<!-- -->
|
||||
<!-- Sipp 'uac' scenario with pcap (rtp) play -->
|
||||
<!-- -->
|
||||
|
||||
<scenario name="UAC with media">
|
||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:john@foo.bar 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:john@foo.bar>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-sip-uri-proxy
|
||||
X-Account-Sid: ed649e33-e771-403a-8c99-1780eabbc803
|
||||
X-Call-Sid: fff49e33-e771-403a-8c99-1780eabbc803
|
||||
X-Jambonz-Routing: sip
|
||||
X-SIP-Proxy: 172.39.0.20:5060
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[local_ip_type] [local_ip]
|
||||
t=0 0
|
||||
m=audio [auto_media_port] RTP/AVP 8 101
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-11,16
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="200">
|
||||
</recv>
|
||||
|
||||
<!-- Packet lost can be simulated in any send/recv message by -->
|
||||
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:john@foo.bar SIP/2.0
|
||||
[last_Via:]
|
||||
[last_From:]
|
||||
To: <sip:sip:john@jambonz.org>[peer_tag_param]
|
||||
[last_Call-ID:]
|
||||
CSeq: 2 ACK
|
||||
Subject:
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<!-- Play a pre-recorded PCAP file (RTP stream) -->
|
||||
<nop>
|
||||
<action>
|
||||
<exec play_pcap_audio="pcap/g711a.pcap"/>
|
||||
</action>
|
||||
</nop>
|
||||
|
||||
<!-- Pause briefly -->
|
||||
<pause milliseconds="2000"/>
|
||||
|
||||
<!-- The 'crlf' option inserts a blank line in the statistics report. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
BYE sip:john@foo.bar SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
[last_From:]
|
||||
[last_To:]
|
||||
[last_Call-ID:]
|
||||
CSeq: 3 BYE
|
||||
Max-Forwards: 70
|
||||
Subject: uac-sip-uri-proxy
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200" crlf="true">
|
||||
</recv>
|
||||
|
||||
<!-- definition of the response time repartition table (unit is ms) -->
|
||||
<ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
|
||||
|
||||
<!-- definition of the call length repartition table (unit is ms) -->
|
||||
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
|
||||
|
||||
</scenario>
|
||||
+40
-7
@@ -2,7 +2,8 @@ const test = require('tape');
|
||||
const { output, sippUac } = require('./sipp')('test_sbc-outbound');
|
||||
const {execSync} = require('child_process');
|
||||
const debug = require('debug')('jambonz:sbc-outbound');
|
||||
const consoleLogger = {error: console.error, info: console.log, debug: console.log};
|
||||
const bent = require('bent');
|
||||
const getJSON = bent('json');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
@@ -25,10 +26,16 @@ function connect(connectable) {
|
||||
test('sbc-outbound tests', async(t) => {
|
||||
const {srf} = require('../app');
|
||||
const { queryCdrs } = srf.locals;
|
||||
const redisClient = srf.locals.realtimeDbHelpers.client;
|
||||
|
||||
try {
|
||||
await connect(srf);
|
||||
|
||||
let obj = await getJSON('http://127.0.0.1:3050/');
|
||||
t.ok(obj.calls === 0, 'HTTP GET / works (current call count)')
|
||||
obj = await getJSON('http://127.0.0.1:3050/system-health');
|
||||
t.ok(obj.calls === 0, 'HTTP GET /system-health works (health check)')
|
||||
|
||||
/* call to unregistered user */
|
||||
debug('successfully connected to drachtio server');
|
||||
await sippUac('uac-pcap-device-404.xml');
|
||||
@@ -36,7 +43,19 @@ test('sbc-outbound tests', async(t) => {
|
||||
|
||||
/* call to PSTN with no lcr configured */
|
||||
await sippUac('uac-pcap-carrier-success.xml');
|
||||
t.pass('successfully completed outbound call to configured sip trunk');
|
||||
t.pass('successfully completed outbound call to sip trunk');
|
||||
|
||||
/* call to Sip URI with no lcr configured */
|
||||
await sippUac('uac-pcap-sip-routing-success.xml');
|
||||
t.pass('successfully completed outbound call to sip routing trunk');
|
||||
|
||||
/* call to PSTN with no lcr configured */
|
||||
await sippUac('uac-pcap-inbound-carrier-success.xml');
|
||||
t.pass('successfully completed outbound call to sip trunk');
|
||||
|
||||
/* call to PSTN with request uri we see in kubernetes */
|
||||
await sippUac('uac-pcap-carrier-success-k8s.xml');
|
||||
t.pass('successfully completed outbound call to sip trunk (k8S req uri)');
|
||||
|
||||
// re-rack test data
|
||||
execSync(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/jambones-sql.sql`);
|
||||
@@ -44,7 +63,7 @@ test('sbc-outbound tests', async(t) => {
|
||||
|
||||
/* call to PSTN with lcr configured */
|
||||
await sippUac('uac-pcap-carrier-success.xml');
|
||||
t.pass('successfully completed outbound lcr carrier with crankback after failure');
|
||||
t.pass('successfully completed outbound call using LCR');
|
||||
|
||||
// re-rack test data
|
||||
execSync(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/jambones-sql.sql`);
|
||||
@@ -66,6 +85,10 @@ test('sbc-outbound tests', async(t) => {
|
||||
await sippUac('uac-sip-uri-auth-success.xml');
|
||||
t.pass('successfully connected to sip uri that requires auth');
|
||||
|
||||
/* invite to sipUri through proxy */
|
||||
await sippUac('uac-sip-uri-proxy.xml');
|
||||
t.pass('successfully connected to sip uri through proxy');
|
||||
|
||||
// re-rack test data
|
||||
execSync(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/jambones-sql.sql`);
|
||||
execSync(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/populate-test-data5.sql`);
|
||||
@@ -73,12 +96,22 @@ test('sbc-outbound tests', async(t) => {
|
||||
/* fails when session limit exceeded */
|
||||
await sippUac('uac-pcap-carrier-fail-limits.xml');
|
||||
t.pass('fails when max calls in progress');
|
||||
|
||||
await waitFor(10);
|
||||
|
||||
// re-rack test data
|
||||
execSync(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/jambones-sql.sql`);
|
||||
execSync(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/populate-test-data.sql`);
|
||||
|
||||
// Black list good carrier for some seconds
|
||||
await redisClient.setex('blacklist-sip-gateway:124a5339-c62c-4075-9e19-f4de70a96597', 3, '');
|
||||
await sippUac('uac-pcap-carrier-fail-blacklist.xml');
|
||||
t.pass('fails when carrier is blacklisted');
|
||||
await redisClient.del('blacklist-sip-gateway:124a5339-c62c-4075-9e19-f4de70a96597');
|
||||
|
||||
await waitFor(25);
|
||||
|
||||
const res = await queryCdrs({account_sid: 'ed649e33-e771-403a-8c99-1780eabbc803'});
|
||||
//console.log(`cdrs: ${JSON.stringify(res)}`);
|
||||
t.ok(res.total === 6, 'wrote 6 cdrs');
|
||||
console.log(`${res.total} cdrs: ${JSON.stringify(res)}`);
|
||||
t.ok(res.total === 9, 'wrote 9 cdrs');
|
||||
|
||||
srf.disconnect();
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user