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
a new "x-ast-orig-host" URI parameter before rewriting it, and to
restore the original host and port to the Contact headers in the
outgoing response.

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

pjsip_message_filter was also updated to ensure that if a request
comes in with any existing x-ast-* URI parameters, we remove them
so they don't conflict.  Asterisk will never send a request
with those headers in it but someone might just decide to add them
to a request they craft and send to Asterisk.

NOTE: If a device changes its contact address and registers again,
it's a NEW registration.  If the device didn't unregister the
original registration then all existing behavior based
on aor/remove_existing and aor/max_contacts apply.

ASTERISK-28502
Reported-by: Ross Beer

Change-Id: Idc263ad2d2d7bd8faa047e5804d96a5fe1cd282e
This commit is contained in:
George Joseph 2019-08-25 20:20:13 -06:00
parent 7f2bbff48c
commit 63b8664bfa
2 changed files with 122 additions and 2 deletions

View File

@ -400,6 +400,40 @@ static void print_uri_debug(enum uri_type ut, pjsip_rx_data *rdata, pjsip_hdr *h
#endif
}
/*!
* /internal
*
* We want to make sure that any incoming requests don't already
* have x-ast-* parameters in any URIs or we may get confused
* if symmetric transport (x-ast-txp) or rewrite_contact (x-ast-orig-host)
* is used later on.
*/
static void remove_x_ast_params(pjsip_uri *header_uri){
pjsip_sip_uri *uri;
pjsip_param *param;
if (!header_uri) {
return;
}
uri = pjsip_uri_get_uri(header_uri);
if (!uri) {
return;
}
param = uri->other_param.next;
while (param != &uri->other_param) {
/* We need to save off 'next' because pj_list_erase will remove it */
pjsip_param *next = param->next;
if (pj_strncmp2(&param->name, "x-ast-", 6) == 0) {
pj_list_erase(param);
}
param = next;
}
}
static pj_bool_t on_rx_process_uris(pjsip_rx_data *rdata)
{
pjsip_contact_hdr *contact = NULL;
@ -414,6 +448,7 @@ static pj_bool_t on_rx_process_uris(pjsip_rx_data *rdata)
PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
return PJ_TRUE;
}
remove_x_ast_params(rdata->msg_info.msg->line.req.uri);
if (!is_sip_uri(rdata->msg_info.from->uri)) {
print_uri_debug(URI_TYPE_FROM, rdata, (pjsip_hdr *)rdata->msg_info.from);
@ -421,6 +456,7 @@ static pj_bool_t on_rx_process_uris(pjsip_rx_data *rdata)
PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
return PJ_TRUE;
}
remove_x_ast_params(rdata->msg_info.from->uri);
if (!is_sip_uri(rdata->msg_info.to->uri)) {
print_uri_debug(URI_TYPE_TO, rdata, (pjsip_hdr *)rdata->msg_info.to);
@ -428,7 +464,7 @@ static pj_bool_t on_rx_process_uris(pjsip_rx_data *rdata)
PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
return PJ_TRUE;
}
remove_x_ast_params(rdata->msg_info.to->uri);
contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(
rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
@ -448,6 +484,8 @@ static pj_bool_t on_rx_process_uris(pjsip_rx_data *rdata)
PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
return PJ_TRUE;
}
remove_x_ast_params(contact->uri);
contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(
rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next);
}

View File

@ -32,8 +32,43 @@
#include "asterisk/module.h"
#include "asterisk/acl.h"
/*! URI parameter for original host/port */
#define AST_SIP_X_AST_ORIG_HOST "x-ast-orig-host"
#define AST_SIP_X_AST_ORIG_HOST_LEN 15
static void save_orig_contact_host(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
{
pjsip_param *x_orig_host;
pj_str_t p_value;
#define COLON_LEN 1
#define MAX_PORT_LEN 5
if (rdata->msg_info.msg->type != PJSIP_REQUEST_MSG ||
rdata->msg_info.msg->line.req.method.id != PJSIP_REGISTER_METHOD) {
return;
}
ast_debug(1, "Saving contact '%.*s:%d'\n",
(int)uri->host.slen, uri->host.ptr, uri->port);
x_orig_host = PJ_POOL_ALLOC_T(rdata->tp_info.pool, pjsip_param);
x_orig_host->name = pj_strdup3(rdata->tp_info.pool, AST_SIP_X_AST_ORIG_HOST);
p_value.slen = pj_strlen(&uri->host) + COLON_LEN + MAX_PORT_LEN;
p_value.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, p_value.slen + 1);
p_value.slen = snprintf(p_value.ptr, p_value.slen + 1, "%.*s:%d", (int)uri->host.slen, uri->host.ptr, uri->port);
pj_strassign(&x_orig_host->value, &p_value);
pj_list_insert_before(&uri->other_param, x_orig_host);
return;
}
static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
{
if (pj_strcmp2(&uri->host, rdata->pkt_info.src_name) != 0) {
save_orig_contact_host(rdata, uri);
}
pj_cstr(&uri->host, rdata->pkt_info.src_name);
uri->port = rdata->pkt_info.src_port;
if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
@ -264,7 +299,44 @@ static int nat_invoke_hook(void *obj, void *arg, int flags)
return 0;
}
static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
static void restore_orig_contact_host(pjsip_tx_data *tdata)
{
pjsip_contact_hdr *contact;
if (tdata->msg->type != PJSIP_RESPONSE_MSG) {
return;
}
contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
while (contact) {
pjsip_sip_uri *contact_uri = pjsip_uri_get_uri(contact->uri);
pj_str_t x_name = { AST_SIP_X_AST_ORIG_HOST, AST_SIP_X_AST_ORIG_HOST_LEN };
pjsip_param *x_orig_host = pjsip_param_find(&contact_uri->other_param, &x_name);
if (x_orig_host) {
char host_port[x_orig_host->value.slen + 1];
char *sep;
ast_debug(1, "Restoring contact %.*s:%d to %.*s\n", (int)contact_uri->host.slen,
contact_uri->host.ptr, contact_uri->port,
(int)x_orig_host->value.slen, x_orig_host->value.ptr);
strncpy(host_port, x_orig_host->value.ptr, x_orig_host->value.slen);
host_port[x_orig_host->value.slen] = '\0';
sep = strchr(host_port, ':');
if (sep) {
*sep = '\0';
sep++;
pj_strdup2(tdata->pool, &contact_uri->host, host_port);
contact_uri->port = strtol(sep, NULL, 10);
}
pj_list_erase(x_orig_host);
}
contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, contact->next);
}
}
static pj_status_t process_nat(pjsip_tx_data *tdata)
{
RAII_VAR(struct ao2_container *, transport_states, NULL, ao2_cleanup);
RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
@ -364,6 +436,16 @@ static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
return PJ_SUCCESS;
}
static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata) {
pj_status_t rc;
rc = process_nat(tdata);
restore_orig_contact_host(tdata);
return rc;
}
static pjsip_module nat_module = {
.name = { "NAT", 3 },
.id = -1,