The SIP redirect mechanism is a simple and straight forward one – the originally contacted destination indicates, via a 3xx reply, that a different set of destinations should be contacted. The SIP redirect is mainly used for calls (for INVITE requests), even if the RFC3261 does not limit it to that.
Usage cases
The primary purpose of the SIP redirect mechanism is to offer alternative routing destination. A SIP server will decline the incoming INVITE by sending a 3xx reply that contains one or more SIP URI (as Contact header payload) to be used as alternative destinations. Of course, there is not obligation for the SIP client to follow the new SIP URIs pointed by the received 3xx reply. Blindly following such redirect indication can actually by a security breach, if the source of the 3xx replies is not a trusted one.
But being such a simple mechanism, which requires a minimal effort from the SIP server side (just a reply, no transaction & dialog state required), makes the SIP redirect a great tool more other purposes rather than providing alternative SIP destinations.
The 3xx mechanism is widely used also for performing queries (fetch information) against other SIP based services, like LRN / CID / CNAM / e911 lookups. These are typically lookups to be done during a call setup (during the initial INVITE), without requiring any further involvement of the queries server.
Using OpenSIPS as a redirect server
OpenSIPS can implement a SIP redirect server is a very simple way.
If you want to return alternative SIP URI destinations:
if ($rm=="INVITE") { /* add the redirect destinations as branches */ $branch = "sip:batman@gotham.com"; $branch = $avp(my_custom_uri); /* sending a 3xx reply will automatically push all * existing branches as Contact URIs */ send_reply("302","Moved Temporarily"); exit; }
Of course, the SIP URIs may be computed or loaded based on whatever logic fits your service.
If you want to return only some non-SIP information:
if ($rm=="INVITE") { /* add the info as custom header in the reply to send */ append_to_reply('X-cname: {"firstname":"John","lastname":"Doe"}\r\n'); /* simply send the reply */ send_reply("302","Moved Temporarily"); exit; }
Handling redirects with OpenSIPS
Using the uac_redirect module, OpenSIPS can also handle and follow the 3xx redirect indications, as a SIP user client. By using a failure route, if the INVITE transaction terminates with a 3xx reply, we will extract all the contact URIs and do serial forking to all these new URIs:
failure_route[invites] { .... if (t_check_status("30[12]")) { /* print all the Contact headers from the 3xx reply */ xlog("Received contact headers $(<reply>hdr(Contact)[*])\n"); /* extract all the URIs from all the Contact headers * from all the 3xx replies received by this transaction ; * push the URIs as branches */ if (get_redirects("*")) /* do serial forking to all the URIs */ t_relay(); exit; } .... }
Note that the get_redirect() function is able to deal with multiple 3xx replies. If you did parallel forking for your INVITE to multiple destination, it is possible to receive 3xx replies on more than one branch (of the fork). So, when the transaction is completed, you may end up with multiple 3xx to inspect. Well, the get_redirect() function is smart and it is able to collect the redirect URIs from all Contact headers (of the same reply) and from all the 3xx replies received for your INVITE (via different branches of parallel forking).
Realtime redirect handling with OpenSIPS
In parallel forking scenarios, the above approach has a big limitation – in order to follow a 3xx reply, you need to wait for the whole transaction to complete – for all branches of the forking to complete.
So, if I’m forking to destinations A and B, and if A responds with the 3xx reply, I will not be able to follow that redirect until the B branch terminates also – and this may take a while, maybe translating even a ringing timeout.
Shortly said, the classical approach on handling redirects is not REALTIME, as the redirects cannot be followed as they are received.
Even more, the inability to follow the redirects as they are received may change the result of the call setup! Let’s go back to the previously example: parallel forking to A and B with A doing 3xx redirect to C. If we do not follow the redirect to C on the spot, only B will remain in ringing state; and C will start ringing only if B declines the call, so basically C will have less chances to answer the call. If we do realtime redirect, as soon as the 3xx is received from A, the B and C destinations will ring in the same time, giving them equal chances to pick up the call.
Starting with OpenSIPS 3.0, using the support for dynamic branch injection, the 3xx redirects can be handled in realtime (even in parallel forking scenarios).
Instead of using the failure route and wait for the transaction to complete before following any redirect indication, we will use the onreply route to inspect the 3xx reply as they are received (in realtime). For each redirect SIP URI we get, we will dynamically inject a new branch in the ongoing INVITE transaction, making the new destination/branch to ring in parallel with the existing ones:
onreply_route[invite] { if (t_check_status("30[12]") ) { /* iterate through all the received Contact URIs * using var(n) as iterator */ $var(n) = 0; while( $(ct.fields(uri)[$var(n)])!= NULL) { /* extract the current URI from the reply and * push it as a new branch */ $branch = $(ct.fields(uri)[$var(n)]); $var(n) = $var(n) + 1; } /* Take all the new branches we created and inject them * into the ongoing INVITE transaction */ t_inject_branches("msg"); } }
If we look from the security perspective, handling each 3xx reply by its own, gives us better control over the URIs we should redirect to. Before handling a 3xx reply, we can decide if the source for the reply is to be trusted (based on IP or any other criteria). Also, by explicitly iterating through the contacts gives us the possibility to check and validate them, one by one, to decide if they are raising any security concerns if followed.