Git Product home page Git Product logo

sbc-sip-sidecar's Introduction

sbc-sip-sidecar Build Status

This application provides a part of the SBC (Session Border Controller) functionality of jambonz platform. It handles incoming/outgoing REGISTER requests from/to clients/servers (including both sip softphones and WebRTC client applications), incoming OPTIONS. Register Authentication is delegated to customer-side logic via a web callback configured for the account in the jambonz database. Information about active registrations is stored in a redis database.

Configuration

Configuration is provided via environment variables:

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
JAMBONES_LOGLEVEL log level for application, 'info' or 'debug' no
JAMBONES_MYSQL_HOST mysql host yes
JAMBONES_MYSQL_USER mysql username yes
JAMBONES_MYSQL_PASSWORD mysql password yes
JAMBONES_MYSQL_DATABASE mysql data yes
JAMBONES_MYSQL_PORT mysql port no
JAMBONES_MYSQL_CONNECTION_LIMIT mysql connection limit no
JAMBONES_CLUSTER_ID cluster id no
JAMBONES_REDIS_HOST redis host yes
JAMBONES_REDIS_PORT redis port no
JAMBONES_TIME_SERIES_HOST influxdb host yes
CHECK_EXPIRES_INTERVAL servers expiration check interval no
EXPIRES_INTERVAL servers expire no
JWT_SECRET secret for signing JWT token yes
ENCRYPTION_SECRET secret for credential encryption(JWT_SECRET is deprecated) yes
JAMBONES_REGBOT_DEFAULT_EXPIRES_INTERVAL default expire value for outbound registration in seconds (default 3600) no
JAMBONES_REGBOT_MIN_EXPIRES_INTERVAL minimum expire value for outbound registration in seconds (default 30) no

Registrar database

A redis database is used to hold active registrations. When a register request arrives and is authenticated, the following values are parsed from the request:

  • the address of record, or "aor" (e.g, [email protected]),
  • the sip uri, or "contact" that this user is advertising (e.g. sip:[email protected]:5060)
  • the source address and port that sent the REGISTER request to the server
  • the transport protocol that should be used to contact the user (e.g. udp, tcp, wss etc)
  • the sip address of the drachtio server that received the REGISTER request, and
  • the expiration of the registration, in seconds.
  • the application callback that should be invoked when a call is placed from this registered device
  • the application status callback that should invoked for call events on calls placed from this registered device

A hash value is created from these values and stored with an expiry value equal to the number of seconds granted to the registration (note that when a sip client is detected as being behind a firewall, the application will reduce the granted expires value to 30 seconds, in order to force the client to re-register frequently, however the expiry in redis is set to the longer, originally requested expires value).

The hash value is inserted with a key being the aor:

aor => {contact, source, protocol, sbcAddress, call_hook, call_status_hook}, expiry = registration expires value

http callback

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) 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.

For a simple, full-fledged example server doing the same, see here.

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"}

In the case of success, the body MAY include an expires value which specifies the duration of time, in seconds, to grant for this registration. If not provided, the expires value in the REGISTER request is used; if provided, however, the value provided must be less than or equal to the duration requested.

{"status": "ok", "expires": 300}

Additionally in the case of success, the body SHOULD include call_hook and call_status_hook properties that reference the application URLs to use when calls are placed from this device. If these values are not provided, outbound calling from the device will not be allowed.

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.

MYSQL_ROOT_PASSWORD=foobar npm test

sbc-sip-sidecar's People

Contributors

davehorton avatar xquanluu avatar avoylenko avatar andreheber avatar eglehelms avatar javibookline avatar psouzacognigy avatar

Watchers

 avatar

sbc-sip-sidecar's Issues

failure parsing OPTIONS message

I've seen this from time to time

Error: failed to parse sip message
    at new SipMessage (/home/admin/apps/sbc-sip-sidecar/node_modules/drachtio-srf/lib/sip-parser/message.js:13:25)
    at DrachtioAgent._onMsg (/home/admin/apps/sbc-sip-sidecar/node_modules/drachtio-srf/lib/drachtio-agent.js:555:20)
    at WireProtocol.emit (node:events:527:28)
    at WireProtocol.emit (node:domain:475:12)
    at WireProtocol.processMessageBuffer (/home/admin/apps/sbc-sip-sidecar/node_modules/drachtio-srf/lib/wire-protocol.js:270:12)
    at WireProtocol._onData (/home/admin/apps/sbc-sip-sidecar/node_modules/drachtio-srf/lib/wire-protocol.js:304:14)
    at Socket.emit (node:events:527:28)
    at Socket.emit (node:domain:475:12)
    at addChunk (node:internal/streams/readable:315:12)
    at readableAddChunk (node:internal/streams/readable:285:11) unable to parse incoming message: OPTIONS sip:[email protected] SIP/2.0^M
Via: SIP/2.0/UDP 192.241.204.38:32836;branch=DGWKZR.0879634396;rport=32836;alias^M
From: sip:[email protected]:32836;tag=79133487^M
To: sip:[email protected]^M
Call-ID: [email protected]^M
CSeq: 1 OPTIONS^M
Contact: sip:[email protected]:32836^M
Content-Length: 0^M
Max-Forwards: 20^M
User-Agent: rkNLoahi^M
Accept: text/plain^M
^M

Enable the use of two distinct usernames for outbound SIP REGISTER

