Asterisk - The Open Source Telephony Project GIT-master-b023714
Loading...
Searching...
No Matches
Data Structures | Macros | Functions | Variables
res_pjsip_nat.c File Reference
#include "asterisk.h"
#include <pjsip.h>
#include <pjsip_ua.h>
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_session.h"
#include "asterisk/module.h"
#include "asterisk/acl.h"
Include dependency graph for res_pjsip_nat.c:

Go to the source code of this file.

Data Structures

struct  nat_hook_details
 Structure which contains hook details. More...
 

Macros

#define AST_SIP_X_AST_ORIG_HOST   "x-ast-orig-host"
 
#define AST_SIP_X_AST_ORIG_HOST_LEN   15
 
#define COLON_LEN   1
 
#define is_sip_uri(uri)    (PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri))
 
#define MAX_PORT_LEN   5
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static pj_bool_t handle_rx_message (struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
 
static int load_module (void)
 
static int nat_incoming_invite_request (struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 Function called when an INVITE goes out.
 
static void nat_incoming_invite_response (struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 Function called when an INVITE response comes in.
 
static int nat_invoke_hook (void *obj, void *arg, int flags)
 Callback function for invoking hooks.
 
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 void nat_outgoing_invite_request (struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 Function called when an INVITE comes in.
 
static pj_status_t process_nat (pjsip_tx_data *tdata)
 
static void restore_orig_contact_host (pjsip_tx_data *tdata)
 
static int rewrite_contact (pjsip_rx_data *rdata, pjsip_dialog *dlg)
 
static int rewrite_route_set (pjsip_rx_data *rdata, pjsip_dialog *dlg)
 
static void rewrite_uri (pjsip_rx_data *rdata, pjsip_sip_uri *uri, pj_pool_t *pool)
 
static void save_orig_contact_host (pjsip_rx_data *rdata, pjsip_sip_uri *uri)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PJSIP NAT Support" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .requires = "res_pjsip,res_pjsip_session", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static pjsip_module nat_module
 
static struct ast_sip_session_supplement nat_supplement
 Supplement for adding NAT functionality to dialog.
 

Macro Definition Documentation

◆ AST_SIP_X_AST_ORIG_HOST

#define AST_SIP_X_AST_ORIG_HOST   "x-ast-orig-host"

URI parameter for original host/port

Definition at line 37 of file res_pjsip_nat.c.

◆ AST_SIP_X_AST_ORIG_HOST_LEN

#define AST_SIP_X_AST_ORIG_HOST_LEN   15

Definition at line 38 of file res_pjsip_nat.c.

◆ COLON_LEN

#define COLON_LEN   1

◆ is_sip_uri

#define is_sip_uri (   uri)     (PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri))

Definition at line 40 of file res_pjsip_nat.c.

43{
44 pjsip_param *x_orig_host;
45 pj_str_t p_value;
46#define COLON_LEN 1
47#define MAX_PORT_LEN 5
48
49 if (rdata->msg_info.msg->type != PJSIP_REQUEST_MSG ||
50 rdata->msg_info.msg->line.req.method.id != PJSIP_REGISTER_METHOD) {
51 return;
52 }
53
54 ast_debug(1, "Saving contact '%.*s:%d'\n",
55 (int)uri->host.slen, uri->host.ptr, uri->port);
56
57 x_orig_host = PJ_POOL_ALLOC_T(rdata->tp_info.pool, pjsip_param);
58 x_orig_host->name = pj_strdup3(rdata->tp_info.pool, AST_SIP_X_AST_ORIG_HOST);
59 p_value.slen = pj_strlen(&uri->host) + COLON_LEN + MAX_PORT_LEN;
60 p_value.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, p_value.slen + 1);
61 p_value.slen = snprintf(p_value.ptr, p_value.slen + 1, "%.*s:%d", (int)uri->host.slen, uri->host.ptr, uri->port);
62 pj_strassign(&x_orig_host->value, &p_value);
63 pj_list_insert_before(&uri->other_param, x_orig_host);
64
65 return;
66}
67
68static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri, pj_pool_t *pool)
69{
70
71 if (pj_strcmp2(&uri->host, rdata->pkt_info.src_name) != 0) {
72 save_orig_contact_host(rdata, uri);
73 }
74
75 pj_strdup2(pool, &uri->host, rdata->pkt_info.src_name);
76 uri->port = rdata->pkt_info.src_port;
77 if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
78 /* WSS is special, we don't want to overwrite the URI at all as it needs to be ws */
79 } else if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
80 uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
81 } else {
82 uri->transport_param.slen = 0;
83 }
84}
85
86/*
87 * Update the Record-Route headers in the request or response and in the dialog
88 * object if exists.
89 *
90 * When NAT is in use, the address of the next hop in the SIP may be incorrect.
91 * To address this asterisk uses two strategies in parallel:
92 * 1. intercept the messages at the transaction level and rewrite the
93 * messages before arriving at the dialog layer
94 * 2. after the application processing, update the dialog object with the
95 * correct information
96 *
97 * The first strategy has a limitation that the SIP message may not have all
98 * the information required to determine if the next hop is in the route set
99 * or in the contact. Causing risk that asterisk will update the Contact on
100 * receipt of an in-dialog message despite there being a route set saved in
101 * the dialog.
102 *
103 * The second strategy has a limitation that not all UAC layers have interfaces
104 * available to invoke this module after dialog creation. (pjsip_sesion does
105 * but pjsip_pubsub does not), thus this strategy can't update the dialog in
106 * all cases needed.
107 *
108 * The ideal solution would be to implement an "incomming_request" event
109 * in pubsub module that can then pass the dialog object to this module
110 * on SUBSCRIBE, this module then should add itself as a listener to the dialog
111 * for the subsequent requests and responses & then be able to properly update
112 * the dialog object for all required events.
113 */
114static int rewrite_route_set(pjsip_rx_data *rdata, pjsip_dialog *dlg)
115{
116 pjsip_rr_hdr *rr = NULL;
117 pjsip_sip_uri *uri;
118 int res = -1;
119 int ignore_rr = 0;
120 int pubsub = 0;
121
122 if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
123 pjsip_hdr *iter;
124 for (iter = rdata->msg_info.msg->hdr.prev; iter != &rdata->msg_info.msg->hdr; iter = iter->prev) {
125 if (iter->type == PJSIP_H_RECORD_ROUTE) {
126 rr = (pjsip_rr_hdr *)iter;
127 break;
128 }
129 }
130 } else if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method)) {
131 rr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_RECORD_ROUTE, NULL);
132 } else {
133 /**
134 * Record-Route header has no meaning in REGISTER requests
135 * and should be ignored
136 */
137 ignore_rr = 1;
138 }
139
140 if (!pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_subscribe_method) ||
141 !pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_notify_method)) {
142 /**
143 * There is currently no good way to get the dlg object for a pubsub dialog
144 * so we will just look at the rr & contact of the current message and
145 * hope for the best
146 */
147 pubsub = 1;
148 }
149
150 if (rr) {
151 uri = pjsip_uri_get_uri(&rr->name_addr);
152 rewrite_uri(rdata, uri, rdata->tp_info.pool);
153 res = 0;
154 }
155
156 if (dlg && !pj_list_empty(&dlg->route_set) && !dlg->route_set_frozen) {
157 pjsip_routing_hdr *route = dlg->route_set.next;
158 uri = pjsip_uri_get_uri(&route->name_addr);
159 rewrite_uri(rdata, uri, dlg->pool);
160 res = 0;
161 }
162
163 if (!dlg && !rr && !ignore_rr && !pubsub && rdata->msg_info.to->tag.slen){
164 /**
165 * Even if this message doesn't have any route headers
166 * the dialog may, so wait until a later invocation that
167 * has a dialog reference to make sure there isn't a
168 * previously saved routset in the dialog before deciding
169 * the contact needs to be modified
170 */
171 res = 0;
172 }
173
174 return res;
175}
176
177static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg)
178{
179 pjsip_contact_hdr *contact;
180
181 contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
182 if (contact && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
183 pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
184
185 rewrite_uri(rdata, uri, rdata->tp_info.pool);
186
187 if (dlg && pj_list_empty(&dlg->route_set) && (!dlg->remote.contact
188 || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
189 dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
190 dlg->target = dlg->remote.contact->uri;
191 }
192 return 0;
193 }
194
195 return -1;
196}
197
198static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
199{
200 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
201
202 if (!endpoint) {
203 return PJ_FALSE;
204 }
205
206 if (endpoint->nat.rewrite_contact) {
207 /* rewrite_contact is intended to ensure we send requests/responses to
208 * a routable address when NAT is involved. The URI that dictates where
209 * we send requests/responses can be determined either by Record-Route
210 * headers or by the Contact header if no Record-Route headers are present.
211 * We therefore will attempt to rewrite a Record-Route header first, and if
212 * none are present, we fall back to rewriting the Contact header instead.
213 */
214 if (rewrite_route_set(rdata, dlg)) {
215 rewrite_contact(rdata, dlg);
216 }
217 }
218
219 if (endpoint->nat.force_rport) {
220 rdata->msg_info.via->rport_param = rdata->pkt_info.src_port;
221 }
222
223 return PJ_FALSE;
224}
225
226static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata)
227{
228 pj_bool_t res;
229 struct ast_sip_endpoint *endpoint;
230
231 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
232 res = handle_rx_message(endpoint, rdata);
233 ao2_cleanup(endpoint);
234 return res;
235}
236
237/*! \brief Structure which contains hook details */
238struct nat_hook_details {
239 /*! \brief Outgoing message itself */
240 pjsip_tx_data *tdata;
241 /*! \brief Chosen transport */
243};
244
245/*! \brief Callback function for invoking hooks */
246static int nat_invoke_hook(void *obj, void *arg, int flags)
247{
248 struct ast_sip_nat_hook *hook = obj;
249 struct nat_hook_details *details = arg;
250
251 if (hook->outgoing_external_message) {
252 hook->outgoing_external_message(details->tdata, details->transport);
253 }
254
255 return 0;
256}
257
258static void restore_orig_contact_host(pjsip_tx_data *tdata)
259{
260 pjsip_contact_hdr *contact;
262 pjsip_param *x_orig_host;
263 pjsip_sip_uri *uri;
264 pjsip_hdr *hdr;
265
266 if (tdata->msg->type == PJSIP_REQUEST_MSG) {
267 if (is_sip_uri(tdata->msg->line.req.uri)) {
268 uri = pjsip_uri_get_uri(tdata->msg->line.req.uri);
269 while ((x_orig_host = pjsip_param_find(&uri->other_param, &x_name))) {
270 pj_list_erase(x_orig_host);
271 }
272 }
273 for (hdr = tdata->msg->hdr.next; hdr != &tdata->msg->hdr; hdr = hdr->next) {
274 if (hdr->type == PJSIP_H_TO) {
275 if (is_sip_uri(((pjsip_fromto_hdr *) hdr)->uri)) {
276 uri = pjsip_uri_get_uri(((pjsip_fromto_hdr *) hdr)->uri);
277 while ((x_orig_host = pjsip_param_find(&uri->other_param, &x_name))) {
278 pj_list_erase(x_orig_host);
279 }
280 }
281 }
282 }
283 }
284
285 if (tdata->msg->type != PJSIP_RESPONSE_MSG) {
286 return;
287 }
288
289 contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
290 while (contact) {
291 pjsip_sip_uri *contact_uri = pjsip_uri_get_uri(contact->uri);
292 x_orig_host = pjsip_param_find(&contact_uri->other_param, &x_name);
293
294 if (x_orig_host) {
295 char host_port[x_orig_host->value.slen + 1];
296 char *sep;
297
298 ast_debug(1, "Restoring contact %.*s:%d to %.*s\n", (int)contact_uri->host.slen,
299 contact_uri->host.ptr, contact_uri->port,
300 (int)x_orig_host->value.slen, x_orig_host->value.ptr);
301
302 strncpy(host_port, x_orig_host->value.ptr, x_orig_host->value.slen);
303 host_port[x_orig_host->value.slen] = '\0';
304 sep = strchr(host_port, ':');
305 if (sep) {
306 *sep = '\0';
307 sep++;
308 pj_strdup2(tdata->pool, &contact_uri->host, host_port);
309 contact_uri->port = strtol(sep, NULL, 10);
310 }
311 pj_list_erase(x_orig_host);
312 }
313 contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, contact->next);
314 }
315}
316
317static pj_status_t process_nat(pjsip_tx_data *tdata)
318{
320 RAII_VAR(struct ast_sip_transport_state *, transport_state, NULL, ao2_cleanup);
321 struct ast_sip_request_transport_details details = { 0, };
322 pjsip_via_hdr *via = NULL;
323 struct ast_sockaddr addr = { { 0, } };
324 pjsip_sip_uri *uri = NULL;
325 RAII_VAR(struct ao2_container *, hooks, NULL, ao2_cleanup);
326
327 if (ast_sip_set_request_transport_details(&details, tdata, 0)) {
328 return PJ_SUCCESS;
329 }
330
331 uri = ast_sip_get_contact_sip_uri(tdata);
332 via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
333
334 if (!(transport_state = ast_sip_find_transport_state_in_use(&details))) {
335 return PJ_SUCCESS;
336 }
337
338 if (!(transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_state->id))) {
339 return PJ_SUCCESS;
340 }
341
342 if (transport_state->localnet) {
343 ast_sockaddr_parse(&addr, tdata->tp_info.dst_name, PARSE_PORT_FORBID);
344 ast_sockaddr_set_port(&addr, tdata->tp_info.dst_port);
345
346 /* See if where we are sending this request is local or not, and if not that we can get a Contact URI to modify */
347 if (ast_sip_transport_is_local(transport_state, &addr)) {
348 ast_debug(5, "Request is being sent to local address, skipping NAT manipulation\n");
349 return PJ_SUCCESS;
350 }
351 }
352
353 if (!ast_sockaddr_isnull(&transport_state->external_signaling_address)) {
354 pjsip_cseq_hdr *cseq = PJSIP_MSG_CSEQ_HDR(tdata->msg);
355
356 /* Update the Contact header with the external address. We only do this if
357 * a CSeq is not present (which should not happen - but we are extra safe),
358 * if a request is being sent, or if a response is sent that is not a response
359 * to a REGISTER. We specifically don't do this for a response to a REGISTER
360 * as the Contact headers would contain the registered Contacts, and not our
361 * own Contact. Nor do we do it for a 302 response to an INVITE request.
362 */
363 if ((!cseq || tdata->msg->type == PJSIP_REQUEST_MSG ||
364 pjsip_method_cmp(&cseq->method, &pjsip_register_method)) &&
365 (tdata->msg->type != PJSIP_RESPONSE_MSG ||
366 pjsip_method_cmp(&cseq->method, &pjsip_invite_method) ||
367 tdata->msg->line.status.code != PJSIP_SC_MOVED_TEMPORARILY )) {
368 /* We can only rewrite the URI when one is present */
369 if (uri || (uri = ast_sip_get_contact_sip_uri(tdata))) {
370 pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
371 if (transport->external_signaling_port) {
372 uri->port = transport->external_signaling_port;
373 ast_debug(4, "Re-wrote Contact URI port to %d\n", uri->port);
374 }
375 }
376 }
377
378 /* Update the via header if relevant */
379 if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) {
380 pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
381 if (transport->external_signaling_port) {
382 via->sent_by.port = transport->external_signaling_port;
383 }
384 }
385 }
386
387 /* Invoke any additional hooks that may be registered */
389 struct nat_hook_details hook_details = {
390 .tdata = tdata,
391 .transport = transport,
392 };
393 ao2_callback(hooks, 0, nat_invoke_hook, &hook_details);
394 }
395
396 return PJ_SUCCESS;
397}
398
399static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata) {
400 pj_status_t rc;
401
402 rc = process_nat(tdata);
404
405 return rc;
406}
407
408
409static pjsip_module nat_module = {
410 .name = { "NAT", 3 },
411 .id = -1,
412 .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,
413 .on_rx_request = nat_on_rx_message,
414 .on_rx_response = nat_on_rx_message,
415 .on_tx_request = nat_on_tx_message,
416 .on_tx_response = nat_on_tx_message,
417};
418
419/*! \brief Function called when an INVITE goes out */
420static int nat_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
421{
422 if (session->inv_session->state == PJSIP_INV_STATE_INCOMING) {
423 pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
424 }
425
426 return 0;
427}
428
429/*! \brief Function called when an INVITE response comes in */
430static void nat_incoming_invite_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
431{
432 handle_rx_message(session->endpoint, rdata);
433}
434
435/*! \brief Function called when an INVITE comes in */
436static void nat_outgoing_invite_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
437{
438 if (session->inv_session->state == PJSIP_INV_STATE_NULL) {
439 pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
440 }
441}
442
443/*! \brief Supplement for adding NAT functionality to dialog */
445 .method = "INVITE",
446 .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST + 1,
447 .incoming_request = nat_incoming_invite_request,
448 .outgoing_request = nat_outgoing_invite_request,
449 .incoming_response = nat_incoming_invite_response,
450};
451
452
453static int unload_module(void)
454{
457 return 0;
458}
459
460static int load_module(void)
461{
463 ast_log(LOG_ERROR, "Could not register NAT module for incoming and outgoing requests\n");
465 }
466
468
470}
471
473 .support_level = AST_MODULE_SUPPORT_CORE,
474 .load = load_module,
475 .unload = unload_module,
476 .load_pri = AST_MODPRI_APP_DEPEND,
477 .requires = "res_pjsip,res_pjsip_session",
478);
static struct ast_mansession session
#define ast_log
Definition astobj2.c:42
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition astobj2.h:1693
#define ao2_cleanup(obj)
Definition astobj2.h:1934
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_APP_DEPEND
Definition module.h:342
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition module.h:78
static char * ast_sockaddr_stringify_host(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only, suitable for a URL (with brack...
Definition netsock2.h:327
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition netsock2.c:230
static int ast_sockaddr_isnull(const struct ast_sockaddr *addr)
Checks if the ast_sockaddr is null. "null" in this sense essentially means uninitialized,...
Definition netsock2.h:127
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition netsock2.h:532
void ast_sip_unregister_service(pjsip_module *module)
Definition res_pjsip.c:133
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition res_pjsip.c:117
@ AST_SIP_SUPPLEMENT_PRIORITY_FIRST
Definition res_pjsip.h:3334
struct ast_sip_endpoint * ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata)
Get the looked-up endpoint on an out-of dialog request or response.
struct ast_sip_transport_state * ast_sip_find_transport_state_in_use(struct ast_sip_request_transport_details *details)
Returns the transport state currently in use based on request transport details.
Definition res_pjsip.c:595
int ast_sip_set_request_transport_details(struct ast_sip_request_transport_details *details, pjsip_tx_data *tdata, int use_ipv6)
Sets request transport details based on tdata.
Definition res_pjsip.c:648
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
#define ast_sip_transport_is_local(transport_state, addr)
Definition res_pjsip.h:213
pjsip_sip_uri * ast_sip_get_contact_sip_uri(pjsip_tx_data *tdata)
Return the SIP URI of the Contact header.
Definition res_pjsip.c:564
static void nat_incoming_invite_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
Function called when an INVITE response comes in.
static int nat_invoke_hook(void *obj, void *arg, int flags)
Callback function for invoking hooks.
static void save_orig_contact_host(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
static int nat_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
Function called when an INVITE goes out.
#define MAX_PORT_LEN
static int rewrite_route_set(pjsip_rx_data *rdata, pjsip_dialog *dlg)
static pjsip_module nat_module
#define COLON_LEN
static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg)
#define is_sip_uri(uri)
#define AST_SIP_X_AST_ORIG_HOST_LEN
static void nat_outgoing_invite_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
Function called when an INVITE comes in.
static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
static void restore_orig_contact_host(pjsip_tx_data *tdata)
static int load_module(void)
#define AST_SIP_X_AST_ORIG_HOST
static int unload_module(void)
static struct ast_sip_session_supplement nat_supplement
Supplement for adding NAT functionality to dialog.
static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri, pj_pool_t *pool)
static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata)
static pj_status_t process_nat(pjsip_tx_data *tdata)
#define ast_sip_session_register_supplement(supplement)
void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
Unregister a an supplement to SIP session processing.
#define NULL
Definition resample.c:96
@ AST_RETRIEVE_FLAG_MULTIPLE
Return all matching objects.
Definition sorcery.h:120
@ AST_RETRIEVE_FLAG_ALL
Perform no matching, return all objects.
Definition sorcery.h:123
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition sorcery.c:1917
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition sorcery.c:1961
Generic container type.
An entity with which Asterisk communicates.
Definition res_pjsip.h:1051
struct ast_sip_endpoint_nat_configuration nat
Definition res_pjsip.h:1088
Structure for SIP nat hook information.
Definition res_pjsip.h:327
void(* outgoing_external_message)(struct pjsip_tx_data *tdata, struct ast_sip_transport *transport)
Definition res_pjsip.h:331
Structure which contains information about a transport.
Definition res_pjsip.h:335
A supplement to SIP message processing.
struct ast_module *const char * method
A structure describing a SIP session.
Structure for SIP transport information.
Definition res_pjsip.h:117
Transport to bind to.
Definition res_pjsip.h:219
Socket address structure.
Definition netsock2.h:97
Structure which contains hook details.
pjsip_tx_data * tdata
Outgoing message itself.
struct ast_sip_transport * transport
Chosen transport.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:978

