res_pjsip_nat: Rewrite route set when required.
authorMark Michelson <mmichelson@digium.com>
Tue, 23 Jun 2015 22:43:31 +0000 (17:43 -0500)
committerJoshua Colp <jcolp@digium.com>
Fri, 26 Jun 2015 14:53:26 +0000 (09:53 -0500)
When performing some provider testing, the rewrite_contact option was
interfering with proper construction of a route set when sending an ACK
after receiving a 200 OK response to an INVITE.

The initial INVITE was sent to address sip:foo. The 200 OK had a Contact
header with URI sip:bar. In addition, the 200 OK had Record-Route
headers for sip:baz and sip:foo, in that order. Since the Record-Route
headers had the lr parameter, the result should have been:

* Set R-URI of the ACK to sip:bar.
* Add Route headers for sip:foo and sip:baz, in that order.

However, the rewrite_contact option resulted in our rewriting the
Contact header on the 200 OK to sip:foo. The result was:

* R-URI remained sip:foo.
* We added Route headers for sip:foo and sip:baz, in that order.

The result was that sip:bar was not indicated in the ACK at all, so the
far end never received our ACK. The call eventually dropped.

The intention of rewrite_contact is to rewrite the most immediate
destination of our SIP request to be the same address on which we
received a request or response. In the case of processing a SIP response
with Record-Route headers, this means that instead of rewriting the
Contact header, we should instead rewrite the bottom-most Record-Route
header. In the case of processing a SIP request with Record-Route
headers, this means we rewrite the top-most Record-route header.
Like when we rewrite the Contact header, we also ensure to update
the dialog's route set if it exists.

ASTERISK-25196 #close
Reported by Mark Michelson

Change-Id: I9702157c3603a2d0bd8a8215ac27564d366b666f

res/res_pjsip.c
res/res_pjsip_nat.c

index 8d5adf6..6a769f0 100644 (file)
                                <configOption name="rewrite_contact">
                                        <synopsis>Allow Contact header to be rewritten with the source IP address-port</synopsis>
                                        <description><para>
                                <configOption name="rewrite_contact">
                                        <synopsis>Allow Contact header to be rewritten with the source IP address-port</synopsis>
                                        <description><para>
-                                               On inbound SIP messages from this endpoint, the Contact header will be changed to have the
-                                               source IP address and port. This option does not affect outbound messages send to this
-                                               endpoint.
+                                               On inbound SIP messages from this endpoint, the Contact header or an appropriate Record-Route
+                                               header will be changed to have the source IP address and port. This option does not affect
+                                               outbound messages sent to this endpoint.
                                        </para></description>
                                </configOption>
                                <configOption name="rtp_ipv6" default="no">
                                        </para></description>
                                </configOption>
                                <configOption name="rtp_ipv6" default="no">
index c717ba2..fadefd8 100644 (file)
 #include "asterisk/module.h"
 #include "asterisk/acl.h"
 
 #include "asterisk/module.h"
 #include "asterisk/acl.h"
 
-static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
 {
 {
-       pjsip_contact_hdr *contact;
+       pj_cstr(&uri->host, rdata->pkt_info.src_name);
+       if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
+               uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
+       } else {
+               uri->transport_param.slen = 0;
+       }
+       uri->port = rdata->pkt_info.src_port;
+}
 
 
-       if (!endpoint) {
-               return PJ_FALSE;
+static int rewrite_route_set(pjsip_rx_data *rdata, pjsip_dialog *dlg)
+{
+       pjsip_rr_hdr *rr = NULL;
+       pjsip_sip_uri *uri;
+
+       if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
+               pjsip_hdr *iter;
+               for (iter = rdata->msg_info.msg->hdr.prev; iter != &rdata->msg_info.msg->hdr; iter = iter->prev) {
+                       if (iter->type == PJSIP_H_RECORD_ROUTE) {
+                               rr = (pjsip_rr_hdr *)iter;
+                               break;
+                       }
+               }
+       } else {
+               rr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_RECORD_ROUTE, NULL);
+       }
+
+       if (rr) {
+               uri = pjsip_uri_get_uri(&rr->name_addr);
+               rewrite_uri(rdata, uri);
+               if (dlg && dlg->route_set.next && !dlg->route_set_frozen) {
+                       pjsip_routing_hdr *route = dlg->route_set.next;
+                       uri = pjsip_uri_get_uri(&route->name_addr);
+                       rewrite_uri(rdata, uri);
+               }
+
+               return 0;
        }
 
        }
 
-       if (endpoint->nat.rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) &&
-               !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
+       return -1;
+}
+
+static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg)
+{
+       pjsip_contact_hdr *contact;
+
+       contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+       if (contact && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
                pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
                pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
-               pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
 
 
-               pj_cstr(&uri->host, rdata->pkt_info.src_name);
-               if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
-                       uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
-               } else {
-                       uri->transport_param.slen = 0;
-               }
-               uri->port = rdata->pkt_info.src_port;
-               ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
-                       (int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);
+               rewrite_uri(rdata, uri);
 
 
-               /* rewrite the session target since it may have already been pulled from the contact header */
-               if (dlg && (!dlg->remote.contact
+               if (dlg && !dlg->route_set_frozen && (!dlg->remote.contact
                        || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
                        dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
                        dlg->target = dlg->remote.contact->uri;
                }
                        || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
                        dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
                        dlg->target = dlg->remote.contact->uri;
                }
+               return 0;
+       }
+
+       return -1;
+}
+
+static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+{
+       pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
+
+       if (!endpoint) {
+               return PJ_FALSE;
+       }
+
+       if (endpoint->nat.rewrite_contact) {
+               /* rewrite_contact is intended to ensure we send requests/responses to
+                * a routeable address when NAT is involved. The URI that dictates where
+                * we send requests/responses can be determined either by Record-Route
+                * headers or by the Contact header if no Record-Route headers are present.
+                * We therefore will attempt to rewrite a Record-Route header first, and if
+                * none are present, we fall back to rewriting the Contact header instead.
+                */
+               if (rewrite_route_set(rdata, dlg)) {
+                       rewrite_contact(rdata, dlg);
+               }
        }
 
        if (endpoint->nat.force_rport) {
        }
 
        if (endpoint->nat.force_rport) {