[Asterisk-code-review] res_pjsip_nat: Restore original contact for REGISTER responses (...asterisk[13])

George Joseph asteriskteam at digium.com
Fri Aug 30 09:30:05 CDT 2019


George Joseph has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/12824


Change subject: res_pjsip_nat: Restore original contact for REGISTER responses
......................................................................

res_pjsip_nat: Restore original contact for REGISTER responses

RFC3261 Section 10 "Registrations", specifically paragraph
"10.2.4: Refreshing Bindings", states that a user agent compares
each contact address (in a 200 REGISTER response) to see if it
created the contact.  If the Asterisk endpoint has the
rewrite_contact option set however, the contact host and port sent
back in the 200 response will be the rewritten one and not the
one sent by the user agent.  This prevents the user agent from
matching its own contact.  Some user agents get very upset when
this happens and will not consider the registration successful.
While this is rare, it is acceptable behavior especially if more
than 1 user agent is allowed to register to a single endpoint/aor.

This commit updates res_pjsip_nat (where rewrite_contact is
implemented) to store the original incoming Contact header in
the request rdata before rewriting it, and to restore the original
host and port to the Contact header in the outgoing response.

This is only done if the request is a REGISTER and rewrite_contact
is enabled.

ASTERISK-28502
Reported-by: Ross Beer

Change-Id: Idc263ad2d2d7bd8faa047e5804d96a5fe1cd282e
---
M res/res_pjsip_nat.c
1 file changed, 71 insertions(+), 10 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/24/12824/1

diff --git a/res/res_pjsip_nat.c b/res/res_pjsip_nat.c
index a0161d7..ea143c2 100644
--- a/res/res_pjsip_nat.c
+++ b/res/res_pjsip_nat.c
@@ -32,6 +32,21 @@
 #include "asterisk/module.h"
 #include "asterisk/acl.h"
 
+static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata);
+static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata);
+
+static pjsip_module nat_module = {
+	.name = { "NAT", 3 },
+	.id = -1,
+	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,
+	.on_rx_request = nat_on_rx_message,
+	.on_rx_response = nat_on_rx_message,
+	.on_tx_request = nat_on_tx_message,
+	.on_tx_response = nat_on_tx_message,
+};
+
+#define MOD_DATA_ORIGINAL_CONTACT "original_contact"
+
 static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
 {
 	pj_cstr(&uri->host, rdata->pkt_info.src_name);
@@ -145,6 +160,18 @@
 	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);
 
+		/*
+		 * If the incoming request is a REGISTER, we need to preserve the original
+		 * contact to use on the response.
+		 */
+		if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG
+			&& pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) == 0) {
+			pjsip_contact_hdr *original_contact = pjsip_hdr_clone(rdata->tp_info.pool, contact);
+
+			ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data,
+				nat_module.id, MOD_DATA_ORIGINAL_CONTACT, original_contact);
+		}
+
 		rewrite_uri(rdata, uri);
 
 		if (dlg && pj_list_empty(&dlg->route_set) && (!dlg->remote.contact
@@ -166,6 +193,7 @@
 		return PJ_FALSE;
 	}
 
+	rdata->endpt_info.mod_data[nat_module.id] = NULL;
 	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
@@ -174,6 +202,7 @@
 		 * 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);
 		}
@@ -264,6 +293,35 @@
 	return 0;
 }
 
+static void on_stateful_response(struct ast_sip_endpoint *endpoint,
+	struct ast_sip_contact *remote_contact, const pjsip_rx_data *rdata, pjsip_tx_data *tdata)
+{
+	pjsip_contact_hdr *original_contact = ast_sip_mod_data_get(rdata->endpt_info.mod_data,
+		nat_module.id, MOD_DATA_ORIGINAL_CONTACT);
+	pjsip_contact_hdr *rewritten_contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
+	pjsip_via_hdr *via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+
+	/*
+	 * If we rewrote the contact on the incoming request and saved the original,
+	 * we need to restore the original host and port.  Since there could be
+	 * multiple Contact headers in the case where more than 1 client is registered
+	 * to the same AOR, we need to make sure we restore this client's host
+	 * and port ONLY to this client's Contact.
+	 */
+	while (original_contact && via && rewritten_contact) {
+		pjsip_sip_uri *original_contact_uri = pjsip_uri_get_uri(original_contact->uri);
+		pjsip_sip_uri *rewritten_contact_uri = pjsip_uri_get_uri(rewritten_contact->uri);
+
+		if (pj_strcmp(&rewritten_contact_uri->host, &via->sent_by.host) == 0
+			&& rewritten_contact_uri->port == via->sent_by.port) {
+			pj_strdup(tdata->pool, &rewritten_contact_uri->host, &original_contact_uri->host);
+			rewritten_contact_uri->port = original_contact_uri->port;
+		}
+
+		rewritten_contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, rewritten_contact->next);
+	}
+}
+
 static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
 {
 	RAII_VAR(struct ao2_container *, transport_states, NULL, ao2_cleanup);
@@ -364,16 +422,6 @@
 	return PJ_SUCCESS;
 }
 
-static pjsip_module nat_module = {
-	.name = { "NAT", 3 },
-	.id = -1,
-	.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,
-	.on_rx_request = nat_on_rx_message,
-	.on_rx_response = nat_on_rx_message,
-	.on_tx_request = nat_on_tx_message,
-	.on_tx_response = nat_on_tx_message,
-};
-
 /*! \brief Function called when an INVITE goes out */
 static int nat_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
@@ -407,9 +455,16 @@
 	.incoming_response = nat_incoming_invite_response,
 };
 
+/*! \brief Supplement for adding NAT functionality to stateful responses */
+static struct ast_sip_supplement nat_stateful_supplement = {
+	.method = "REGISTER",
+	.priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST + 1,
+	.outgoing_stateful_response = on_stateful_response,
+};
 
 static int unload_module(void)
 {
+	ast_sip_unregister_supplement(&nat_stateful_supplement);
 	ast_sip_session_unregister_supplement(&nat_supplement);
 	ast_sip_unregister_service(&nat_module);
 	return 0;
@@ -430,6 +485,12 @@
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
+	if (ast_sip_register_supplement(&nat_stateful_supplement)) {
+		ast_log(LOG_ERROR, "Could not register NAT supplement for stateful responses\n");
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/12824
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 13
Gerrit-Change-Id: Idc263ad2d2d7bd8faa047e5804d96a5fe1cd282e
Gerrit-Change-Number: 12824
Gerrit-PatchSet: 1
Gerrit-Owner: George Joseph <gjoseph at digium.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20190830/a077ae90/attachment.html>


More information about the asterisk-code-review mailing list