Asterisk - The Open Source Telephony Project GIT-master-a358458
res_pjsip_endpoint_identifier_ip.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 * Mark Michelson <mmichelson@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
29#include "asterisk/res_pjsip.h"
31#include "asterisk/module.h"
32#include "asterisk/acl.h"
33#include "asterisk/manager.h"
35
36/*** DOCUMENTATION
37 <configInfo name="res_pjsip_endpoint_identifier_ip" language="en_US">
38 <synopsis>Module that identifies endpoints</synopsis>
39 <configFile name="pjsip.conf">
40 <configObject name="identify">
41 <synopsis>Identifies endpoints via some criteria.</synopsis>
42 <description>
43 <para>This module provides alternatives to matching inbound requests to
44 a configured endpoint. At least one of the matching mechanisms
45 must be provided, or the object configuration is invalid.</para>
46 <para>The matching mechanisms are provided by the following
47 configuration options:</para>
48 <enumlist>
49 <enum name="match"><para>Match by source IP address.</para></enum>
50 <enum name="match_header"><para>Match by SIP header.</para></enum>
51 </enumlist>
52 <note><para>If multiple matching criteria are provided then an inbound
53 request will be matched to the endpoint if it matches
54 <emphasis>any</emphasis> of the criteria.</para></note>
55 </description>
56 <configOption name="endpoint">
57 <synopsis>Name of endpoint identified</synopsis>
58 </configOption>
59 <configOption name="match">
60 <synopsis>IP addresses or networks to match against.</synopsis>
61 <description>
62 <para>The value is a comma-delimited list of IP addresses or
63 hostnames.</para>
64 <para>IP addresses may have a subnet mask appended. The subnet
65 mask may be written in either CIDR or dotted-decimal
66 notation. Separate the IP address and subnet mask with a slash
67 ('/'). A source port can also be specified by adding a colon (':')
68 after the address but before the subnet mask, e.g.
69 3.2.1.0:5061/24. To specify a source port for an IPv6 address, the
70 address itself must be enclosed in square brackets
71 ('[2001:db8:0::1]:5060')</para>
72 <para>When a hostname is used, the behavior depends on whether
73 <replaceable>srv_lookups</replaceable> is enabled and/or a source
74 port is provided. If <replaceable>srv_lookups</replaceable> is
75 enabled and a source port is not provided, Asterisk will perform
76 an SRV lookup on the provided hostname, adding all of the A and
77 AAAA records that are resolved.</para>
78 <para>If the SRV lookup fails,
79 <replaceable>srv_lookups</replaceable> is disabled, or a source
80 port is specified when the hostname is configured, Asterisk will
81 resolve the hostname and add all A and AAAA records that are
82 resolved.</para>
83 </description>
84 </configOption>
85 <configOption name="srv_lookups" default="yes">
86 <synopsis>Perform SRV lookups for provided hostnames.</synopsis>
87 <description>
88 <para>When enabled, <replaceable>srv_lookups</replaceable> will
89 perform SRV lookups for _sip._udp, _sip._tcp, and _sips._tcp of
90 the given hostnames to determine additional addresses that traffic
91 may originate from.
92 </para>
93 </description>
94 </configOption>
95 <configOption name="match_header">
96 <synopsis>Header/value pair to match against.</synopsis>
97 <description>
98 <para>A SIP header whose value is used to match against. SIP
99 requests containing the header, along with the specified value,
100 will be mapped to the specified endpoint. The header must be
101 specified with a <literal>:</literal>, as in
102 <literal>match_header = SIPHeader: value</literal>.
103 </para>
104 <para>The specified SIP header value can be a regular
105 expression if the value is of the form
106 /<replaceable>regex</replaceable>/.
107 </para>
108 <note><para>Use of a regex is expensive so be sure you need
109 to use a regex to match your endpoint.
110 </para></note>
111 </description>
112 </configOption>
113 <configOption name="match_request_uri">
114 <synopsis>Request URI to match against.</synopsis>
115 <description>
116 <para>The SIP request URI is used to match against.
117 </para>
118 <para>The specified SIP request URI can be a regular
119 expression if the value is of the form
120 /<replaceable>regex</replaceable>/.
121 </para>
122 <note><para>Use of a regex is expensive so be sure you need
123 to use a regex to match your endpoint.
124 </para></note>
125 </description>
126 </configOption>
127 <configOption name="transport">
128 <synopsis>Match against a transport type.</synopsis>
129 <description>
130 <para>When using the ip or transport identifier, this option
131 can be used to match the transport type <literal>(udp or tcp)
132 </literal> as well.</para>
133 <para>When omitted, or left empty, which is the default, it
134 won't match against the transport type.</para>
135 </description>
136 </configOption>
137 <configOption name="type">
138 <synopsis>Must be of type 'identify'.</synopsis>
139 </configOption>
140 </configObject>
141 </configFile>
142 </configInfo>
143 ***/
144
145/*! \brief The number of buckets for storing hosts for resolution */
146#define HOSTS_BUCKETS 53
147
148/*! \brief Structure for an IP identification matching object */
150 /*! \brief Sorcery object details */
152 /*! \brief Stringfields */
154 /*! The name of the endpoint */
156 /*! If matching by request, the value to match against */
158 /*! If matching by header, the header/value to match against */
160 /*! SIP header name of the match_header string */
162 /*! SIP header value of the match_header string */
164 /*! The name of the transport type */
166 );
167 /*! Compiled match_header regular expression when is_header_regex is non-zero */
169 /*! Compiled match_request_uri regular expression when is_request_uri_regex is non-zero */
171 /*! \brief Networks or addresses that should match this */
173 /*! \brief Hosts to be resolved when applying configuration */
175 /*! \brief Perform SRV resolution of hostnames */
176 unsigned int srv_lookups;
177 /*! Non-zero if match_header has a regular expression (i.e., regex_header_buf is valid) */
178 unsigned int is_header_regex:1;
179 /*! Non-zero if match_header or match_request has a regular expression (i.e., regex_request_uri_buf is valid) */
180 unsigned int is_request_uri_regex:1;
181};
182
183/*! \brief Structure for a socket address with transport */
186 char tp[128];
187};
188
189/*! \brief Destructor function for a matching object */
190static void ip_identify_destroy(void *obj)
191{
192 struct ip_identify_match *identify = obj;
193
195 ast_free_ha(identify->matches);
196 ao2_cleanup(identify->hosts);
197 if (identify->is_header_regex) {
198 regfree(&identify->regex_header_buf);
199 }
200 if (identify->is_request_uri_regex) {
201 regfree(&identify->regex_request_uri_buf);
202 }
203}
204
205/*! \brief Allocator function for a matching object */
206static void *ip_identify_alloc(const char *name)
207{
208 struct ip_identify_match *identify = ast_sorcery_generic_alloc(sizeof(*identify), ip_identify_destroy);
209
210 if (!identify || ast_string_field_init(identify, 256)) {
211 ao2_cleanup(identify);
212 return NULL;
213 }
214
215 return identify;
216}
217
218/*! \brief Comparator function for matching an object by header */
219static int header_identify_match_check(void *obj, void *arg, int flags)
220{
221 struct ip_identify_match *identify = obj;
222 struct pjsip_rx_data *rdata = arg;
223 pjsip_hdr *header;
224 pj_str_t pj_header_name;
225 int header_present;
226
227 if (ast_strlen_zero(identify->match_header)) {
228 return 0;
229 }
230
231 pj_header_name = pj_str((void *) identify->match_header_name);
232
233 /* Check all headers of the given name for a match. */
234 header_present = 0;
235 for (header = NULL;
236 (header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &pj_header_name, header));
237 header = header->next) {
238 char *pos;
239 int len;
240 char buf[PATH_MAX];
241
242 header_present = 1;
243
244 /* Print header line to buf */
245 len = pjsip_hdr_print_on(header, buf, sizeof(buf) - 1);
246 if (len < 0) {
247 /* Buffer not large enough or no header vptr! */
248 ast_assert(0);
249 continue;
250 }
251 buf[len] = '\0';
252
253 /* Remove header name from pj_buf and trim blanks. */
254 pos = strchr(buf, ':');
255 if (!pos) {
256 /* No header name? Bug in PJPROJECT if so. */
257 ast_assert(0);
258 continue;
259 }
260 pos = ast_strip(pos + 1);
261
262 /* Does header value match what we are looking for? */
263 if (identify->is_header_regex) {
264 if (!regexec(&identify->regex_header_buf, pos, 0, NULL, 0)) {
265 return CMP_MATCH;
266 }
267 } else if (!strcmp(identify->match_header_value, pos)) {
268 return CMP_MATCH;
269 }
270
271 ast_debug(3, "Identify '%s': SIP message has '%s' header but value '%s' does not match '%s'.\n",
273 identify->match_header_name,
274 pos,
275 identify->match_header_value);
276 }
277 if (!header_present) {
278 ast_debug(3, "Identify '%s': SIP message does not have '%s' header.\n",
280 identify->match_header_name);
281 }
282 return 0;
283}
284
285/*! \brief Comparator function for matching an object by request URI */
286static int request_identify_match_check(void *obj, void *arg, int flags)
287{
288 struct ip_identify_match *identify = obj;
289 struct pjsip_rx_data *rdata = arg;
290 int len;
291 char buf[PJSIP_MAX_URL_SIZE];
292
293 if (ast_strlen_zero(identify->match_request_uri)) {
294 return 0;
295 }
296
297 /* Print request URI to req_buf */
298 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, rdata->msg_info.msg->line.req.uri, buf, sizeof(buf) - 1);
299 if (len < 0) {
300 /* Buffer not large enough or no pj uri vptr! */
301 ast_assert(0);
302 } else {
303 /* Terminate the pj_str */
304 buf[len] = '\0';
305 /* Does request URI match what we are looking for? */
306 if (identify->is_request_uri_regex) {
307 if (!regexec(&identify->regex_request_uri_buf, buf, 0, NULL, 0)) {
308 return CMP_MATCH;
309 }
310 } else if (!strcmp(identify->match_request_uri, buf)) {
311 return CMP_MATCH;
312 }
313 ast_debug(3, "Identify '%s': request URI not match '%s' (value='%s').\n",
314 ast_sorcery_object_get_id(identify), identify->match_request_uri, buf);
315 }
316
317 return 0;
318}
319
320/*! \brief Comparator function for matching an object by IP address */
321static int ip_identify_match_check(void *obj, void *arg, int flags)
322{
323 struct ip_identify_match *identify = obj;
324 struct ast_sockaddr_with_tp *addr_with_tp = arg;
325 struct ast_sockaddr address = addr_with_tp->addr;
326 int sense;
327
328 sense = ast_apply_ha(identify->matches, &address);
329 if (sense != AST_SENSE_ALLOW) {
330 ast_debug(3, "Address %s matches identify '%s'\n",
332 ast_sorcery_object_get_id(identify));
333 if (ast_strlen_zero(identify->transport) || !strcasecmp(identify->transport, addr_with_tp->tp)) {
334 ast_debug(3, "Transport %s matches identify '%s'\n",
335 addr_with_tp->tp,
336 ast_sorcery_object_get_id(identify));
337 return CMP_MATCH;
338 } else {
339 ast_debug(3, "Transport %s match not matched identify '%s'\n",
340 addr_with_tp->tp,
341 ast_sorcery_object_get_id(identify));
342 return 0;
343 }
344 } else {
345 ast_debug(3, "Address %s does not match identify '%s'\n",
347 ast_sorcery_object_get_id(identify));
348 return 0;
349 }
350}
351
352static struct ast_sip_endpoint *common_identify(ao2_callback_fn *identify_match_cb, void *arg)
353{
354 RAII_VAR(struct ao2_container *, candidates, NULL, ao2_cleanup);
355 struct ip_identify_match *match;
356 struct ast_sip_endpoint *endpoint;
357
358 /* If no possibilities exist return early to save some time */
359 candidates = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
361 if (!candidates || !ao2_container_count(candidates)) {
362 ast_debug(3, "No identify sections to match against\n");
363 return NULL;
364 }
365
366 match = ao2_callback(candidates, 0, identify_match_cb, arg);
367 if (!match) {
368 return NULL;
369 }
370
371 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
372 match->endpoint_name);
373 if (endpoint) {
374 ast_debug(3, "Identify '%s' SIP message matched to endpoint %s\n",
375 ast_sorcery_object_get_id(match), match->endpoint_name);
376 } else {
377 ast_log(LOG_WARNING, "Identify '%s' points to endpoint '%s' but endpoint could not be found\n",
378 ast_sorcery_object_get_id(match), match->endpoint_name);
379 }
380
381 ao2_ref(match, -1);
382 return endpoint;
383}
384
385static struct ast_sip_endpoint *ip_identify(pjsip_rx_data *rdata)
386{
387 struct ast_sockaddr_with_tp addr_with_tp = { { { 0, } }, };
388 pj_ansi_strxcpy(addr_with_tp.tp, rdata->tp_info.transport->type_name, sizeof(addr_with_tp.tp));
389
390 ast_sockaddr_parse(&addr_with_tp.addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
391 ast_sockaddr_set_port(&addr_with_tp.addr, rdata->pkt_info.src_port);
392
393 return common_identify(ip_identify_match_check, &addr_with_tp);
394}
395
396static struct ast_sip_endpoint *transport_identify(pjsip_rx_data *rdata)
397{
398 char buffer[PJ_INET6_ADDRSTRLEN];
399 pj_status_t status;
400 struct ast_sockaddr_with_tp addr_with_tp = { { { 0, } }, };
401 union pj_sockaddr sock = rdata->tp_info.transport->local_addr;
402
403 pj_ansi_strxcpy(addr_with_tp.tp, rdata->tp_info.transport->type_name, sizeof(addr_with_tp.tp));
404
405 if (sock.addr.sa_family == PJ_AF_INET6) {
406 status = pj_inet_ntop(PJ_AF_INET6, &(sock.ipv6.sin6_addr), buffer, PJ_INET6_ADDRSTRLEN);
407 if (status == PJ_SUCCESS && !strcmp(buffer, "::")) {
408 ast_log(LOG_WARNING, "Matching against '::' may be unpredictable.\n");
409 }
410 } else {
411 status = pj_inet_ntop(PJ_AF_INET, &(sock.ipv4.sin_addr), buffer, PJ_INET_ADDRSTRLEN);
412 if (status == PJ_SUCCESS && !strcmp(buffer, "0.0.0.0")) {
413 ast_log(LOG_WARNING, "Matching against '0.0.0.0' may be unpredictable.\n");
414 }
415 }
416
417 if (status == PJ_SUCCESS) {
418 ast_sockaddr_parse(&addr_with_tp.addr, buffer, PARSE_PORT_FORBID);
419 ast_sockaddr_set_port(&addr_with_tp.addr, rdata->tp_info.transport->local_name.port);
420
421 return common_identify(ip_identify_match_check, &addr_with_tp);
422 } else {
423 return NULL;
424 }
425}
426
429};
430
433};
434
435static struct ast_sip_endpoint *header_identify(pjsip_rx_data *rdata)
436{
438}
439
440static struct ast_sip_endpoint *request_identify(pjsip_rx_data *rdata)
441{
443}
444
447};
448
451};
452
453/*! \brief Helper function which performs a host lookup and adds result to identify match */
454static int ip_identify_match_host_lookup(struct ip_identify_match *identify, const char *host)
455{
456 struct ast_sockaddr *addrs;
457 int num_addrs = 0, error = 0, i;
458 int results = 0;
459
460 num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC);
461 if (!num_addrs) {
462 return -1;
463 }
464
465 for (i = 0; i < num_addrs; ++i) {
466 /* Check if the address is already in the list, if so don't add it again */
467 if (identify->matches && (ast_apply_ha(identify->matches, &addrs[i]) != AST_SENSE_ALLOW)) {
468 continue;
469 }
470
471 /* We deny what we actually want to match because there is an implicit permit all rule for ACLs */
472 identify->matches = ast_append_ha_with_port("d", ast_sockaddr_stringify(&addrs[i]), identify->matches, &error);
473
474 if (!identify->matches || error) {
475 results = -1;
476 break;
477 }
478
479 results += 1;
480 }
481
482 ast_free(addrs);
483
484 return results;
485}
486
487/*! \brief Helper function which performs an SRV lookup and then resolves the hostname */
488static int ip_identify_match_srv_lookup(struct ip_identify_match *identify, const char *prefix, const char *host, int results)
489{
490 char service[NI_MAXHOST];
491 struct srv_context *context = NULL;
492 int srv_ret;
493 const char *srvhost;
494 unsigned short srvport;
495
496 snprintf(service, sizeof(service), "%s.%s", prefix, host);
497
498 while (!(srv_ret = ast_srv_lookup(&context, service, &srvhost, &srvport))) {
499 int hosts;
500
501 /* In the case of the SRV lookup we don't care if it fails, we will output a log message
502 * when we fallback to a normal lookup.
503 */
504 hosts = ip_identify_match_host_lookup(identify, srvhost);
505 if (hosts == -1) {
506 results = -1;
507 break;
508 } else {
509 results += hosts;
510 }
511 }
512
514
515 return results;
516}
517
518/*! \brief Custom handler for match field */
519static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
520{
521 struct ip_identify_match *identify = obj;
522 char *input_string = ast_strdupa(var->value);
523 char *current_string;
524
525 if (ast_strlen_zero(var->value)) {
526 return 0;
527 }
528
529 while ((current_string = ast_strip(strsep(&input_string, ",")))) {
530 char *mask;
531 struct ast_sockaddr address;
532 int error = 0;
533
534 if (ast_strlen_zero(current_string)) {
535 continue;
536 }
537
538 mask = strrchr(current_string, '/');
539
540 /* If it looks like a netmask is present, or we can immediately parse as an IP,
541 * hand things off to the ACL */
542 if (mask || ast_sockaddr_parse(&address, current_string, 0)) {
543 identify->matches = ast_append_ha_with_port("d", current_string, identify->matches, &error);
544
545 if (!identify->matches || error) {
546 ast_log(LOG_ERROR, "Failed to add address '%s' to ip endpoint identifier '%s'\n",
547 current_string, ast_sorcery_object_get_id(obj));
548 return -1;
549 }
550
551 continue;
552 }
553
554 if (!identify->hosts) {
556 if (!identify->hosts) {
557 ast_log(LOG_ERROR, "Failed to create container to store hosts on ip endpoint identifier '%s'\n",
559 return -1;
560 }
561 }
562
563 error = ast_str_container_add(identify->hosts, current_string);
564 if (error) {
565 ast_log(LOG_ERROR, "Failed to store host '%s' for resolution on ip endpoint identifier '%s'\n",
566 current_string, ast_sorcery_object_get_id(obj));
567 return -1;
568 }
569 }
570
571 return 0;
572}
573
574/*! \brief Apply handler for identify type */
575static int ip_identify_apply(const struct ast_sorcery *sorcery, void *obj)
576{
577 struct ip_identify_match *identify = obj;
578 char *current_string;
579 struct ao2_iterator i;
580
581 /* Validate the identify object configuration */
582 if (ast_strlen_zero(identify->endpoint_name)) {
583 ast_log(LOG_ERROR, "Identify '%s' missing required endpoint name.\n",
584 ast_sorcery_object_get_id(identify));
585 return -1;
586 }
587 if (ast_strlen_zero(identify->match_header) /* No header to match */
588 && ast_strlen_zero(identify->match_request_uri) /* and no request to match */
589 /* and no static IP addresses with a mask */
590 && !identify->matches
591 /* and no addresses to resolve */
592 && (!identify->hosts || !ao2_container_count(identify->hosts))) {
593 ast_log(LOG_ERROR, "Identify '%s' is not configured to match anything.\n",
594 ast_sorcery_object_get_id(identify));
595 return -1;
596 }
597
598 if (!ast_strlen_zero(identify->transport)) {
599 if (ast_string_field_set(identify, transport, identify->transport)) {
600 return -1;
601 }
602 }
603
604 if (!ast_strlen_zero(identify->match_header)) {
605 char *c_header;
606 char *c_value;
607 int len;
608
609 /* Split the header name and value */
610 c_header = ast_strdupa(identify->match_header);
611 c_value = strchr(c_header, ':');
612 if (!c_value) {
613 ast_log(LOG_ERROR, "Identify '%s' missing ':' separator in match_header '%s'.\n",
614 ast_sorcery_object_get_id(identify), identify->match_header);
615 return -1;
616 }
617 *c_value = '\0';
618 c_value = ast_strip(c_value + 1);
619 c_header = ast_strip(c_header);
620
621 if (ast_strlen_zero(c_header)) {
622 ast_log(LOG_ERROR, "Identify '%s' has no SIP header to match in match_header '%s'.\n",
623 ast_sorcery_object_get_id(identify), identify->match_header);
624 return -1;
625 }
626
627 if (!strcmp(c_value, "//")) {
628 /* An empty regex is the same as an empty literal string. */
629 c_value = "";
630 }
631
632 if (ast_string_field_set(identify, match_header_name, c_header)
633 || ast_string_field_set(identify, match_header_value, c_value)) {
634 return -1;
635 }
636
637 len = strlen(c_value);
638 if (2 < len && c_value[0] == '/' && c_value[len - 1] == '/') {
639 /* Make "/regex/" into "regex" */
640 c_value[len - 1] = '\0';
641 ++c_value;
642
643 if (regcomp(&identify->regex_header_buf, c_value, REG_EXTENDED | REG_NOSUB)) {
644 ast_log(LOG_ERROR, "Identify '%s' failed to compile match_request_uri regex '%s'.\n",
645 ast_sorcery_object_get_id(identify), c_value);
646 return -1;
647 }
648 identify->is_header_regex = 1;
649 }
650 }
651
652 if (!ast_strlen_zero(identify->match_request_uri)) {
653 char *c_string;
654 int len;
655
656 len = strlen(identify->match_request_uri);
657 c_string = ast_strdupa(identify->match_request_uri);
658
659 if (!strcmp(c_string, "//")) {
660 /* An empty regex is the same as an empty literal string. */
661 c_string = "";
662 }
663
664 if (2 < len && c_string[0] == '/' && c_string[len - 1] == '/') {
665 /* Make "/regex/" into "regex" */
666 c_string[len - 1] = '\0';
667 ++c_string;
668
669 if (regcomp(&identify->regex_request_uri_buf, c_string, REG_EXTENDED | REG_NOSUB)) {
670 ast_log(LOG_ERROR, "Identify '%s' failed to compile match_header regex '%s'.\n",
671 ast_sorcery_object_get_id(identify), c_string);
672 return -1;
673 }
674 identify->is_request_uri_regex = 1;
675 }
676 }
677
678 if (!identify->hosts) {
679 /* No match addresses to resolve */
680 return 0;
681 }
682
683 /* Hosts can produce dynamic content, so mark the identify as such */
685
686 /* Resolve the match addresses now */
687 i = ao2_iterator_init(identify->hosts, 0);
688 while ((current_string = ao2_iterator_next(&i))) {
689 int results = 0;
690 char *colon = strrchr(current_string, ':');
691
692 /* We skip SRV lookup if a colon is present, assuming a port was specified */
693 if (!colon) {
694 /* No port, and we know this is not an IP address, so perform SRV resolution on it */
695 if (identify->srv_lookups) {
696 results = ip_identify_match_srv_lookup(identify, "_sip._udp", current_string,
697 results);
698 if (results != -1) {
699 results = ip_identify_match_srv_lookup(identify, "_sip._tcp",
700 current_string, results);
701 }
702 if (results != -1) {
703 results = ip_identify_match_srv_lookup(identify, "_sips._tcp",
704 current_string, results);
705 }
706 }
707 }
708
709 /* If SRV fails fall back to a normal lookup on the host itself */
710 if (!results) {
711 results = ip_identify_match_host_lookup(identify, current_string);
712 }
713
714 if (results == 0) {
715 ast_log(LOG_WARNING, "Identify '%s' provided address '%s' did not resolve to any address\n",
716 ast_sorcery_object_get_id(identify), current_string);
717 } else if (results == -1) {
718 ast_log(LOG_ERROR, "Identify '%s' failed when adding resolution results of '%s'\n",
719 ast_sorcery_object_get_id(identify), current_string);
720 ao2_ref(current_string, -1);
722 return -1;
723 }
724
725 ao2_ref(current_string, -1);
726 }
728
729 ao2_ref(identify->hosts, -1);
730 identify->hosts = NULL;
731
732 return 0;
733}
734
735static int match_to_str(const void *obj, const intptr_t *args, char **buf)
736{
738 const struct ip_identify_match *identify = obj;
739
740 ast_ha_join(identify->matches, &str);
742 return 0;
743}
744
745static void match_to_var_list_append(struct ast_variable **head, struct ast_ha *ha)
746{
747 char str[MAX_OBJECT_FIELD];
748 const char *addr;
749
750 if (ast_sockaddr_port(&ha->addr)) {
752 } else {
754 }
755
756 snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "",
758
759 ast_variable_list_append(head, ast_variable_new("match", str, ""));
760}
761
762static int match_to_var_list(const void *obj, struct ast_variable **fields)
763{
764 const struct ip_identify_match *identify = obj;
765 struct ast_variable *head = NULL;
766 struct ast_ha *ha = identify->matches;
767
768 for (; ha; ha = ha->next) {
769 match_to_var_list_append(&head, ha);
770 }
771
772 if (head) {
773 *fields = head;
774 }
775
776 return 0;
777}
778
779static int sip_identify_to_ami(const struct ip_identify_match *identify,
780 struct ast_str **buf)
781{
782 return ast_sip_sorcery_object_to_ami(identify, buf);
783}
784
785static int send_identify_ami_event(void *obj, void *arg, void *data, int flags)
786{
787 struct ip_identify_match *identify = obj;
788 const char *endpoint_name = arg;
789 struct ast_sip_ami *ami = data;
790 struct ast_str *buf;
791
792 /* Build AMI event */
793 buf = ast_sip_create_ami_event("IdentifyDetail", ami);
794 if (!buf) {
795 return CMP_STOP;
796 }
797 if (sip_identify_to_ami(identify, &buf)) {
798 ast_free(buf);
799 return CMP_STOP;
800 }
801 ast_str_append(&buf, 0, "EndpointName: %s\r\n", endpoint_name);
802
803 /* Send AMI event */
804 astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
805 ++ami->count;
806
807 ast_free(buf);
808 return 0;
809}
810
811static int format_ami_endpoint_identify(const struct ast_sip_endpoint *endpoint,
812 struct ast_sip_ami *ami)
813{
814 struct ao2_container *identifies;
815 struct ast_variable fields = {
816 .name = "endpoint",
817 .value = ast_sorcery_object_get_id(endpoint),
818 };
819
820 identifies = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
822 if (!identifies) {
823 return -1;
824 }
825
826 /* Build and send any found identify object's AMI IdentifyDetail event. */
829 (void *) ast_sorcery_object_get_id(endpoint),
830 ami);
831
832 ao2_ref(identifies, -1);
833 return 0;
834}
835
838};
839
840static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
841{
842 const struct ast_sip_endpoint *endpoint = container;
843 struct ao2_container *identifies;
844
845 struct ast_variable fields = {
846 .name = "endpoint",
847 .value = ast_sorcery_object_get_id(endpoint),
848 .next = NULL,
849 };
850
851 identifies = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
853 if (!identifies) {
854 return -1;
855 }
856
857 ao2_callback(identifies, OBJ_NODATA, callback, args);
858 ao2_cleanup(identifies);
859
860 return 0;
861}
862
863static struct ao2_container *cli_get_container(const char *regex)
864{
866 struct ao2_container *s_container;
867
869 if (!container) {
870 return NULL;
871 }
872
875 if (!s_container) {
876 return NULL;
877 }
878
879 if (ao2_container_dup(s_container, container, 0)) {
880 ao2_ref(s_container, -1);
881 return NULL;
882 }
883
884 return s_container;
885}
886
887static void *cli_retrieve_by_id(const char *id)
888{
889 return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "identify", id);
890}
891
892static int cli_print_header(void *obj, void *arg, int flags)
893{
894 struct ast_sip_cli_context *context = arg;
895 int indent = CLI_INDENT_TO_SPACES(context->indent_level);
896 int filler = CLI_MAX_WIDTH - indent - 22;
897
898 ast_assert(context->output_buffer != NULL);
899
900 ast_str_append(&context->output_buffer, 0,
901 "%*s: <Identify/Endpoint%*.*s>\n",
902 indent, "Identify", filler, filler, CLI_HEADER_FILLER);
903
904 if (context->recurse) {
905 context->indent_level++;
906 indent = CLI_INDENT_TO_SPACES(context->indent_level);
907 filler = CLI_LAST_TABSTOP - indent - 24;
908
909 ast_str_append(&context->output_buffer, 0,
910 "%*s: <criteria%*.*s>\n",
911 indent, "Match", filler, filler, CLI_HEADER_FILLER);
912
913 context->indent_level--;
914 }
915
916 return 0;
917}
918
919static int cli_print_body(void *obj, void *arg, int flags)
920{
922 struct ip_identify_match *ident = obj;
923 struct ast_sip_cli_context *context = arg;
924 struct ast_ha *match;
925 int indent;
926
927 ast_assert(context->output_buffer != NULL);
928
929 ast_str_append(&context->output_buffer, 0, "%*s: %s/%s\n",
930 CLI_INDENT_TO_SPACES(context->indent_level), "Identify",
932
933 if (context->recurse) {
934 context->indent_level++;
935 indent = CLI_INDENT_TO_SPACES(context->indent_level);
936
937 for (match = ident->matches; match; match = match->next) {
938 const char *addr;
939
940 if (ast_sockaddr_port(&match->addr)) {
942 } else {
944 }
945
946 ast_str_append(&context->output_buffer, 0, "%*s: %s%s/%d\n",
947 indent,
948 "Match",
949 match->sense == AST_SENSE_ALLOW ? "!" : "",
950 addr, ast_sockaddr_cidr_bits(&match->netmask));
951 }
952
953 if (!ast_strlen_zero(ident->transport)) {
954 ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
955 indent,
956 "Transport",
957 ident->transport);
958 }
959
960 if (!ast_strlen_zero(ident->match_header)) {
961 ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
962 indent,
963 "Header",
964 ident->match_header);
965 }
966
967 if (!ast_strlen_zero(ident->match_request_uri)) {
968 ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
969 indent,
970 "RequestURI",
971 ident->match_request_uri);
972 }
973
974 context->indent_level--;
975
976 if (context->indent_level == 0) {
977 ast_str_append(&context->output_buffer, 0, "\n");
978 }
979 }
980
981 if (context->show_details
982 || (context->show_details_only_level_0 && context->indent_level == 0)) {
983 ast_str_append(&context->output_buffer, 0, "\n");
985 }
986
987 return 0;
988}
989
990/*
991 * A function pointer to callback needs to be within the
992 * module in order to avoid problems with an undefined
993 * symbol when the module is loaded.
994 */
995static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd,
996 struct ast_cli_args *a)
997{
998 return ast_sip_cli_traverse_objects(e, cmd, a);
999}
1000
1001static struct ast_cli_entry cli_identify[] = {
1002AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Identifies",
1003 .command = "pjsip list identifies",
1004 .usage = "Usage: pjsip list identifies [ like <pattern> ]\n"
1005 " List the configured PJSIP Identifies\n"
1006 " Optional regular expression pattern is used to filter the list.\n"),
1007AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identifies",
1008 .command = "pjsip show identifies",
1009 .usage = "Usage: pjsip show identifies [ like <pattern> ]\n"
1010 " Show the configured PJSIP Identifies\n"
1011 " Optional regular expression pattern is used to filter the list.\n"),
1012AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identify",
1013 .command = "pjsip show identify",
1014 .usage = "Usage: pjsip show identify <id>\n"
1015 " Show the configured PJSIP Identify\n"),
1016};
1017
1019
1020static int load_module(void)
1021{
1022 ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_endpoint_identifier_ip");
1023 ast_sorcery_apply_default(ast_sip_get_sorcery(), "identify", "config", "pjsip.conf,criteria=type=identify");
1024
1027 }
1028
1029 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0);
1030 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name));
1032 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "match_header", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_header));
1033 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "match_request_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_request_uri));
1034 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "srv_lookups", "yes", OPT_BOOL_T, 1, FLDSET(struct ip_identify_match, srv_lookups));
1035 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, transport));
1037
1043
1045 if (!cli_formatter) {
1046 ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
1048 }
1049 cli_formatter->name = "identify";
1056
1059
1061}
1062
1063static int reload_module(void)
1064{
1066
1067 return 0;
1068}
1069
1070static int unload_module(void)
1071{
1079
1080 return 0;
1081}
1082
1083AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP IP endpoint identifier",
1084 .support_level = AST_MODULE_SUPPORT_CORE,
1085 .load = load_module,
1087 .unload = unload_module,
1088 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
1089 .requires = "res_pjsip",
Access Control of various sorts.
void ast_free_ha(struct ast_ha *ha)
Free a list of HAs.
Definition: acl.c:222
@ AST_SENSE_ALLOW
Definition: acl.h:38
enum ast_acl_sense ast_apply_ha(const struct ast_ha *ha, const struct ast_sockaddr *addr)
Apply a set of rules to a given IP address.
Definition: acl.c:807
void ast_ha_join(const struct ast_ha *ha, struct ast_str **buf)
Convert HAs to a comma separated string value.
Definition: acl.c:722
struct ast_ha * ast_append_ha_with_port(const char *sense, const char *stuff, struct ast_ha *path, int *error)
Add a new rule with optional port to a list of HAs.
Definition: acl.c:717
jack_status_t status
Definition: app_jack.c:146
const char * str
Definition: app_jack.c:147
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define PATH_MAX
Definition: asterisk.h:40
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
Copy all object references in the src container into the dest container.
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
#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
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1723
int() ao2_callback_fn(void *obj, void *arg, int flags)
Type of a generic callback function.
Definition: astobj2.h:1226
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
enum ast_cc_service_type service
Definition: ccss.c:383
static int match(struct ast_sockaddr *addr, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition: chan_iax2.c:2362
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
@ OPT_NOOP_T
Type for a default handler that should do nothing.
@ OPT_BOOL_T
Type for default option handler for bools (ast_true/ast_false)
@ OPT_STRINGFIELD_T
Type for default option handler for stringfields.
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
char * address
Definition: f2c.h:59
static const char name[]
Definition: format_mp3.c:68
static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3302
static char prefix[MAX_PREFIX]
Definition: http.c:144
char * strsep(char **str, const char *delims)
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_WARNING
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ 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
@ AST_AF_UNSPEC
Definition: netsock2.h:54
int ast_sockaddr_cidr_bits(const struct ast_sockaddr *sa)
Count the 1 bits in a netmask.
Definition: netsock2.c:130
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition: netsock2.c:280
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
static char * ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only.
Definition: netsock2.h:286
static int reload(void)
struct ao2_container * container
Definition: res_fax.c:501
void ast_sip_unregister_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier)
Unregister a SIP endpoint identifier.
Definition: res_pjsip.c:315
void ast_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
Register an endpoint formatter.
Definition: res_pjsip.c:481
void ast_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
Unregister an endpoint formatter.
Definition: res_pjsip.c:487
int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf)
Converts a sorcery object to a string of object properties.
int ast_sip_register_endpoint_identifier_with_name(struct ast_sip_endpoint_identifier *identifier, const char *name)
Register a SIP endpoint identifier with a name.
Definition: res_pjsip.c:233
struct ast_str * ast_sip_create_ami_event(const char *event, struct ast_sip_ami *ami)
Creates a string to store AMI event data in.
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
Unregisters a CLI formatter.
Definition: pjsip_cli.c:326
#define CLI_HEADER_FILLER
Definition: res_pjsip_cli.h:24
#define CLI_MAX_WIDTH
Definition: res_pjsip_cli.h:26
int ast_sip_cli_print_sorcery_objectset(void *obj, void *arg, int flags)
Prints a sorcery object's ast_variable list.
Definition: pjsip_cli.c:36
#define CLI_LAST_TABSTOP
Definition: res_pjsip_cli.h:27
char * ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pjsip_cli.c:109
#define CLI_INDENT_TO_SPACES(x)
Definition: res_pjsip_cli.h:29
int ast_sip_register_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
Registers a CLI formatter.
Definition: pjsip_cli.c:310
static int sip_identify_to_ami(const struct ip_identify_match *identify, struct ast_str **buf)
static int request_identify_match_check(void *obj, void *arg, int flags)
Comparator function for matching an object by request URI.
static int cli_print_header(void *obj, void *arg, int flags)
static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
static void * ip_identify_alloc(const char *name)
Allocator function for a matching object.
static int ip_identify_match_check(void *obj, void *arg, int flags)
Comparator function for matching an object by IP address.
static struct ast_sip_endpoint_identifier request_identifier
static int format_ami_endpoint_identify(const struct ast_sip_endpoint *endpoint, struct ast_sip_ami *ami)
static int ip_identify_match_srv_lookup(struct ip_identify_match *identify, const char *prefix, const char *host, int results)
Helper function which performs an SRV lookup and then resolves the hostname.
static struct ast_sip_endpoint * ip_identify(pjsip_rx_data *rdata)
static int reload_module(void)
static int ip_identify_apply(const struct ast_sorcery *sorcery, void *obj)
Apply handler for identify type.
static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
Custom handler for match field.
static struct ast_sip_endpoint_identifier header_identifier
static struct ast_sip_endpoint * transport_identify(pjsip_rx_data *rdata)
#define HOSTS_BUCKETS
The number of buckets for storing hosts for resolution.
static void * cli_retrieve_by_id(const char *id)
static int send_identify_ami_event(void *obj, void *arg, void *data, int flags)
static void ip_identify_destroy(void *obj)
Destructor function for a matching object.
static int ip_identify_match_host_lookup(struct ip_identify_match *identify, const char *host)
Helper function which performs a host lookup and adds result to identify match.
static struct ast_cli_entry cli_identify[]
static void match_to_var_list_append(struct ast_variable **head, struct ast_ha *ha)
static struct ao2_container * cli_get_container(const char *regex)
static struct ast_sip_endpoint * request_identify(pjsip_rx_data *rdata)
struct ast_sip_endpoint_formatter endpoint_identify_formatter
static int load_module(void)
static int cli_print_body(void *obj, void *arg, int flags)
static struct ast_sip_endpoint * header_identify(pjsip_rx_data *rdata)
static char * my_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int unload_module(void)
static struct ast_sip_endpoint * common_identify(ao2_callback_fn *identify_match_cb, void *arg)
static struct ast_sip_endpoint_identifier transport_identifier
static struct ast_sip_cli_formatter_entry * cli_formatter
static int header_identify_match_check(void *obj, void *arg, int flags)
Comparator function for matching an object by header.
static struct ast_sip_endpoint_identifier ip_identifier
static int match_to_str(const void *obj, const intptr_t *args, char **buf)
static int match_to_var_list(const void *obj, struct ast_variable **fields)
static struct ast_sorcery * sorcery
#define NULL
Definition: resample.c:96
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
@ 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_object_set_has_dynamic_contents(const void *object)
Set the dynamic contents flag on a sorcery object.
Definition: sorcery.c:2379
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
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:837
struct ao2_container * ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex)
Retrieve multiple objects using a regular expression on their id.
Definition: sorcery.c:1949
void ast_sorcery_load_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to load persistent objects.
Definition: sorcery.c:1393
#define ast_sorcery_object_field_register_custom(sorcery, type, name, default_val, config_handler, sorcery_handler, multiple_handler, flags,...)
Register a field within an object with custom handlers.
Definition: sorcery.h:1005
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition: sorcery.c:2459
#define ast_sorcery_apply_config(sorcery, name)
Definition: sorcery.h:455
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
int ast_sorcery_object_id_sort(const void *obj, const void *arg, int flags)
ao2 object sorter based on sorcery id.
Definition: sorcery.c:2435
#define MAX_OBJECT_FIELD
Maximum length of an object field name.
Definition: sorcery.h:110
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition: sorcery.h:476
void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects.
Definition: sorcery.c:1442
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
void ast_srv_cleanup(struct srv_context **context)
Cleanup resources associated with ast_srv_lookup.
Definition: srv.c:248
int ast_srv_lookup(struct srv_context **context, const char *service, const char **host, unsigned short *port)
Retrieve set of SRV lookups, in order.
Definition: srv.c:202
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
struct ao2_container * ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets)
Allocates a hash container for bare strings.
Definition: strings.c:200
int ast_str_container_add(struct ao2_container *str_container, const char *add)
Adds a string to a string container allocated by ast_str_container_alloc.
Definition: strings.c:205
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
internal representation of ACL entries In principle user applications would have no need for this,...
Definition: acl.h:51
struct ast_sockaddr addr
Definition: acl.h:53
struct ast_sockaddr netmask
Definition: acl.h:54
struct ast_ha * next
Definition: acl.h:56
enum ast_acl_sense sense
Definition: acl.h:55
AMI variable container.
Definition: res_pjsip.h:3047
struct mansession * s
Definition: res_pjsip.h:3049
CLI Formatter Context passed to all formatters.
Definition: res_pjsip_cli.h:34
CLI Formatter Registry Entry.
Definition: res_pjsip_cli.h:52
int(* iterate)(void *container, ao2_callback_fn callback, void *args)
Definition: res_pjsip_cli.h:66
ao2_callback_fn * print_header
Definition: res_pjsip_cli.h:60
void *(* retrieve_by_id)(const char *id)
Definition: res_pjsip_cli.h:68
const char *(* get_id)(const void *obj)
Definition: res_pjsip_cli.h:70
const char * name
Definition: res_pjsip_cli.h:58
ao2_callback_fn * print_body
Definition: res_pjsip_cli.h:62
struct ao2_container *(* get_container)(const char *regex)
Definition: res_pjsip_cli.h:64
An entity responsible formatting endpoint information.
Definition: res_pjsip.h:3073
int(* format_ami)(const struct ast_sip_endpoint *endpoint, struct ast_sip_ami *ami)
Callback used to format endpoint information over AMI.
Definition: res_pjsip.h:3077
An entity responsible for identifying the source of a SIP message.
Definition: res_pjsip.h:1290
struct ast_sip_endpoint *(* identify_endpoint)(pjsip_rx_data *rdata)
Callback used to identify the source of a message. See ast_sip_identify_endpoint for more details.
Definition: res_pjsip.h:1295
An entity with which Asterisk communicates.
Definition: res_pjsip.h:963
Structure for a socket address with transport.
Socket address structure.
Definition: netsock2.h:97
Full structure for sorcery.
Definition: sorcery.c:230
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
struct header * next
Structure for an IP identification matching object.
struct ao2_container * hosts
Hosts to be resolved when applying configuration.
SORCERY_OBJECT(details)
Sorcery object details.
const ast_string_field match_header
struct ast_ha * matches
Networks or addresses that should match this.
const ast_string_field transport
unsigned int srv_lookups
Perform SRV resolution of hostnames.
const ast_string_field match_request_uri
const ast_string_field endpoint_name
const ast_string_field match_header_value
const ast_string_field match_header_name
const char * args
static struct test_val a
int error(const char *format,...)
Definition: utils/frame.c:999
char * usage
Definition: utils/frame.c:37
#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
#define ast_assert(a)
Definition: utils.h:739
#define ARRAY_LEN(a)
Definition: utils.h:666