◆ MAX_PORT_LEN

#define MAX_PORT_LEN   5

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 479 of file res_pjsip_nat.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 479 of file res_pjsip_nat.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 479 of file res_pjsip_nat.c.

◆ handle_rx_message()

static pj_bool_t handle_rx_message ( struct ast_sip_endpoint endpoint,
pjsip_rx_data *  rdata 
)
static

Definition at line 199 of file res_pjsip_nat.c.

200{
201 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
202
203 if (!endpoint) {
204 return PJ_FALSE;
205 }
206
207 if (endpoint->nat.rewrite_contact) {
208 /* rewrite_contact is intended to ensure we send requests/responses to
209 * a routable address when NAT is involved. The URI that dictates where
210 * we send requests/responses can be determined either by Record-Route
211 * headers or by the Contact header if no Record-Route headers are present.
212 * We therefore will attempt to rewrite a Record-Route header first, and if
213 * none are present, we fall back to rewriting the Contact header instead.
214 */
215 if (rewrite_route_set(rdata, dlg)) {
216 rewrite_contact(rdata, dlg);
217 }
218 }
219
220 if (endpoint->nat.force_rport) {
221 rdata->msg_info.via->rport_param = rdata->pkt_info.src_port;
222 }
223
224 return PJ_FALSE;
225}

