<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/12824">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_nat: Restore original contact for REGISTER responses<br><br>RFC3261 Section 10 "Registrations", specifically paragraph<br>"10.2.4: Refreshing Bindings", states that a user agent compares<br>each contact address (in a 200 REGISTER response) to see if it<br>created the contact.  If the Asterisk endpoint has the<br>rewrite_contact option set however, the contact host and port sent<br>back in the 200 response will be the rewritten one and not the<br>one sent by the user agent.  This prevents the user agent from<br>matching its own contact.  Some user agents get very upset when<br>this happens and will not consider the registration successful.<br>While this is rare, it is acceptable behavior especially if more<br>than 1 user agent is allowed to register to a single endpoint/aor.<br><br>This commit updates res_pjsip_nat (where rewrite_contact is<br>implemented) to store the original incoming Contact header in<br>the request rdata before rewriting it, and to restore the original<br>host and port to the Contact header in the outgoing response.<br><br>This is only done if the request is a REGISTER and rewrite_contact<br>is enabled.<br><br>ASTERISK-28502<br>Reported-by: Ross Beer<br><br>Change-Id: Idc263ad2d2d7bd8faa047e5804d96a5fe1cd282e<br>---<br>M res/res_pjsip_nat.c<br>1 file changed, 71 insertions(+), 10 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/24/12824/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/res/res_pjsip_nat.c b/res/res_pjsip_nat.c</span><br><span>index a0161d7..ea143c2 100644</span><br><span>--- a/res/res_pjsip_nat.c</span><br><span>+++ b/res/res_pjsip_nat.c</span><br><span>@@ -32,6 +32,21 @@</span><br><span> #include "asterisk/module.h"</span><br><span> #include "asterisk/acl.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata);</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static pjsip_module nat_module = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .name = { "NAT", 3 },</span><br><span style="color: hsl(120, 100%, 40%);">+       .id = -1,</span><br><span style="color: hsl(120, 100%, 40%);">+     .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_rx_request = nat_on_rx_message,</span><br><span style="color: hsl(120, 100%, 40%);">+   .on_rx_response = nat_on_rx_message,</span><br><span style="color: hsl(120, 100%, 40%);">+  .on_tx_request = nat_on_tx_message,</span><br><span style="color: hsl(120, 100%, 40%);">+   .on_tx_response = nat_on_tx_message,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define MOD_DATA_ORIGINAL_CONTACT "original_contact"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)</span><br><span> {</span><br><span>       pj_cstr(&uri->host, rdata->pkt_info.src_name);</span><br><span>@@ -145,6 +160,18 @@</span><br><span>      if (contact && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {</span><br><span>               pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+          /*</span><br><span style="color: hsl(120, 100%, 40%);">+             * If the incoming request is a REGISTER, we need to preserve the original</span><br><span style="color: hsl(120, 100%, 40%);">+             * contact to use on the response.</span><br><span style="color: hsl(120, 100%, 40%);">+             */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG</span><br><span style="color: hsl(120, 100%, 40%);">+                      && pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      pjsip_contact_hdr *original_contact = pjsip_hdr_clone(rdata->tp_info.pool, contact);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data,</span><br><span style="color: hsl(120, 100%, 40%);">+                           nat_module.id, MOD_DATA_ORIGINAL_CONTACT, original_contact);</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>          rewrite_uri(rdata, uri);</span><br><span> </span><br><span>                 if (dlg && pj_list_empty(&dlg->route_set) && (!dlg->remote.contact</span><br><span>@@ -166,6 +193,7 @@</span><br><span>           return PJ_FALSE;</span><br><span>     }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ rdata->endpt_info.mod_data[nat_module.id] = NULL;</span><br><span>         if (endpoint->nat.rewrite_contact) {</span><br><span>              /* rewrite_contact is intended to ensure we send requests/responses to</span><br><span>                * a routeable address when NAT is involved. The URI that dictates where</span><br><span>@@ -174,6 +202,7 @@</span><br><span>                * We therefore will attempt to rewrite a Record-Route header first, and if</span><br><span>           * none are present, we fall back to rewriting the Contact header instead.</span><br><span>            */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>                if (rewrite_route_set(rdata, dlg)) {</span><br><span>                         rewrite_contact(rdata, dlg);</span><br><span>                 }</span><br><span>@@ -264,6 +293,35 @@</span><br><span>     return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void on_stateful_response(struct ast_sip_endpoint *endpoint,</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_sip_contact *remote_contact, const pjsip_rx_data *rdata, pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_contact_hdr *original_contact = ast_sip_mod_data_get(rdata->endpt_info.mod_data,</span><br><span style="color: hsl(120, 100%, 40%);">+             nat_module.id, MOD_DATA_ORIGINAL_CONTACT);</span><br><span style="color: hsl(120, 100%, 40%);">+    pjsip_contact_hdr *rewritten_contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+      pjsip_via_hdr *via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * If we rewrote the contact on the incoming request and saved the original,</span><br><span style="color: hsl(120, 100%, 40%);">+   * we need to restore the original host and port.  Since there could be</span><br><span style="color: hsl(120, 100%, 40%);">+        * multiple Contact headers in the case where more than 1 client is registered</span><br><span style="color: hsl(120, 100%, 40%);">+         * to the same AOR, we need to make sure we restore this client's host</span><br><span style="color: hsl(120, 100%, 40%);">+     * and port ONLY to this client's Contact.</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+   while (original_contact && via && rewritten_contact) {</span><br><span style="color: hsl(120, 100%, 40%);">+                pjsip_sip_uri *original_contact_uri = pjsip_uri_get_uri(original_contact->uri);</span><br><span style="color: hsl(120, 100%, 40%);">+            pjsip_sip_uri *rewritten_contact_uri = pjsip_uri_get_uri(rewritten_contact->uri);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                if (pj_strcmp(&rewritten_contact_uri->host, &via->sent_by.host) == 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    && rewritten_contact_uri->port == via->sent_by.port) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  pj_strdup(tdata->pool, &rewritten_contact_uri->host, &original_contact_uri->host);</span><br><span style="color: hsl(120, 100%, 40%);">+                   rewritten_contact_uri->port = original_contact_uri->port;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           rewritten_contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, rewritten_contact->next);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)</span><br><span> {</span><br><span>    RAII_VAR(struct ao2_container *, transport_states, NULL, ao2_cleanup);</span><br><span>@@ -364,16 +422,6 @@</span><br><span>        return PJ_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static pjsip_module nat_module = {</span><br><span style="color: hsl(0, 100%, 40%);">-   .name = { "NAT", 3 },</span><br><span style="color: hsl(0, 100%, 40%);">- .id = -1,</span><br><span style="color: hsl(0, 100%, 40%);">-       .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,</span><br><span style="color: hsl(0, 100%, 40%);">-   .on_rx_request = nat_on_rx_message,</span><br><span style="color: hsl(0, 100%, 40%);">-     .on_rx_response = nat_on_rx_message,</span><br><span style="color: hsl(0, 100%, 40%);">-    .on_tx_request = nat_on_tx_message,</span><br><span style="color: hsl(0, 100%, 40%);">-     .on_tx_response = nat_on_tx_message,</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*! \brief Function called when an INVITE goes out */</span><br><span> static int nat_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)</span><br><span> {</span><br><span>@@ -407,9 +455,16 @@</span><br><span>  .incoming_response = nat_incoming_invite_response,</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Supplement for adding NAT functionality to stateful responses */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_sip_supplement nat_stateful_supplement = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .method = "REGISTER",</span><br><span style="color: hsl(120, 100%, 40%);">+       .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST + 1,</span><br><span style="color: hsl(120, 100%, 40%);">+    .outgoing_stateful_response = on_stateful_response,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span> </span><br><span> static int unload_module(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_sip_unregister_supplement(&nat_stateful_supplement);</span><br><span>         ast_sip_session_unregister_supplement(&nat_supplement);</span><br><span>  ast_sip_unregister_service(&nat_module);</span><br><span>         return 0;</span><br><span>@@ -430,6 +485,12 @@</span><br><span>             return AST_MODULE_LOAD_DECLINE;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_sip_register_supplement(&nat_stateful_supplement)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "Could not register NAT supplement for stateful responses\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           unload_module();</span><br><span style="color: hsl(120, 100%, 40%);">+              return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  return AST_MODULE_LOAD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/12824">change 12824</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/12824"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-Change-Id: Idc263ad2d2d7bd8faa047e5804d96a5fe1cd282e </div>
<div style="display:none"> Gerrit-Change-Number: 12824 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>