res_pjsip_nat.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2013, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*** MODULEINFO
00020    <depend>pjproject</depend>
00021    <depend>res_pjsip</depend>
00022    <support_level>core</support_level>
00023  ***/
00024 
00025 #include "asterisk.h"
00026 
00027 #include <pjsip.h>
00028 #include <pjsip_ua.h>
00029 
00030 #include "asterisk/res_pjsip.h"
00031 #include "asterisk/res_pjsip_session.h"
00032 #include "asterisk/module.h"
00033 #include "asterisk/acl.h"
00034 
00035 static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
00036 {
00037    pjsip_contact_hdr *contact;
00038 
00039    if (!endpoint) {
00040       return PJ_FALSE;
00041    }
00042 
00043    if (endpoint->nat.rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) &&
00044       !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
00045       pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
00046       pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
00047 
00048       pj_cstr(&uri->host, rdata->pkt_info.src_name);
00049       if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
00050          uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
00051       } else {
00052          uri->transport_param.slen = 0;
00053       }
00054       uri->port = rdata->pkt_info.src_port;
00055       ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
00056          (int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);
00057 
00058       /* rewrite the session target since it may have already been pulled from the contact header */
00059       if (dlg && (!dlg->remote.contact
00060          || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
00061          dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
00062          dlg->target = dlg->remote.contact->uri;
00063       }
00064    }
00065 
00066    if (endpoint->nat.force_rport) {
00067       rdata->msg_info.via->rport_param = rdata->pkt_info.src_port;
00068    }
00069 
00070    return PJ_FALSE;
00071 }
00072 
00073 static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata)
00074 {
00075    pj_bool_t res;
00076    struct ast_sip_endpoint *endpoint;
00077 
00078    endpoint = ast_pjsip_rdata_get_endpoint(rdata);
00079    res = handle_rx_message(endpoint, rdata);
00080    ao2_cleanup(endpoint);
00081    return res;
00082 }
00083 
00084 /*! \brief Structure which contains information about a transport */
00085 struct request_transport_details {
00086    /*! \brief Type of transport */
00087    enum ast_transport type;
00088    /*! \brief Potential pointer to the transport itself, if UDP */
00089    pjsip_transport *transport;
00090    /*! \brief Potential pointer to the transport factory itself, if TCP/TLS */
00091    pjsip_tpfactory *factory;
00092    /*! \brief Local address for transport */
00093    pj_str_t local_address;
00094    /*! \brief Local port for transport */
00095    int local_port;
00096 };
00097 
00098 /*! \brief Callback function for finding the transport the request is going out on */
00099 static int find_transport_in_use(void *obj, void *arg, int flags)
00100 {
00101    struct ast_sip_transport *transport = obj;
00102    struct request_transport_details *details = arg;
00103 
00104    /* If an explicit transport or factory matches then this is what is in use, if we are unavailable
00105     * to compare based on that we make sure that the type is the same and the source IP address/port are the same
00106     */
00107    if ((details->transport && details->transport == transport->state->transport) ||
00108       (details->factory && details->factory == transport->state->factory) ||
00109       ((details->type == transport->type) && (transport->state->factory) &&
00110          !pj_strcmp(&transport->state->factory->addr_name.host, &details->local_address) &&
00111          transport->state->factory->addr_name.port == details->local_port)) {
00112       return CMP_MATCH | CMP_STOP;
00113    }
00114 
00115    return 0;
00116 }
00117 
00118 /*! \brief Helper function which returns the SIP URI of a Contact header */
00119 static pjsip_sip_uri *nat_get_contact_sip_uri(pjsip_tx_data *tdata)
00120 {
00121    pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
00122 
00123    if (!contact || (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
00124       return NULL;
00125    }
00126 
00127    return pjsip_uri_get_uri(contact->uri);
00128 }
00129 
00130 /*! \brief Structure which contains hook details */
00131 struct nat_hook_details {
00132    /*! \brief Outgoing message itself */
00133    pjsip_tx_data *tdata;
00134    /*! \brief Chosen transport */
00135    struct ast_sip_transport *transport;
00136 };
00137 
00138 /*! \brief Callback function for invoking hooks */
00139 static int nat_invoke_hook(void *obj, void *arg, int flags)
00140 {
00141    struct ast_sip_nat_hook *hook = obj;
00142    struct nat_hook_details *details = arg;
00143 
00144    if (hook->outgoing_external_message) {
00145       hook->outgoing_external_message(details->tdata, details->transport);
00146    }
00147 
00148    return 0;
00149 }
00150 
00151 static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
00152 {
00153    RAII_VAR(struct ao2_container *, transports, NULL, ao2_cleanup);
00154    RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
00155    struct request_transport_details details = { 0, };
00156    pjsip_via_hdr *via = NULL;
00157    struct ast_sockaddr addr = { { 0, } };
00158    pjsip_sip_uri *uri = NULL;
00159    RAII_VAR(struct ao2_container *, hooks, NULL, ao2_cleanup);
00160 
00161    /* If a transport selector is in use we know the transport or factory, so explicitly find it */
00162    if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) {
00163       details.transport = tdata->tp_sel.u.transport;
00164    } else if (tdata->tp_sel.type == PJSIP_TPSELECTOR_LISTENER) {
00165       details.factory = tdata->tp_sel.u.listener;
00166    } else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP || tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
00167       /* Connectionless uses the same transport for all requests */
00168       details.type = AST_TRANSPORT_UDP;
00169       details.transport = tdata->tp_info.transport;
00170    } else {
00171       if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TCP) {
00172          details.type = AST_TRANSPORT_TCP;
00173       } else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TLS) {
00174          details.type = AST_TRANSPORT_TLS;
00175       } else {
00176          /* Unknown transport type, we can't map and thus can't apply NAT changes */
00177          return PJ_SUCCESS;
00178       }
00179 
00180       if ((uri = nat_get_contact_sip_uri(tdata))) {
00181          details.local_address = uri->host;
00182          details.local_port = uri->port;
00183       } else if ((tdata->msg->type == PJSIP_REQUEST_MSG) &&
00184          (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) {
00185          details.local_address = via->sent_by.host;
00186          details.local_port = via->sent_by.port;
00187       } else {
00188          return PJ_SUCCESS;
00189       }
00190 
00191       if (!details.local_port) {
00192          details.local_port = (details.type == AST_TRANSPORT_TLS) ? 5061 : 5060;
00193       }
00194    }
00195 
00196    if (!(transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL)) ||
00197       !(transport = ao2_callback(transports, 0, find_transport_in_use, &details)) || !transport->localnet ||
00198       ast_sockaddr_isnull(&transport->external_address)) {
00199       return PJ_SUCCESS;
00200    }
00201 
00202    ast_sockaddr_parse(&addr, tdata->tp_info.dst_name, PARSE_PORT_FORBID);
00203    ast_sockaddr_set_port(&addr, tdata->tp_info.dst_port);
00204 
00205    /* See if where we are sending this request is local or not, and if not that we can get a Contact URI to modify */
00206    if (ast_apply_ha(transport->localnet, &addr) != AST_SENSE_ALLOW) {
00207       return PJ_SUCCESS;
00208    }
00209 
00210    /* Update the contact header with the external address */
00211    if (uri || (uri = nat_get_contact_sip_uri(tdata))) {
00212       pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport->external_address));
00213       if (transport->external_signaling_port) {
00214          uri->port = transport->external_signaling_port;
00215          ast_debug(4, "Re-wrote Contact URI port to %d\n", uri->port);
00216       }
00217    }
00218 
00219    /* Update the via header if relevant */
00220    if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) {
00221       pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport->external_address));
00222       if (transport->external_signaling_port) {
00223          via->sent_by.port = transport->external_signaling_port;
00224       }
00225    }
00226 
00227    /* Invoke any additional hooks that may be registered */
00228    if ((hooks = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "nat_hook", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
00229       struct nat_hook_details hook_details = {
00230          .tdata = tdata,
00231          .transport = transport,
00232       };
00233       ao2_callback(hooks, 0, nat_invoke_hook, &hook_details);
00234    }
00235 
00236    return PJ_SUCCESS;
00237 }
00238 
00239 static pjsip_module nat_module = {
00240    .name = { "NAT", 3 },
00241    .id = -1,
00242    .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,
00243    .on_rx_request = nat_on_rx_message,
00244    .on_rx_response = nat_on_rx_message,
00245    .on_tx_request = nat_on_tx_message,
00246    .on_tx_response = nat_on_tx_message,
00247 };
00248 
00249 /*! \brief Function called when an INVITE goes out */
00250 static int nat_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
00251 {
00252    if (session->inv_session->state == PJSIP_INV_STATE_INCOMING) {
00253       pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
00254    }
00255 
00256    return 0;
00257 }
00258 
00259 /*! \brief Function called when an INVITE response comes in */
00260 static void nat_incoming_invite_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
00261 {
00262    handle_rx_message(session->endpoint, rdata);
00263 }
00264 
00265 /*! \brief Function called when an INVITE comes in */
00266 static void nat_outgoing_invite_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
00267 {
00268    if (session->inv_session->state == PJSIP_INV_STATE_NULL) {
00269       pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
00270    }
00271 }
00272 
00273 /*! \brief Supplement for adding NAT functionality to dialog */
00274 static struct ast_sip_session_supplement nat_supplement = {
00275    .method = "INVITE",
00276    .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST + 1,
00277    .incoming_request = nat_incoming_invite_request,
00278    .outgoing_request = nat_outgoing_invite_request,
00279    .incoming_response = nat_incoming_invite_response,
00280 };
00281 
00282 
00283 static int unload_module(void)
00284 {
00285    ast_sip_session_unregister_supplement(&nat_supplement);
00286    ast_sip_unregister_service(&nat_module);
00287    return 0;
00288 }
00289 
00290 static int load_module(void)
00291 {
00292    CHECK_PJSIP_SESSION_MODULE_LOADED();
00293 
00294    if (ast_sip_register_service(&nat_module)) {
00295       ast_log(LOG_ERROR, "Could not register NAT module for incoming and outgoing requests\n");
00296       return AST_MODULE_LOAD_FAILURE;
00297    }
00298 
00299    if (ast_sip_session_register_supplement(&nat_supplement)) {
00300       ast_log(LOG_ERROR, "Could not register NAT session supplement for incoming and outgoing INVITE requests\n");
00301       unload_module();
00302       return AST_MODULE_LOAD_FAILURE;
00303    }
00304 
00305    return AST_MODULE_LOAD_SUCCESS;
00306 }
00307 
00308 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP NAT Support",
00309       .support_level = AST_MODULE_SUPPORT_CORE,
00310       .load = load_module,
00311       .unload = unload_module,
00312       .load_pri = AST_MODPRI_APP_DEPEND,
00313           );

Generated on Thu Apr 16 06:27:48 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6