References ast_sip_endpoint_nat_configuration::force_rport, ast_sip_endpoint::nat, ast_sip_endpoint_nat_configuration::rewrite_contact, rewrite_contact(), and rewrite_route_set().

Referenced by nat_incoming_invite_response(), and nat_on_rx_message().

◆ load_module()

static int load_module ( void  )
static

Definition at line 461 of file res_pjsip_nat.c.

462{
464 ast_log(LOG_ERROR, "Could not register NAT module for incoming and outgoing requests\n");
466 }
467
469
471}

References ast_log, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_sip_register_service(), ast_sip_session_register_supplement, LOG_ERROR, nat_module, and nat_supplement.

◆ nat_incoming_invite_request()

static int nat_incoming_invite_request ( struct ast_sip_session session,
struct pjsip_rx_data *  rdata 
)
static

Function called when an INVITE goes out.

Definition at line 421 of file res_pjsip_nat.c.

422{
423 if (session->inv_session->state == PJSIP_INV_STATE_INCOMING) {
424 pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
425 }
426
427 return 0;
428}

References nat_module, NULL, and session.

◆ nat_incoming_invite_response()

static void nat_incoming_invite_response ( struct ast_sip_session session,
struct pjsip_rx_data *  rdata 
)
static

Function called when an INVITE response comes in.