Some PBX systems require additional SIP credentials for registration. One of these cases is where on top of the usual Domain/Username/Password triplet, the system uses a separate authentication username to be used in the authentication digest.

This means that for jambonz to register as an extension to that system, it would need to use the "standard" username in all SIP headers but when generating the Auth Digest, use the provided auth username instead.

Example:
-Username: 702
-Password: XXXXXX
-Auth Username: myauthuser

Should result in a request where all header fields use the Username (702) but the Digest is generated with the Auth Username (myauthuser)

Captura de pantalla 2023-06-29 a las 11 39 17

Currently in Jambonz we can specify Username/Password for authenticating oubound calls - and that user/pass is used throughout all the fields in the SIP REGISTER request. The only additional field we can tweak at the moment (0.8.3) is the SIP From user.

Captura de pantalla 2023-06-29 a las 11 45 27

This only changes the SIP From header but the other fields still use the Username/Password specified in the authentication of outbound calls. The mix of usernames in the headers does not result in a successful registration.

Captura de pantalla 2023-06-29 a las 11 47 41

MS teams endpoints have changed

getting failure now from these

2024-04-01 18:17:43.674110 send 364 bytes to tls/[52.114.76.76]:5061 at 18:17:43.673960:
OPTIONS sip:sip2.pstnhub.microsoft.com SIP/2.0
Via: SIP/2.0/TLS 54.226.115.101;branch=z9hG4bKrQQcZNygZ7tea
Max-Forwards: 70
From: <sip:foobar.com:5061;transport=tls>;tag=mt25ZHyU4BvUc
To: <sip:sip2.pstnhub.microsoft.com>
Call-ID: f96fd546-6af6-123d-9aac-0afffb788a3b
CSeq: 81443939 OPTIONS
Contact: <sip:foobar.com:5061;transport=tls>
Content-Length: 0

2024-04-01 18:17:43.751039 nta.c:8721 outgoing_print_tport_error() nta: OPTIONS f96fd546-6af6-123d-9aac-0afffb788a3b (81443939): Input/output error (5) with tls/[52.114.76.76]:5061
2024-04-01 18:17:43.850163 send 303 bytes to udp/[174.142.205.31]:63106 at 18:17:43.850016:
SIP/2.0 404 Not Found
Via: SIP/2.0/UDP 0.0.0.0:63106;branch=z9hG4bK1579283766;rport=63106;received=174.142.205.31
From: <sip:[email protected]>;tag=979277823
To: <sip:[email protected]>;tag=gpX2r5tDg82ge
Call-ID: 1468399806-2078574427-1359795331
CSeq: 1 INVITE
Content-Length: 0

bugfix: handle clients that request very short expires time in REGISTER request

Currently, we assume that clients will request a relatively long (several minutes or more) expires time when they send a REGISTER. This is desirable, as we can cache the successful registration for that longer period, while cranking down the expires value in our 200 OK if they are behind a nat device. This forces them to re-register often, which keeps a pinhole open on their router/nat device allowing us to send them INVITEs for incoming (to them) calls. However, we don't need to continually call their auth webhook as we have cached their subscription in redis for the longer, originally-requested time frame. This is important for scalability since it could be possible to have thousands or tens of thousands sip devices actively registering.

However, there is a bug in a situation where a client requests, say, a 30 second expires value. What happens is this

  • the initial REGISTER succeeds (we call their auth hook) and we cache the registration for 30 seconds
  • some time before the 30 seconds expires (say, 20 seconds in), they send another REGISTER
  • we see they have a cached registration in redis and respond 200 OK
  • 30 seconds after the initial register, their cached registration is flushed

Since we did not update the expires when we got the second register request, they now do not have an active registration and we will temporarily not be able to deliver them calls, until the re-register again.

The fix should be to cached the expiry time as part of the registration and when we receive a REGISTER from a device for which we have a cached registration, check to see if it within 30 seconds of expiring. If so, call the auth hook and then if successful update the cached registration.

Control direct calling via registration webhook

Currently I can return an application_sid with a regsitration webhook response and that will determine the applicaiton that is invoked for originating calls from that client.

However I can't control the direct calling options like I can in the GUI in this response, I need to be able to disable direct calling to other users so that ALL calls from a client hit the specified application.

Need more flexibility in constructing Contact header in outgoing REGISTER

Currently, when sending REGISTERs to carriers that require it, we define the Contact header to have the address of record (aor) in the host part of the uri:

'Contact': `<sip:${this.aor}>;expires=${DEFAULT_EXPIRES}`,

However, some carriers (e.g. sipgate) require that the host part of the Contact header be the public address of the server sending the REGISTER. Therefore, we need to add an environment variable which, if set, would cause the Contact header to be constructed using the local public IP of the drachtio server.

The env variable should perhaps be named JAMBONES_REGBOT_CONTACT_USE_IP and if set to '1' or true the code should construct the Contact header accordingly. This will require learning the drachtio public ip in the callback from the 'connect' call and then storing it in srf.locals somwhere.

Use OPTIONS to discover available remote SIP Gateways

When a carrier has multiple SIP gateways configured for outbound calls, it would be useful to have a health check mechanism via OPTIONS to ascertain which of those gateways are ready to receive SIP requests.

As a nice-to-have, maybe have the status of the gateways visible on the web app.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.