Asterisk - The Open Source Telephony Project GIT-master-f36a736
res_pjsip_nat.c
Go to the documentation of this file.
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 <depend>res_pjsip_session</depend>
23 <support_level>core</support_level>
24 ***/
25
26#include "asterisk.h"
27
28#include <pjsip.h>
29#include <pjsip_ua.h>
30
31#include "asterisk/res_pjsip.h"
33#include "asterisk/module.h"
34#include "asterisk/acl.h"
35
36/*! URI parameter for original host/port */
37#define AST_SIP_X_AST_ORIG_HOST "x-ast-orig-host"
38#define AST_SIP_X_AST_ORIG_HOST_LEN 15
39
40#define is_sip_uri(uri) \
41 (PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri))
42
43static void save_orig_contact_host(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
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}
68
69static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri, pj_pool_t *pool)
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}
86
87/*
88 * Update the Record-Route headers in the request or response and in the dialog
89 * object if exists.
90 *
91 * When NAT is in use, the address of the next hop in the SIP may be incorrect.
92 * To address this asterisk uses two strategies in parallel:
93 * 1. intercept the messages at the transaction level and rewrite the
94 * messages before arriving at the dialog layer
95 * 2. after the application processing, update the dialog object with the
96 * correct information
97 *
98 * The first strategy has a limitation that the SIP message may not have all
99 * the information required to determine if the next hop is in the route set
100 * or in the contact. Causing risk that asterisk will update the Contact on
101 * receipt of an in-dialog message despite there being a route set saved in
102 * the dialog.
103 *
104 * The second strategy has a limitation that not all UAC layers have interfaces
105 * available to invoke this module after dialog creation. (pjsip_sesion does
106 * but pjsip_pubsub does not), thus this strategy can't update the dialog in
107 * all cases needed.
108 *
109 * The ideal solution would be to implement an "incomming_request" event
110 * in pubsub module that can then pass the dialog object to this module
111 * on SUBSCRIBE, this module then should add itself as a listener to the dialog
112 * for the subsequent requests and responses & then be able to properly update
113 * the dialog object for all required events.
114 */
115static int rewrite_route_set(pjsip_rx_data *rdata, pjsip_dialog *dlg)
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}
177
178static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg)
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}
198
199static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
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}
226
227static pj_bool_t nat_on_rx_message(pjsip_rx_data *rdata)
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}
237
238/*! \brief Structure which contains hook details */
240 /*! \brief Outgoing message itself */
241 pjsip_tx_data *tdata;
242 /*! \brief Chosen transport */
244};
245
246/*! \brief Callback function for invoking hooks */
247static int nat_invoke_hook(void *obj, void *arg, int flags)
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}
258
259static void restore_orig_contact_host(pjsip_tx_data *tdata)
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}
317
318static pj_status_t process_nat(pjsip_tx_data *tdata)
319{
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.
363 */
364 if (!cseq || tdata->msg->type == PJSIP_REQUEST_MSG ||
365 pjsip_method_cmp(&cseq->method, &pjsip_register_method)) {
366 /* We can only rewrite the URI when one is present */
367 if (uri || (uri = ast_sip_get_contact_sip_uri(tdata))) {
368 pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
369 if (transport->external_signaling_port) {
370 uri->port = transport->external_signaling_port;
371 ast_debug(4, "Re-wrote Contact URI port to %d\n", uri->port);
372 }
373 }
374 }
375
376 /* Update the via header if relevant */
377 if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) {
378 pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
379 if (transport->external_signaling_port) {
380 via->sent_by.port = transport->external_signaling_port;
381 }
382 }
383 }
384
385 /* Invoke any additional hooks that may be registered */
387 struct nat_hook_details hook_details = {
388 .tdata = tdata,
389 .transport = transport,
390 };
391 ao2_callback(hooks, 0, nat_invoke_hook, &hook_details);
392 }
393
394 return PJ_SUCCESS;
395}
396
397static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata) {
398 pj_status_t rc;
399
400 rc = process_nat(tdata);
402
403 return rc;
404}
405
406
407static pjsip_module nat_module = {
408 .name = { "NAT", 3 },
409 .id = -1,
410 .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2,
411 .on_rx_request = nat_on_rx_message,
412 .on_rx_response = nat_on_rx_message,
413 .on_tx_request = nat_on_tx_message,
414 .on_tx_response = nat_on_tx_message,
415};
416
417/*! \brief Function called when an INVITE goes out */
418static int nat_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
419{
420 if (session->inv_session->state == PJSIP_INV_STATE_INCOMING) {
421 pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
422 }
423
424 return 0;
425}
426
427/*! \brief Function called when an INVITE response comes in */
428static void nat_incoming_invite_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
429{
430 handle_rx_message(session->endpoint, rdata);
431}
432
433/*! \brief Function called when an INVITE comes in */
434static void nat_outgoing_invite_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
435{
436 if (session->inv_session->state == PJSIP_INV_STATE_NULL) {
437 pjsip_dlg_add_usage(session->inv_session->dlg, &nat_module, NULL);
438 }
439}
440
441/*! \brief Supplement for adding NAT functionality to dialog */
443 .method = "INVITE",
444 .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST + 1,
445 .incoming_request = nat_incoming_invite_request,
446 .outgoing_request = nat_outgoing_invite_request,
447 .incoming_response = nat_incoming_invite_response,
448};
449
450
451static int unload_module(void)
452{
455 return 0;
456}
457
458static int load_module(void)
459{
461 ast_log(LOG_ERROR, "Could not register NAT module for incoming and outgoing requests\n");
463 }
464
466
468}
469
471 .support_level = AST_MODULE_SUPPORT_CORE,
472 .load = load_module,
473 .unload = unload_module,
474 .load_pri = AST_MODPRI_APP_DEPEND,
475 .requires = "res_pjsip,res_pjsip_session",
Access Control of various sorts.
Asterisk main include file. File version handling, generic pbx functions.
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
Asterisk module definitions.
@ 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:3180
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:212
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)
Definition: res_pjsip_nat.c:43
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)
Definition: res_pjsip_nat.c:40
#define AST_SIP_X_AST_ORIG_HOST_LEN
Definition: res_pjsip_nat.c:38
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
Definition: res_pjsip_nat.c:37
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)
Definition: res_pjsip_nat.c:69
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.
Definition: pjsip_session.c:63
#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:1853
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:1897
Generic container type.
An entity with which Asterisk communicates.
Definition: res_pjsip.h:958
struct ast_sip_endpoint_nat_configuration nat
Definition: res_pjsip.h:995
Structure for SIP nat hook information.
Definition: res_pjsip.h:326
void(* outgoing_external_message)(struct pjsip_tx_data *tdata, struct ast_sip_transport *transport)
Definition: res_pjsip.h:330
Structure which contains information about a transport.
Definition: res_pjsip.h:334
A supplement to SIP message processing.
A structure describing a SIP session.
Structure for SIP transport information.
Definition: res_pjsip.h:116
Transport to bind to.
Definition: res_pjsip.h:218
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:941