Definition at line 431 of file res_pjsip_nat.c.

432{
433 handle_rx_message(session->endpoint, rdata);
434}

References handle_rx_message(), and session.

◆ nat_invoke_hook()

static int nat_invoke_hook ( void *  obj,
void *  arg,
int  flags 
)
static

Callback function for invoking hooks.

Definition at line 247 of file res_pjsip_nat.c.

248{
249 struct ast_sip_nat_hook *hook = obj;
250 struct nat_hook_details *details = arg;
251
252 if (hook->outgoing_external_message) {
253 hook->outgoing_external_message(details->tdata, details->transport);
254 }
255
256 return 0;
257}

References ast_sip_nat_hook::outgoing_external_message, nat_hook_details::tdata, and nat_hook_details::transport.

Referenced by process_nat().

◆ nat_on_rx_message()

static pj_bool_t nat_on_rx_message ( pjsip_rx_data *  rdata)
static

Definition at line 227 of file res_pjsip_nat.c.

228{
229 pj_bool_t res;
230 struct ast_sip_endpoint *endpoint;
231
232 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
233 res = handle_rx_message(endpoint, rdata);
234 ao2_cleanup(endpoint);
235 return res;
236}

References ao2_cleanup, ast_pjsip_rdata_get_endpoint(), and handle_rx_message().

