PJSIP: Enforce module load dependencies
[asterisk/asterisk.git] / res / res_pjsip_nat.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*** MODULEINFO
20         <depend>pjproject</depend>
21         <depend>res_pjsip</depend>
22         <support_level>core</support_level>
23  ***/
24
25 #include "asterisk.h"
26
27 #include <pjsip.h>
28 #include <pjsip_ua.h>
29
30 #include "asterisk/res_pjsip.h"
31 #include "asterisk/res_pjsip_session.h"
32 #include "asterisk/module.h"
33 #include "asterisk/acl.h"
34
35 static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
36 {
37         pjsip_contact_hdr *contact;
38
39         if (!endpoint) {
40                 return PJ_FALSE;
41         }
42
43         if (endpoint->nat.rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) &&
44                 !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
45                 pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
46                 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
47
48                 pj_cstr(&uri->host, rdata->pkt_info.src_name);
49                 if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
50                         uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
51                 } else {
52                         uri->transport_param.slen = 0;
53                 }
54                 uri->port = rdata->pkt_info.src_port;
55
56                 /* rewrite the session target since it may have already been pulled from the contact header */
57                 if (dlg && (!dlg->remote.contact
58                         || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
59                         dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
60                         dlg->target = dlg->remote.contact->uri;
61                 }
62         }
63
64         if (endpoint->nat.force_rport) {
65                 rdata->msg_info.via->rport_param = rdata->pkt_info.src_port;
66         }
67
68         return PJ_FALSE;
69 }
70
71 static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata)
72 {
73         RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
74         return handle_rx_message(endpoint, rdata);
75 }
76
77 /*! \brief Structure which contains information about a transport */
78 struct request_transport_details {
79         /*! \brief Type of transport */
80         enum ast_transport type;
81         /*! \brief Potential pointer to the transport itself, if UDP */
82         pjsip_transport *transport;
83         /*! \brief Potential pointer to the transport factory itself, if TCP/TLS */
84         pjsip_tpfactory *factory;
85         /*! \brief Local address for transport */
86         pj_str_t local_address;
87         /*! \brief Local port for transport */
88         int local_port;
89 };
90
91 /*! \brief Callback function for finding the transport the request is going out on */
92 static int find_transport_in_use(void *obj, void *arg, int flags)
93 {
94         struct ast_sip_transport *transport = obj;
95         struct request_transport_details *details = arg;
96
97         /* If an explicit transport or factory matches then this is what is in use, if we are unavailable
98          * to compare based on that we make sure that the type is the same and the source IP address/port are the same
99          */
100         if ((details->transport && details->transport == transport->state->transport) ||
101                 (details->factory && details->factory == transport->state->factory) ||
102                 ((details->type == transport->type) && (transport->state->factory) &&
103                         !pj_strcmp(&transport->state->factory->addr_name.host, &details->local_address) &&
104                         transport->state->factory->addr_name.port == details->local_port)) {
105                 return CMP_MATCH | CMP_STOP;
106         }
107
108         return 0;
109 }
110
111 /*! \brief Helper function which returns the SIP URI of a Contact header */
112 static pjsip_sip_uri *nat_get_contact_sip_uri(pjsip_tx_data *tdata)
113 {
114         pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
115
116         if (!contact || (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
117                 return NULL;
118         }
119
120         return pjsip_uri_get_uri(contact->uri);
121 }
122
123 /*! \brief Structure which contains hook details */
124 struct nat_hook_details {
125         /*! \brief Outgoing message itself */
126         pjsip_tx_data *tdata;
127         /*! \brief Chosen transport */
128         struct ast_sip_transport *transport;
129 };
130
131 /*! \brief Callback function for invoking hooks */
132 static int nat_invoke_hook(void *obj, void *arg, int flags)
133 {
134         struct ast_sip_nat_hook *hook = obj;
135         struct nat_hook_details *details = arg;
136
137         if (hook->outgoing_external_message) {
138                 hook->outgoing_external_message(details->tdata, details->transport);
139         }
140
141         return 0;
142 }
143
144 static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
145 {
146         RAII_VAR(struct ao2_container *, transports, NULL, ao2_cleanup);
147         RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
148         struct request_transport_details details = { 0, };
149         pjsip_via_hdr *via = NULL;
150         struct ast_sockaddr addr = { { 0, } };
151         pjsip_sip_uri *uri = NULL;
152         RAII_VAR(struct ao2_container *, hooks, NULL, ao2_cleanup);
153
154         /* If a transport selector is in use we know the transport or factory, so explicitly find it */
155         if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) {
156                 details.transport = tdata->tp_sel.u.transport;
157         } else if (tdata->tp_sel.type == PJSIP_TPSELECTOR_LISTENER) {
158                 details.factory = tdata->tp_sel.u.listener;
159         } else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP || tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
160                 /* Connectionless uses the same transport for all requests */
161                 details.type = AST_TRANSPORT_UDP;
162                 details.transport = tdata->tp_info.transport;
163         } else {
164                 if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TCP) {
165                         details.type = AST_TRANSPORT_TCP;
166                 } else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TLS) {
167                         details.type = AST_TRANSPORT_TLS;
168                 } else {
169                         /* Unknown transport type, we can't map and thus can't apply NAT changes */
170                         return PJ_SUCCESS;
171                 }
172
173                 if ((uri = nat_get_contact_sip_uri(tdata))) {
174                         details.local_address = uri->host;
175                         details.local_port = uri->port;
176                 } else if ((tdata->msg->type == PJSIP_REQUEST_MSG) &&
177                         (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) {
178                         details.local_address = via->sent_by.host;
179                         details.local_port = via->sent_by.port;
180                 } else {
181                         return PJ_SUCCESS;
182                 }
183
184                 if (!details.local_port) {
185                         details.local_port = (details.type == AST_TRANSPORT_TLS) ? 5061 : 5060;
186                 }
187         }
188
189         if (!(transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL)) ||
190                 !(transport = ao2_callback(transports, 0, find_transport_in_use, &details)) || !transport->localnet ||
191                 ast_sockaddr_isnull(&transport->external_address)) {
192                 return PJ_SUCCESS;
193         }
194
195         ast_sockaddr_parse(&addr, tdata->tp_info.dst_name, PARSE_PORT_FORBID);
196         ast_sockaddr_set_port(&addr, tdata->tp_info.dst_port);
197
198         /* See if where we are sending this request is local or not, and if not that we can get a Contact URI to modify */
199         if (ast_apply_ha(transport->localnet, &addr) != AST_SENSE_ALLOW) {
200                 return PJ_SUCCESS;
201         }
202
203         /* Update the contact header with the external address */
204         if (uri || (uri = nat_get_contact_sip_uri(tdata))) {
205                 pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport->external_address));
206                 if (transport->external_signaling_port) {
207                         uri->port = transport->external_signaling_port;
208                 }
209         }
210
211         /* Update the via header if relevant */
212         if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) {
213                 pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport->external_address));
214                 if (transport->external_signaling_port) {
215                         via->sent_by.port = transport->external_signaling_port;
216                 }
217         }
218
219         /* Invoke any additional hooks that may be registered */
220         if ((hooks = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "nat_hook", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
221                 struct nat_hook_details hook_details = {
222                         .tdata = tdata,
223                         .transport = transport,
224                 };
225                 ao2_callback(hooks, 0, nat_invoke_hook, &hook_details);
226         }
227
228         return PJ_SUCCESS;
229 }
230
231 static pjsip_module nat_module = {
232         .name = { "NAT", 3 },
233         .id = -1,
234         .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,
235         .on_rx_request = nat_on_rx_message,
236         .on_rx_response = nat_on_rx_message,
237         .on_tx_request = nat_on_tx_message,
238         .on_tx_response = nat_on_tx_message,
239 };
240
241 /*! \brief Function called when an INVITE goes out */
242 static int nat_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
243 {
244         if (session->inv_session->state == PJSIP_INV_STATE_INCOMING) {
245                 pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
246         }
247
248         return 0;
249 }
250
251 /*! \brief Function called when an INVITE response comes in */
252 static void nat_incoming_invite_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
253 {
254         handle_rx_message(session->endpoint, rdata);
255 }
256
257 /*! \brief Function called when an INVITE comes in */
258 static void nat_outgoing_invite_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
259 {
260         if (session->inv_session->state == PJSIP_INV_STATE_NULL) {
261                 pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
262         }
263 }
264
265 /*! \brief Supplement for adding NAT functionality to dialog */
266 static struct ast_sip_session_supplement nat_supplement = {
267         .method = "INVITE",
268         .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST + 1,
269         .incoming_request = nat_incoming_invite_request,
270         .outgoing_request = nat_outgoing_invite_request,
271         .incoming_response = nat_incoming_invite_response,
272 };
273
274
275 static int unload_module(void)
276 {
277         ast_sip_session_unregister_supplement(&nat_supplement);
278         ast_sip_unregister_service(&nat_module);
279         return 0;
280 }
281
282 static int load_module(void)
283 {
284         CHECK_PJSIP_SESSION_MODULE_LOADED();
285
286         if (ast_sip_register_service(&nat_module)) {
287                 ast_log(LOG_ERROR, "Could not register NAT module for incoming and outgoing requests\n");
288                 return AST_MODULE_LOAD_FAILURE;
289         }
290
291         if (ast_sip_session_register_supplement(&nat_supplement)) {
292                 ast_log(LOG_ERROR, "Could not register NAT session supplement for incoming and outgoing INVITE requests\n");
293                 unload_module();
294                 return AST_MODULE_LOAD_FAILURE;
295         }
296
297         return AST_MODULE_LOAD_SUCCESS;
298 }
299
300 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP NAT Support",
301                 .support_level = AST_MODULE_SUPPORT_CORE,
302                 .load = load_module,
303                 .unload = unload_module,
304                 .load_pri = AST_MODPRI_APP_DEPEND,
305                );