Code Monkey home page Code Monkey logo

sip.js's People

Contributors

486 avatar blakmatrix avatar bryanpaluch avatar clshortfuse avatar dependabot[bot] avatar garronej avatar gtheron avatar jeremy-j-ackso avatar jpc-technologies avatar kirm avatar kirm2 avatar mainiak avatar manur avatar mayamatakeshi avatar nguyer avatar sweiz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sip.js's Issues

Option for public address in sip.start

Currently, the address option is used to specify an IP address that sip.js binds to. However, this same address is used for calculating Via headers. This solution works great on LAN but not on production environments. It would be great to have an option like host or publicAddress option to specify the IP of the server in the SIP packets.

Thanks!

Patch for sending in-dialog requests

There's a bug in your code handling the loose-route detection, here's the patch:

diff --git a/sip.js b/sip.js
index 90f8163..4d738f8 100644
--- a/sip.js
+++ b/sip.js
@@ -1100,7 +1100,7 @@ function makeTransactionLayer(options, transport) {
           rq.headers.route = parsers.route({s: rq.headers.route, i:0});

         hop = parseUri(rq.headers.route[0].uri);
-        if(routeuri.lr === undefined ) {
+        if(hop.params.lr === undefined ) {
           rq.headers.route.shift();
           rq.headers.route.push({uri: rq.uri});
           rq.uri = hop;

Yeah, I should learn how to send pull requests, I know :)

Using port 0 to get random port

It would be nice if you could use port 0 to retrieve a new port like the net library uses itself. I can make a pull request if it will be accepted.

Request is retransmitted after receiving reply

When I send a REGISTER to another proxy using send(msg, cb), I get back 100 and 401. For some reason, sip.js sends another REGISTER with the same cseq as the first one (a retransmit) ~90-100ms after it sent the first request, although it received a 100 and even a 401 in the meanwhile. The same happens when I increase cseq and send the message again including Authorize header from within my callback, just that I get a 100 back there, then sip.js retransmits the authenticated request, then I get the 200 and it stops.

So to sum it up, each request seems to be retransmitted once, even though we already got a reply. Is there some way to manually stop a specific transaction, or is it just a timing issue?

digest.signRequest can't handle object URIs (patch)

diff --git a/digest.js b/digest.js
index d014f4e..209449e 100644
--- a/digest.js
+++ b/digest.js
@@ -1,5 +1,6 @@
 var crypto = require('crypto');
 var util = require('util');
+var stringifyUri = require('./sip').stringifyUri;

 function unq(a) {
   if(a && a[0] === '"' && a[a.length-1] === '"')
@@ -211,6 +212,8 @@ exports.signRequest = function (ctx, rq, rs, creds) {

   var nc = ctx.nc !== undefined ? numberTo8Hex(++ctx.nc) : undefined;

+  rq.uri = stringifyUri(rq.uri); // FIXME - dirty kludge to allow object URIs
+
   ctx.uri = rq.uri;

   var signature = {

Example of response to INVITE

I've been having a play with this module - very cool! I've thrown together a quick registrar example using Redis - https://gist.github.com/906216

I was wondering if you could provide a quick example of how to use this module respond to an INVITE.

In other words, if I have two clients registered, and one sends an INVITE for the other, I want to retrieve the registration info. from Redis and then format a response to the client that initiated the call (putting the SIP address for the called party in the "contact" header).

Just not sure how to format the response.

Thanks!

Built-in support for Dialogs or "Sessions"

I am really amazed by sip.js. It would be nice to have a more complete support of RFC 3261.

Naturally, that means support for Dialogs. I know the example in the repo, but there are still some things missing. Besides, it should be directly provided by the stack and not on top.

It would be nice to have an API for sending subsequent requests within the same Dialog. Most SIP stacks provide this with a Dialog or Session object (where the Session is a more application-focused abstraction of the Dialog).

  • Are there any efforts planned for this?
  • Besides Dialog support, are there any other features of RFC 3261 missing?

Max-Forwards header missing from ACK

Hi kirm-

During an interop today I received an interesting response, 400 "Max-Forwards field is missing". I realized after looking at line 845 in sip.js (see below). That the 'Max-Forwards' field is missing. According to the following from RFC 3261 it is mandatory.

Just wanted to give you the heads up.
Thanks.

"RFC 3261 - 8.1.1.6 page 39 states that Max-Forwards header must be
inserted by UACs in all generated requests."

var ack = {
method: 'ACK',
uri: rq.uri,
headers: {
from: rq.headers.from,
cseq: {method: 'ACK', seq: rq.headers.cseq.seq},
'call-id': rq.headers['call-id'],
via: [rq.headers.via[0]]
}
};

TCP is not working

in sip.js makeStreamTransport() has bugs.
I have fixed connectionsid -> remotes and it passed it.

but get() in return object is called without open() call,
so flows[] returns undefined.
-> It is occured that Object.create in wrap() is error...

How can I fix it?

Error when sending INVITE via proxy

When Trying to use the proxy.js to forward an Invite request to another host, I get the following error:

/Users/me/javascript/node_modules/sip/proxy.js:7
var via = msg.headers.via[0];
^
TypeError: Cannot read property 'via' of undefined
at makeContextId (/Users/me/javascript/node_modules/sip/proxy.js:7:24)
at Object.send (/Users/me/javascript/node_modules/sip/proxy.js:18:22)
at /Users/me/javascript/e:109:8
at /Users/me/javascript/node_modules/sip/proxy.js:66:5
at next (/Users/me/javascript/node_modules/sip/:1121:15)
at /Users/me/javascript/node_modules/sip/:1138:7
at resolve (/Users/me/javascript/node_modules/sip/:796:12)
at Object.createClientTransaction (/Users/me/javascript/node_modules/sip/:1099:7)
at Object.send (/Users/me/javascript/node_modules/sip/:1204:30)
at forwardRequest (/Users/me/javascript/node_modules/sip/proxy.js:54:7)

Apparently the error is thrown in line 1113 of sip.js (I executed the code inside the debugger):

send.reliable = cn.local.protocol.toUpperCase() !== 'UDP';

From what I see, cn is an Object that does not have any 'local' property, so cn.local is undefined.
Therefore trying to access to cn.local.protocol results in a TypeError.

Node v0.6.x compatiblity

In you run any example of sip.js usage on node version greater than 0.4.x it fails with the following exception:

node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: No such module
at /Users/kirill/Documents/github/sip.js/sip.js:18:30
at Object. (/Users/kirill/Documents/github/sip.js/sip.js:117:2)
at Module._compile (module.js:432:26)
at Object..js (module.js:450:10)
at Module.load (module.js:351:31)
at Function._load (module.js:310:12)
at Module.require (module.js:357:17)
at require (module.js:368:17)
at Object. (/Users/kirill/Documents/github/sip.js/examples/registrar-proxy.js:3:11)
at Module._compile (module.js:432:26)

Switch-over to TCP on timeout

When trying to send e.g. a REGISTER and we don't get back a response within 30sec, sip.js switches over automatically to TCP. One issue here is that TCP requires a Content-Length header, which you don't need for UDP. So it would be good to check for the header and insert it if it's not present when switching from UDP to TCP (it might be not clear for the app-developer that it's required to add that to the crafted request because the switch of the transport protocol is done implicitely in sip.js).

The other thing is that once we get a response for TCP (e.g. a 401 for a REGISTER) and send out the new authenticated REGISTER, it tries UDP again for 30sec, then again switches to TCP. Not sure if there's a simple way to immediately use TCP again? Should I as an application developer check the Via of the last response and use the transport indicated there? And what is the best practice in sip.js to indicate TCP as transport protocol for a specific request (just adding ";transport=TCP" to the R-URI)?

sip.js:540 if(m.headers['content-length']) { TypeError: Cannot read property 'headers' of undefined

I get this when trying to run examples/registrar-redirector-auth.js on 0.6.5 using http://itunes.apple.com/us/app/telephone/id406825478?mt=12 as a SIP client.

sip.js:540
if(m.headers['content-length']) {
^
TypeError: Cannot read property 'headers' of undefined
at parseMessage (/home/zip/node_modules/sip/sip.js:540:9)
at Socket. (/home/zip/node_modules/sip/sip.js:633:15)
at Socket.emit (events.js:70:17)
at UDP.onMessage as onmessage

Merge the websocket branch

Hi,

I'm very interested in using the Websocket functionality. Do you know when the websocket branch will me merged into master?

parseUri problem? or bug of mobicents?

I have one question.

in BYE message, mobicents sip servlet send SIP URI like this
-> sip:[email protected]:53010;ob=;transport=TCP SIP/2.0
so parseUri function can not parse 'ob=' and sip.js responses 503.

I must fix part of param's

(?:=[^\s?\;]+)?)*) -> (?:=[^\s?\;]*)?)*

and server runs well...

Is it mobicents sip servlet's bug? or must fix other way not mine?

Issue parsing headers part in parseUri

Just a small issue:
The regular expression in parseUri

  var re = /^.......(\?([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*)?$/;

[removed initial part of the regular expression for clarity]
causes the first header to contain the "?" separator.

just moving the grouping parentheses one character should fix the issue:

  var re = /^.......\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*)?$/;

Cheers

Julian

send() request to specific remote address?

When sending an initial request to a SIP peer, one might want to do a DNS SRV lookup with the domain part of the request URI and use the result as remote address (like an outbound-proxy setting), while leaving the request URI intact. Is there a setting/param I might have missed? Something like:

send(options,msg[,callback]), e.g.:
send({address:'server1.example.com', port:5070}, {uri:'sip:example.com', ...}, cb);

SIP 'CANCEL' message: [RangeError: Maximum call stack size exceeded]

Hi,

I am using this repository to make a sip gateway server against other services. But when I create a sip Cancel message and send it to the sip server, I got RangeError: Maximum call stack size exceeded in the sip.js library.
I think it could be some recursive functions lead to an infinite loop when the message method is 'CANCEL'.

SipGateway.prototype.sendCancel = function(from,to,callback){

var client = registry[from];

var cancel = createCancel(client,to);

util.debug("sip:Cancel: \n" + util.inspect(cancel, false, null));

sip.send(cancel,function(rs){
    if(rs.status === 200){
        callback();
    }
});

}

function createCancel (client,to) {

//util.debug("createCancel: \n" + util.inspect(client, false, null));

return {
  method: 'CANCEL',
  uri: 'sip:' + to + '@' + client.hostname,
  headers: 
  {
    'call-id': client.callid,
    cseq: {method: 'CANCEL', seq: 2},
        from: {
    name: '',
            uri: 'sip:' + client.name + '@'+ client.hostname,
            params: {
                tag: client.tag
            }
        },
        to: {
            uri: 'sip:' + to +'@'+ client.hostname
        },
  authorization: client.inviteAuth

  }
}

}

Here is my code part, it basically generate the 'CANCEL' message with previous INVITE message authorization and some sip client information.

ACK should follow Route

Unless I'm mistaken, ACKs (to 200 OK) must follow the Route headers (e.g. from Record-Routes previously received), same as other requests.

while the code seems to be routing just based on the Request-URI:

        if(m.method === 'ACK') {
          if(m.headers.via === undefined)
            m.headers.via = [];

          m.headers.via.unshift({params: {branch: generateBranch()}});

          resolve(parseUri(m.uri), function(address) {
            if(address.length === 0) {

I've modified my copy to be:

        if(m.method === 'ACK') {
          if(m.headers.via === undefined)
            m.headers.via = [];

          m.headers.via.unshift({params: {branch: generateBranch()}});

      var hop = parseUri(m.uri);

      if(m.headers.route) {
        if(typeof m.headers.route === 'string')
          m.headers.route = parsers.route({s: m.headers.route, i:0});

        hop = parseUri(m.headers.route[0].uri);
        if(hop.params.lr === undefined ) {
          m.headers.route.shift();
          m.headers.route.push({uri: rq.uri});
          m.uri = hop;
        }
      }


          resolve(hop, function(address) {

but it might require some refactoring to avoid duplicating code.

Catching Errors

If I send invalid requests to the sip server, the process crashes. Here's an example of what I've been seeing:

INVITE sip:[email protected] SIP/2.0
To: sip:[email protected]
From: <>;tag=676797
call-id: 636631
CSeq: 49864 INVITE
content-type: application/sdp
Max-Forwards: 70
Contact: sip:[email protected]
content-length: 0

Notice the empty "From" field.

If the library could handle invalid requests more gracefully, if would help make the library much more robust.

Use TCP instead of UDP on client

I am trying to send a REGISTER with a SIP server using TCP. If I understand correct, the send options are just for listening.
I defined the REGISTER with TCP:

rq = {
method: 'REGISTER',
uri: 'sip:10.10.1.12:65060',
version: '2.0',
protocol: 'TCP',
...

Wireshark show the Register being sent on UDP. Am I missing something, or is this not supported?

crashes on line 651

I'm getting:

        msg.headers.via[0].params.received = rinfo.address;
                       ^
TypeError: Cannot read property '0' of undefined
    at Socket.onMessage (/root/vlnp-server/node_modules/sip/sip.js:651:24)
    at Socket.EventEmitter.emit (events.js:99:17)
    at UDP.onMessage (dgram.js:350:8)

I rewrote line 651 to not crash but provide some debug info:

   if(msg) {
      if(msg.method && msg.headers && msg.headers.via && msg.headers.via[0]) {
        msg.headers.via[0].params.received = rinfo.address;
        if(msg.headers.via[0].params.hasOwnProperty('rport'))
          msg.headers.via[0].params.rport = rinfo.port;
      }
else if (msg.method)console.warn(msg);

And I'm seeing these: (real IP redacted)

{ method: 'OPTIONS',
  uri: 'sip:[email protected]',
  version: '2.0',
  headers: {},
  content: undefined }

What's the best way to be robust to these kind of packets?

Possible Bug with DNS SRV resolve code

Thanks for the great module. I'm using it as a client against a SIP Server SMS proxy which uses the two step DNS lookup approach. Mostly it works fine. However, every so often sip.js returns a '404' without having contacted the SIP Server. Here is a typical message:
{ headers:
{ cseq: { seq: 1, method: 'MESSAGE' },
'call-id': '42673',
'content-type': 'text/plain',
from: { uri: 'sip:[email protected]' },
to: { uri: 'sip:[email protected]' } },
uri: 'sip:[email protected]',
method: 'MESSAGE',
content: 'A Text Message' }

The domain 'sms-proxy-01.bandwidthclec.com' will return a DNS SRV record for the tcp prototocl (udp is not supported on this server) with two addresses in the array.

Generally, sip.js will call dns.resolveSrv (sip.js:resolve), which will return the array of two domains, and things will work.

Here is the interesting part: debugging code inserted into sip.js:resolve46 demonstrates that when the failure occurs, resolve46 had been called with the DNS SRV uri instead of the protocol specific uri's typically returned from the dns.resolveSrv call. N aturally both attempts to resolve that uri with resolve4 and resolve6 fail and addresses ends up empty, provoking the 404.

I have workaround code in place, that attempts to re-send the request 2 seconds later. This code so far always succeeds.

I have conducted extensive testing independently against my local dns and this URL (along with the derived uris) and found 0 failures over hundreds of thousands of resolve requests over several days. So I don't think this is dns failure.

I'm sorry I can't isolate this further, but since I cannot reproduce the failure easily, and it is very intermittent, I haven't been able to step through the failing scenario. Looking at the code in sip.js:resolve, I'm wondering about the use of the variable n. Perhaps there is some sort of concurrency bug where every so often, node sequences the callbacks in here in such a way that this code is executed even if one or more of the SRV resolves will (eventually) be successful:
else if(0 === n) {
// all srv requests failed
resolve46(uri.host, function(err, address) {

Thanks

After receiving the "SUBSCRIBE" message, I want to send a "NOTIFY" and "200 OK".

After receiving the "SUBSCRIBE" message, I want to send a "NOTIFY" and "200 OK".

I want to move the following sequence.

sip.js                                         client
                      SUBSCRIBE
ใ€€ใ€€ใ€€ใ€€ ใ€€๏ผœ--------------------------
                       200 OK
            --------------------------๏ผž
                       NOTIFY
       ร—   --------------------------๏ผž
                       200 OK
ใ€€ใ€€ใ€€ใ€€ ใ€€๏ผœ--------------------------

After receiving the "SUBSCRIBE" message, that is sending the "200 OK".
But, "NOTIFY" message can not be sent.
Please tell me how to send a "NOTIFY" message.

Currently, I have implemented as described below.

sip.start({},
function(rq) {
 if(rq.method === 'REGISTER'){
  sip.send({
   status: 200,   
    });
 }
 else if(rq.method === 'SUBSCRIBE') {
    sip.send({
     status:200,
    });

  sip.send({
   method: 'NOTIFY',
   },
   function(rs) {
    if(rs.status === 200){
    }
   }
  );
});

DEBUG: TypeError: Cannot read property 'nonce' of undefined

Using the test/digest-client.coffee converted to javascript and changed ip, username and password:

var digest, rq, sip, util, date;
date = new Date();


sip = require('sip');
digest = require('sip/digest');
util = require('util');
sip.start({
  logger: {
    send: function(rq) {
      return util.debug("send\n" + util.inspect(rq, null, null));
    },
    recv: function(rq) {
   return util.debug("recv\n" + util.inspect(rq, null, null));
    }
  }
}, function() {});
rq = {
  method: 'REGISTER',
  uri: 'sip:192.168.130.10',
  headers: {
    to: {
      uri: 'sip:[email protected]'
    },
    from: {
      uri: 'sip:[email protected]'
    },
    cseq: {
      method: 'REGISTER',
      seq: Math.round((date.getTime()/1000))
    },
    'call-id': 772,
    contact: [
      {
        uri: 'sip:[email protected]',
        params: {
          expires: 120
        }
      }
    ],
    'content-length': 0
  }
};
sip.send(rq, function(rs) {
  var context, _ref;
  try {
    if (rs.status === 401 || rs.status === 407) {
      rq.headers.via.pop();
      rq.headers.cseq.seq++;
      context = {};
      digest.signRequest(context, rq, rs, {
       user: '772',
        password: '1111'
      });
      return sip.send(rq, function(rs) {
        var _ref;
        if ((200 <= (_ref = rs.status) && _ref < 300)) {
          if (false === digest.autheneticateResponse(context, rs)) {
            util.debug('failed to authenticate server');
          }
          return util.debug('Ok');
        }   
      });
    } else if ((300 > (_ref = rs.status) && _ref >= 200)) {
      return util.debug('Ok');
    } else {
      return util.debug('failed to register');
    } 
  } catch (e) {
    util.debug(e);
    return util.debug(e.stack);
  }   
});

Output:

DEBUG: send
{ method: 'REGISTER',
  uri: 'sip:192.168.130.10',
  headers: 
   { to: { uri: 'sip:[email protected]' },
     from: { uri: 'sip:[email protected]' },
     cseq: { method: 'REGISTER', seq: 1315833302 },
     'call-id': 772,
     contact: 
      [ { uri: 'sip:[email protected]',
          params: { expires: 120 } } ],
     'content-length': 0,
     via: 
      [ { params: { branch: 'z9hG4bK208768', rport: null },
          host: '192.168.90.14',
          port: 5060,
          protocol: 'UDP' } ] } }
DEBUG: recv
{ version: '2.0',
  status: 401,
  reason: 'Unauthorized',
  headers: 
   { via: 
      [ { version: '2.0',
          protocol: 'UDP',
          host: '192.168.90.14',
          port: 5060,
          params: { branch: 'z9hG4bK208768', rport: undefined } } ],
     'www-authenticate': 
      { scheme: 'Digest',
        realm: '"WM-ICP"',
        nonce: '"20cdVZ!5<KKUJQ9kukI?Fw1/*Gb>7*DK>^^#;~svFzn^#Lzs)GBWJ*0aH7*].NU#"',
        algorithm: 'MD5',
        qop: '"auth"' },
     from: 
      { name: undefined,
        uri: 'sip:[email protected]',
        params: {} },
     to: 
      { name: undefined,
        uri: 'sip:[email protected]',
        params: { tag: '0_1351853664-91919043' } },
     'call-id': '772',
     cseq: { seq: 1315833302, method: 'REGISTER' },
     contact: 
      [ { name: undefined,
          uri: 'sip:192.168.130.10',
          params: {} } ],
     'content-length': 0 },
  content: '' }
DEBUG: TypeError: Cannot read property 'nonce' of undefined
DEBUG: TypeError: Cannot read property 'nonce' of undefined
    at initClientContext (/home/dev/node_modules/sip/digest.js:185:33)
    at Object.signRequest (/home/dev/node_modules/sip/digest.js:212:5)
    at /home/dev/sip/sip.js:51:14
    at Object.message (/home/dev/node_modules/sip/sip.js:964:7)
    at Object.signal (/home/dev/node_modules/sip/sip.js:774:55)
    at Object.message (native)
    at /home/dev/node_modules/sip/sip.js:1101:24
    at /home/dev/node_modules/sip/sip.js:688:7
    at Socket.listener (/home/dev/node_modules/sip/sip.js:625:5)
    at Socket.emit (events.js:67:17)

Simple question

Sending email to author shows no good email account from gmail, so I will ask here. Sorry if it is the wrong place....

We've started using your sip.js npm package and are testing with the
example for the 302/redirect server. It works fine with just one URI
change to the contact header, but we are wondering if there is
multi-contact support for performing a 300 or 302 with multiple contacts.

We are trying to read through the .js source to determine how to do this
with either a single Contact header that has multiple URI's, or with
multiple Contact headers. Also, would the "q=" parameter be used so the
priority of the contacts can be provided?

Thanks for any help you might be able to offer.

NAT support in Via Headers

Just to check, but lines 646 and 678 in sip.js read as:

callback(parseMessage(data), {protocol: 'UDP', address: rinfo.address, port: rinfo.port});

should it be

callback(msg, {protocol: 'UDP', address: rinfo.address, port: rinfo.port});

instead?

Kind Regards

Supported Use Case?

Hi Kirill,

We spoke approx. a couple of years ago about your SIP.js library. It looks pretty cool and may meet my needs.

I am interested in building a simple SIP redirector application. No registrar involved. Simply redirect incoming SIP calls to different SIP addresses. Here's my use case:

  1. Someone calls sip:[email protected]
  2. The application (pseudo registrar) accepts the call and redirects/forwards it to sip:[email protected]
  3. The voxeolabs.net SIP address is a valid SIP address (again no registrar)

I'm using Phono (http://phono.com) as a jQuery SIP phone. It assigns a SIP address (like the one above) upon initialization in the web page and their SIP address changes every time it's loaded. I would like to track the user's current SIP address in Mongo and transfer calls from the static address ([email protected]) to their dynamically assigned Phono address.

Would SIP.js help me solve this problem? This is what I started developing from our last conversation.

Thanks,
@chrismatthieu
[email protected]

Stateless Redirect Server

Hi Kirm-

Great lib man, thanks again for all the hard work. We have a simple redirect server that we use to serve query responses to a variety of database (using a 302 response).

We're experiencing some performance issues when the server is under load. My best guess is this is due to the overhead of saving state (stateful mode) like keeping track of timers for dialogs and transactions.

These requests are very time sensitive, so the responses need to come back as quick as possible. It would seem at this point that the "stateful" pieces of the lib (transactions, dialogs,etc) are not needed.

I was wondering what your thoughts are on this? I came across proxy.js which peaked my interest. What do you think?

Thanks in advance!

Uri Compatability

I'm dealing with a switch which is sending a uri of sip:@192.168.1.1 which the parseUri function doesn't parse since the user is nil. Is there any way support could get added for this?

Problems with sdp.js

Since it is not described in the API document, perhaps the sdp.js module is not yet ready for prime time, however I found that not everything is parsed... and then when regenerating the SDP there are missing bits.

I've rewritten the sdp.js as:

//
// Functions made available
//

exports.parse=parse;
exports.stringify=stringify;


var reM = /^(\w+) +(\d+)(?:\/(\d))? +(\S+) (\d+( +\d+)*)/;
var parsers = {
    //v=0
    //o=+34913305131 0 0 IN IP4 127.0.0.1 
    o : function(r, value) {
        var t = value.split(/\s+/);
        r.o = {
            username : t[0],
            id : t[1],
            version : t[2],
            nettype : t[3],
            addrtype : t[4],
            address : t[5]
        };
        return r.o;
    },
    //c=IN IP4 127.0.0.1        
    c : function(r, value) {
        var t = value.split(/\s+/);
        r.c = {
            nettype : t[0],
            addrtype : t[1],
            address : t[2]
        };
        return r.c;
    },
    //m=audio 5000 RTP/AVP 9 96 97 98 100 0 8 102 3 103 5 6 4 104 101
    m : function(r, value) {
        var t = reM.exec(value);
        if (!r.m) {
            r.m = new Array();
        }
        var m = {
            media : t[1],
            port : +t[2],
            portnum : +(t[3] || 1),
            proto : t[4],
            fmt : t[5].split(/\s+/).map(function(x) {
                return +x;
            })
        };
        r.m.push(m);
        return m;
    },
    //a=rtpmap:9 G722/8000
    //a=recvonly
    a : function(r, value) {
        if (!r.a) {
            r.a = new Array();
        }
        r.a.push(value);
        return value;
    }
};

var stringifiers = {
    o : function(o) {
        return [ o.username || '-', o.id, o.version, o.nettype || 'IN',
                o.addrtype || 'IP4', o.address ].join(' ');
    },
    c : function(c) {
        return [ c.nettype || 'IN', c.addrtype || 'IP4', c.address ].join(' ');
    },
    m : function(m) {
        return [ m.media || 'audio', m.port, m.transport || 'RTP/AVP',
                m.fmt.join(' ') ].join(' ');
    }
};

/**
 * Parse an SDP content
 * @param sdp
 * @returns 
 */
function parse(sdp) {
    var lines = sdp.split(/\r\n/);
    var result = {};
    var r = result;
    for ( var i = 0; i < lines.length; ++i) {       
        var tmp = /^(\w)=(.*)/.exec(lines[i]);
        if(tmp) {
            if (tmp[1] == 'm') {
                r = result;
            }
            var c = parseLine(r, tmp[1], tmp[2]);
            if (tmp[1] == 'm') {
                r = c;
            }           
        }
    }
    return result;
}

function parseLine(r, key, value) {
    if (parsers[key]) {
        return parsers[key](r, value);
    } else {
        return r[key] = value;
    }
}

function stringify(sdp) {
    var s = '';
    s += stringifyLine(sdp, 'v', 0);
    s += stringifyLine(sdp, 'o');
    s += stringifyLine(sdp, 's', '-');
    s += stringifyLine(sdp, 'i');
    s += stringifyLine(sdp, 'u');
    s += stringifyLine(sdp, 'e');
    s += stringifyLine(sdp, 'p');
    s += stringifyLine(sdp, 'c');
    s += stringifyLine(sdp, 'b');
    s += stringifyLine(sdp, 't', '0 0');
    s += stringifyLine(sdp, 'r');
    s += stringifyLine(sdp, 'z');
    s += stringifyLine(sdp, 'k');
    s += stringifyLine(sdp, 'a');
    sdp.m.forEach(function(m) {
        s += stringifyLine({
            m : m
        }, 'm');
        s += stringifyLine(m, 'i');
        s += stringifyLine(m, 'c');
        s += stringifyLine(m, 'b');
        s += stringifyLine(m, 'k');
        s += stringifyLine(m, 'a');
    });

    return s;
}

function stringifyLine(sdp, type, def) {
    if (sdp[type] != undefined) {
        var stringifier = function(x) {
            return type + '='
                    + ((stringifiers[type] && stringifiers[type](x)) || x)
                    + '\r\n';
        };
        if (Array.isArray(sdp[type])) {
            return sdp[type].map(stringifier).join('');
        }
        return stringifier(sdp[type]);
    }

    if (def != undefined) {
        return type + '=' + def + '\r\n';
    }
    return '';
}

Hope this is of any use.

Probably it requires further parsing of the attribute lines.

TCP transport if no port in R-URI?

I'm disabling tcp transport like this:

sip.start({address: myaddr, port: myport, tcp:false}, function(rq) { ... });

However if I call sip.send() with a domain but no port in the R-URI, e.g. "REGISTER sip:example.com\r\n...", then sip.js still tries to send the request via TCP, which fails in the line

return wrap(protocols[target.protocol.toUpperCase()].open(target, error), target);

because target.protocol.toUpperCase() is TCP, so it tries to call open() on an undefined object. It works perfectly fine though if the R-URI contains a port, like "REGISTER sip:example.com:5060\r\n..."

Port is 0 when replying to request

Consider this code:

sip.start({address: '1.2.3.4', port: 35060}, function(rq) {
  sip.send(sip.makeResponse(rq, 501, 'Not Implemented'));
});

When it receives a request (tried INVITE, MESSAGE, OPTION) from 1.2.3.5:5060, then it sends back the response to port 0, see ngrep line:

U 1.2.3.4:35060 -> 1.2.3.5:0
SIP/2.0 501 Not Implemented
<snipped headers>

Am I missing something, or could it be a bug?

503 response translated to 404.

The sip.js library translates all 503 responses to 404's. Is there a reason for this? There appears to be special handling in sip.js for this (see code below). Removing this special handling allows the 503 to be returned to the application, where it can be handled differently than a 404, if necessary.

function searching(rs) {
if(rs.status === 503)
return next();
else if(rs.status > 100)
onresponse = callback;

callback(rs);
}

increase throughput

sip.js seems slow down exponentially on a simple request > response scenario at 1000 calls per second on an 8 core Ubuntu Linux box.

eg
INVITE =====>
<===== 180

The slow down happens almost immediately after starting a load test.
Is there something that can be done to improve this ie bypassing the transaction layer, and if so how?

Judging by the simplicity of the sip.js codebase, I would have assumed the throughput leveraging node could be comparable to that of a C based SIP proxy like kamailio.

Multiple 302 Response loops after ACK

Hey kirm-

First of all, awesome lib man. It's working well at fairly high CPS levels I did some performance testing earlier this morning and saw ~2500 CPS on a Quad [email protected] box.

I ran into an interesting issue tonight while investigating several "time outs" that I was receiving. I've isolated the problem to a "302 response loop".

Please see: http://snag.gy/WUMgg.jpg which depicts the "interesting" call flow in WireShark, as you can see we are continually sending a 302 after the ACK is received for this particular call. As far as I can tell the box is not under significant load when this occurs.

I've attached the folllowing pcap (which you can download here: https://filetea.me/default/#t1sYw6V9WWCT2mcSGumSpxcCA)

local_trace.cap - This contains a capture running on the box during a perf test Note, the one with start time of: 8.421337 size 12, the flow is very strange: INVITE ->, <- 302, -> ACK, -> INVITE, <- 302, <- 302, .....

Any thoughts on this?
Thanks in advance!

sip proxy forward requests and responses

Hi, sry for creating issue i just wanted to ask if u could give me some hints how to properly forward requests.

i have two clients connected to proxy , which are registered
and then i send invite request to proxy with specific request uri, and i dont know to properly send that request to second client, i tried sending as proxy.send, or as sip.send, but without any success, and the second client keeps sending 200(ok) responses after i confirm call on the second device

please can u give me some advices or tell me if its possible to make it work

thank you

code :

sip.start({
logger: {
send: function(message, address) { debugger; util.debug("send\n" + util.inspect(message, false, null)); },
recv: function(message, address) { debugger; util.debug("recv\n" + util.inspect(message, false, null)); }
}
},
function(rq) {
try {
if(rq.method === 'REGISTER') {
i save data from contact to registry to hold information about clients
}
else if(rq.method === 'INVITE') {
var called = registry[sip.parseUri(rq.headers.to.uri).user];
if(called) {
sip.send(sip.makeResponse(rq, 100, 'Trying'));
rq.uri='sip:'+sip.parseUri(called.contact[0].uri).user+'@'+sip.parseUri(called.contact[0].uri).host+':'+sip.parseUri(called.contact[0].uri).port;

    proxy.send(rq,function(rs) {

if(rs.status >= 300) {
console.log('call failed with status ' + rs.status);
}
else if(rs.status < 200) {
console.log('call progress status ' + rs.status);
}
else {
// yes we can get multiple 2xx response with different tags
console.log('call answered with tag ' + rs.headers.to.params.tag);

//rs.headers.to.uri='sip:'+sip.parseUri(caller.contact[0].uri).user+'@'+sip.parseUri(caller.contact[0].uri).host+':'+sip.parseUri(caller.contact[0].uri).port;

////////////////////////////////////////////////////////
proxy.send(rs);// this is not working
///////////////////////////////////////////////////////

  }

});
}
}
else {
sip.send(sip.makeResponse(rq, 405, 'Method Not Allowed'));
}
} catch(e) {
util.debug(e);
util.debug(e.stack);

sip.send(sip.makeResponse(rq, 500, "Server Internal Error end"));

}
});

uncaught timer exception

I'm having some trouble, hoping you can help with this error that is being uncaught.
timers.js:103
if (!process.listeners('uncaughtException').length) throw e;
^
Error: Not running
at Socket._healthCheck (dgram.js:311:11)
at Socket.send (dgram.js:179:8)
at Object.open.send (/usr/local/app/node_modules/sip/sip.js:564:18)
at Object.create.send.value as send
at Object.trying.timerE (/usr/local/app/node_modules/sip/sip.js:910:7)
at Object.signal (/usr/local/app/node_modules/sip/sip.js:712:55)
at Object._onTimeout (/usr/local/app/node_modules/sip/sip.js:911:38)
at Timer.list.ontimeout (timers.js:101:19)

Can you point me in a direction for fixing or avoiding this error?

CANCEL results in 503

One more thing :)

Here's my INVITE (which works fine, so I get back 100 and 180) and my CANCEL, which results in a 503 and is never sent over the wire):

invite request: {
    "method":"INVITE",
    "uri":"sip:[email protected]",
    "headers":{
        "to":{"uri":"sip:[email protected]"},
        "from":{"uri":"sip:[email protected]","params":{"tag":"742456"}},
        "call-id":"6734197970945388",
        "cseq":{"method":"INVITE","seq":60636},
        "contact":[{"uri":"sip:[email protected]"}],
        "Content-Type":"application/sdp",
        "Content-Length":201
    },
    "content":"snipped\r\n"
}

cancel request: {
    "method":"CANCEL",
    "uri":"sip:[email protected]",
    "headers":{
        "to":{"uri":"sip:[email protected]"},
        "from":{"uri":"sip:[email protected]","params":{"tag":"742456"}},
        "call-id":"6734197970945388",
        "cseq":{"method":"CANCEL","seq":60636},
        "Content-Length":0
    }
}

For me, the CANCEL looks fine (a copy of the INVITE with replaced cseq-method). Any idea why sip.js refuses to process it?

ACK sip messages are not being received

Hi,
I am using SIPP to test sip stack. SIPP utility sends invite,wait for 100, 300 and once 300 is received it sends ACK back.But sip.js stack never receives ack back.Below is the code i am using to track invite and ack message. Please help

function(rq) {
try {
switch (rq.method) {
case 'INVITE': {
console.log('message recd::');
var response = sip.makeResponse(rq, 100, 'trying');
sip.send(response);
setTimeout(function(){
var response = sip.makeResponse(rq, 300, 'Success!!');
sip.send(response,function(rs) {
console.log(rs);
});

            },1000);
            break;
      }
      case 'ACK':{
          console.log('got ack back');
          break;
      }
      default: console.log('here!!!');break;
  }

} catch(e) {
console.log(e.stack);

}
});

End-to-End ACK is not sent

When starting an INVITE transaction from within sip.js, no ACK is sent when receiving the 200. This is not a real issue because I can craft one by myself. However it seems like sip.js also won't let me send an ACK manually, not even the most basic one I can think of (not taking into account any route headers from the response etc). The ACK doesn't leave the system, and the callback is not called as well. Here's the code:

var inviteResponseCallback = function(rs) {
  if(rs.status == 200) {
    sendAck(rs);
  } else {
    // other stuff here
  }
}

var sendAck = function(rs) {
  var rq = {
    method: 'ACK',
    uri: 'sip:[email protected]',
    headers: {
      to: {uri: 'sip:[email protected]' },
      from: {uri: 'sip:[email protected]' },
      'call-id': '12345',
      cseq: { method: 'ACK', seq: 1 },
    },
  };
  sip.send(rq, function(rs){console.log('ack sent');});
}

Any ideas what's wrong here?

High level API

Hi

I think this module is in need of some high level API, to ease the pain for many ppl.
Is this something thats planned?

Im thinking of something like this:

var sip = require('sip'),
    router;

router = new sip({
  server: 127.0.0.1,
  ...
});

router.on('register', function(a, b, c) {
  ...
});

router.on('subscribe', function(a, b, c) {
  ...
});

router.on('call', function(a, b, c) {
  ...
});

router.notify({param1: true}, function(err, res) {
  ...
});

router.call({param1: true}, function(err, res) {
  ...
});

Running Multiple SIP clients -> port issue

I have the following code to register multiple sip clients (4 here) on the same host but on different port (5061-5064)

var os = require('os');
var sipServerIp='192.168.111.223';
var sipClientIp='192.168.111.107';
var expire = 300;
sipUsers= [{name: '101', password:'101', port: 5061},{name: '102', password:'102', port: 5062},{name: '103', password:'103', port: 5063},{name: '104', password:'104', port: 5064}];
var dialogs = {};

//Functions
function rstring() { return Math.floor(Math.random()*1e6).toString(); }

function sendRegister(sip, digest, name, password) {
    //REGISTER request format
    var rq = {
      method: 'REGISTER',
      uri: 'sip:' + sipServerIp,
      headers: {
        to: {
          uri: 'sip:'+ name + '@' + sipServerIp
        },
        from: {
          uri: 'sip:'+ name +'@' + sipServerIp
        },
        cseq: {
          method: 'REGISTER',
          seq: 1
        },
        'call-id': rstring(),
        contact: [
        {
          uri: 'sip:'+ name +'@' + sipClientIp,
          params: {
            expires: expire
          }
        }
        ],
        'content-length': 0
      }
    };

    //Send Register
    sip.send(rq, function(rs) {
      var context;
      try {
        if (rs.status === 401 || rs.status === 407) {
          // Send DIGEST Authentication
          rq.headers.via.pop();
          rq.headers.cseq.seq++;
          context = {};

          digest.signRequest(context, rq, rs, {
            user: name,
            password: password
          });

          return sip.send(rq, function(rs) {
            if ((200 <= rs.status && rs.status  < 300)) {
              if (false === digest.autheneticateResponse(context, rs)) {
                util.debug('failed to authenticate server');
              }

              console.log('Ext ' + name + ' is registered (DIGEST)');
              return util.debug('Ok');
            }
          });
        }
        else if ((300 > rs.status  && rs.status  >= 200)) {

          console.log('Ext ' + name + ' is registered (NONE)');
          return util.debug('Ok');

        } else {
          return util.debug('failed to register');
        }
      } catch (e) {
        util.debug(e);
        return util.debug(e.stack);
      }
    });
}


//Register each user
sipUsers.forEach( function(sipUser) {

  var sip = require('sip');
  var digest = require('sip/digest');
  var util = require('util');

  sip.start({
      port: sipUser.port || 5060,
      logger: {
        send: function(rq) {
          return util.debug("send\n" + util.inspect(rq, null, null));
        },
        recv: function(rq) {
          return util.debug("recv\n" + util.inspect(rq, null, null));
        }
      }
    }, function(inRq) {
      if(inRq.headers.to.params.tag) { // check if it's an in dialog request
        var id = [inRq.headers['call-id'], inRq.headers.to.params.tag, inRq.headers.from.params.tag].join(':');
      if(dialogs[id])
        dialogs[id](inRq);
      else
        sip.send(sip.makeResponse(inRq, 481, "Call doesn't exists"));
    }
    else {
            sip.send(sip.makeResponse(rq, 405, 'Method not allowed'));
      }
    }
  });

  sendRegister(sip, digest, sipUser.name, sipUser.password);
});

All seems OK, each client dialogs on it own port.
But when one requires to use DIGEST to authenticate, it use the last port (5064) instead of its own.

This sound to be problem of scope, but not sure if it's in your code or mine ... (I'm new to javascript)

Any help will be apreciated.

Thanks

Need to support multiple end-points

Unless I'm mistaken, the way the API is defined, only one end-point can be supported. What I was writing (a simple SBC) required to enable multiple stacks.

I believe the change should be easy (at least this is the way I've modified it in my copy):

exports.start = function(options, callback) {
  var r = exports.create(options, callback);

  exports.send = r.send;
  exports.stop = r.destroy;
}

can be replaced with

exports.start = function(options, callback) {
  return exports.create(options, callback);
}

This way you have sip as factory and toolkit, and the reference returned from start can be used to send and stop.

Might be wrong, my javascript is not that good.

Let me know if you would like a patch.

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.