◆ nat_on_tx_message()

static pj_status_t nat_on_tx_message ( pjsip_tx_data *  tdata)
static

Definition at line 400 of file res_pjsip_nat.c.

400 {
401 pj_status_t rc;
402
403 rc = process_nat(tdata);
405
406 return rc;
407}

References process_nat(), restore_orig_contact_host(), and nat_hook_details::tdata.

◆ nat_outgoing_invite_request()

static void nat_outgoing_invite_request ( struct ast_sip_session session,
struct pjsip_tx_data *  tdata 
)
static

Function called when an INVITE comes in.

Definition at line 437 of file res_pjsip_nat.c.

438{
439 if (session->inv_session->state == PJSIP_INV_STATE_NULL) {
440 pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
441 }
442}

References nat_module, NULL, and session.

◆ process_nat()

static pj_status_t process_nat ( pjsip_tx_data *  tdata)
static

Definition at line 318 of file res_pjsip_nat.c.

319{
320 RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
321 RAII_VAR(struct ast_sip_transport_state *, transport_state, NULL, ao2_cleanup);
322 struct ast_sip_request_transport_details details = { 0, };
323 pjsip_via_hdr *via = NULL;
324 struct ast_sockaddr addr = { { 0, } };
325 pjsip_sip_uri *uri = NULL;
326 RAII_VAR(struct ao2_container *, hooks, NULL, ao2_cleanup);
327
328 if (ast_sip_set_request_transport_details(&details, tdata, 0)) {
329 return PJ_SUCCESS;
330 }
331
332 uri = ast_sip_get_contact_sip_uri(tdata);
333 via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
334
335 if (!(transport_state = ast_sip_find_transport_state_in_use(&details))) {
336 return PJ_SUCCESS;
337 }
338
339 if (!(transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_state->id))) {
340 return PJ_SUCCESS;
341 }
342
343 if (transport_state->localnet) {
344 ast_sockaddr_parse(&addr, tdata->tp_info.dst_name, PARSE_PORT_FORBID);
345 ast_sockaddr_set_port(&addr, tdata->tp_info.dst_port);
346
347 /* See if where we are sending this request is local or not, and if not that we can get a Contact URI to modify */
348 if (ast_sip_transport_is_local(transport_state, &addr)) {
349 ast_debug(5, "Request is being sent to local address, skipping NAT manipulation\n");
350 return PJ_SUCCESS;
351 }
352 }
353
354 if (!ast_sockaddr_isnull(&transport_state->external_signaling_address)) {
355 pjsip_cseq_hdr *cseq = PJSIP_MSG_CSEQ_HDR(tdata->msg);
356
357 /* Update the Contact header with the external address. We only do this if
358 * a CSeq is not present (which should not happen - but we are extra safe),
359 * if a request is being sent, or if a response is sent that is not a response
360 * to a REGISTER. We specifically don't do this for a response to a REGISTER
361 * as the Contact headers would contain the registered Contacts, and not our
362 * own Contact. Nor do we do it for a 302 response to an INVITE request.
363 */
364 if ((!cseq || tdata->msg->type == PJSIP_REQUEST_MSG ||
365 pjsip_method_cmp(&cseq->method, &pjsip_register_method)) &&
366 (tdata->msg->type != PJSIP_RESPONSE_MSG ||
367 pjsip_method_cmp(&cseq->method, &pjsip_invite_method) ||
368 tdata->msg->line.status.code != PJSIP_SC_MOVED_TEMPORARILY )) {
369 /* We can only rewrite the URI when one is present */
370 if (uri || (uri = ast_sip_get_contact_sip_uri(tdata))) {
371 pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
372 if (transport->external_signaling_port) {
373 uri->port = transport->external_signaling_port;
374 ast_debug(4, "Re-wrote Contact URI port to %d\n", uri->port);
375 }
376 }
377 }
378
379 /* Update the via header if relevant */
380 if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) {
381 pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
382 if (transport->external_signaling_port) {
383 via->sent_by.port = transport->external_signaling_port;
384 }
385 }
386 }
387
388 /* Invoke any additional hooks that may be registered */
390 struct nat_hook_details hook_details = {
391 .tdata = tdata,
392 .transport = transport,
393 };
394 ao2_callback(hooks, 0, nat_invoke_hook, &hook_details);
395 }
396
397 return PJ_SUCCESS;
398}

