Compare commits

...
264 Commits
Author SHA1 Message Date
Sam Machin b882c0de2d Recording update (#212)
* if call hasRecording then add url to cdr

* handle missing hasRecording value in redis

* typo

* lint

* use nullish coalescing for null response

* typo
2026-01-02 10:29:51 -05:00
Dave Horton b3fee43c7f include codec-accept on answer to rtpengine during reinvites (#209)
* include codec-accept on answer to rtpengine during reinvites

* attempt to simplify

* fixes from testing
2025-12-02 07:37:18 -05:00
Dave Horton 5fab8a7515 when far end answers with only pcma, passthrough instead of transcoding to pcmu (#208) 2025-11-24 11:23:16 -06:00
Anton Voylenko 178105acd7 chore: bump node version (#205) 2025-11-04 18:02:31 -05:00
Sam Machin 70a09c10b3 Fix/200 (#202)
* Update call-session.js

* Update call-session.js
2025-10-28 16:58:54 -04:00
Dave Horton 49bc11bbb6 update pino and eslint (#201) 2025-10-21 07:52:29 -04:00
Sam Machin 2ebe4d3d78 bump db_helpers for cache change (#199) 2025-10-15 11:40:12 -04:00
Sam Machin cd0d360561 Redirect outbound user calls to private IP of other SBC (#197)
* redirect client calls to private address of other SBC

* remove unused util

* use address not port
2025-10-04 20:08:57 -04:00
Sam Machin 32d82ed67d Fix/193 (#195)
* pass sip failure reason back to FS

* Update call-session.js

* update drachtio-srf dep
2025-09-12 09:21:13 -04:00
Dave Horton c17f27ab2c fix prev commit (#192) 2025-09-04 07:57:38 -04:00
Dave Horton 2fc570f731 when sending to retell with user starting with call_ dont prepend plus (#191) 2025-09-03 23:35:56 -04:00
Sam Machin c87f831868 Fix/transport in contact (#190)
* use the req transport param in the From and Contact headers if set.

* Update call-session.js
2025-09-03 13:53:51 -04:00
Sam Machin 616228bf09 update isPrivateVoipNetwork function (#189) 2025-09-02 08:02:59 -04:00
Dave Horton 55fef10f0e revert change (for now) that caused audio issues when reinviting to partial media (#187) 2025-08-18 12:45:48 -04:00
Sam Machin 9357920f76 set strict source (#185)
* set strict source

RTPBleed

* change to env var for strict source

* Update srtp-transcoding.json

* lint

* lint

* reverse the logic

* and argghhh

* clarification

* change
2025-08-03 19:44:31 -04:00
Dave Horton 6771cbdefc bump version 2025-07-15 11:46:40 -04:00
Vinod Dharashive e422c2ed9c increase dtmf volume (#184)
https://github.com/jambonz/jambonz-feature-server/issues/1272
2025-07-09 08:22:53 -04:00
rammohan-y fd5e2f1a6c Remove video sdp incase of reinvite (#183)
https://github.com/jambonz/sbc-outbound/issues/182
2025-07-08 09:17:16 -04:00
sathish kumar pasham 5cc37265a9 Fix vulnerabilities by adding @babel/helpers@7.26.10 to dependencies (#181) 2025-06-27 08:04:03 -04:00
Dave Horton 1a9c438321 update version 2025-05-14 15:42:31 -04:00
Dave Horton fb62fd9bf4 update db-helpers 2025-05-13 10:36:08 -04:00
rammohan-y e02e0fa8c1 Updated drachtio version to 5.0.5 (#177)
https://github.com/drachtio/drachtio-server/issues/424
2025-04-22 11:05:45 -04:00
Hoan Luu Huu dd198105e3 update drachtio srf 5.0.4 (#176) 2025-04-18 07:50:21 -04:00
rammohan-y a34491a356 Updated db-helper to 0.9.11 (#175)
https://github.com/jambonz/jambonz-feature-server/issues/1151
2025-04-14 08:22:09 -04:00
Dave Horton 3a467921a6 return 482 loop detected if call to a sip uri would loop back to us (#173)
* return 482 loop detected if call to a sip uri would loop back to us

* this pr fixes #172

* typo

* wip

* wip
2025-03-20 11:25:08 -04:00
rammohan-y 9cb75a0b6b updated realtimedb-helper to 0.8.13 (#169) 2025-03-10 09:53:49 -04:00
Dave Horton 28dae50202 REFER should have sips contact if far end is using sips (#168) 2025-02-24 09:47:47 -05:00
Dave Horton 0b45e766b9 update drachtio-srf 2025-02-20 10:16:59 -05:00
Dave Horton 7380457b5a reject calls on hosted jambonz with no activ (#166)
e subscriptions
2025-02-19 13:04:44 -05:00
Hoan Luu Huu 9feb6f3c8f support voip carrier sip proxy (#165)
* support voiip carrier sip proxy

* wip

* wip

* wip
2025-02-17 09:47:59 -05:00
Hoan Luu Huu c449feeb9c support sip recording from siprec call (#164)
* support sip recording from siprec call

* update srsclient version
2025-02-12 09:24:33 -05:00
Dave Horton b2abe9891e refined the method for syncing call count updates and added debugging (#163)
* refined the method for syncing call count updates and added debugging

* wip

* include callId in debug key
2025-02-05 13:01:04 -05:00
Dave Horton a89b54b19e update deps 2025-01-14 10:50:08 -05:00
Dave Horton ab0f3ed2b5 update dractio-srf and mrf to main branch releases (#162) 2025-01-14 10:06:27 -05:00
Vinod Dharashive d8ae824559 Fresh/466 (#160)
* Hold and unhold does not resume transcript for outbound call to webrtc

* WIP

* jslint

* jslint

* handle null

* Added direction

* re-invite with opus codec need to change pcmu 

on reinvite on unhold opus codec was been sent from rtpegine to freeswitch due to which transcripts were not getting generated , hence it needs to be changed to pcmu

* jslint

jslint
2024-12-23 07:20:04 -05:00
Hoan Luu Huu 334db6f84f support referby display name (#161) 2024-12-11 10:34:27 -05:00
Hoan Luu Huu c762accce8 forward all refer to feature server (#156)
* forward all refer to feature server

* forwawrd extra custom header
2024-12-05 21:16:12 -05:00
Dave Horton c6c63e26da fix duplicate attempt to destroy dialog 2024-11-22 10:46:24 -05:00
Dave Horton 916d577b75 wip (#155) 2024-11-19 09:37:53 -05:00
Dave Horton b976a62a60 when forcing PCMU or PCMA we must also include telephone-event (#153) 2024-11-14 08:37:40 -05:00
Hoan Luu Huu 5e2369e5e8 support opus transcode (#150)
* support opus transcode

* dont resend reinvite when release media for srtp

* wip

* fix review comment
2024-11-13 07:41:43 -05:00
Dave Horton 41528db630 fix call count race condition #151 (#152)
* fix call count race condition #151

* logging fix
2024-11-12 14:57:04 -05:00
Hoan Luu Huu c22104fbe2 allow outbound cdr has sip_parent_callid (#149)
* allow outbound cdr has sip_parent_callid

* update time series version
2024-10-17 08:05:25 -04:00
Hoan Luu Huu 3701e20295 fix sbc crash while outbound calling to user (#148) 2024-10-15 07:28:40 -04:00
Hoan Luu Huu 821275cb67 support X-CID for feature server to detect sip callid (#147)
* support X-CID for feature server to detect sip callid

* fixed review comment

* fix review comment
2024-10-15 07:19:25 -04:00
Dave Horton f5ac51a8f7 minor logging 2024-10-13 10:12:55 -04:00
Hoan Luu Huu 0cf90f37b8 support change log level runtime (#146) 2024-10-07 09:52:22 -04:00
Dave Horton 62fb716236 bump version 2024-09-04 13:42:02 +01:00
Dave Horton b8fdf9f429 when sending out registered trunk use auth.username as userinfo in Contact header of INVITE 2024-09-04 13:40:59 +01:00
Dave Horton 503aa73bc6 Fix/invite use realm to registered trunk (#144)
* special header X-Preferred-From-Host was ignored

* when sending INVITE to registered trunk with a sip realm, use realm in the uri and send via proxy as defined in outbound gateway
2024-08-29 15:26:44 -04:00
Dave Horton a7406ddb8a use system_information.private_network_cidr (#143)
* use system_information.private_network_cidr

* make gh actions work

* fix: export logger
2024-08-18 12:50:19 -04:00
Dave HortonandMarkus Frindt bf1c670b40 Fix/tls transport (#141)
* fix scheme

* add missing initialization of scheme

* delete contact header explicitly

* wip

* wip

* wip

* fix bug with cseq

* wip

* deps

* wip

* wip

---------

Co-authored-by: Markus Frindt <m.frindt@cognigy.com>
2024-07-24 15:27:01 -04:00
Markus FrindtandMarkus Frindt b21eaad1ff Support contact header with sips or sip over tls (#139)
Co-authored-by: Markus Frindt <m.frindt@cognigy.com>
2024-07-23 08:53:38 -04:00
Dave Horton c671fbdbd0 after challenge register with creds should stay tls if it started that way (#137) 2024-06-24 15:08:43 -04:00
Hoan Luu Huu 73e3779eb1 only send Sips if it's enabled in outbound gateway configuration (#136)
* only send Sips if it's enabled in outbound gateway configuration

* update license
2024-06-15 09:12:02 -04:00
Dave Horton 9ad7dc76c7 fix for #133 (#134) 2024-05-17 07:21:19 -04:00
Hoan Luu Huu 1baf7fa824 Feat/refer support conplex uri for refer-to refered-by header (#131)
* respond to reinvite request incase error

* support complex refer-to refer-by uri

* wip
2024-05-07 08:28:08 -04:00
Hoan Luu Huu d2807c69ed Feat/stats version (#132)
* respond to reinvite request incase error

* update stats colector version
2024-05-06 20:06:23 -04:00
Dave Horton 3e6dc35e86 update deps (#130) 2024-04-07 18:33:39 -04:00
Hoan Luu Huu 16a4709b7d check if sip gateway is in blacklist before sending outbound call (#119)
* check if sip gateway is in blacklist before sending outbound call

* wip

* wip

* wip

* add testcase for blacklist

* wip
2024-03-30 11:10:13 -04:00
Hoan Luu Huu 01adb5cbf0 fix cannot clear siprec records (#127) 2024-03-27 07:50:50 -04:00
Dave Horton 49ba872f51 remove asymmetric flag on offer to B party reinvite as port learning should still happen (#128) 2024-03-26 12:13:01 -04:00
Hoan Luu Huu 591b8f2f8a respond to reinvite request incase error (#126) 2024-03-07 07:49:05 -05:00
Hoan Luu Huu 16ac56e50e start/stop/pause/resume recording success when one of siprec server success. (#125)
* start/stop/pause/resume recording success when one of siprec server success.

* wip

* allow send custom header on pause, resume recording

* allow timeout for siprec action

* wip

* wip

* update siprec-client-utils
2024-03-05 13:06:04 -05:00
Markus FrindtandMarkus Frindt 9412790366 add fix to lint script (#124)
Co-authored-by: Markus Frindt <m.frindt@cognigy.com>
2024-02-15 09:44:56 -05:00
Dave Horton 453b27d6e1 avoid sips scheme if best effort TLS is enabled via env (#122) 2024-01-30 20:09:58 -08:00
Dave Horton 4ba3d5b2b0 update db-helpers 2024-01-17 13:37:39 -05:00
Hoan Luu Huu bc3e2334ed fix hangup custom headers (#121) 2023-12-28 20:44:33 -05:00
Hoan Luu Huu d70a01578d INVITE from gw should have register_from_domain if it's available (#120) 2023-12-13 07:57:28 -05:00
Hoan Luu Huu eb0de5b0ee Feat support Outbound DTMF (#115)
* feat/support inband dtmf

* wip

* wip

* wip

* wip

* wip

* wip

* wip
2023-12-11 10:40:24 -05:00
Anton Voylenko d684fbb59b Set display name (#117)
* JAMBONES_MYSQL_PORT env added

* set display name from header

* remove double quotes
2023-12-10 09:07:21 -05:00
Hoan Luu Huu d86d03c792 feat support invite private to private network (#111)
* feat support invite private to private network

* feat support invite private to private network

* support multi private cidrs

* fix review comment

* fix matching regex

* fix regex to extract IP from sip uri
2023-11-27 08:26:41 -05:00
Hoan Luu Huu b93e220fef let realtimedb-helper build configuration from env vars (#109) 2023-11-14 09:12:48 -05:00
Dave Horton 43ee944023 add support for sending calls to a sip uri through a proxy (#114) 2023-11-13 14:38:05 -05:00
Dave Horton 5a6a9b34d3 0.8.5 2023-11-09 12:39:49 -05:00
Dave Horton 1b215ed77b update drachtio-srf 2023-11-09 10:04:08 -05:00
Hoan Luu Huu 0b141c55bf fix outbound info (#107) 2023-10-10 08:18:43 -04:00
Hoan Luu Huu f7bd1f2bf5 add sentinelPassword option (#106) 2023-10-04 19:41:43 -04:00
Dave Horton 454986edcd #104 - pad crypto option (#105) 2023-09-15 13:53:35 -04:00
Dave Horton 114d34def4 fix for PAD_CRYPTO feature 2023-09-14 10:14:27 -04:00
Dave Horton 6cf59732fb add env var PAD_CRYPTO to pad crypto params (#102) 2023-08-30 22:09:28 -04:00
two56andMatt Preskett 7f2bce927d Fix: Feature server REFER (#90)
* Update _onFeatureServerTransfer to fix REFER case

* Avoid remote re-invite by using port latching

* Add INFO listener and pretend we're a uas

---------

Co-authored-by: Matt Preskett <matt.preskett@netcall.com>
2023-08-20 08:40:07 -04:00
Paulo Tellesandp.souza 41eaa4286f fix service_provider_sid undefined (#101)
Co-authored-by: p.souza <p.souza@cognigy.com>
2023-08-16 12:37:20 -04:00
Hoan Luu Huu 9fe731ab65 fix outbound sip uri To header and Request Line are not same (#99)
* fix outbound sip uri To header and Request Line are not same

* fix outbound sip uri To header and Request Line are not same
2023-08-09 07:47:52 -04:00
Dave Horton 0977dd16b3 remove X-Preferred-From-Host on outdial 2023-08-08 13:03:33 -04:00
Hoan Luu Huu 33971ed6d8 update stats collector version (#97) 2023-08-07 21:26:28 -04:00
Hoan Luu Huu 6200f2b50a feat: siprect custom headers (#95)
* feat: siprect custom headers

* update siprec client utils

* wip
2023-07-20 09:02:44 -04:00
Hoan Luu Huu 7e13c5559f feat: multi srs (#93) 2023-07-04 16:42:17 +01:00
Dave Horton 4343b01505 0.8.4 2023-06-28 09:33:32 +01:00
Hoan Luu Huu 81d7a9f1e6 feat: record all call (#84)
* feat: record all call

* feat: record all call

* feat: record all call

* feat: record all call

* add record format in recording url

* add record format to recording url

* fix ext
2023-06-09 15:01:46 -04:00
Dave Horton 695e44da29 Fix/trace (#92)
* fix docker build

* fix: trace_id in cdrs
2023-06-09 13:58:36 -04:00
Hoan Luu Huu 9b1a70d4a5 Merge pull request #91 from jambonz/feat/sentinel
Feat/sentinel
2023-06-08 21:17:37 +07:00
Quan HL db38269a77 fix 2023-06-07 21:13:08 +07:00
Quan HL 337c24df41 update 2023-06-07 20:42:26 +07:00
Quan HL 554cdbcacf feat: sentinel configuration 2023-06-07 15:16:06 +07:00
Hoan Luu Huu d50272d7da update dbhelper and redis cache (#86) 2023-05-29 10:21:03 -04:00
Dave Horton b2c4199c04 update siprec-client-utils 2023-05-29 09:22:45 -04:00
Hoan Luu Huu f79f29bd7e fix: remove X-Voip-Carrier-Sid for outbound INVITE (#88) 2023-05-29 09:21:23 -04:00
Hoan Luu Huu 738429a46f additional routing rule (#87)
* additional routing rule

* add testcase

* update failing testcase
2023-05-29 09:06:11 -04:00
Dave Horton 9015ac4e13 0.8.3 2023-05-11 09:25:44 -04:00
Dave Horton 750294eab2 add support for tls with srtp media encryption (#83) 2023-05-10 16:02:08 -04:00
Hoan Luu HuuandDave Horton 76558cc5c9 add protocol for outbound call (#82)
* add protocol for outbound call

* add protocol for outbound call

* update db-helper version

* lcr fixes

* fix cancel test

---------

Co-authored-by: Dave Horton <daveh@beachdognet.com>
2023-05-10 14:43:46 -04:00
Dave Horton d604440907 update deps 2023-05-08 13:13:49 -04:00
Hoan Luu Huu 039a60c1b7 feat: lcr added (#80)
* feat: lcr added

* fix: await for async

* fix: add lookupCarrierByAccountLcr to locals

* fix typo

* fix typo

* fix typo
2023-05-08 09:06:37 -04:00
Hoan Luu Huu e00379d119 update media session to callee (#78) 2023-04-13 07:43:29 -04:00
EgleH 5174e140a2 Update base image to node:18.15-alpine3.16 (#77) 2023-04-12 13:18:19 -04:00
Dave Horton 7923ececaf push to docker 2023-04-10 09:43:25 -04:00
Anton Voylenko 66974c467d JAMBONES_MYSQL_PORT env added (#76) 2023-04-08 12:16:46 -04:00
Hoan Luu Huu 95bd061e2d feat: update stat collector version (#75) 2023-04-05 12:04:00 -04:00
Anton Voylenko 2f53c0bb4f Update README and add validation (#74)
* update README

* add JAMBONES_TIME_SERIES_HOST validation
2023-04-01 17:51:32 -04:00
Dave Horton f03308b11d bump version 2023-03-28 14:16:20 -04:00
Hoan Luu HuuandQuan HL 7c91ac9d68 feat: add instance_id tag to active call metric (#71)
Co-authored-by: Quan HL <quanluuhoang8@gmail.com>
2023-03-21 07:55:22 -04:00
Dave Horton fcffa1041c refactor of speech-utils 2023-03-14 09:54:46 -04:00
Dave Horton a6d5b25e40 bump version 2023-02-24 10:06:57 -05:00
Snyk bot 5cba07ee44 fix: Dockerfile to reduce vulnerabilities (#69)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326666
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326668
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326685
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326686
- https://snyk.io/vuln/SNYK-UPSTREAM-NODE-3326688
2023-02-23 07:23:07 -05:00
EgleH 579e21b5bf Upgrade node to node:18.14.0-alpine3.16 (#68) 2023-02-21 07:55:27 -05:00
Dave Horton 8864ab1430 update siprec client with fix for stop recording 2023-02-17 12:11:51 -05:00
Dave Horton 2187b03ca6 update siprec-client with fix for smart tap xml 2023-02-14 14:32:48 -05:00
Dave Horton 0808eeeda4 update to latest siprec client 2023-02-13 16:50:28 -05:00
Dave Horton f841e88bef bump version 2023-02-13 09:15:26 -05:00
Dave Horton b7873654b8 #63: dont select a carrier from a different account in LCR (#64)
* #63: dont select a carrier from a different account in LCR

* handle SIGTERM in K8S

* handle SIGTERM in K8S
2023-02-08 14:30:54 -05:00
Dave Horton b6f9f214c3 fix test case 2023-02-01 09:35:08 -05:00
Dave Horton dbb93732bf bugfix: calls in K8S may have request-uri like sip:<num>:sbc-sip and that's ok 2023-02-01 09:25:12 -05:00
two56andMatt Preskett a553105e55 If running stop srs (#60)
client on destruction

Co-authored-by: Matt Preskett <matt.preskett@netcall.com>
2023-01-23 12:35:41 -05:00
Hoan Luu HuuandQuan HL 36b2c752da fix: B2B uac parse Signal 0 (#59)
Co-authored-by: Quan HL <quanluuhoang8@gmail.com>
2023-01-18 10:33:01 -05:00
Dave Horton f6c0ee6c0d bump version 2023-01-12 16:19:43 -05:00
Dave Horton 96daad8ea1 bugfix: fix ref error on this.req.locals.account.disable_cdrs 2023-01-12 16:04:30 -05:00
Dave Horton b68edf425d minor changes in subscribe dtmf (#57)
* minor changes in subscribe dtmf

* update to rtpengine-utils with fix for not discarding dtmf as dups
2023-01-12 13:17:40 -05:00
Dave Horton 6d44c6c986 gh: run tests on PR 2023-01-09 10:13:53 -05:00
Dave Horton 6d593cbc7d add optional env var to specify udp localPort to bind to 2022-12-30 10:41:39 -05:00
Dave Horton 35b15c1f2c update deps 2022-12-29 09:58:44 -05:00
Dave Horton 7f1d1d61db update to drachtio-srf@4.5.21 with some perf fixes 2022-12-29 09:52:06 -05:00
Dave Horton 20c17fd723 update drachtio-srf 2022-12-28 11:10:15 -06:00
Dave Horton da6fa1da1b bump version 2022-12-24 12:07:10 -06:00
Dave Horton f74dff3b59 bugfix: when releasing media we were restarting ICE which we dont want to do 2022-12-19 21:28:43 -05:00
Dave Horton d9c4e01c36 add env JAMBONES_RECORD_ALL_CALLS to enable global call recording 2022-12-02 13:53:29 -05:00
Dave Horton 3d902c65a4 minor logging 2022-12-01 14:05:23 -05:00
Dave Horton d641504797 fix test 2022-11-29 13:05:51 -05:00
Dave Horton b871812a70 bugfix: was incorrectly making additional attempts after caller hung up or other failures on first attempt 2022-11-28 16:45:21 -05:00
Guilherme RauenandGuilherme Rauen e123a2ef88 update node image to the latest and most secure (#56)
Co-authored-by: Guilherme Rauen <g.rauen@cognigy.com>
2022-11-11 11:26:42 -05:00
Dave Horton 775e63518a include application_sid in cdr 2022-11-07 18:34:13 -05:00
Dave Horton 307cf9bd65 update deps 2022-11-02 13:38:02 -04:00
Dave Horton f48ca4821e update db-helpers 2022-11-01 21:22:55 -04:00
Dave Horton 43ed2bafe1 strip X-Preferred-From-User and X-Preferred-From-Host from outgoing call 2022-10-26 09:35:11 -04:00
Dave Horton 5ee041bdee feature: specify user or host part of From on outdial 2022-10-23 15:27:03 -04:00
Dave Horton 5bbe6a8752 update package-lock.json 2022-10-23 12:20:12 -04:00
Dave Horton 2e5d609bab update rtpengine-utils again 2022-10-23 11:27:34 -04:00
Dave Horton 8f598bf7b0 update to latest rtpengine-utils 2022-10-22 22:26:02 -04:00
Markus FrindtandMarkus Frindt c7d717b3ee [snyk] fix vulnerability (#55)
Co-authored-by: Markus Frindt <m.frindt@cognigy.com>
2022-10-20 21:36:07 -04:00
Dave Horton e9209b37ca include X-Account-Sid header when moving call between FS 2022-10-15 10:59:29 -04:00
Dave Horton 3b98dc6ec2 Bugfix/release media investigation (#54)
* added logging

* when releasing media, include asymettric flag on offer

* update package-lock.json

* remove media handover flag when releasing media
2022-10-14 12:46:14 -04:00
Dave Horton 1c84dd799c update time-series 2022-10-10 09:16:42 +01:00
Dave Horton 4338ae9411 bugfix: inject DMTF flag was inserted over and over 2022-10-07 11:52:36 +01:00
Dave Horton e17e7dbddd add support for connecting to rtpengine via ws 2022-09-27 09:43:05 +01:00
Dave Horton 254479e289 Feature/app call count tracking (#52)
* add call count tracking at the app level (optional)

* update call_counts_app schema

* update rtpengine-utils
2022-09-22 23:44:58 +02:00
Dave Horton a10a311dcb only track service provider calls if JAMBONES_TRACK_SP_CALLS is set 2022-09-20 16:42:22 +02:00
Dave Horton 806cb89c37 include application_sid in cdr 2022-09-20 14:00:33 +02:00
Dave Horton fffa2748d1 Feature/sp limits (#51)
* add account and service provider call limits

* add custom headers when rejecting calls due to max calls limit
2022-09-20 13:13:24 +02:00
Dave Horton 76625c7596 Feature/recent calls enhancement with sp (#50)
* write cdrs with service_provider_sid

* write call counts at the SP level
2022-09-16 13:45:33 +02:00
Paulo Tellesandp.souza 3b0f7ff6eb change node image (#48)
Co-authored-by: p.souza <p.souza@cognigy.com>
2022-09-07 13:20:13 +02:00
Dave Horton 33c75acc9e bump version to 0.7.6 2022-08-26 20:09:43 +02:00
xquanluu bae9ef7638 feat: update time-series 0.11.12 (#47) 2022-08-19 16:21:11 +02:00
Dave Horton 4ed4b38301 update time-series 2022-08-19 09:57:48 +02:00
Dave Horton 6b6f89264f minor logging change 2022-08-17 16:11:10 +02:00
Dave Horton f0e0fba2f1 make rtpengine transcode if non-preferred codec is selected by far end 2022-08-17 14:14:45 +02:00
Dave Horton 290723f234 initial changes for sip info dtmf (#46) 2022-08-11 15:13:07 +02:00
Dave Horton 5a14aa807a update to latest @jambonz/siprec-utils with fix for sdp version 2022-08-08 16:27:59 +02:00
Dave Horton 2505a36db6 fix 2022-08-05 10:11:21 +01:00
Dave Horton 90818206f5 Dockerfile: update base image 2022-08-05 10:10:24 +01:00
Dave Horton 1de4db6ebc Dockerfile: update base image 2022-07-28 12:58:23 +01:00
Dave Horton 7d2125788f when releasing media, use asymetric flag so that rtpengine does react to a spurious final packet from freeswitch by incorrectly sending rtp there 2022-07-26 12:25:58 +01:00
Snyk bot 3b83c1bda8 fix: upgrade drachtio-srf from 4.5.0 to 4.5.1 (#42)
Snyk has created this PR to upgrade drachtio-srf from 4.5.0 to 4.5.1.

See this package in npm:
https://www.npmjs.com/package/drachtio-srf

See this project in Snyk:
https://app.snyk.io/org/davehorton/project/282b1881-3e13-4fad-85bd-3b1c662c2134?utm_source=github&utm_medium=referral&page=upgrade-pr
2022-07-13 09:16:37 +02:00
Paulo Tellesandp.souza f352bf885c improve dockerfile to fix snyk security issues (#41)
Co-authored-by: p.souza <p.souza@cognigy.com>
2022-07-07 15:19:14 +02:00
Dave Horton 08a4f5defb update Dockerfile 2022-06-23 09:57:38 -04:00
Dave Horton 87df38110b update to azure 1.22.0 2022-06-11 16:23:08 -04:00
Dave Horton 06e370fa59 update deps 2022-06-11 11:55:21 -04:00
Dave Horton e4ed2cea26 healthcheck improvements (#38) 2022-04-12 15:45:33 -04:00
Dave Horton 0c8967acdb bump version 2022-04-06 08:18:22 -04:00
Dave Horton 1dfeed3ac8 bugfix: mem leak 2022-04-05 19:57:03 -04:00
Dave Horton d10bda2926 update deps 2022-04-01 13:46:16 -04:00
Dave Horton 3938773738 mem leak fix 2022-03-28 09:09:14 -04:00
Dave Horton f881002943 write otel trace_id to call history 2022-03-23 09:26:04 -04:00
Dave Horton 2266b80e73 release drachtio connection on final outbound failure 2022-03-18 14:34:30 -04:00
Snyk bot 4c3d6ddf0c fix: Dockerfile to reduce vulnerabilities (#35)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DEBIAN11-GNUTLS28-2419151
- https://snyk.io/vuln/SNYK-DEBIAN11-OPENSSL-2388380
- https://snyk.io/vuln/SNYK-DEBIAN11-OPENSSL-2426309
- https://snyk.io/vuln/SNYK-DEBIAN11-UTILLINUX-2401081
- https://snyk.io/vuln/SNYK-DEBIAN11-UTILLINUX-2401081
2022-03-18 07:55:10 -04:00
Dave Horton 3d1bcb23f4 bump version 2022-03-08 20:16:19 -05:00
Dave Horton 98ecfa20aa add support for redis auth 2022-03-08 09:30:44 -05:00
Dave Horton 56efe50aec forward incoming REFER to FS (#34) 2022-03-05 15:21:49 -05:00
Dave Horton 891d0ff38b use registered contact as uri when sending to user (#32) 2022-02-23 15:09:18 -05:00
Dave Horton 78d6cb5f22 use registered contact as uri when sending to user (#31) 2022-02-17 21:35:19 -05:00
Dave Horton 23255a71db added pre-commit hook for linting 2022-02-14 13:18:46 -05:00
Dave Horton 4cb34ad784 regression handling 302 redirect 2022-02-14 12:11:32 -05:00
Dave Horton f096a31a95 bump version 2022-02-09 15:43:50 -05:00
Dave Horton f671ef56bb update to latest @jambonz/realtimedb-helpers with support for redis username / password auth 2022-02-09 15:16:03 -05:00
Dave Horton 3a2fcf5976 update deps 2022-02-09 08:24:43 -05:00
Dave Horton dd85ec762d merge support for X-Override-To feature back into main branch 2022-02-01 12:30:34 -05:00
Dave Horton 61ec935ab8 0.7.2 version 2022-01-28 09:13:25 -05:00
Snyk bot cd7c425f7e fix: Dockerfile to reduce vulnerabilities (#30)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-1296898
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-2340908
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-2340908
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-2340922
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-2340922
2022-01-28 08:03:40 -05:00
Dave Horton e9b8cd595a Feature/rtpengine locate by dns (#29)
* k8s: query dns for rtpengine endpoints

* use udp now in k8s

* catch error on rtpengine failure

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

* use udp now in k8s

* catch error on rtpengine failure
2022-01-23 17:32:51 -05:00
Dave Horton 2ca44c4f5a healthcheck only in k8s 2022-01-22 21:43:18 -05:00
Dave Horton 50ff35c582 update to rtpengine-utils that reconnects ws 2022-01-19 19:25:47 -05:00
Dave Horton a318b07f61 Merge branch 'main' of github.com:jambonz/sbc-outbound into main 2022-01-17 18:40:07 -05:00
Dave Horton be354cde10 avoid adding multiple reset flags to rtpengine params 2022-01-17 18:39:51 -05:00
Snyk bot 594e8983e8 fix: Dockerfile to reduce vulnerabilities (#27)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-1296898
- https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-1296898
- https://snyk.io/vuln/SNYK-DEBIAN11-GMP-1920938
- https://snyk.io/vuln/SNYK-DEBIAN11-LIBGCRYPT20-1297892
- https://snyk.io/vuln/SNYK-DEBIAN11-PERL-1925976
2022-01-17 07:50:37 -05:00
Dave Horton 13f7d583df use ws rather than tcp for rtpengine connection in K8S 2022-01-12 08:28:59 -05:00
Dave Horton 6085452469 add support for tcp connections to rtpengine (#26) 2022-01-11 15:46:52 -05:00
Dave Horton 64ac043656 add support for using ws to connect to rtpengine 2022-01-10 22:01:41 -05:00
Dave Horton 7e9d48ee5b JAMBONES_NETWORK_CIDR not needed for K8S (#25) 2022-01-09 14:59:44 -05:00
Dave Horton fa7de71650 support for routing based on X-Jambonz-Routing header 2022-01-08 13:01:02 -05:00
Dave Horton 9d228d4805 K8s changes (#24)
* support multiple CIDRs

* use outbound in k8s

* k8s: user service for rtpengine

* bugfix: drachtio connection was dropped after successful connect

* Dockerfile

* k8s pre-stop hook

* make hjook executable

* update deps
2022-01-06 12:37:04 -05:00
Dave Horton 607fb3d127 bugfix: dialed numbers less than 8 digits are allowed since a voip_carrier may handle calls that are not actually routed to the PSTN (e.g. PBX) 2021-12-21 20:18:31 -05:00
Dave Horton 8da9c4ffa1 bump version 2021-12-21 09:46:04 -05:00
Dave Horton 97131be582 added docker publish 2021-12-13 14:17:27 -05:00
Dave Horton 3144cfd9c9 version bump 2021-12-13 09:53:11 -05:00
Dave Horton a1810e577a bugfix: outbound calls where carrier was specified in dial verb was losing auth 2021-12-02 19:20:04 -05:00
Dave Horton bd6c39d9ac Feature/specify trunk on dial (#19)
* add support for outdials specifying carrier to use

* fixes from testing

* add support for outdials specifying carrier to use

* rebase

* fix package lock
2021-11-28 11:07:19 -05:00
Snyk bot 24a974f48d fix: upgrade pino from 6.11.3 to 6.13.3 (#14)
Snyk has created this PR to upgrade pino from 6.11.3 to 6.13.3.

See this package in npm:
https://www.npmjs.com/package/pino

See this project in Snyk:
https://app.snyk.io/org/davehorton/project/282b1881-3e13-4fad-85bd-3b1c662c2134?utm_source=github&utm_medium=referral&page=upgrade-pr
2021-11-21 10:42:28 -05:00
Snyk bot 1dfad575b9 fix: upgrade debug from 4.3.1 to 4.3.2 (#17)
Snyk has created this PR to upgrade debug from 4.3.1 to 4.3.2.

See this package in npm:
https://www.npmjs.com/package/debug

See this project in Snyk:
https://app.snyk.io/org/davehorton/project/282b1881-3e13-4fad-85bd-3b1c662c2134?utm_source=github&utm_medium=referral&page=upgrade-pr
2021-11-21 10:42:17 -05:00
Snyk bot dc30075665 fix: upgrade @jambonz/realtimedb-helpers from 0.4.3 to 0.4.8 (#15)
Snyk has created this PR to upgrade @jambonz/realtimedb-helpers from 0.4.3 to 0.4.8.

See this package in npm:
https://www.npmjs.com/package/@jambonz/realtimedb-helpers

See this project in Snyk:
https://app.snyk.io/org/davehorton/project/282b1881-3e13-4fad-85bd-3b1c662c2134?utm_source=github&utm_medium=referral&page=upgrade-pr
2021-11-20 23:03:53 -05:00
Snyk bot e2bcc16568 fix: upgrade @jambonz/db-helpers from 0.6.12 to 0.6.13 (#16)
Snyk has created this PR to upgrade @jambonz/db-helpers from 0.6.12 to 0.6.13.

See this package in npm:
https://www.npmjs.com/package/@jambonz/db-helpers

See this project in Snyk:
https://app.snyk.io/org/davehorton/project/282b1881-3e13-4fad-85bd-3b1c662c2134?utm_source=github&utm_medium=referral&page=upgrade-pr
2021-11-20 23:03:28 -05:00
Snyk bot a9ee51d671 fix: upgrade @jambonz/stats-collector from 0.1.5 to 0.1.6 (#18)
Snyk has created this PR to upgrade @jambonz/stats-collector from 0.1.5 to 0.1.6.

See this package in npm:
https://www.npmjs.com/package/@jambonz/stats-collector

See this project in Snyk:
https://app.snyk.io/org/davehorton/project/282b1881-3e13-4fad-85bd-3b1c662c2134?utm_source=github&utm_medium=referral&page=upgrade-pr
2021-11-20 23:03:00 -05:00
Dave Horton 9e45de261c add support for app-initiated call transfer via REFER (#13) 2021-11-20 11:40:44 -05:00
Dave Horton 5eb209354a Dockerfile 2021-11-04 12:57:24 -04:00
Dave Horton 4c3c167fe0 version bump 2021-11-03 13:52:04 -04:00
Dave Horton a018b03310 bump version 2021-10-21 13:08:55 -04:00
Dave Horton a61740556d bump version 2021-10-21 13:01:09 -04:00
Dave Horton 07f3a55ec3 Feature/minimal media anchoring (#12)
* feature: release media from freeswitch

* handle mute/unmute

* linting

* update dep

* support for relaying dtmf via SIP INFO to FS

* fix ci test

* handle outdial error
2021-10-21 11:59:54 -04:00
Dave Horton 2ea516be8a bugfix: reject invalid request-uri with 400 Bad Request 2021-10-06 19:12:34 -04:00
Dave Horton d9d2fa9918 update a dep 2021-08-12 09:09:31 -04:00
Dave Horton 16573632fc use single codec in response to freeswitch, otherwise it will get confused 2021-08-12 08:51:07 -04:00
Dave Horton d0f1f16d9a bugfix: write sbc.terminations stat 2021-08-08 10:43:29 -07:00
Dave Horton 98886cfdbf dont check limits on self-hosted deployments 2021-08-04 10:47:46 -04:00
Dave Horton 2ec513b1b0 LICENSE 2021-07-21 12:40:13 -04:00
Dave Horton c3d1e1d605 major merge of features from com version (#11)
major merge of features from the hosted branch that was created temporarily during the initial launch of jambonz.org
2021-06-17 16:51:31 -04:00
Dave Horton 574cdcb216 bugfix: re-invite handling from UAC side now handled properly 2021-04-27 07:36:06 -04:00
Dave Horton 3e01cdeced send 302 to move call to correct SBC, do not send on Session-Expires from response 2021-04-22 14:40:52 -04:00
Dave Horton 05db83a0b3 bugfix prev checkin 2021-04-06 10:21:14 -04:00
Dave Horton 5b801bea84 add support for X-Override-To 2021-04-06 07:42:25 -04:00
Dave Horton e8840c20a0 update deps 2021-01-17 09:55:59 -05:00
Dave Horton 793346fac4 update to latest db-helpers 2021-01-10 12:32:56 -05:00
Dave Horton fd83adadd7 Merge pull request #9 from radicaldrew/master
Updated Dockerfile
2020-12-30 08:59:49 -05:00
Andrew 8a7cc9f3e8 Updated Dockerfile
created multistage build and tested in compose
2020-12-30 15:59:11 +02:00
Dave Horton bc76fc6495 do not pass on X-Account-Sid header to outside world 2020-12-16 13:45:52 -05:00
Dave Horton 0b115a803f ci cleanup 2020-12-14 14:04:31 -05:00
Dave Horton 25a95a5deb ci: fix for docker tty issue 2020-12-14 13:53:32 -05:00
Dave Horton fc5c17848f ci: typo in docker output 2020-12-14 13:47:41 -05:00
Dave Horton 3d2178897c ci: healthcheck for mysqldb 2020-12-14 13:36:29 -05:00
Dave Horton fdf334ec61 ci: debug 2020-12-14 10:27:26 -05:00
Dave Horton da3193f45b ci: more debug 2020-12-14 10:10:52 -05:00
Dave Horton f3564c57bb ci: give mysql more time to come up before starting tests 2020-12-14 10:05:12 -05:00
Dave Horton f580eea102 typo 2020-12-14 09:59:41 -05:00
Dave Horton 5e690b081a pull needed docker image for ci test 2020-12-14 09:57:30 -05:00
Dave Horton a7f5dc4f35 actions 2020-12-14 09:48:44 -05:00
Dave Horton d842dd99b8 more gh actions 2020-12-14 09:43:35 -05:00
Dave Horton 7d0930f6b2 remove mysql service from github actions 2020-12-14 09:41:34 -05:00
Dave Horton 30161b1da0 migrate to github Actions from travis 2020-12-14 09:36:37 -05:00
Dave Horton 2b94a344b1 for ci test, connect to mysql using tcp socket rather than unix socket 2020-12-14 08:41:24 -05:00
Dave Horton 1da347c479 Create npm-publish.yml 2020-12-14 08:34:32 -05:00
Dave Horton 03d6591f8a changes for outbound LCR calls to handle challenges 2020-12-13 16:06:47 -05:00
Dave Horton 180b33f803 update deps 2020-12-13 14:25:13 -05:00
Dave Horton 4e5fbbd650 bugfix #8: maintain Call-ID when sending follow-up INVITE with credentials to endpoint that challenged with 401/407 2020-12-13 13:41:47 -05:00
Dave Horton 1026bcb9cd update deps 2020-11-29 14:55:38 -05:00
Dave Horton 4956e13b9e bugfix: send INVITE to sip device not to contact from register but to source address:port (NAT) 2020-11-29 14:52:13 -05:00
Dave Horton f0cbd6aef5 further fix for proxying outbound call to other SBC 2020-11-29 14:51:16 -05:00
55 changed files with 8323 additions and 2692 deletions
-1
View File
@@ -1 +0,0 @@
test/*
-126
View File
@@ -1,126 +0,0 @@
{
"env": {
"node": true,
"es6": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": false,
"modules": false
},
"ecmaVersion": 2017
},
"plugins": ["promise"],
"rules": {
"promise/always-return": "error",
"promise/no-return-wrap": "error",
"promise/param-names": "error",
"promise/catch-or-return": "error",
"promise/no-native": "off",
"promise/no-nesting": "warn",
"promise/no-promise-in-callback": "warn",
"promise/no-callback-in-promise": "warn",
"promise/no-return-in-finally": "warn",
// Possible Errors
// http://eslint.org/docs/rules/#possible-errors
"comma-dangle": [2, "only-multiline"],
"no-control-regex": 2,
"no-debugger": 2,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty-character-class": 2,
"no-ex-assign": 2,
"no-extra-boolean-cast" : 2,
"no-extra-parens": [2, "functions"],
"no-extra-semi": 2,
"no-func-assign": 2,
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-negated-in-lhs": 2,
"no-obj-calls": 2,
"no-proto": 2,
"no-unexpected-multiline": 2,
"no-unreachable": 2,
"use-isnan": 2,
"valid-typeof": 2,
// Best Practices
// http://eslint.org/docs/rules/#best-practices
"no-fallthrough": 2,
"no-octal": 2,
"no-redeclare": 2,
"no-self-assign": 2,
"no-unused-labels": 2,
// Strict Mode
// http://eslint.org/docs/rules/#strict-mode
"strict": [2, "never"],
// Variables
// http://eslint.org/docs/rules/#variables
"no-delete-var": 2,
"no-undef": 2,
"no-unused-vars": [2, {"args": "none"}],
// Node.js and CommonJS
// http://eslint.org/docs/rules/#nodejs-and-commonjs
"no-mixed-requires": 2,
"no-new-require": 2,
"no-path-concat": 2,
"no-restricted-modules": [2, "sys", "_linklist"],
// Stylistic Issues
// http://eslint.org/docs/rules/#stylistic-issues
"comma-spacing": 2,
"eol-last": 2,
"indent": [2, 2, {"SwitchCase": 1}],
"keyword-spacing": 2,
"max-len": [2, 120, 2],
"new-parens": 2,
"no-mixed-spaces-and-tabs": 2,
"no-multiple-empty-lines": [2, {"max": 2}],
"no-trailing-spaces": [2, {"skipBlankLines": false }],
"quotes": [2, "single", "avoid-escape"],
"semi": 2,
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, "never"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": 2,
// ECMAScript 6
// http://eslint.org/docs/rules/#ecmascript-6
"arrow-parens": [2, "always"],
"arrow-spacing": [2, {"before": true, "after": true}],
"constructor-super": 2,
"no-class-assign": 2,
"no-confusing-arrow": 2,
"no-const-assign": 2,
"no-dupe-class-members": 2,
"no-new-symbol": 2,
"no-this-before-super": 2,
"prefer-const": 2
},
"globals": {
"DTRACE_HTTP_CLIENT_REQUEST" : false,
"LTTNG_HTTP_CLIENT_REQUEST" : false,
"COUNTER_HTTP_CLIENT_REQUEST" : false,
"DTRACE_HTTP_CLIENT_RESPONSE" : false,
"LTTNG_HTTP_CLIENT_RESPONSE" : false,
"COUNTER_HTTP_CLIENT_RESPONSE" : false,
"DTRACE_HTTP_SERVER_REQUEST" : false,
"LTTNG_HTTP_SERVER_REQUEST" : false,
"COUNTER_HTTP_SERVER_REQUEST" : false,
"DTRACE_HTTP_SERVER_RESPONSE" : false,
"LTTNG_HTTP_SERVER_RESPONSE" : false,
"COUNTER_HTTP_SERVER_RESPONSE" : false,
"DTRACE_NET_STREAM_END" : false,
"LTTNG_NET_STREAM_END" : false,
"COUNTER_NET_SERVER_CONNECTION_CLOSE" : false,
"DTRACE_NET_SERVER_CONNECTION" : false,
"LTTNG_NET_SERVER_CONNECTION" : false,
"COUNTER_NET_SERVER_CONNECTION" : false
}
}
+22
View File
@@ -0,0 +1,22 @@
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
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
+54
View File
@@ -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
+1
View File
@@ -36,3 +36,4 @@ node_modules
.DS_Store
examples/*
CLAUDE.md
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run jslint
-10
View File
@@ -1,10 +0,0 @@
sudo: required
language: node_js
node_js:
- "lts/*"
services:
- docker
- mysql
script:
- npm test
+18 -8
View File
@@ -1,13 +1,23 @@
FROM node:lts-alpine
FROM --platform=linux/amd64 node:24-alpine AS base
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
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
COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app
ENV NODE_ENV=$NODE_ENV
CMD [ "npm", "start" ]
CMD [ "node", "app.js" ]
+1 -1
View File
@@ -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
+65 -69
View File
@@ -1,80 +1,76 @@
# sbc-outbound [![Build Status](https://secure.travis-ci.org/jambonz/sbc-outbound.png)](http://travis-ci.org/jambonz/sbc-outbound)
# sbc-outbound ![Build Status](https://github.com/jambonz/sbc-outbound/workflows/CI/badge.svg)
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
Configuration is provided via the [npmjs config](https://www.npmjs.com/package/config) package. Much of the configuration is standard "where is drachtio?, where is rtpengine?, where is redis?" type of thing. This application also needs information about the sip trunk to send PSTN calls out on. In the initial release, only a single outbound SIP trunk is supported.
Configuration is provided via environment variables:
The following elements make up the configuration for the application:
##### outbound sip trunk
```
"trunks": {
"outbound": {
"name": "carrier1",
"host": "sip:172.39.0.20"
}
},
```
The sip uri specifies the sip trunk uri to send PSTN calls to. All outbound PSTN calls from the cpaas application server are expected to be in E.164 format with a leading plus (+) sign. Outbound sip uris that do not begin with a '+' are assumed to be calls to registered devices/users.
| variable | meaning | required?|
|----------|----------|---------|
|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|
|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|no|
|JAMBONES_RTPENGINES| commans-separated list of ip:ng-port for rtpengines (e.g. '172.31.32.10:22222')|yes|
|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|
##### drachtio server location
### 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:
```js
module.exports = {
apps : [
{
name: 'sbc-outbound',
cwd: '/home/admin/apps/sbc-outbound',
script: 'app.js',
instance_var: 'INSTANCE_ID',
out_file: '/home/admin/.pm2/logs/jambonz-sbc-outbound.log',
err_file: '/home/admin/.pm2/logs/jambonz-sbc-outbound.log',
exec_mode: 'fork',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G', env: {
NODE_ENV: 'production',
JAMBONES_LOGLEVEL: 'info',
DRACHTIO_HOST: '127.0.0.1',
DRACHTIO_PORT: 9022,
DRACHTIO_SECRET: 'cymru',
JAMBONES_RTPENGINES: '172.31.32.10:22222',
JAMBONES_MYSQL_HOST: 'aurora-cluster-jambonz.cluster-xxxxxxxxxxxxx.us-west-1.rds.amazonaws.com',
JAMBONES_MYSQL_USER: 'admin',
JAMBONES_MYSQL_PASSWORD: 'JambonzR0ck$',
JAMBONES_MYSQL_DATABASE: 'jambones',
JAMBONES_MYSQL_CONNECTION_LIMIT: 10,
JAMBONES_REDIS_HOST: 'jambonz.zzzzzzz.0001.usw1.cache.amazonaws.com',
JAMBONES_REDIS_PORT: 6379,
JAMBONES_TIME_SERIES_HOST: '172.31.32.11',
JAMBONES_NETWORK_CIDR: '172.31.0.0/16'
}
}]
};
```
"drachtio": {
"port": 3001,
"secret": "cymru"
},
```
the `drachtio` object specifies the port to listen on for tcp connections from drachtio servers as well as the shared secret that is used to authenticate to the server.
> Note: either inbound or [outbound connections](https://drachtio.org/docs#outbound-connections) may be used, depending on the configuration supplied. In production, it is the intent to use outbound connections for easier centralization and clustering of application logic, while inbound connections are used for the automated test suite.
##### rtpengine location
```
"rtpengine": {
"host": "127.0.0.1",
"port": 22222
},
```
the `rtpengine` object specifies the location of the rtpengine, which will typically be running on the same server as drachtio.
##### redis location
```
"redis": {
"port": 6379,
"host": "127.0.0.1"
},
```
the `redis` object specifies the ip/dns and port that redis is listening on.
##### application log level
```
"logging": {
"level": "info"
}
```
##### transcoding options
The transcoding options for rtpengine are found in the configuration file, however these should not need to be modified.
```
"transcoding": {
"rtpCharacteristics" : {
"transport protocol": "RTP/AVP",
"DTLS": "off",
"SDES": "off",
"ICE": "remove",
"rtcp-mux": ["demux"]
},
"srtpCharacteristics": {
"transport-protocol": "UDP/TLS/RTP/SAVPF",
"ICE": "force",
"SDES": "off",
"flags": ["generate mid", "SDES-no"],
"rtcp-mux": ["require"]
}
}
```
#### Running the test suite
To run the included test suite, you will need to have a mysql server installed on your laptop/server. You will need to set the MYSQL_ROOT_PASSWORD env variable to the mysql root password before running the tests. The test suite creates a database named 'jambones_test' in your mysql server to run the tests against, and removes it when done.
To run the included test suite, you will need to have docker installed on your laptop.
```
MYSQL_ROOT_PASSWORD=foobar npm test
npm test
```
+238 -19
View File
@@ -3,43 +3,155 @@ 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_RTPENGINES, 'missing JAMBONES_RTPENGINES 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,
AlertType
} = require('@jambonz/time-series')(logger, {
host: process.env.JAMBONES_TIME_SERIES_HOST,
commitSize: 50,
commitInterval: 'test' === process.env.NODE_ENV ? 7 : 20
});
const StatsCollector = require('@jambonz/stats-collector');
const stats = srf.locals.stats = new StatsCollector(logger);
const {route, setLogger} = require('./lib/middleware');
const stats = new StatsCollector(logger);
const CallSession = require('./lib/call-session');
const {performLcr, lookupAllTeamsFQDNs, lookupAccountBySipRealm} = require('@jambonz/db-helpers')({
const setNameRtp = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-rtp`;
const rtpServers = [];
const {
ping,
lookupOutboundCarrierForAccount,
lookupAllTeamsFQDNs,
lookupAccountBySipRealm,
lookupAccountBySid,
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);
srf.locals.dbHelpers = {performLcr, lookupAllTeamsFQDNs, lookupAccountBySipRealm};
const {getRtpEngine} = require('@jambonz/rtpengine-utils')(process.env.JAMBONES_RTPENGINES.split(','), logger, {
emitter: srf.locals.stats
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: {
ping,
lookupOutboundCarrierForAccount,
lookupAllTeamsFQDNs,
lookupAccountBySipRealm,
lookupAccountBySid,
lookupAccountCapacitiesBySid,
lookupSipGatewaysByCarrier,
lookupCarrierBySid,
queryCallLimits,
lookupCarrierByAccountLcr,
lookupSystemInformation
},
realtimeDbHelpers: {
client: redisClient,
addKey,
deleteKey,
retrieveKey,
createHash,
retrieveHash,
incrKey,
decrKey,
isMemberOfSet
}
};
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
});
srf.locals.getRtpEngine = getRtpEngine;
const activeCallIds = srf.locals.activeCallIds = new Set();
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);
if (process.env.DRACHTIO_HOST) {
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 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') {
@@ -48,20 +160,127 @@ if (process.env.NODE_ENV === 'test') {
});
}
srf.use('invite', [setLogger(logger), route({
host: process.env.JAMBONES_REDIS_HOST,
port: process.env.JAMBONES_REDIS_PORT || 6379
})]);
srf.use('invite', [initLocals, checkLimits, route]);
srf.invite((req, res) => {
const session = new CallSession(logger, req, res);
session.connect();
});
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 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}`));
}
});
};
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 (!equalsIgnoreOrder(newArray, rtpServers)) {
logger.info({newArray}, 'resetting active rtpengines');
setRtpEngines(newArray.map((a) => `${a}:${ngPort}`));
rtpServers.length = 0;
Array.prototype.push.apply(rtpServers, newArray);
}
} catch (err) {
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};
+29
View File
@@ -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);
}
})();
+1
View File
@@ -3,5 +3,6 @@
"DTLS": "off",
"SDES": "off",
"ICE": "remove",
"flags": ["port latching"],
"rtcp-mux": ["demux"]
}
+2 -2
View File
@@ -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",
+137
View File
@@ -0,0 +1,137 @@
const promisePlugin = require('eslint-plugin-promise');
module.exports = [
{
ignores: ['test/*']
},
{
files: ['**/*.js'],
languageOptions: {
ecmaVersion: 2020,
sourceType: 'commonjs',
globals: {
// Node.js globals
console: 'readonly',
process: 'readonly',
Buffer: 'readonly',
__dirname: 'readonly',
__filename: 'readonly',
module: 'readonly',
require: 'readonly',
exports: 'readonly',
setTimeout: 'readonly',
clearTimeout: 'readonly',
setInterval: 'readonly',
clearInterval: 'readonly',
setImmediate: 'readonly',
clearImmediate: 'readonly',
// DTrace/LTTNG globals
DTRACE_HTTP_CLIENT_REQUEST: false,
LTTNG_HTTP_CLIENT_REQUEST: false,
COUNTER_HTTP_CLIENT_REQUEST: false,
DTRACE_HTTP_CLIENT_RESPONSE: false,
LTTNG_HTTP_CLIENT_RESPONSE: false,
COUNTER_HTTP_CLIENT_RESPONSE: false,
DTRACE_HTTP_SERVER_REQUEST: false,
LTTNG_HTTP_SERVER_REQUEST: false,
COUNTER_HTTP_SERVER_REQUEST: false,
DTRACE_HTTP_SERVER_RESPONSE: false,
LTTNG_HTTP_SERVER_RESPONSE: false,
COUNTER_HTTP_SERVER_RESPONSE: false,
DTRACE_NET_STREAM_END: false,
LTTNG_NET_STREAM_END: false,
COUNTER_NET_SERVER_CONNECTION_CLOSE: false,
DTRACE_NET_SERVER_CONNECTION: false,
LTTNG_NET_SERVER_CONNECTION: false,
COUNTER_NET_SERVER_CONNECTION: false
}
},
plugins: {
promise: promisePlugin
},
rules: {
// Promise plugin rules
'promise/always-return': 'error',
'promise/no-return-wrap': 'error',
'promise/param-names': 'error',
'promise/catch-or-return': 'error',
'promise/no-native': 'off',
'promise/no-nesting': 'warn',
'promise/no-promise-in-callback': 'warn',
'promise/no-callback-in-promise': 'warn',
'promise/no-return-in-finally': 'warn',
// Possible Errors
'comma-dangle': [2, 'only-multiline'],
'no-control-regex': 2,
'no-debugger': 2,
'no-dupe-args': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-ex-assign': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-extra-semi': 2,
'no-func-assign': 2,
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-obj-calls': 2,
'no-proto': 2,
'no-unexpected-multiline': 2,
'no-unreachable': 2,
'use-isnan': 2,
'valid-typeof': 2,
// Best Practices
'no-fallthrough': 2,
'no-octal': 2,
'no-redeclare': 2,
'no-self-assign': 2,
'no-unused-labels': 2,
// Strict Mode
'strict': [2, 'never'],
// Variables
'no-delete-var': 2,
'no-undef': 2,
'no-unused-vars': [2, {args: 'none'}],
// Node.js and CommonJS
'no-mixed-requires': 2,
'no-new-require': 2,
'no-path-concat': 2,
// Stylistic Issues
'comma-spacing': 2,
'eol-last': 2,
'indent': [2, 2, {SwitchCase: 1}],
'keyword-spacing': 2,
'max-len': [2, 120, 2],
'new-parens': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multiple-empty-lines': [2, {max: 2}],
'no-trailing-spaces': [2, {skipBlankLines: false}],
'quotes': [2, 'single', 'avoid-escape'],
'semi': 2,
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': 2,
// ECMAScript 6
'arrow-parens': [2, 'always'],
'arrow-spacing': [2, {before: true, after: true}],
'constructor-super': 2,
'no-class-assign': 2,
'no-confusing-arrow': 2,
'no-const-assign': 2,
'no-dupe-class-members': 2,
'no-new-symbol': 2,
'no-this-before-super': 2,
'prefer-const': 2
}
}
];
+1075 -124
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
{
"MediaPath": {
"NoMedia": "no-media",
"PartialMedia": "partial-media",
"FullMedia": "full-media"
}
}
+242 -61
View File
@@ -1,109 +1,290 @@
const debug = require('debug')('jambonz:sbc-outbound');
const parseUri = require('drachtio-srf').parseUri;
const srf = require('drachtio-srf');
const Registrar = require('jambonz-mw-registrar');
const {selectHostPort} = require('./utils');
const Registrar = require('@jambonz/mw-registrar');
const { nudgeCallCounts} = require('./utils');
const FS_UUID_SET_NAME = 'fsUUIDs';
function setLogger(logger) {
return (req, res, next) => {
debug('setting logger');
module.exports = (srf, logger, redisClient) => {
const {incrKey, decrKey, isMemberOfSet} = srf.locals.realtimeDbHelpers;
const {stats} = srf.locals;
const registrar = new Registrar(logger, redisClient);
const {
lookupAccountCapacitiesBySid,
lookupAccountBySid,
queryCallLimits
} = srf.locals.dbHelpers;
const initLocals = async(req, res, next) => {
req.locals = req.locals || {};
req.locals.logger = logger.child({callId: req.get('Call-ID')});
req.srf.locals.stats.increment('sbc.invites', ['direction:outbound']);
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.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');
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 {
res.send(500);
return req.srf.endSession(req);
}
}
stats.increment('sbc.invites', ['direction:outbound']);
req.on('cancel', () => {
req.locals.logger.info({callId}, 'caller hungup before connecting');
req.canceled = true;
const tags = ['canceled:yes', 'sipStatus:487'];
if (req.locals.originator) tags.push(`originator:${req.locals.originator}`);
stats.increment('sbc.origination', tags);
});
try {
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}`);
res.send(500);
return req.srf.endSession(req);
}
next();
};
}
function isLocalUri(host, req) {
debug({hostport: req.server.hostport}, `is ${host} local?`);
return req.server.hostport.includes(host);
}
const checkLimits = async(req, res, next) => {
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
}
function route(opts) {
const registrar = new Registrar(opts);
return async(req, res, next) => {
const {writeCallCount, writeCallCountSP, writeCallCountApp, writeAlerts, AlertType} = req.srf.locals;
try {
/* decrement count if INVITE is later rejected */
res.once('end', async({status}) => {
if (status > 200) {
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);
}
else {
const tags = ['accepted:yes', 'sipStatus:200'];
stats.increment('sbc.originations', tags);
}
});
/* 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) :
0;
if (calls <= minLimit) return next();
const capacities = await lookupAccountCapacitiesBySid(account_sid);
const limit = capacities.find((c) => c.category == 'voice_call_session');
if (limit) {
const limit_sessions = limit.quantity;
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) {
logger.error({err}, 'error checking limits error for inbound call');
res.send(500);
}
};
const route = async(req, res, next) => {
const logger = req.locals.logger;
const {lookupAccountBySipRealm} = req.srf.locals.dbHelpers;
logger.info(`received outbound INVITE to ${req.uri} from server at ${req.server.hostport}`);
const uri = parseUri(req.uri);
if (!uri.user || !uri.host) {
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'
}
});
const tags = ['accepted:no', 'sipStatus:404'];
req.srf.locals.stats.increment('sbc.originations', tags);
return;
return req.srf.endSession(req);
}
const aor = `${uri.user}@${uri.host}`;
req.locals = req.locals || {};
debug(`received outbound INVITE to ${req.calledNumber} from server at ${req.server.hostport}`);
let reg;
const dotDecimalHost = /^[0-9\.]+$/.test(uri.host);
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
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
// other sbc => proxy the call there
logger.info({details: reg}, `sending call to registered user ${aor}`);
if (req.server.hostport !== reg.sbcAddress) {
//proxy
const proxyAddress = selectHostPort(reg.sbcAddress, 'udp');
logger.info(`proxying call to SBC at ${proxyAddress}`);
//req.proxy(`sip:${proxyAddress[1]}`);
req.srf.proxyRequest(req, `sip:${proxyAddress[1]}`);
return;
/* redirect to the correct SBC where this user is connected */
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`);
const tags = ['accepted:no', 'sipStatus:404'];
req.srf.locals.stats.increment('sbc.originations', tags);
return res.send(404);
}
debug('sending call to LCR');
req.locals.target = 'lcr';
next();
};
}
module.exports = {
setLogger,
route
return {
initLocals,
checkLimits,
route
};
};
+323 -18
View File
@@ -1,26 +1,73 @@
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 common = {'call-id': req.get('Call-ID'), 'from-tag': from.params.tag};
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 = JSON.parse(JSON.stringify(dstIsUsingSrtp ? srtpOpts : rtpCopy));
const srcOpts = JSON.parse(JSON.stringify(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'],
'record call': process.env.JAMBONES_RECORD_ALL_CALLS ? 'yes' : 'no'
};
const codec = {
accept: ['PCMU', 'PCMA', 'telephone-event'],
...(process.env.JAMBONES_CODEC_OFFER_WITH_ORDER &&
{
offer: process.env.JAMBONES_CODEC_OFFER_WITH_ORDER.split(','),
strip: 'all'
}),
};
return {
common,
offer: Object.assign(
{'sdp': req.body, 'replace': ['origin', 'session-connection']},
{'direction': [ 'private', 'public']},
common,
dstIsUsingSrtp ? srtpOpts : rtpCharacteristics),
answer: Object.assign(
{'replace': ['origin', 'session-connection']},
common,
srcIsUsingSrtp ? srtpOpts : rtpCharacteristics)
uas: {
tag: from.params.tag,
mediaOpts: srcOpts
},
uac: {
tag: null,
mediaOpts: {
...dstOpts,
codec,
}
}
};
}
function selectHostPort(hostport, protocol) {
const selectHostPort = (hostport, protocol) => {
debug(`selectHostPort: ${hostport}, ${protocol}`);
const sel = hostport
.split(',')
@@ -32,9 +79,9 @@ function selectHostPort(hostport, protocol) {
return hp[0] === protocol && hp[1] !== '127.0.0.1';
});
return sel[0];
}
};
function pingMs(logger, srf, gateway, fqdns) {
const pingMs = (logger, srf, gateway, fqdns) => {
const uri = `sip:${gateway}`;
const proxy = `sip:${gateway}:5061;transport=tls`;
fqdns.forEach((fqdn) => {
@@ -48,8 +95,9 @@ function pingMs(logger, srf, gateway, fqdns) {
}
}).catch((err) => logger.error(err, `Error pinging MS Teams at ${gateway}`));
});
}
function pingMsTeamsGateways(logger, srf) {
};
const pingMsTeamsGateways = (logger, srf) => {
const {lookupAllTeamsFQDNs} = srf.locals.dbHelpers;
lookupAllTeamsFQDNs()
.then((fqdns) => {
@@ -64,10 +112,267 @@ function pingMsTeamsGateways(logger, srf) {
.catch((err) => {
logger.error(err, 'Error looking up all ms teams fqdns');
});
};
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
pingMsTeamsGateways,
makeAccountCallCountKey,
makeSPCallCountKey,
equalsIgnoreOrder,
systemHealth,
createHealthCheckApp,
nudgeCallCounts,
isPrivateVoipNetwork,
isBlackListedSipGateway,
makeFullMediaReleaseKey,
makePartnerFullMediaReleaseKey,
isValidDomainOrIP,
removeVideoSdp,
};
+4397 -2062
View File
File diff suppressed because it is too large Load Diff
+24 -17
View File
@@ -1,9 +1,9 @@
{
"name": "sbc-outbound",
"version": "0.4.3",
"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 JAMBONES_MYSQL_HOST=localhost JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 node test/ | ./node_modules/.bin/tap-spec",
"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.5.1",
"@jambonz/rtpengine-utils": "^0.1.7",
"@jambonz/stats-collector": "0.1.1",
"debug": "^4.1.1",
"drachtio-fn-b2b-sugar": "^0.0.12",
"drachtio-srf": "^4.4.39",
"jambonz-mw-registrar": "^0.1.3",
"pino": "^6.5.1",
"rtpengine-client": "^0.1.1"
"@babel/helpers": "^7.26.10",
"@jambonz/db-helpers": "^0.9.18",
"@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.4.3",
"drachtio-fn-b2b-sugar": "^0.2.1",
"drachtio-srf": "^5.0.12",
"express": "^4.21.2",
"pino": "^10.1.0",
"sdp-transform": "^2.15.0"
},
"devDependencies": {
"blue-tape": "^1.0.0",
"eslint": "^7.12.0",
"eslint-plugin-promise": "^4.2.1",
"bent": "^7.3.12",
"eslint": "^9.17.0",
"eslint-plugin-promise": "^7.2.1",
"nyc": "^15.1.0",
"tap-spec": "^5.0.0"
"tape": "^5.7.5"
}
}
+5 -6
View File
@@ -1,10 +1,9 @@
const test = require('blue-tape');
//const test = require('tape').test ;
const test = require('tape');
const exec = require('child_process').exec ;
const pwd = process.env.TRAVIS ? '' : '-p$MYSQL_ROOT_PASSWORD';
const pwd = '-p$MYSQL_ROOT_PASSWORD';
test('creating jambones_test database', (t) => {
exec(`mysql -h localhost -u root ${pwd} < ${__dirname}/db/create_test_db.sql`, (err, stdout, stderr) => {
exec(`mysql -h 127.0.0.1 -u root --protocol=tcp < ${__dirname}/db/create_test_db.sql`, (err, stdout, stderr) => {
if (err) return t.end(err);
t.pass('database successfully created');
t.end();
@@ -12,7 +11,7 @@ test('creating jambones_test database', (t) => {
});
test('creating schema', (t) => {
exec(`mysql -h localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/jambones-sql.sql`, (err, stdout, stderr) => {
exec(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/jambones-sql.sql`, (err, stdout, stderr) => {
if (err) return t.end(err);
t.pass('schema successfully created');
t.end();
@@ -20,7 +19,7 @@ test('creating schema', (t) => {
});
test('populating test case data', (t) => {
exec(`mysql -h localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/populate-test-data.sql`, (err, stdout, stderr) => {
exec(`mysql -h 127.0.0.1 -u root --protocol=tcp -D jambones_test < ${__dirname}/db/populate-test-data.sql`, (err, stdout, stderr) => {
if (err) return t.end(err);
t.pass('test data set created');
t.end();
+2 -2
View File
@@ -1,3 +1,3 @@
create database jambones_test;
create user jambones_test@localhost IDENTIFIED WITH mysql_native_password by 'jambones_test';
grant all on jambones_test.* to jambones_test@localhost;
create user jambones_test@'%' IDENTIFIED WITH mysql_native_password by 'jambones_test';
grant all on jambones_test.* to jambones_test@'%';
+546 -63
View File
@@ -2,20 +2,68 @@
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;
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 api_keys;
DROP TABLE IF EXISTS password_settings;
DROP TABLE IF EXISTS ms_teams_tenants;
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;
DROP TABLE IF EXISTS phone_numbers;
DROP TABLE IF EXISTS sip_gateways;
@@ -30,6 +78,50 @@ DROP TABLE IF EXISTS service_providers;
DROP TABLE IF EXISTS webhooks;
CREATE TABLE account_static_ips
(
account_static_ip_sid CHAR(36) NOT NULL UNIQUE ,
account_sid CHAR(36) NOT NULL,
public_ipv4 VARCHAR(16) NOT NULL UNIQUE ,
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 ,
account_sid CHAR(36) NOT NULL,
pending BOOLEAN NOT NULL DEFAULT false,
effective_start_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
effective_end_date DATETIME,
change_reason VARCHAR(255),
stripe_subscription_id VARCHAR(56),
stripe_payment_method_id VARCHAR(56),
stripe_statement_descriptor VARCHAR(255),
last4 VARCHAR(512),
exp_month INTEGER,
exp_year INTEGER,
card_type VARCHAR(16),
pending_reason VARBINARY(52),
PRIMARY KEY (account_subscription_sid)
);
CREATE TABLE beta_invite_codes
(
invite_code CHAR(6) NOT NULL UNIQUE ,
in_use BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (invite_code)
);
CREATE TABLE call_routes
(
call_route_sid CHAR(36) NOT NULL UNIQUE ,
@@ -38,16 +130,139 @@ account_sid CHAR(36) NOT NULL,
regex VARCHAR(255) NOT NULL,
application_sid CHAR(36) NOT NULL,
PRIMARY KEY (call_route_sid)
) ENGINE=InnoDB COMMENT='a regex-based pattern match for call routing';
) 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 ,
account_sid CHAR(36) NOT NULL,
record_type VARCHAR(6) NOT NULL,
record_id INTEGER NOT NULL,
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
(
predefined_carrier_sid CHAR(36) NOT NULL UNIQUE ,
name VARCHAR(64) NOT NULL,
requires_static_ip BOOLEAN NOT NULL DEFAULT false,
e164_leading_plus BOOLEAN NOT NULL DEFAULT false COMMENT 'if true, a leading plus should be prepended to outbound phone numbers',
requires_register BOOLEAN NOT NULL DEFAULT false,
register_username VARCHAR(64),
register_sip_realm VARCHAR(64),
register_password VARCHAR(64),
tech_prefix VARCHAR(16) COMMENT 'tech prefix to prepend to outbound calls to this carrier',
inbound_auth_username VARCHAR(64),
inbound_auth_password VARCHAR(64),
diversion VARCHAR(32),
PRIMARY KEY (predefined_carrier_sid)
);
CREATE TABLE predefined_sip_gateways
(
predefined_sip_gateway_sid CHAR(36) NOT NULL UNIQUE ,
ipv4 VARCHAR(128) NOT NULL COMMENT 'ip address or DNS name of the gateway. For gateways providing inbound calling service, ip address is required.',
port INTEGER NOT NULL DEFAULT 5060 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',
netmask INTEGER NOT NULL DEFAULT 32,
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 ,
name VARCHAR(32) NOT NULL,
category ENUM('api_rate','voice_call_session', 'device') NOT NULL,
PRIMARY KEY (product_sid)
);
CREATE TABLE account_products
(
account_product_sid CHAR(36) NOT NULL UNIQUE ,
account_subscription_sid CHAR(36) NOT NULL,
product_sid CHAR(36) NOT NULL,
quantity INTEGER NOT NULL,
PRIMARY KEY (account_product_sid)
);
CREATE TABLE account_offers
(
account_offer_sid CHAR(36) NOT NULL UNIQUE ,
account_sid CHAR(36) NOT NULL,
product_sid CHAR(36) NOT NULL,
stripe_product_id VARCHAR(56) NOT NULL,
PRIMARY KEY (account_offer_sid)
);
CREATE TABLE schema_version
(
version VARCHAR(16)
);
CREATE TABLE api_keys
(
@@ -55,11 +270,23 @@ api_key_sid CHAR(36) NOT NULL UNIQUE ,
token CHAR(36) NOT NULL UNIQUE ,
account_sid CHAR(36),
service_provider_sid CHAR(36),
expires_at TIMESTAMP NULL,
last_used TIMESTAMP NULL,
expires_at TIMESTAMP NULL DEFAULT NULL,
last_used TIMESTAMP NULL DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (api_key_sid)
) ENGINE=InnoDB COMMENT='An authorization token that is used to access the REST api';
) COMMENT='An authorization token that is used to access the REST api';
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)
);
CREATE TABLE ms_teams_tenants
(
@@ -71,65 +298,172 @@ tenant_fqdn VARCHAR(255) NOT NULL UNIQUE ,
PRIMARY KEY (ms_teams_tenant_sid)
) COMMENT='A Microsoft Teams customer tenant';
CREATE TABLE sbc_addresses
CREATE TABLE service_provider_limits
(
sbc_address_sid CHAR(36) NOT NULL UNIQUE ,
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,
name VARCHAR(255),
signed_up_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (email)
);
CREATE TABLE smpp_addresses
(
smpp_address_sid CHAR(36) NOT NULL UNIQUE ,
ipv4 VARCHAR(255) NOT NULL,
port INTEGER NOT NULL DEFAULT 5060,
use_tls BOOLEAN NOT NULL DEFAULT 0,
is_primary BOOLEAN NOT NULL DEFAULT 1,
service_provider_sid CHAR(36),
PRIMARY KEY (sbc_address_sid)
PRIMARY KEY (smpp_address_sid)
);
CREATE TABLE speech_credentials
(
speech_credential_sid CHAR(36) NOT NULL UNIQUE ,
service_provider_sid CHAR(36),
account_sid CHAR(36),
vendor VARCHAR(32) NOT NULL,
credential VARCHAR(8192) NOT NULL,
use_for_tts BOOLEAN DEFAULT true,
use_for_stt BOOLEAN DEFAULT true,
last_used DATETIME,
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 ,
name CHAR(36) NOT NULL UNIQUE ,
hashed_password VARCHAR(1024) NOT NULL,
salt CHAR(16) NOT NULL,
force_change BOOLEAN NOT NULL DEFAULT TRUE,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
pending_email VARCHAR(255),
phone VARCHAR(20) UNIQUE ,
hashed_password VARCHAR(1024),
account_sid CHAR(36),
service_provider_sid CHAR(36),
force_change BOOLEAN NOT NULL DEFAULT FALSE,
provider VARCHAR(255) NOT NULL,
provider_userid VARCHAR(255),
scope VARCHAR(16) NOT NULL DEFAULT 'read-write',
phone_activation_code VARCHAR(16),
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)
);
CREATE TABLE voip_carriers
(
voip_carrier_sid CHAR(36) NOT NULL UNIQUE ,
name VARCHAR(64) NOT NULL UNIQUE ,
name VARCHAR(64) NOT NULL,
description VARCHAR(255),
account_sid CHAR(36) COMMENT 'if provided, indicates this entity represents a customer PBX that is associated with a specific account',
account_sid CHAR(36) COMMENT 'if provided, indicates this entity represents a sip trunk that is associated with a specific account',
service_provider_sid CHAR(36),
application_sid CHAR(36) COMMENT 'If provided, all incoming calls from this source will be routed to the associated application',
e164_leading_plus BOOLEAN NOT NULL DEFAULT false,
e164_leading_plus BOOLEAN NOT NULL DEFAULT false COMMENT 'if true, a leading plus should be prepended to outbound phone numbers',
requires_register BOOLEAN NOT NULL DEFAULT false,
register_username VARCHAR(64),
register_sip_realm VARCHAR(64),
register_password VARCHAR(64),
tech_prefix VARCHAR(16) COMMENT 'tech prefix to prepend to outbound calls to this carrier',
inbound_auth_username VARCHAR(64),
inbound_auth_password VARCHAR(64),
diversion VARCHAR(32),
is_active BOOLEAN NOT NULL DEFAULT true,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
smpp_system_id VARCHAR(255),
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)
) ENGINE=InnoDB COMMENT='A Carrier or customer PBX that can send or receive calls';
) 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 ,
ipv4 VARCHAR(128) NOT NULL,
port INTEGER NOT NULL DEFAULT 2775,
netmask INTEGER NOT NULL DEFAULT 32,
is_primary BOOLEAN NOT NULL DEFAULT 1,
inbound BOOLEAN NOT NULL DEFAULT 0 COMMENT 'if true, whitelist this IP to allow inbound calls from the gateway',
outbound BOOLEAN NOT NULL DEFAULT 1 COMMENT 'if true, include in least-cost routing when placing calls to the PSTN',
use_tls BOOLEAN DEFAULT 0,
voip_carrier_sid CHAR(36) NOT NULL,
PRIMARY KEY (smpp_gateway_sid)
);
CREATE TABLE phone_numbers
(
phone_number_sid CHAR(36) UNIQUE ,
number VARCHAR(32) NOT NULL UNIQUE ,
voip_carrier_sid CHAR(36) NOT NULL,
number VARCHAR(132) NOT NULL,
voip_carrier_sid CHAR(36),
account_sid CHAR(36),
application_sid CHAR(36),
service_provider_sid CHAR(36) COMMENT 'if not null, this number is a test number for the associated service provider',
PRIMARY KEY (phone_number_sid)
) ENGINE=InnoDB COMMENT='A phone number that has been assigned to an account';
CREATE TABLE webhooks
(
webhook_sid CHAR(36) NOT NULL UNIQUE ,
url VARCHAR(1024) NOT NULL,
method ENUM("GET","POST") NOT NULL DEFAULT 'POST',
username VARCHAR(255),
password VARCHAR(255),
PRIMARY KEY (webhook_sid)
) COMMENT='An HTTP callback';
) COMMENT='A phone number that has been assigned to an account';
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.',
port INTEGER NOT NULL DEFAULT 5060 COMMENT 'sip signaling port',
netmask INTEGER NOT NULL DEFAULT 32,
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';
@@ -143,20 +477,45 @@ priority INTEGER NOT NULL DEFAULT 0 COMMENT 'lower priority carriers are attempt
PRIMARY KEY (lcr_carrier_set_entry_sid)
) COMMENT='An entry in the LCR routing list';
CREATE TABLE webhooks
(
webhook_sid CHAR(36) NOT NULL UNIQUE ,
url VARCHAR(1024) NOT NULL,
method ENUM("GET","POST") NOT NULL DEFAULT 'POST',
username VARCHAR(255),
password VARCHAR(255),
PRIMARY KEY (webhook_sid)
) COMMENT='An HTTP callback';
CREATE TABLE applications
(
application_sid CHAR(36) NOT NULL UNIQUE ,
name VARCHAR(64) NOT NULL,
account_sid CHAR(36) NOT NULL COMMENT 'account that this application belongs to',
call_hook_sid CHAR(36) COMMENT 'webhook to call for inbound calls to phone numbers owned by this account',
service_provider_sid CHAR(36) COMMENT 'if non-null, this application is a test application that can be used by any account under the associated service provider',
account_sid CHAR(36) COMMENT 'account that this application belongs to (if null, this is a service provider test application)',
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)
) ENGINE=InnoDB COMMENT='A defined set of behaviors to be applied to phone calls ';
) COMMENT='A defined set of behaviors to be applied to phone calls ';
CREATE TABLE service_providers
(
@@ -167,7 +526,7 @@ root_domain VARCHAR(128) UNIQUE ,
registration_hook_sid CHAR(36),
ms_teams_fqdn VARCHAR(255),
PRIMARY KEY (service_provider_sid)
) ENGINE=InnoDB COMMENT='A partition of the platform used by one service provider';
) COMMENT='A partition of the platform used by one service provider';
CREATE TABLE accounts
(
@@ -176,72 +535,192 @@ name VARCHAR(64) NOT NULL,
sip_realm VARCHAR(132) UNIQUE COMMENT 'sip domain that will be used for devices registering under this account',
service_provider_sid CHAR(36) NOT NULL COMMENT 'service provider that owns the customer relationship with this account',
registration_hook_sid CHAR(36) COMMENT 'webhook to call when devices underr this account attempt to register',
queue_event_hook_sid CHAR(36),
device_calling_application_sid CHAR(36) COMMENT 'application to use for outbound calling from an account',
is_active BOOLEAN NOT NULL DEFAULT true,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
plan_type ENUM('trial','free','paid') NOT NULL DEFAULT 'trial',
stripe_customer_id VARCHAR(56),
webhook_secret VARCHAR(36) NOT NULL,
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)
) ENGINE=InnoDB COMMENT='An enterprise that uses the platform for comm services';
) COMMENT='An enterprise that uses the platform for comm services';
CREATE INDEX account_static_ip_sid_idx ON account_static_ips (account_static_ip_sid);
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_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 (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 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);
ALTER TABLE account_products ADD FOREIGN KEY account_subscription_sid_idxfk (account_subscription_sid) REFERENCES account_subscriptions (account_subscription_sid);
ALTER TABLE account_products ADD FOREIGN KEY product_sid_idxfk (product_sid) REFERENCES products (product_sid);
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_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_1 (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);
CREATE INDEX ms_teams_tenant_sid_idx ON ms_teams_tenants (ms_teams_tenant_sid);
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY service_provider_sid_idxfk_1 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY account_sid_idxfk_2 (account_sid) REFERENCES accounts (account_sid);
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY application_sid_idxfk_1 (application_sid) REFERENCES applications (application_sid);
CREATE INDEX tenant_fqdn_idx ON ms_teams_tenants (tenant_fqdn);
CREATE INDEX sbc_addresses_idx_host_port ON sbc_addresses (ipv4,port);
CREATE INDEX sbc_address_sid_idx ON sbc_addresses (sbc_address_sid);
CREATE INDEX service_provider_sid_idx ON sbc_addresses (service_provider_sid);
ALTER TABLE sbc_addresses ADD FOREIGN KEY service_provider_sid_idxfk_2 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
ALTER TABLE sbc_addresses ADD FOREIGN KEY service_provider_sid_idxfk_1 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
CREATE INDEX ms_teams_tenant_sid_idx ON ms_teams_tenants (ms_teams_tenant_sid);
ALTER TABLE ms_teams_tenants ADD FOREIGN KEY service_provider_sid_idxfk_2 (service_provider_sid) REFERENCES service_providers (service_provider_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_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_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_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 name_idx ON users (name);
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_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_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 name_idx ON voip_carriers (name);
ALTER TABLE voip_carriers ADD FOREIGN KEY account_sid_idxfk_3 (account_sid) REFERENCES accounts (account_sid);
CREATE INDEX account_sid_idx ON voip_carriers (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_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 phone_number_sid_idx ON phone_numbers (phone_number_sid);
CREATE INDEX voip_carrier_sid_idx ON phone_numbers (voip_carrier_sid);
ALTER TABLE phone_numbers ADD FOREIGN KEY voip_carrier_sid_idxfk (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_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 phone_numbers ADD FOREIGN KEY account_sid_idxfk_4 (account_sid) REFERENCES accounts (account_sid);
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_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 webhook_sid_idx ON webhooks (webhook_sid);
CREATE UNIQUE INDEX sip_gateway_idx_hostport ON sip_gateways (ipv4,port);
CREATE INDEX service_provider_sid_idx ON phone_numbers (service_provider_sid);
ALTER TABLE phone_numbers ADD FOREIGN KEY service_provider_sid_idxfk_8 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
ALTER TABLE sip_gateways ADD FOREIGN KEY voip_carrier_sid_idxfk_1 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
CREATE INDEX sip_gateway_idx_hostport ON sip_gateways (ipv4,port);
CREATE INDEX voip_carrier_sid_idx ON sip_gateways (voip_carrier_sid);
ALTER TABLE sip_gateways ADD FOREIGN KEY voip_carrier_sid_idxfk_2 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
ALTER TABLE lcr_carrier_set_entry ADD FOREIGN KEY lcr_route_sid_idxfk (lcr_route_sid) REFERENCES lcr_routes (lcr_route_sid);
ALTER TABLE lcr_carrier_set_entry ADD FOREIGN KEY voip_carrier_sid_idxfk_2 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
ALTER TABLE lcr_carrier_set_entry ADD FOREIGN KEY voip_carrier_sid_idxfk_3 (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid);
CREATE INDEX webhook_sid_idx ON webhooks (webhook_sid);
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_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_5 (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);
ALTER TABLE applications ADD FOREIGN KEY call_status_hook_sid_idxfk (call_status_hook_sid) REFERENCES webhooks (webhook_sid);
ALTER TABLE applications ADD FOREIGN KEY messaging_hook_sid_idxfk (messaging_hook_sid) REFERENCES webhooks (webhook_sid);
CREATE INDEX service_provider_sid_idx ON service_providers (service_provider_sid);
CREATE INDEX name_idx ON service_providers (name);
CREATE INDEX root_domain_idx ON service_providers (root_domain);
@@ -250,10 +729,14 @@ 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_3 (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);
ALTER TABLE accounts ADD FOREIGN KEY queue_event_hook_sid_idxfk (queue_event_hook_sid) REFERENCES webhooks (webhook_sid);
ALTER TABLE accounts ADD FOREIGN KEY device_calling_application_sid_idxfk (device_calling_application_sid) REFERENCES applications (application_sid);
ALTER TABLE accounts ADD FOREIGN KEY siprec_hook_sid_idxfk (siprec_hook_sid) REFERENCES applications (application_sid);
SET FOREIGN_KEY_CHECKS=1;
+15 -3
View File
@@ -1,11 +1,23 @@
-- create our products
insert into products (product_sid, name, category)
values
('c4403cdb-8e75-4b27-9726-7d8315e3216d', 'concurrent call session', 'voice_call_session'),
('2c815913-5c26-4004-b748-183b459329df', 'registered device', 'device'),
('35a9fb10-233d-4eb9-aada-78de5814d680', 'api call', 'api_rate');
insert into webhooks(webhook_sid, url, username, password) values('90dda62e-0ea2-47d1-8164-5bd49003476c', 'http://127.0.0.1:4000/auth', 'foo', 'bar');
insert into service_providers (service_provider_sid, name, root_domain, registration_hook_sid)
values ('3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'SP A', 'jambonz.org', '90dda62e-0ea2-47d1-8164-5bd49003476c');
insert into accounts(account_sid, service_provider_sid, name, sip_realm, registration_hook_sid)
values ('ed649e33-e771-403a-8c99-1780eabbc803', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'test account', 'sip.example.com', '90dda62e-0ea2-47d1-8164-5bd49003476c');
insert into voip_carriers (voip_carrier_sid, name) values ('287c1452-620d-4195-9f19-c9814ef90d78', 'westco');
insert into accounts(account_sid, service_provider_sid, name, sip_realm, registration_hook_sid, webhook_secret)
values ('ed649e33-e771-403a-8c99-1780eabbc803', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'test account', 'sip.example.com', '90dda62e-0ea2-47d1-8164-5bd49003476c', 'foobar');
insert into account_subscriptions(account_subscription_sid, account_sid, pending)
values ('f4e1848d-3ff8-40eb-b9c1-30e1ef053f94','ed649e33-e771-403a-8c99-1780eabbc803',0);
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);
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);
insert into sip_gateways (sip_gateway_sid, voip_carrier_sid, ipv4, port, inbound, outbound)
+31 -8
View File
@@ -1,26 +1,49 @@
-- create our products
insert into products (product_sid, name, category)
values
('c4403cdb-8e75-4b27-9726-7d8315e3216d', 'concurrent call session', 'voice_call_session'),
('2c815913-5c26-4004-b748-183b459329df', 'registered device', 'device'),
('35a9fb10-233d-4eb9-aada-78de5814d680', 'api call', 'api_rate');
insert into webhooks(webhook_sid, url, username, password) values('90dda62e-0ea2-47d1-8164-5bd49003476c', 'http://127.0.0.1:4000/auth', 'foo', 'bar');
insert into service_providers (service_provider_sid, name, root_domain, registration_hook_sid)
values ('3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'SP A', 'jambonz.org', '90dda62e-0ea2-47d1-8164-5bd49003476c');
insert into accounts(account_sid, service_provider_sid, name, sip_realm, registration_hook_sid)
values ('ed649e33-e771-403a-8c99-1780eabbc803', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'test account', 'sip.example.com', '90dda62e-0ea2-47d1-8164-5bd49003476c');
insert into accounts(account_sid, service_provider_sid, name, sip_realm, registration_hook_sid, webhook_secret)
values ('ed649e33-e771-403a-8c99-1780eabbc803', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'test account', 'sip.example.com', '90dda62e-0ea2-47d1-8164-5bd49003476c', 'foobar');
insert into account_subscriptions(account_subscription_sid, account_sid, pending)
values ('f4e1848d-3ff8-40eb-b9c1-30e1ef053f94','ed649e33-e771-403a-8c99-1780eabbc803',0);
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) values ('287c1452-620d-4195-9f19-c9814ef90d78', 'westco');
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) values ('1d8ef351-062a-4487-94f8-7698d5a40d24', 'eastco');
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);
-- 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);
+14 -23
View File
@@ -1,32 +1,23 @@
-- create our products
insert into products (product_sid, name, category)
values
('c4403cdb-8e75-4b27-9726-7d8315e3216d', 'concurrent call session', 'voice_call_session'),
('2c815913-5c26-4004-b748-183b459329df', 'registered device', 'device'),
('35a9fb10-233d-4eb9-aada-78de5814d680', 'api call', 'api_rate');
insert into webhooks(webhook_sid, url, username, password) values('90dda62e-0ea2-47d1-8164-5bd49003476c', 'http://127.0.0.1:4000/auth', 'foo', 'bar');
insert into service_providers (service_provider_sid, name, root_domain, registration_hook_sid)
values ('3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'SP A', 'jambonz.org', '90dda62e-0ea2-47d1-8164-5bd49003476c');
insert into accounts(account_sid, service_provider_sid, name, sip_realm, registration_hook_sid)
values ('ed649e33-e771-403a-8c99-1780eabbc803', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'test account', 'sip.example.com', '90dda62e-0ea2-47d1-8164-5bd49003476c');
-- "good" carrier - "westco" at 172.39.0.20
insert into voip_carriers (voip_carrier_sid, name) values ('287c1452-620d-4195-9f19-c9814ef90d78', 'westco');
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) values ('1d8ef351-062a-4487-94f8-7698d5a40d24', 'eastco');
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);
insert into accounts(account_sid, service_provider_sid, name, sip_realm, registration_hook_sid, webhook_secret)
values ('ed649e33-e771-403a-8c99-1780eabbc803', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'test account', 'sip.example.com', '90dda62e-0ea2-47d1-8164-5bd49003476c', 'foobar');
insert into account_subscriptions(account_subscription_sid, account_sid, pending)
values ('f4e1848d-3ff8-40eb-b9c1-30e1ef053f94','ed649e33-e771-403a-8c99-1780eabbc803',0);
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);
-- "bad" carrier - "northco" at 172.39.0.22 (returns 100 Trying and never answers)
insert into voip_carriers (voip_carrier_sid, name) values ('7b4b9c56-4d81-4f31-9d70-62cd7d82193b', 'northco');
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);
+16 -3
View File
@@ -1,11 +1,24 @@
-- create our products
insert into products (product_sid, name, category)
values
('c4403cdb-8e75-4b27-9726-7d8315e3216d', 'concurrent call session', 'voice_call_session'),
('2c815913-5c26-4004-b748-183b459329df', 'registered device', 'device'),
('35a9fb10-233d-4eb9-aada-78de5814d680', 'api call', 'api_rate');
insert into webhooks(webhook_sid, url, username, password) values('90dda62e-0ea2-47d1-8164-5bd49003476c', 'http://127.0.0.1:4000/auth', 'foo', 'bar');
insert into service_providers (service_provider_sid, name, root_domain, registration_hook_sid)
values ('3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'SP A', 'jambonz.org', '90dda62e-0ea2-47d1-8164-5bd49003476c');
insert into accounts(account_sid, service_provider_sid, name, sip_realm, registration_hook_sid)
values ('ed649e33-e771-403a-8c99-1780eabbc803', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'test account', 'sip.example.com', '90dda62e-0ea2-47d1-8164-5bd49003476c');
insert into accounts(account_sid, service_provider_sid, name, sip_realm, registration_hook_sid, webhook_secret)
values ('ed649e33-e771-403a-8c99-1780eabbc803', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'test account', 'sip.example.com', '90dda62e-0ea2-47d1-8164-5bd49003476c', 'foobar');
insert into account_subscriptions(account_subscription_sid, account_sid, pending)
values ('f4e1848d-3ff8-40eb-b9c1-30e1ef053f94','ed649e33-e771-403a-8c99-1780eabbc803',0);
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);
-- one carrier, uas script expecting a reinvite
insert into voip_carriers (voip_carrier_sid, name) values ('287c1452-620d-4195-9f19-c9814ef90d78', 'westco');
insert into voip_carriers (voip_carrier_sid, name, service_provider_sid, e164_leading_plus, requires_register, register_username, register_sip_realm, register_password)
values ('287c1452-620d-4195-9f19-c9814ef90d78', 'westco', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 0, 1, 'foo', 'jambonz.org', 'bar');
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.23', true, true);
+25
View File
@@ -0,0 +1,25 @@
-- create our products
insert into products (product_sid, name, category)
values
('c4403cdb-8e75-4b27-9726-7d8315e3216d', 'concurrent call session', 'voice_call_session'),
('2c815913-5c26-4004-b748-183b459329df', 'registered device', 'device'),
('35a9fb10-233d-4eb9-aada-78de5814d680', 'api call', 'api_rate');
insert into webhooks(webhook_sid, url, username, password) values('90dda62e-0ea2-47d1-8164-5bd49003476c', 'http://127.0.0.1:4000/auth', 'foo', 'bar');
insert into service_providers (service_provider_sid, name, root_domain, registration_hook_sid)
values ('3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'SP A', 'jambonz.org', '90dda62e-0ea2-47d1-8164-5bd49003476c');
insert into accounts(account_sid, service_provider_sid, name, sip_realm, registration_hook_sid, webhook_secret)
values ('ed649e33-e771-403a-8c99-1780eabbc803', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 'test account', 'sip.example.com', '90dda62e-0ea2-47d1-8164-5bd49003476c', 'foobar');
insert into account_subscriptions(account_subscription_sid, account_sid, pending)
values ('f4e1848d-3ff8-40eb-b9c1-30e1ef053f94','ed649e33-e771-403a-8c99-1780eabbc803',0);
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', 0);
-- one carrier, uas script expecting a reinvite
insert into voip_carriers (voip_carrier_sid, name, service_provider_sid, e164_leading_plus, requires_register, register_username, register_sip_realm, register_password)
values ('287c1452-620d-4195-9f19-c9814ef90d78', 'westco', '3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', 0, 1, 'foo', 'jambonz.org', 'bar');
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.23', true, true);
+38 -1
View File
@@ -8,14 +8,32 @@ networks:
- subnet: 172.39.0.0/16
services:
mysql:
image: mysql:5.7
platform: linux/x86_64
ports:
- "3306:3306"
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "--protocol", "tcp"]
timeout: 5s
retries: 10
networks:
sbc-outbound:
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"
networks:
sbc-outbound:
ipv4_address: 172.39.0.10
depends_on:
mysql:
condition: service_healthy
redis:
image: redis:5-alpine
@@ -89,3 +107,22 @@ services:
networks:
sbc-outbound:
ipv4_address: 172.39.0.23
sip-uri-auth:
image: drachtio/sipp:latest
command: sipp -sf /tmp/uas-auth.xml
volumes:
- ./scenarios:/tmp
tty: true
networks:
sbc-outbound:
ipv4_address: 172.39.0.24
influxdb:
image: influxdb:1.8
ports:
- "8086:8086"
networks:
sbc-outbound:
ipv4_address: 172.39.0.90
+2 -3
View File
@@ -1,11 +1,10 @@
const test = require('blue-tape');
//const test = require('tape').test ;
const test = require('tape');
const exec = require('child_process').exec ;
test('starting docker network..', (t) => {
exec(`docker-compose -f ${__dirname}/docker-compose-testbed.yaml up -d`, (err, stdout, stderr) => {
t.pass('docker started');
t.end(err);
t.end(err);
});
});
+1 -11
View File
@@ -1,17 +1,7 @@
const test = require('blue-tape');
const test = require('tape');
//const test = require('tape').test ;
const exec = require('child_process').exec ;
/*
test('docker logs', (t) => {
exec('docker logs test_sbc_1', (err, stdout, stderr) => {
console.log(stdout);
console.log(stderr);
t.end();
});
});
*/
test('stopping docker network..', (t) => {
t.timeoutAfter(10000);
exec(`docker-compose -f ${__dirname}/docker-compose-testbed.yaml down`, (err, stdout, stderr) => {
-1
View File
@@ -1,5 +1,4 @@
require('./docker_start');
require('./create-test-db');
require('./sip-tests');
require('./remove-test-db');
require('./docker_stop');
+4
View File
@@ -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
-11
View File
@@ -1,11 +0,0 @@
const test = require('blue-tape') ;
const exec = require('child_process').exec ;
const pwd = process.env.TRAVIS ? '' : '-p$MYSQL_ROOT_PASSWORD';
test('dropping jambones_test database', (t) => {
exec(`mysql -h localhost -u root ${pwd} < ${__dirname}/db/remove_test_db.sql`, (err, stdout, stderr) => {
if (err) return t.end(err);
t.pass('database successfully dropped');
t.end();
});
});
+5 -3
View File
@@ -33,7 +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]
@@ -57,7 +60,7 @@
<recv response="100" response_txn="invite"/>
<pause milliseconds="3000"/>
<pause milliseconds="1000"/>
<send>
<![CDATA[
@@ -101,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>
@@ -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="503" 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-limits
Content-Length: 0
]]>
</send>
</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@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>
@@ -15,6 +15,9 @@
Contact: sip:sipp@[local_ip]:[local_port]
Max-Forwards: 70
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]
@@ -14,6 +14,9 @@
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]
+3
View File
@@ -35,6 +35,9 @@
Contact: sip:sipp@[local_ip]:[local_port]
Max-Forwards: 70
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>
+173
View File
@@ -0,0 +1,173 @@
<?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@172.39.0.24 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@172.39.0.24>
Call-ID: [call_id]
CSeq: 1 INVITE
Contact: sip:sipp@[local_ip]:[local_port]
Max-Forwards: 70
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]
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="401" auth="true">
</recv>
<send>
<![CDATA[
ACK sip:sip:john@172.39.0.24 SIP/2.0
[last_Via:]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
To: <sip:sip:john@jambonz.org>[peer_tag_param]
[last_Call-ID:]
CSeq: 1 ACK
Subject: uac-sip-uri-auth-success
Content-Length: 0
]]>
</send>
<send retrans="500">
<![CDATA[
INVITE sip:john@172.39.0.24 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]xx
To: <sip:john@172.39.0.24>
[last_Call-ID:]
CSeq: 2 INVITE
Contact: sip:sipp@[local_ip]:[local_port]
[authentication username=foo password=bar]
Max-Forwards: 70
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]
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@172.39.0.24 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@172.39.0.24 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-auth-success
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>
+120
View File
@@ -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>
+155
View File
@@ -0,0 +1,155 @@
<?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 default 'uas' scenario. -->
<!-- -->
<scenario name="Basic UAS responder">
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
<!-- are saved and used for following messages sent. Useful to test -->
<!-- against stateful SIP proxies/B2BUAs. -->
<recv request="INVITE" crlf="true">
</recv>
<send retrans="500">
<![CDATA[
SIP/2.0 401 Unauthorized
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
WWW-Authenticate: Digest realm="jambonz.org", nonce="4cdbb733645816512687270b83d2ae5d11e4d9d8"
Content-Length: 0
]]>
</send>
<recv request="ACK"
rtd="true"
crlf="true">
</recv>
<recv request="INVITE" crlf="true">
<action>
<verifyauth assign_to="authvalid" username="foo" password="bar" />
</action>
</recv>
<nop hide="true" test="authvalid" next="goodauth" />
<send retrans="500">
<![CDATA[
SIP/2.0 403 Forbidden
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>;expires=60
Content-Type: application/sdp
Content-Length: 0
]]>
</send>
<recv request="ACK" next="endit"
rtd="true"
crlf="true">
</recv>
<label id="goodauth"/>
<send>
<![CDATA[
SIP/2.0 100 Trying
[last_Via:]
[last_From:]
[last_To:]
[last_Call-ID:]
[last_CSeq:]
Content-Length: 0
]]>
</send>
<send retrans="500">
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
Content-Type: application/sdp
Content-Length: [len]
v=0
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
s=-
c=IN IP[media_ip_type] [media_ip]
t=0 0
m=audio [media_port] RTP/AVP 0
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv request="ACK"
optional="true"
rtd="true"
crlf="true">
</recv>
<recv request="BYE">
</recv>
<send>
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
Content-Length: 0
]]>
</send>
<label id="endit"/>
<!-- Keep the call open for a while in case the 200 is lost to be -->
<!-- able to retransmit it if we receive the BYE again. -->
<timewait milliseconds="4000"/>
<!-- 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>
+51 -21
View File
@@ -2,14 +2,60 @@
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<scenario name="Basic UAS responder">
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
<!-- are saved and used for following messages sent. Useful to test -->
<!-- against stateful SIP proxies/B2BUAs. -->
<recv request="INVITE" crlf="true">
</recv>
<send retrans="500">
<![CDATA[
SIP/2.0 401 Unauthorized
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
WWW-Authenticate: Digest realm="jambonz.org", nonce="4cdbb733645816512687270b83d2ae5d11e4d9d8"
Content-Length: 0
]]>
</send>
<recv request="ACK"
rtd="true"
crlf="true">
</recv>
<recv request="INVITE" crlf="true">
<action>
<ereg regexp=".*" search_in="hdr" header="Subject:" assign_to="1" />
<verifyauth assign_to="authvalid" username="foo" password="bar" />
</action>
</recv>
<nop hide="true" test="authvalid" next="goodauth" />
<send retrans="500">
<![CDATA[
SIP/2.0 403 Forbidden
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>;expires=60
Content-Type: application/sdp
Content-Length: 0
]]>
</send>
<recv request="ACK" next="endit"
rtd="true"
crlf="true">
</recv>
<label id="goodauth"/>
<send>
<![CDATA[
@@ -93,7 +139,7 @@
<recv request="BYE">
</recv>
<send next="2">
<send>
<![CDATA[
SIP/2.0 200 OK
@@ -108,23 +154,7 @@
]]>
</send>
<label id="1"/>
<send>
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
Content-Length: 0
]]>
</send>
<label id="2"/>
<label id="endit"/>
</scenario>
+69 -10
View File
@@ -1,13 +1,20 @@
const test = require('blue-tape');
const test = require('tape');
const { output, sippUac } = require('./sipp')('test_sbc-outbound');
const {execSync} = require('child_process');
const pwd = process.env.TRAVIS ? '' : '-p$MYSQL_ROOT_PASSWORD';
const debug = require('debug')('jambonz:sbc-outbound');
const bent = require('bent');
const getJSON = bent('json');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
function waitFor(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms * 1000);
});
}
function connect(connectable) {
return new Promise((resolve, reject) => {
connectable.on('connect', () => {
@@ -18,42 +25,94 @@ 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');
t.pass('return 404 to outbound attempt to unregistered user/device');
/* 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 localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/jambones-sql.sql`);
execSync(`mysql -h localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/populate-test-data2.sql`);
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-data2.sql`);
/* 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 localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/jambones-sql.sql`);
execSync(`mysql -h localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/populate-test-data3.sql`);
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-data3.sql`);
/* call to PSTN where caller hangs up during outdial */
await sippUac('uac-cancel.xml');
t.pass('successfully handled caller hangup during lcr outdial');
// re-rack test data
execSync(`mysql -h localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/jambones-sql.sql`);
execSync(`mysql -h localhost -u root ${pwd} -D jambones_test < ${__dirname}/db/populate-test-data4.sql`);
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-data4.sql`);
/* reinvite after call established */
await sippUac('uac-pcap-carrier-success-reinvite.xml');
t.pass('successfully handled reinvite during lcr outdial');
/* invite to sipUri that challenges */
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`);
/* fails when session limit exceeded */
await sippUac('uac-pcap-carrier-fail-limits.xml');
t.pass('fails when max calls in progress');
// 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(`${res.total} cdrs: ${JSON.stringify(res)}`);
t.ok(res.total === 9, 'wrote 9 cdrs');
srf.disconnect();
} catch (err) {
console.error(err);
+5 -5
View File
@@ -1,5 +1,5 @@
const { spawn } = require('child_process');
const debug = require('debug')('test:sipp');
const debug = require('debug')('jambonz:ci');
let network;
const obj = {};
let output = '';
@@ -28,7 +28,7 @@ obj.sippUac = (file, opts) => {
opts = opts || {};
const cmd = 'docker';
let args = [
'run', '-ti', '--rm', '--net', `${network}`
'run', '--rm', '--net', `${network}`
]
.concat(opts.ip ? ['--ip', opts.ip] : [])
.concat([
@@ -46,7 +46,7 @@ obj.sippUac = (file, opts) => {
'sbc'
]);
//console.log(`args: ${args}`);
debug(`args: ${args}`);
clearOutput();
return new Promise((resolve, reject) => {
@@ -67,8 +67,8 @@ obj.sippUac = (file, opts) => {
debug(`stdout: ${data}`);
addOutput(data.toString());
});
child_process.stdout.on('data', (data) => {
debug(`stdout: ${data}`);
child_process.stderr.on('data', (data) => {
debug(`stderr: ${data}`);
addOutput(data.toString());
});
});