diff --git a/.travis.yml b/.travis.yml index 2f081dbb..956789d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,5 @@ node_js: - "lts/*" services: - docker - - mysql script: - npm test diff --git a/README.md b/README.md index 9cef86bb..8b131d34 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# sbc-inbound [![Build Status](https://secure.travis-ci.org/jambonz/sbc-inbound.png)](http://travis-ci.org/jambonz/sbc-inbound) +# jambones-feature-server [![Build Status](https://secure.travis-ci.org/jambonz/jambones-feature-server.png)](http://travis-ci.org/jambonz/jambones-feature-server) -This application provides a part of the SBC (Session Border Controller) functionality of jambonz. It handles incoming INVITE requests from carrier sip trunks or from sip devices and webrtc applications. SIP INVITEs from known carriers are allowed in, while INVITEs from sip devices are challenged to authenticate. SIP traffic that is allowed in is sent on to a jambonz application server in a private subnet. +This application implements the core feature server of the jambones platform. ## Configuration @@ -15,16 +15,19 @@ Configuration is provided via the [npmjs config](https://www.npmjs.com/package/c ``` 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. +> 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. -##### rtpengine location +##### freeswitch location ``` - "rtpengine": { - "host": "127.0.0.1", - "port": 22222 - }, + "freeswitch: [ + { + "address": "127.0.0.1", + "port": 8021, + "secret": "ClueCon" + } + ], ``` -the `rtpengine` object specifies the location of the rtpengine, which will typically be running on the same server as drachtio. +the `freeswitch` property specifies an array of freeswitch servers to use to handle incoming calls. ##### application log level ``` @@ -32,77 +35,19 @@ the `rtpengine` object specifies the location of the rtpengine, which will typic "level": "info" } ``` -##### application server location -The sip trunk routing to internal application servers are specified as an array of IP addresses. +##### mysql server location +Login credentials for the mysql server databas. ``` - "trunks": { - "appserver": ["sip:10.10.120.1"] + "mysql": { + "host": "localhost", + "user": "jambones", + "password": "jambones", + "database": "jambones" } ``` -##### 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"] - } -} -``` -## Authentication -Authenticating users is the responsibility of the client by exposing an http callback. A POST request will be sent to the configured callback (i.e. the value in the `accounts.registration_hook` column in the associated sip realm value in the REGISTER request). The body of the POST will be a json payload including the following information: -``` -{ - "method": "REGISTER", - "expires": 3600, - "scheme": "digest", - "username": "john", - "realm": "jambonz.org", - "nonce": "157590482938000", - "uri": "sip:172.37.0.10:5060", - "response": "be641cf7951ff23ab04c57907d59f37d", - "qop": "auth", - "nc": "00000001", - "cnonce": "6b8b4567", - "algorithm": "MD5" -} -``` -It is the responsibility of the customer-side logic to retrieve the associated password for the given username and to then authenticate the request by calculating a response hash value (per the algorithm described in [RFC 2617](https://tools.ietf.org/html/rfc2617#section-3.2.2)) and comparing it to the response property in the http body. - -For example code showing how to calculate the response hash given the above inputs, [see here](https://github.com/jambonz/customer-auth-server/blob/master/lib/utils.js). - -For a simple, full-fledged example server doing the same, [see here](https://github.com/jambonz/customer-auth-server). - -The customer server SHOULD return a 200 OK response to the http request in all cases with a json body indicating whether the request was successfully authenticated. - -The body MUST include a `status` field with a value of either `ok` or `fail`, indicating whether the request was authenticated or not. -``` -{"status": "ok"} -``` - -Additionally, in the case of failure, the body MAY include a `msg` field with a human-readable description of why the authentication failed. -``` -{"status": "fail", "msg": "invalid username"} -``` - -## Forwarding behavior -This application acts as a back-to-back user agent and media proxy. When sending INVITEs on to the jambonz application servers, it adds the following headers onto the INVITE: - -- `X-Forwarded-For`: the IP address of the client that sent the INVITE -- `X-Forwarded-Carrier`: the name of the inbound carrier, if applicable #### 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. +The test suite currently only consists of JSON-parsing unit tests. A full end-to-end sip test suite should be added. ``` -MYSQL_ROOT_PASSWORD=foobar npm test +npm test ``` diff --git a/lib/tasks/make_task.js b/lib/tasks/make_task.js index f9b725f6..9fb8fc95 100644 --- a/lib/tasks/make_task.js +++ b/lib/tasks/make_task.js @@ -10,7 +10,9 @@ function makeTask(logger, obj) { const name = keys[0]; const data = obj[name]; logger.debug(data, `makeTask: ${name}`); - if (typeof data !== 'object') throw errBadInstruction; + if (typeof data !== 'object') { + throw errBadInstruction; + } Task.validate(name, data); switch (name) { case TaskName.SipDecline: diff --git a/lib/tasks/say.js b/lib/tasks/say.js index 40df8a22..2f60e082 100644 --- a/lib/tasks/say.js +++ b/lib/tasks/say.js @@ -38,6 +38,7 @@ class TaskSay extends Task { } kill() { + super.kill(); if (this.ep.connected && !this.sayComplete) { this.logger.debug('TaskSay:kill - killing audio'); this.ep.api('uuid_break', this.ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio')); diff --git a/lib/utils/normalize-jamones.js b/lib/utils/normalize-jamones.js index 73ecd367..10b87502 100644 --- a/lib/utils/normalize-jamones.js +++ b/lib/utils/normalize-jamones.js @@ -4,12 +4,7 @@ function normalizeJambones(logger, obj) { const document = []; for (const tdata of obj) { if (typeof tdata !== 'object') throw new Error('invalid JSON: jambones docs must be array of objects'); - if (Object.keys(tdata).length === 1) { - // {'say': {..}} - logger.debug(`pushing ${JSON.stringify(tdata)}`); - document.push(tdata); - } - else if ('verb' in tdata) { + if ('verb' in tdata) { // {verb: 'say', text: 'foo..bar'..} const name = tdata.verb; const o = {}; @@ -20,6 +15,11 @@ function normalizeJambones(logger, obj) { o2[name] = o; document.push(o2); } + else if (Object.keys(tdata).length === 1) { + // {'say': {..}} + logger.debug(`pushing ${JSON.stringify(tdata)}`); + document.push(tdata); + } else { logger.info(tdata, `invalid JSON: invalid verb form, numkeys ${Object.keys(tdata).length}`); throw new Error('invalid JSON: invalid verb form');