References ao2_callback, ao2_cleanup, ast_debug, AST_RETRIEVE_FLAG_ALL, AST_RETRIEVE_FLAG_MULTIPLE, ast_sip_find_transport_state_in_use(), ast_sip_get_contact_sip_uri(), ast_sip_get_sorcery(), ast_sip_set_request_transport_details(), ast_sip_transport_is_local, ast_sockaddr_isnull(), ast_sockaddr_parse(), ast_sockaddr_set_port, ast_sockaddr_stringify_host(), ast_sorcery_retrieve_by_fields(), ast_sorcery_retrieve_by_id(), nat_invoke_hook(), NULL, PARSE_PORT_FORBID, RAII_VAR, nat_hook_details::tdata, and nat_hook_details::transport.

Referenced by nat_on_tx_message().

◆ restore_orig_contact_host()

static void restore_orig_contact_host ( pjsip_tx_data *  tdata)
static

Definition at line 259 of file res_pjsip_nat.c.

260{
261 pjsip_contact_hdr *contact;
263 pjsip_param *x_orig_host;
264 pjsip_sip_uri *uri;
265 pjsip_hdr *hdr;
266
267 if (tdata->msg->type == PJSIP_REQUEST_MSG) {
268 if (is_sip_uri(tdata->msg->line.req.uri)) {
269 uri = pjsip_uri_get_uri(tdata->msg->line.req.uri);
270 while ((x_orig_host = pjsip_param_find(&uri->other_param, &x_name))) {
271 pj_list_erase(x_orig_host);
272 }
273 }
274 for (hdr = tdata->msg->hdr.next; hdr != &tdata->msg->hdr; hdr = hdr->next) {
275 if (hdr->type == PJSIP_H_TO) {
276 if (is_sip_uri(((pjsip_fromto_hdr *) hdr)->uri)) {
277 uri = pjsip_uri_get_uri(((pjsip_fromto_hdr *) hdr)->uri);
278 while ((x_orig_host = pjsip_param_find(&uri->other_param, &x_name))) {
279 pj_list_erase(x_orig_host);
280 }
281 }
282 }
283 }
284 }
285
286 if (tdata->msg->type != PJSIP_RESPONSE_MSG) {
287 return;
288 }
289
290 contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
291 while (contact) {
292 pjsip_sip_uri *contact_uri = pjsip_uri_get_uri(contact->uri);
293 x_orig_host = pjsip_param_find(&contact_uri->other_param, &x_name);
294
295 if (x_orig_host) {
296 char host_port[x_orig_host->value.slen + 1];
297 char *sep;
298
299 ast_debug(1, "Restoring contact %.*s:%d to %.*s\n", (int)contact_uri->host.slen,
300 contact_uri->host.ptr, contact_uri->port,
301 (int)x_orig_host->value.slen, x_orig_host->value.ptr);
302
303 strncpy(host_port, x_orig_host->value.ptr, x_orig_host->value.slen);
304 host_port[x_orig_host->value.slen] = '\0';
305 sep = strchr(host_port, ':');
306 if (sep) {
307 *sep = '\0';
308 sep++;
309 pj_strdup2(tdata->pool, &contact_uri->host, host_port);
310 contact_uri->port = strtol(sep, NULL, 10);
311 }
312 pj_list_erase(x_orig_host);
313 }
314 contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, contact->next);
315 }
316}

