Search code examples
node.jssipfreeswitchsip-server

SIP Redirect via Proxy (SIP.js)


I'm trying to create a minimal SIP Proxy that serves one purpose: redirects requests to another domain. The catch is the domain I'm redirecting to requires authorization so I assume I need to rewrite some SIP attributes since SIP authorization is based partly on the domain name of the destination.

I've tried issuing a 302 redirect as well as simply proxying and changing the values of each SIP request but none seem to quit do the trick. I'm using a node.js library (sip.js) and have tried the redirect and proxy modules (https://github.com/kirm/sip.js/blob/master/doc/api.markdown).

Any ideas exactly how I need to modify the SIP data to redirect requests to another domain and enable authentication to take place against that other domain?


Solution

  • Below is a basic node script I got working with my own SIP server. You'll need to replace the credentials and IP address fro your own testing.

    The proxy script does not send a redirect response to the client but instead initiates a new transaction to the server on the client's behalf. A SIP server operating in this mode is more correctly called a Back-to-Back User Agent (B2BUA). I haven't added all the functionality needed, such as matching up and passing responses back to the original client; there is a fair bit of work involved in that.

    var sip = require('sip');
    var digest = require('sip/digest');
    var util = require('util');
    var os = require('os');
    var proxy = require('sip/proxy');
    
    var registry = {
      'user': { user: "user", password: "password", realm: "sipserver.com"},
    };
    
    function rstring() { return Math.floor(Math.random()*1e6).toString(); }
    
    sip.start({
      address: "192.168.33.116", // If the IP is not specified here the proxy uses a hostname in the Via header which will causes an issue if it's not fully qualified.
      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 === 'INVITE') {  
    
           proxy.send(sip.makeResponse(rq, 100, 'Trying'));
    
          //looking up user info
          var username = sip.parseUri(rq.headers.to.uri).user;    
          var creds = registry[username];
    
          if(!creds) {  
            proxy.send(sip.makeResponse(rq, 404, 'User not found'));
          }
          else {
            proxy.send(rq, function(rs) {
    
                if(rs.status === 401) {
    
                    // Update the original request so that it's not treated as a duplicate.
                    rq.headers['cseq'].seq++;
                    rq.headers.via.shift ();
                    rq.headers['call-id'] = rstring();
    
                    digest.signRequest(creds, rq, rs, creds);
    
                    proxy.send(rq);
                }
            });
          }
        }
        else {
          proxy.send(sip.makeResponse(rq, 405, 'Method Not Allowed'));
        }
      } catch(e) {
        util.debug(e);
        util.debug(e.stack);
    
       proxy.send(sip.makeResponse(rq, 500, "Server Internal Error"));
      }
    });