res_pjsip_nat: Rewrite route set when required.
[asterisk/asterisk.git] / res / res_pjsip_nat.c
index 092ff00..fadefd8 100644 (file)
 #include "asterisk/module.h"
 #include "asterisk/acl.h"
 
-static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata)
+static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
+{
+       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;
+}
+
+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;
+       }
+
+       return -1;
+}
+
+static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg)
 {
-       RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
        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);
+
+               rewrite_uri(rdata, uri);
+
+               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;
+               }
+               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 && (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))) {
-               pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
-
-               pj_cstr(&uri->host, rdata->pkt_info.src_name);
-               uri->port = rdata->pkt_info.src_port;
+       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) {
-               rdata->msg_info.via->rport_param = 0;
+               rdata->msg_info.via->rport_param = rdata->pkt_info.src_port;
        }
 
        return PJ_FALSE;
 }
 
+static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata)
+{
+       pj_bool_t res;
+       struct ast_sip_endpoint *endpoint;
+
+       endpoint = ast_pjsip_rdata_get_endpoint(rdata);
+       res = handle_rx_message(endpoint, rdata);
+       ao2_cleanup(endpoint);
+       return res;
+}
+
 /*! \brief Structure which contains information about a transport */
 struct request_transport_details {
        /*! \brief Type of transport */
@@ -187,6 +266,7 @@ static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
                pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport->external_address));
                if (transport->external_signaling_port) {
                        uri->port = transport->external_signaling_port;
+                       ast_debug(4, "Re-wrote Contact URI port to %d\n", uri->port);
                }
        }
 
@@ -230,6 +310,12 @@ static int nat_incoming_invite_request(struct ast_sip_session *session, struct p
        return 0;
 }
 
+/*! \brief Function called when an INVITE response comes in */
+static void nat_incoming_invite_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
+{
+       handle_rx_message(session->endpoint, rdata);
+}
+
 /*! \brief Function called when an INVITE comes in */
 static void nat_outgoing_invite_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 {
@@ -244,6 +330,7 @@ static struct ast_sip_session_supplement nat_supplement = {
        .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST + 1,
        .incoming_request = nat_incoming_invite_request,
        .outgoing_request = nat_outgoing_invite_request,
+       .incoming_response = nat_incoming_invite_response,
 };
 
 
@@ -256,6 +343,8 @@ static int unload_module(void)
 
 static int load_module(void)
 {
+       CHECK_PJSIP_SESSION_MODULE_LOADED();
+
        if (ast_sip_register_service(&nat_module)) {
                ast_log(LOG_ERROR, "Could not register NAT module for incoming and outgoing requests\n");
                return AST_MODULE_LOAD_FAILURE;
@@ -271,7 +360,8 @@ static int load_module(void)
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP NAT Support",
-               .load = load_module,
-               .unload = unload_module,
-               .load_pri = AST_MODPRI_APP_DEPEND,
-              );
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .load_pri = AST_MODPRI_APP_DEPEND,
+);