References ast_debug, AST_SIP_X_AST_ORIG_HOST, AST_SIP_X_AST_ORIG_HOST_LEN, is_sip_uri, NULL, and nat_hook_details::tdata.

Referenced by nat_on_tx_message().

◆ rewrite_contact()

static int rewrite_contact ( pjsip_rx_data *  rdata,
pjsip_dialog *  dlg 
)
static

Definition at line 178 of file res_pjsip_nat.c.

179{
180 pjsip_contact_hdr *contact;
181
182 contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
183 if (contact && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
184 pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
185
186 rewrite_uri(rdata, uri, rdata->tp_info.pool);
187
188 if (dlg && pj_list_empty(&dlg->route_set) && (!dlg->remote.contact
189 || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
190 dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
191 dlg->target = dlg->remote.contact->uri;
192 }
193 return 0;
194 }
195
196 return -1;
197}

References NULL, and rewrite_uri().

Referenced by handle_rx_message().

◆ rewrite_route_set()

static int rewrite_route_set ( pjsip_rx_data *  rdata,
pjsip_dialog *  dlg 
)
static

Record-Route header has no meaning in REGISTER requests and should be ignored

There is currently no good way to get the dlg object for a pubsub dialog so we will just look at the rr & contact of the current message and hope for the best

Even if this message doesn't have any route headers the dialog may, so wait until a later invocation that has a dialog reference to make sure there isn't a previously saved routset in the dialog before deciding the contact needs to be modified

Definition at line 115 of file res_pjsip_nat.c.

116{
117 pjsip_rr_hdr *rr = NULL;
118 pjsip_sip_uri *uri;
119 int res = -1;
120 int ignore_rr = 0;
121 int pubsub = 0;
122
123 if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
124 pjsip_hdr *iter;
125 for (iter = rdata->msg_info.msg->hdr.prev; iter != &rdata->msg_info.msg->hdr; iter = iter->prev) {
126 if (iter->type == PJSIP_H_RECORD_ROUTE) {
127 rr = (pjsip_rr_hdr *)iter;
128 break;
129 }
130 }
131 } else if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method)) {
132 rr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_RECORD_ROUTE, NULL);
133 } else {
134 /**
135 * Record-Route header has no meaning in REGISTER requests
136 * and should be ignored
137 */
138 ignore_rr = 1;
139 }
140
141 if (!pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_subscribe_method) ||
142 !pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_notify_method)) {
143 /**
144 * There is currently no good way to get the dlg object for a pubsub dialog
145 * so we will just look at the rr & contact of the current message and
146 * hope for the best
147 */
148 pubsub = 1;
149 }
150
151 if (rr) {
152 uri = pjsip_uri_get_uri(&rr->name_addr);
153 rewrite_uri(rdata, uri, rdata->tp_info.pool);
154 res = 0;
155 }
156
157 if (dlg && !pj_list_empty(&dlg->route_set) && !dlg->route_set_frozen) {
158 pjsip_routing_hdr *route = dlg->route_set.next;
159 uri = pjsip_uri_get_uri(&route->name_addr);
160 rewrite_uri(rdata, uri, dlg->pool);
161 res = 0;
162 }
163
164 if (!dlg && !rr && !ignore_rr && !pubsub && rdata->msg_info.to->tag.slen){
165 /**
166 * Even if this message doesn't have any route headers
167 * the dialog may, so wait until a later invocation that
168 * has a dialog reference to make sure there isn't a
169 * previously saved routset in the dialog before deciding
170 * the contact needs to be modified
171 */
172 res = 0;
173 }
174
175 return res;
176}

