Asterisk - The Open Source Telephony Project GIT-master-3dee037
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="type">
128 <synopsis>Must be of type 'identify'.</synopsis>
129 </configOption>
130 </configObject>
131 </configFile>
132 </configInfo>
133 ***/
134
135/*! \brief The number of buckets for storing hosts for resolution */
136#define HOSTS_BUCKETS 53
137
138/*! \brief Structure for an IP identification matching object */
140 /*! \brief Sorcery object details */
142 /*! \brief Stringfields */
144 /*! The name of the endpoint */
146 /*! If matching by request, the value to match against */
148 /*! If matching by header, the header/value to match against */
150 /*! SIP header name of the match_header string */
152 /*! SIP header value of the match_header string */
154 );
155 /*! Compiled match_header regular expression when is_header_regex is non-zero */
157 /*! Compiled match_request_uri regular expression when is_request_uri_regex is non-zero */
159 /*! \brief Networks or addresses that should match this */
161 /*! \brief Hosts to be resolved when applying configuration */
163 /*! \brief Perform SRV resolution of hostnames */
164 unsigned int srv_lookups;
165 /*! Non-zero if match_header has a regular expression (i.e., regex_header_buf is valid) */
166 unsigned int is_header_regex:1;
167 /*! Non-zero if match_header or match_request has a regular expression (i.e., regex_request_uri_buf is valid) */
168 unsigned int is_request_uri_regex:1;
169};
170
171/*! \brief Destructor function for a matching object */
172static void ip_identify_destroy(void *obj)
173{
174 struct ip_identify_match *identify = obj;
175
177 ast_free_ha(identify->matches);
178 ao2_cleanup(identify->hosts);
179 if (identify->is_header_regex) {
180 regfree(&identify->regex_header_buf);
181 }
182 if (identify->is_request_uri_regex) {
183 regfree(&identify->regex_request_uri_buf);
184 }
185}
186
187/*! \brief Allocator function for a matching object */
188static void *ip_identify_alloc(const char *name)
189{
190 struct ip_identify_match *identify = ast_sorcery_generic_alloc(sizeof(*identify), ip_identify_destroy);
191
192 if (!identify || ast_string_field_init(identify, 256)) {
193 ao2_cleanup(identify);
194 return NULL;
195 }
196
197 return identify;
198}
199
200/*! \brief Comparator function for matching an object by header */
201static int header_identify_match_check(void *obj, void *arg, int flags)
202{
203 struct ip_identify_match *identify = obj;
204 struct pjsip_rx_data *rdata = arg;
205 pjsip_hdr *header;
206 pj_str_t pj_header_name;
207 int header_present;
208
209 if (ast_strlen_zero(identify->match_header)) {
210 return 0;
211 }
212
213 pj_header_name = pj_str((void *) identify->match_header_name);
214
215 /* Check all headers of the given name for a match. */
216 header_present = 0;
217 for (header = NULL;
218 (header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &pj_header_name, header));
219 header = header->next) {
220 char *pos;
221 int len;
222 char buf[PATH_MAX];
223
224 header_present = 1;
225
226 /* Print header line to buf */
227 len = pjsip_hdr_print_on(header, buf, sizeof(buf) - 1);
228 if (len < 0) {
229 /* Buffer not large enough or no header vptr! */
230 ast_assert(0);
231 continue;
232 }
233 buf[len] = '\0';
234
235 /* Remove header name from pj_buf and trim blanks. */
236 pos = strchr(buf, ':');
237 if (!pos) {
238 /* No header name? Bug in PJPROJECT if so. */
239 ast_assert(0);
240 continue;
241 }
242 pos = ast_strip(pos + 1);
243
244 /* Does header value match what we are looking for? */
245 if (identify->is_header_regex) {
246 if (!regexec(&identify->regex_header_buf, pos, 0, NULL, 0)) {
247 return CMP_MATCH;
248 }
249 } else if (!strcmp(identify->match_header_value, pos)) {
250 return CMP_MATCH;
251 }
252
253 ast_debug(3, "Identify '%s': SIP message has '%s' header but value '%s' does not match '%s'.\n",
255 identify->match_header_name,
256 pos,
257 identify->match_header_value);
258 }
259 if (!header_present) {
260 ast_debug(3, "Identify '%s': SIP message does not have '%s' header.\n",
262 identify->match_header_name);
263 }
264 return 0;
265}
266
267/*! \brief Comparator function for matching an object by request URI */
268static int request_identify_match_check(void *obj, void *arg, int flags)
269{
270 struct ip_identify_match *identify = obj;
271 struct pjsip_rx_data *rdata = arg;
272 int len;
273 char buf[PJSIP_MAX_URL_SIZE];
274
275 if (ast_strlen_zero(identify->match_request_uri)) {
276 return 0;
277 }
278
279 /* Print request URI to req_buf */
280 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, rdata->msg_info.msg->line.req.uri, buf, sizeof(buf) - 1);
281 if (len < 0) {
282 /* Buffer not large enough or no pj uri vptr! */
283 ast_assert(0);
284 } else {
285 /* Terminate the pj_str */
286 buf[len] = '\0';
287 /* Does request URI match what we are looking for? */
288 if (identify->is_request_uri_regex) {
289 if (!regexec(&identify->regex_request_uri_buf, buf, 0, NULL, 0)) {
290 return CMP_MATCH;
291 }
292 } else if (!strcmp(identify->match_request_uri, buf)) {
293 return CMP_MATCH;
294 }
295 ast_debug(3, "Identify '%s': request URI not match '%s' (value='%s').\n",
296 ast_sorcery_object_get_id(identify), identify->match_request_uri, buf);
297 }
298
299 return 0;
300}
301
302/*! \brief Comparator function for matching an object by IP address */
303static int ip_identify_match_check(void *obj, void *arg, int flags)
304{
305 struct ip_identify_match *identify = obj;
306 struct ast_sockaddr *addr = arg;
307 int sense;
308
309 sense = ast_apply_ha(identify->matches, addr);
310 if (sense != AST_SENSE_ALLOW) {
311 ast_debug(3, "Source address %s matches identify '%s'\n",
313 ast_sorcery_object_get_id(identify));
314 return CMP_MATCH;
315 } else {
316 ast_debug(3, "Source address %s does not match identify '%s'\n",
318 ast_sorcery_object_get_id(identify));
319 return 0;
320 }
321}
322
323static struct ast_sip_endpoint *common_identify(ao2_callback_fn *identify_match_cb, void *arg)
324{
325 RAII_VAR(struct ao2_container *, candidates, NULL, ao2_cleanup);
326 struct ip_identify_match *match;
327 struct ast_sip_endpoint *endpoint;
328
329 /* If no possibilities exist return early to save some time */
330 candidates = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
332 if (!candidates || !ao2_container_count(candidates)) {
333 ast_debug(3, "No identify sections to match against\n");
334 return NULL;
335 }
336
337 match = ao2_callback(candidates, 0, identify_match_cb, arg);
338 if (!match) {
339 return NULL;
340 }
341
342 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
343 match->endpoint_name);
344 if (endpoint) {
345 ast_debug(3, "Identify '%s' SIP message matched to endpoint %s\n",
346 ast_sorcery_object_get_id(match), match->endpoint_name);
347 } else {
348 ast_log(LOG_WARNING, "Identify '%s' points to endpoint '%s' but endpoint could not be found\n",
349 ast_sorcery_object_get_id(match), match->endpoint_name);
350 }
351
352 ao2_ref(match, -1);
353 return endpoint;
354}
355
356static struct ast_sip_endpoint *ip_identify(pjsip_rx_data *rdata)
357{
358 struct ast_sockaddr addr = { { 0, } };
359
360 ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
361 ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port);
362
364}
365
368};
369
370static struct ast_sip_endpoint *header_identify(pjsip_rx_data *rdata)
371{
373}
374
375static struct ast_sip_endpoint *request_identify(pjsip_rx_data *rdata)
376{
378}
379
382};
383
386};
387
388/*! \brief Helper function which performs a host lookup and adds result to identify match */
389static int ip_identify_match_host_lookup(struct ip_identify_match *identify, const char *host)
390{
391 struct ast_sockaddr *addrs;
392 int num_addrs = 0, error = 0, i;
393 int results = 0;
394
395 num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC);
396 if (!num_addrs) {
397 return -1;
398 }
399
400 for (i = 0; i < num_addrs; ++i) {
401 /* Check if the address is already in the list, if so don't add it again */
402 if (identify->matches && (ast_apply_ha(identify->matches, &addrs[i]) != AST_SENSE_ALLOW)) {
403 continue;
404 }
405
406 /* We deny what we actually want to match because there is an implicit permit all rule for ACLs */
407 identify->matches = ast_append_ha_with_port("d", ast_sockaddr_stringify(&addrs[i]), identify->matches, &error);
408
409 if (!identify->matches || error) {
410 results = -1;
411 break;
412 }
413
414 results += 1;
415 }
416
417 ast_free(addrs);
418
419 return results;
420}
421
422/*! \brief Helper function which performs an SRV lookup and then resolves the hostname */
423static int ip_identify_match_srv_lookup(struct ip_identify_match *identify, const char *prefix, const char *host, int results)
424{
425 char service[NI_MAXHOST];
426 struct srv_context *context = NULL;
427 int srv_ret;
428 const char *srvhost;
429 unsigned short srvport;
430
431 snprintf(service, sizeof(service), "%s.%s", prefix, host);
432
433 while (!(srv_ret = ast_srv_lookup(&context, service, &srvhost, &srvport))) {
434 int hosts;
435
436 /* In the case of the SRV lookup we don't care if it fails, we will output a log message
437 * when we fallback to a normal lookup.
438 */
439 hosts = ip_identify_match_host_lookup(identify, srvhost);
440 if (hosts == -1) {
441 results = -1;
442 break;
443 } else {
444 results += hosts;
445 }
446 }
447
449
450 return results;
451}
452
453/*! \brief Custom handler for match field */
454static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
455{
456 struct ip_identify_match *identify = obj;
457 char *input_string = ast_strdupa(var->value);
458 char *current_string;
459
460 if (ast_strlen_zero(var->value)) {
461 return 0;
462 }
463
464 while ((current_string = ast_strip(strsep(&input_string, ",")))) {
465 char *mask;
466 struct ast_sockaddr address;
467 int error = 0;
468
469 if (ast_strlen_zero(current_string)) {
470 continue;
471 }
472
473 mask = strrchr(current_string, '/');
474
475 /* If it looks like a netmask is present, or we can immediately parse as an IP,
476 * hand things off to the ACL */
477 if (mask || ast_sockaddr_parse(&address, current_string, 0)) {
478 identify->matches = ast_append_ha_with_port("d", current_string, identify->matches, &error);
479
480 if (!identify->matches || error) {
481 ast_log(LOG_ERROR, "Failed to add address '%s' to ip endpoint identifier '%s'\n",
482 current_string, ast_sorcery_object_get_id(obj));
483 return -1;
484 }
485
486 continue;
487 }
488
489 if (!identify->hosts) {
491 if (!identify->hosts) {
492 ast_log(LOG_ERROR, "Failed to create container to store hosts on ip endpoint identifier '%s'\n",
494 return -1;
495 }
496 }
497
498 error = ast_str_container_add(identify->hosts, current_string);
499 if (error) {
500 ast_log(LOG_ERROR, "Failed to store host '%s' for resolution on ip endpoint identifier '%s'\n",
501 current_string, ast_sorcery_object_get_id(obj));
502 return -1;
503 }
504 }
505
506 return 0;
507}
508
509/*! \brief Apply handler for identify type */
510static int ip_identify_apply(const struct ast_sorcery *sorcery, void *obj)
511{
512 struct ip_identify_match *identify = obj;
513 char *current_string;
514 struct ao2_iterator i;
515
516 /* Validate the identify object configuration */
517 if (ast_strlen_zero(identify->endpoint_name)) {
518 ast_log(LOG_ERROR, "Identify '%s' missing required endpoint name.\n",
519 ast_sorcery_object_get_id(identify));
520 return -1;
521 }
522 if (ast_strlen_zero(identify->match_header) /* No header to match */
523 && ast_strlen_zero(identify->match_request_uri) /* and no request to match */
524 /* and no static IP addresses with a mask */
525 && !identify->matches
526 /* and no addresses to resolve */
527 && (!identify->hosts || !ao2_container_count(identify->hosts))) {
528 ast_log(LOG_ERROR, "Identify '%s' is not configured to match anything.\n",
529 ast_sorcery_object_get_id(identify));
530 return -1;
531 }
532
533 if (!ast_strlen_zero(identify->match_header)) {
534 char *c_header;
535 char *c_value;
536 int len;
537
538 /* Split the header name and value */
539 c_header = ast_strdupa(identify->match_header);
540 c_value = strchr(c_header, ':');
541 if (!c_value) {
542 ast_log(LOG_ERROR, "Identify '%s' missing ':' separator in match_header '%s'.\n",
543 ast_sorcery_object_get_id(identify), identify->match_header);
544 return -1;
545 }
546 *c_value = '\0';
547 c_value = ast_strip(c_value + 1);
548 c_header = ast_strip(c_header);
549
550 if (ast_strlen_zero(c_header)) {
551 ast_log(LOG_ERROR, "Identify '%s' has no SIP header to match in match_header '%s'.\n",
552 ast_sorcery_object_get_id(identify), identify->match_header);
553 return -1;
554 }
555
556 if (!strcmp(c_value, "//")) {
557 /* An empty regex is the same as an empty literal string. */
558 c_value = "";
559 }
560
561 if (ast_string_field_set(identify, match_header_name, c_header)
562 || ast_string_field_set(identify, match_header_value, c_value)) {
563 return -1;
564 }
565
566 len = strlen(c_value);
567 if (2 < len && c_value[0] == '/' && c_value[len - 1] == '/') {
568 /* Make "/regex/" into "regex" */
569 c_value[len - 1] = '\0';
570 ++c_value;
571
572 if (regcomp(&identify->regex_header_buf, c_value, REG_EXTENDED | REG_NOSUB)) {
573 ast_log(LOG_ERROR, "Identify '%s' failed to compile match_request_uri regex '%s'.\n",
574 ast_sorcery_object_get_id(identify), c_value);
575 return -1;
576 }
577 identify->is_header_regex = 1;
578 }
579 }
580
581 if (!ast_strlen_zero(identify->match_request_uri)) {
582 char *c_string;
583 int len;
584
585 len = strlen(identify->match_request_uri);
586 c_string = ast_strdupa(identify->match_request_uri);
587
588 if (!strcmp(c_string, "//")) {
589 /* An empty regex is the same as an empty literal string. */
590 c_string = "";
591 }
592
593 if (2 < len && c_string[0] == '/' && c_string[len - 1] == '/') {
594 /* Make "/regex/" into "regex" */
595 c_string[len - 1] = '\0';
596 ++c_string;
597
598 if (regcomp(&identify->regex_request_uri_buf, c_string, REG_EXTENDED | REG_NOSUB)) {
599 ast_log(LOG_ERROR, "Identify '%s' failed to compile match_header regex '%s'.\n",
600 ast_sorcery_object_get_id(identify), c_string);
601 return -1;
602 }
603 identify->is_request_uri_regex = 1;
604 }
605 }
606
607 if (!identify->hosts) {
608 /* No match addresses to resolve */
609 return 0;
610 }
611
612 /* Hosts can produce dynamic content, so mark the identify as such */
614
615 /* Resolve the match addresses now */
616 i = ao2_iterator_init(identify->hosts, 0);
617 while ((current_string = ao2_iterator_next(&i))) {
618 int results = 0;
619 char *colon = strrchr(current_string, ':');
620
621 /* We skip SRV lookup if a colon is present, assuming a port was specified */
622 if (!colon) {
623 /* No port, and we know this is not an IP address, so perform SRV resolution on it */
624 if (identify->srv_lookups) {
625 results = ip_identify_match_srv_lookup(identify, "_sip._udp", current_string,
626 results);
627 if (results != -1) {
628 results = ip_identify_match_srv_lookup(identify, "_sip._tcp",
629 current_string, results);
630 }
631 if (results != -1) {
632 results = ip_identify_match_srv_lookup(identify, "_sips._tcp",
633 current_string, results);
634 }
635 }
636 }
637
638 /* If SRV fails fall back to a normal lookup on the host itself */
639 if (!results) {
640 results = ip_identify_match_host_lookup(identify, current_string);
641 }
642
643 if (results == 0) {
644 ast_log(LOG_WARNING, "Identify '%s' provided address '%s' did not resolve to any address\n",
645 ast_sorcery_object_get_id(identify), current_string);
646 } else if (results == -1) {
647 ast_log(LOG_ERROR, "Identify '%s' failed when adding resolution results of '%s'\n",
648 ast_sorcery_object_get_id(identify), current_string);
649 ao2_ref(current_string, -1);
651 return -1;
652 }
653
654 ao2_ref(current_string, -1);
655 }
657
658 ao2_ref(identify->hosts, -1);
659 identify->hosts = NULL;
660
661 return 0;
662}
663
664static int match_to_str(const void *obj, const intptr_t *args, char **buf)
665{
667 const struct ip_identify_match *identify = obj;
668
669 ast_ha_join(identify->matches, &str);
671 return 0;
672}
673
674static void match_to_var_list_append(struct ast_variable **head, struct ast_ha *ha)
675{
676 char str[MAX_OBJECT_FIELD];
677 const char *addr;
678
679 if (ast_sockaddr_port(&ha->addr)) {
681 } else {
683 }
684
685 snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "",
687
688 ast_variable_list_append(head, ast_variable_new("match", str, ""));
689}
690
691static int match_to_var_list(const void *obj, struct ast_variable **fields)
692{
693 const struct ip_identify_match *identify = obj;
694 struct ast_variable *head = NULL;
695 struct ast_ha *ha = identify->matches;
696
697 for (; ha; ha = ha->next) {
698 match_to_var_list_append(&head, ha);
699 }
700
701 if (head) {
702 *fields = head;
703 }
704
705 return 0;
706}
707
708static int sip_identify_to_ami(const struct ip_identify_match *identify,
709 struct ast_str **buf)
710{
711 return ast_sip_sorcery_object_to_ami(identify, buf);
712}
713
714static int send_identify_ami_event(void *obj, void *arg, void *data, int flags)
715{
716 struct ip_identify_match *identify = obj;
717 const char *endpoint_name = arg;
718 struct ast_sip_ami *ami = data;
719 struct ast_str *buf;
720
721 /* Build AMI event */
722 buf = ast_sip_create_ami_event("IdentifyDetail", ami);
723 if (!buf) {
724 return CMP_STOP;
725 }
726 if (sip_identify_to_ami(identify, &buf)) {
727 ast_free(buf);
728 return CMP_STOP;
729 }
730 ast_str_append(&buf, 0, "EndpointName: %s\r\n", endpoint_name);
731
732 /* Send AMI event */
733 astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
734 ++ami->count;
735
736 ast_free(buf);
737 return 0;
738}
739
740static int format_ami_endpoint_identify(const struct ast_sip_endpoint *endpoint,
741 struct ast_sip_ami *ami)
742{
743 struct ao2_container *identifies;
744 struct ast_variable fields = {
745 .name = "endpoint",
746 .value = ast_sorcery_object_get_id(endpoint),
747 };
748
749 identifies = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
751 if (!identifies) {
752 return -1;
753 }
754
755 /* Build and send any found identify object's AMI IdentifyDetail event. */
758 (void *) ast_sorcery_object_get_id(endpoint),
759 ami);
760
761 ao2_ref(identifies, -1);
762 return 0;
763}
764
767};
768
769static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
770{
771 const struct ast_sip_endpoint *endpoint = container;
772 struct ao2_container *identifies;
773
774 struct ast_variable fields = {
775 .name = "endpoint",
776 .value = ast_sorcery_object_get_id(endpoint),
777 .next = NULL,
778 };
779
780 identifies = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
782 if (!identifies) {
783 return -1;
784 }
785
786 ao2_callback(identifies, OBJ_NODATA, callback, args);
787 ao2_cleanup(identifies);
788
789 return 0;
790}
791
792static struct ao2_container *cli_get_container(const char *regex)
793{
795 struct ao2_container *s_container;
796
798 if (!container) {
799 return NULL;
800 }
801
804 if (!s_container) {
805 return NULL;
806 }
807
808 if (ao2_container_dup(s_container, container, 0)) {
809 ao2_ref(s_container, -1);
810 return NULL;
811 }
812
813 return s_container;
814}
815
816static void *cli_retrieve_by_id(const char *id)
817{
818 return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "identify", id);
819}
820
821static int cli_print_header(void *obj, void *arg, int flags)
822{
823 struct ast_sip_cli_context *context = arg;
824 int indent = CLI_INDENT_TO_SPACES(context->indent_level);
825 int filler = CLI_MAX_WIDTH - indent - 22;
826
827 ast_assert(context->output_buffer != NULL);
828
829 ast_str_append(&context->output_buffer, 0,
830 "%*s: <Identify/Endpoint%*.*s>\n",
831 indent, "Identify", filler, filler, CLI_HEADER_FILLER);
832
833 if (context->recurse) {
834 context->indent_level++;
835 indent = CLI_INDENT_TO_SPACES(context->indent_level);
836 filler = CLI_LAST_TABSTOP - indent - 24;
837
838 ast_str_append(&context->output_buffer, 0,
839 "%*s: <criteria%*.*s>\n",
840 indent, "Match", filler, filler, CLI_HEADER_FILLER);
841
842 context->indent_level--;
843 }
844
845 return 0;
846}
847
848static int cli_print_body(void *obj, void *arg, int flags)
849{
851 struct ip_identify_match *ident = obj;
852 struct ast_sip_cli_context *context = arg;
853 struct ast_ha *match;
854 int indent;
855
856 ast_assert(context->output_buffer != NULL);
857
858 ast_str_append(&context->output_buffer, 0, "%*s: %s/%s\n",
859 CLI_INDENT_TO_SPACES(context->indent_level), "Identify",
861
862 if (context->recurse) {
863 context->indent_level++;
864 indent = CLI_INDENT_TO_SPACES(context->indent_level);
865
866 for (match = ident->matches; match; match = match->next) {
867 const char *addr;
868
869 if (ast_sockaddr_port(&match->addr)) {
871 } else {
873 }
874
875 ast_str_append(&context->output_buffer, 0, "%*s: %s%s/%d\n",
876 indent,
877 "Match",
878 match->sense == AST_SENSE_ALLOW ? "!" : "",
879 addr, ast_sockaddr_cidr_bits(&match->netmask));
880 }
881
882 if (!ast_strlen_zero(ident->match_header)) {
883 ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
884 indent,
885 "Header",
886 ident->match_header);
887 }
888
889 if (!ast_strlen_zero(ident->match_request_uri)) {
890 ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
891 indent,
892 "RequestURI",
893 ident->match_request_uri);
894 }
895
896 context->indent_level--;
897
898 if (context->indent_level == 0) {
899 ast_str_append(&context->output_buffer, 0, "\n");
900 }
901 }
902
903 if (context->show_details
904 || (context->show_details_only_level_0 && context->indent_level == 0)) {
905 ast_str_append(&context->output_buffer, 0, "\n");
907 }
908
909 return 0;
910}
911
912/*
913 * A function pointer to callback needs to be within the
914 * module in order to avoid problems with an undefined
915 * symbol when the module is loaded.
916 */
917static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd,
918 struct ast_cli_args *a)
919{
920 return ast_sip_cli_traverse_objects(e, cmd, a);
921}
922
923static struct ast_cli_entry cli_identify[] = {
924AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Identifies",
925 .command = "pjsip list identifies",
926 .usage = "Usage: pjsip list identifies [ like <pattern> ]\n"
927 " List the configured PJSIP Identifies\n"
928 " Optional regular expression pattern is used to filter the list.\n"),
929AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identifies",
930 .command = "pjsip show identifies",
931 .usage = "Usage: pjsip show identifies [ like <pattern> ]\n"
932 " Show the configured PJSIP Identifies\n"
933 " Optional regular expression pattern is used to filter the list.\n"),
934AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identify",
935 .command = "pjsip show identify",
936 .usage = "Usage: pjsip show identify <id>\n"
937 " Show the configured PJSIP Identify\n"),
938};
939
941
942static int load_module(void)
943{
944 ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_endpoint_identifier_ip");
945 ast_sorcery_apply_default(ast_sip_get_sorcery(), "identify", "config", "pjsip.conf,criteria=type=identify");
946
949 }
950
951 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0);
952 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name));
954 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "match_header", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_header));
955 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));
956 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "srv_lookups", "yes", OPT_BOOL_T, 1, FLDSET(struct ip_identify_match, srv_lookups));
958
963
965 if (!cli_formatter) {
966 ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
968 }
969 cli_formatter->name = "identify";
976
979
981}
982
983static int reload_module(void)
984{
986
987 return 0;
988}
989
990static int unload_module(void)
991{
998
999 return 0;
1000}
1001
1002AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP IP endpoint identifier",
1003 .support_level = AST_MODULE_SUPPORT_CORE,
1004 .load = load_module,
1006 .unload = unload_module,
1007 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
1008 .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
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:1890
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:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ 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
#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_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:2317
@ 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:2384
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:1954
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:2464
#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:2440
#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:3050
struct mansession * s
Definition: res_pjsip.h:3052
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:3076
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:3080
An entity responsible for identifying the source of a SIP message.
Definition: res_pjsip.h:1293
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:1298
An entity with which Asterisk communicates.
Definition: res_pjsip.h:962
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.
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