References NULL, and rewrite_uri().

Referenced by handle_rx_message().

◆ rewrite_uri()

static void rewrite_uri ( pjsip_rx_data *  rdata,
pjsip_sip_uri *  uri,
pj_pool_t *  pool 
)
static

Definition at line 69 of file res_pjsip_nat.c.

70{
71
72 if (pj_strcmp2(&uri->host, rdata->pkt_info.src_name) != 0) {
73 save_orig_contact_host(rdata, uri);
74 }
75
76 pj_strdup2(pool, &uri->host, rdata->pkt_info.src_name);
77 uri->port = rdata->pkt_info.src_port;
78 if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
79 /* WSS is special, we don't want to overwrite the URI at all as it needs to be ws */
80 } else if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
81 uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
82 } else {
83 uri->transport_param.slen = 0;
84 }
85}

References save_orig_contact_host().

Referenced by rewrite_contact(), and rewrite_route_set().

◆ save_orig_contact_host()

static void save_orig_contact_host ( pjsip_rx_data *  rdata,
pjsip_sip_uri *  uri 
)
static

Definition at line 43 of file res_pjsip_nat.c.

44{
45 pjsip_param *x_orig_host;
46 pj_str_t p_value;
47#define COLON_LEN 1
48#define MAX_PORT_LEN 5
49
50 if (rdata->msg_info.msg->type != PJSIP_REQUEST_MSG ||
51 rdata->msg_info.msg->line.req.method.id != PJSIP_REGISTER_METHOD) {
52 return;
53 }
54
55 ast_debug(1, "Saving contact '%.*s:%d'\n",
56 (int)uri->host.slen, uri->host.ptr, uri->port);
57
58 x_orig_host = PJ_POOL_ALLOC_T(rdata->tp_info.pool, pjsip_param);
59 x_orig_host->name = pj_strdup3(rdata->tp_info.pool, AST_SIP_X_AST_ORIG_HOST);
60 p_value.slen = pj_strlen(&uri->host) + COLON_LEN + MAX_PORT_LEN;
61 p_value.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, p_value.slen + 1);
62 p_value.slen = snprintf(p_value.ptr, p_value.slen + 1, "%.*s:%d", (int)uri->host.slen, uri->host.ptr, uri->port);
63 pj_strassign(&x_orig_host->value, &p_value);
64 pj_list_insert_before(&uri->other_param, x_orig_host);
65
66 return;
67}

References ast_debug, AST_SIP_X_AST_ORIG_HOST, COLON_LEN, and MAX_PORT_LEN.

Referenced by rewrite_uri().

◆ unload_module()

static int unload_module ( void  )
static

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PJSIP NAT Support" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .requires = "res_pjsip,res_pjsip_session", }
static

Definition at line 479 of file res_pjsip_nat.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 479 of file res_pjsip_nat.c.

◆ nat_module

pjsip_module nat_module
static

Definition at line 410 of file res_pjsip_nat.c.

410 {
411 .name = { "NAT", 3 },
412 .id = -1,
413 .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,
414 .on_rx_request = nat_on_rx_message,
415 .on_rx_response = nat_on_rx_message,
416 .on_tx_request = nat_on_tx_message,
417 .on_tx_response = nat_on_tx_message,
418};

Referenced by load_module(), nat_incoming_invite_request(), nat_outgoing_invite_request(), and unload_module().

◆ nat_supplement

struct ast_sip_session_supplement nat_supplement
static

Supplement for adding NAT functionality to dialog.

Definition at line 445 of file res_pjsip_nat.c.

445 {
446 .method = "INVITE",
447 .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST + 1,
448 .incoming_request = nat_incoming_invite_request,
449 .outgoing_request = nat_outgoing_invite_request,
450 .incoming_response = nat_incoming_invite_response,
451};

Referenced by load_module(), and unload_module().