Asterisk - The Open Source Telephony Project  GIT-master-a24979a
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"
30 #include "asterisk/res_pjsip_cli.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="type">
114  <synopsis>Must be of type 'identify'.</synopsis>
115  </configOption>
116  </configObject>
117  </configFile>
118  </configInfo>
119  ***/
120 
121 /*! \brief The number of buckets for storing hosts for resolution */
122 #define HOSTS_BUCKETS 53
123 
124 /*! \brief Structure for an IP identification matching object */
126  /*! \brief Sorcery object details */
127  SORCERY_OBJECT(details);
128  /*! \brief Stringfields */
130  /*! The name of the endpoint */
132  /*! If matching by header, the header/value to match against */
134  /*! SIP header name of the match_header string */
136  /*! SIP header value of the match_header string */
138  );
139  /*! Compiled match_header regular expression when is_regex is non-zero */
140  regex_t regex_buf;
141  /*! \brief Networks or addresses that should match this */
142  struct ast_ha *matches;
143  /*! \brief Hosts to be resolved when applying configuration */
145  /*! \brief Perform SRV resolution of hostnames */
146  unsigned int srv_lookups;
147  /*! Non-zero if match_header has a regular expression (i.e., regex_buf is valid) */
148  unsigned int is_regex:1;
149 };
150 
151 /*! \brief Destructor function for a matching object */
152 static void ip_identify_destroy(void *obj)
153 {
154  struct ip_identify_match *identify = obj;
155 
157  ast_free_ha(identify->matches);
158  ao2_cleanup(identify->hosts);
159  if (identify->is_regex) {
160  regfree(&identify->regex_buf);
161  }
162 }
163 
164 /*! \brief Allocator function for a matching object */
165 static void *ip_identify_alloc(const char *name)
166 {
167  struct ip_identify_match *identify = ast_sorcery_generic_alloc(sizeof(*identify), ip_identify_destroy);
168 
169  if (!identify || ast_string_field_init(identify, 256)) {
170  ao2_cleanup(identify);
171  return NULL;
172  }
173 
174  return identify;
175 }
176 
177 /*! \brief Comparator function for matching an object by header */
178 static int header_identify_match_check(void *obj, void *arg, int flags)
179 {
180  struct ip_identify_match *identify = obj;
181  struct pjsip_rx_data *rdata = arg;
182  pjsip_hdr *header;
183  pj_str_t pj_header_name;
184  int header_present;
185 
186  if (ast_strlen_zero(identify->match_header)) {
187  return 0;
188  }
189 
190  pj_header_name = pj_str((void *) identify->match_header_name);
191 
192  /* Check all headers of the given name for a match. */
193  header_present = 0;
194  for (header = NULL;
195  (header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &pj_header_name, header));
196  header = header->next) {
197  char *pos;
198  int len;
199  char buf[PATH_MAX];
200 
201  header_present = 1;
202 
203  /* Print header line to buf */
204  len = pjsip_hdr_print_on(header, buf, sizeof(buf) - 1);
205  if (len < 0) {
206  /* Buffer not large enough or no header vptr! */
207  ast_assert(0);
208  continue;
209  }
210  buf[len] = '\0';
211 
212  /* Remove header name from pj_buf and trim blanks. */
213  pos = strchr(buf, ':');
214  if (!pos) {
215  /* No header name? Bug in PJPROJECT if so. */
216  ast_assert(0);
217  continue;
218  }
219  pos = ast_strip(pos + 1);
220 
221  /* Does header value match what we are looking for? */
222  if (identify->is_regex) {
223  if (!regexec(&identify->regex_buf, pos, 0, NULL, 0)) {
224  return CMP_MATCH;
225  }
226  } else if (!strcmp(identify->match_header_value, pos)) {
227  return CMP_MATCH;
228  }
229 
230  ast_debug(3, "Identify '%s': SIP message has '%s' header but value '%s' does not match '%s'.\n",
231  ast_sorcery_object_get_id(identify),
232  identify->match_header_name,
233  pos,
234  identify->match_header_value);
235  }
236  if (!header_present) {
237  ast_debug(3, "Identify '%s': SIP message does not have '%s' header.\n",
238  ast_sorcery_object_get_id(identify),
239  identify->match_header_name);
240  }
241  return 0;
242 }
243 
244 /*! \brief Comparator function for matching an object by IP address */
245 static int ip_identify_match_check(void *obj, void *arg, int flags)
246 {
247  struct ip_identify_match *identify = obj;
248  struct ast_sockaddr *addr = arg;
249  int sense;
250 
251  sense = ast_apply_ha(identify->matches, addr);
252  if (sense != AST_SENSE_ALLOW) {
253  ast_debug(3, "Source address %s matches identify '%s'\n",
255  ast_sorcery_object_get_id(identify));
256  return CMP_MATCH;
257  } else {
258  ast_debug(3, "Source address %s does not match identify '%s'\n",
260  ast_sorcery_object_get_id(identify));
261  return 0;
262  }
263 }
264 
265 static struct ast_sip_endpoint *common_identify(ao2_callback_fn *identify_match_cb, void *arg)
266 {
267  RAII_VAR(struct ao2_container *, candidates, NULL, ao2_cleanup);
268  struct ip_identify_match *match;
269  struct ast_sip_endpoint *endpoint;
270 
271  /* If no possibilities exist return early to save some time */
272  candidates = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
274  if (!candidates || !ao2_container_count(candidates)) {
275  ast_debug(3, "No identify sections to match against\n");
276  return NULL;
277  }
278 
279  match = ao2_callback(candidates, 0, identify_match_cb, arg);
280  if (!match) {
281  return NULL;
282  }
283 
284  endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
285  match->endpoint_name);
286  if (endpoint) {
287  ast_debug(3, "Identify '%s' SIP message matched to endpoint %s\n",
288  ast_sorcery_object_get_id(match), match->endpoint_name);
289  } else {
290  ast_log(LOG_WARNING, "Identify '%s' points to endpoint '%s' but endpoint could not be found\n",
291  ast_sorcery_object_get_id(match), match->endpoint_name);
292  }
293 
294  ao2_ref(match, -1);
295  return endpoint;
296 }
297 
298 static struct ast_sip_endpoint *ip_identify(pjsip_rx_data *rdata)
299 {
300  struct ast_sockaddr addr = { { 0, } };
301 
302  ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
303  ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port);
304 
306 }
307 
310 };
311 
312 static struct ast_sip_endpoint *header_identify(pjsip_rx_data *rdata)
313 {
315 }
316 
319 };
320 
321 /*! \brief Helper function which performs a host lookup and adds result to identify match */
322 static int ip_identify_match_host_lookup(struct ip_identify_match *identify, const char *host)
323 {
324  struct ast_sockaddr *addrs;
325  int num_addrs = 0, error = 0, i;
326  int results = 0;
327 
328  num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC);
329  if (!num_addrs) {
330  return -1;
331  }
332 
333  for (i = 0; i < num_addrs; ++i) {
334  /* Check if the address is already in the list, if so don't add it again */
335  if (identify->matches && (ast_apply_ha(identify->matches, &addrs[i]) != AST_SENSE_ALLOW)) {
336  continue;
337  }
338 
339  /* We deny what we actually want to match because there is an implicit permit all rule for ACLs */
340  identify->matches = ast_append_ha_with_port("d", ast_sockaddr_stringify(&addrs[i]), identify->matches, &error);
341 
342  if (!identify->matches || error) {
343  results = -1;
344  break;
345  }
346 
347  results += 1;
348  }
349 
350  ast_free(addrs);
351 
352  return results;
353 }
354 
355 /*! \brief Helper function which performs an SRV lookup and then resolves the hostname */
356 static int ip_identify_match_srv_lookup(struct ip_identify_match *identify, const char *prefix, const char *host, int results)
357 {
358  char service[NI_MAXHOST];
359  struct srv_context *context = NULL;
360  int srv_ret;
361  const char *srvhost;
362  unsigned short srvport;
363 
364  snprintf(service, sizeof(service), "%s.%s", prefix, host);
365 
366  while (!(srv_ret = ast_srv_lookup(&context, service, &srvhost, &srvport))) {
367  int hosts;
368 
369  /* In the case of the SRV lookup we don't care if it fails, we will output a log message
370  * when we fallback to a normal lookup.
371  */
372  hosts = ip_identify_match_host_lookup(identify, srvhost);
373  if (hosts == -1) {
374  results = -1;
375  break;
376  } else {
377  results += hosts;
378  }
379  }
380 
382 
383  return results;
384 }
385 
386 /*! \brief Custom handler for match field */
387 static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
388 {
389  struct ip_identify_match *identify = obj;
390  char *input_string = ast_strdupa(var->value);
391  char *current_string;
392 
393  if (ast_strlen_zero(var->value)) {
394  return 0;
395  }
396 
397  while ((current_string = ast_strip(strsep(&input_string, ",")))) {
398  char *mask;
399  struct ast_sockaddr address;
400  int error = 0;
401 
402  if (ast_strlen_zero(current_string)) {
403  continue;
404  }
405 
406  mask = strrchr(current_string, '/');
407 
408  /* If it looks like a netmask is present, or we can immediately parse as an IP,
409  * hand things off to the ACL */
410  if (mask || ast_sockaddr_parse(&address, current_string, 0)) {
411  identify->matches = ast_append_ha_with_port("d", current_string, identify->matches, &error);
412 
413  if (!identify->matches || error) {
414  ast_log(LOG_ERROR, "Failed to add address '%s' to ip endpoint identifier '%s'\n",
415  current_string, ast_sorcery_object_get_id(obj));
416  return -1;
417  }
418 
419  continue;
420  }
421 
422  if (!identify->hosts) {
424  if (!identify->hosts) {
425  ast_log(LOG_ERROR, "Failed to create container to store hosts on ip endpoint identifier '%s'\n",
427  return -1;
428  }
429  }
430 
431  error = ast_str_container_add(identify->hosts, current_string);
432  if (error) {
433  ast_log(LOG_ERROR, "Failed to store host '%s' for resolution on ip endpoint identifier '%s'\n",
434  current_string, ast_sorcery_object_get_id(obj));
435  return -1;
436  }
437  }
438 
439  return 0;
440 }
441 
442 /*! \brief Apply handler for identify type */
443 static int ip_identify_apply(const struct ast_sorcery *sorcery, void *obj)
444 {
445  struct ip_identify_match *identify = obj;
446  char *current_string;
447  struct ao2_iterator i;
448 
449  /* Validate the identify object configuration */
450  if (ast_strlen_zero(identify->endpoint_name)) {
451  ast_log(LOG_ERROR, "Identify '%s' missing required endpoint name.\n",
452  ast_sorcery_object_get_id(identify));
453  return -1;
454  }
455  if (ast_strlen_zero(identify->match_header) /* No header to match */
456  /* and no static IP addresses with a mask */
457  && !identify->matches
458  /* and no addresses to resolve */
459  && (!identify->hosts || !ao2_container_count(identify->hosts))) {
460  ast_log(LOG_ERROR, "Identify '%s' is not configured to match anything.\n",
461  ast_sorcery_object_get_id(identify));
462  return -1;
463  }
464 
465  if (!ast_strlen_zero(identify->match_header)) {
466  char *c_header;
467  char *c_value;
468  int len;
469 
470  /* Split the header name and value */
471  c_header = ast_strdupa(identify->match_header);
472  c_value = strchr(c_header, ':');
473  if (!c_value) {
474  ast_log(LOG_ERROR, "Identify '%s' missing ':' separator in match_header '%s'.\n",
475  ast_sorcery_object_get_id(identify), identify->match_header);
476  return -1;
477  }
478  *c_value = '\0';
479  c_value = ast_strip(c_value + 1);
480  c_header = ast_strip(c_header);
481 
482  if (ast_strlen_zero(c_header)) {
483  ast_log(LOG_ERROR, "Identify '%s' has no SIP header to match in match_header '%s'.\n",
484  ast_sorcery_object_get_id(identify), identify->match_header);
485  return -1;
486  }
487 
488  if (!strcmp(c_value, "//")) {
489  /* An empty regex is the same as an empty literal string. */
490  c_value = "";
491  }
492 
493  if (ast_string_field_set(identify, match_header_name, c_header)
494  || ast_string_field_set(identify, match_header_value, c_value)) {
495  return -1;
496  }
497 
498  len = strlen(c_value);
499  if (2 < len && c_value[0] == '/' && c_value[len - 1] == '/') {
500  /* Make "/regex/" into "regex" */
501  c_value[len - 1] = '\0';
502  ++c_value;
503 
504  if (regcomp(&identify->regex_buf, c_value, REG_EXTENDED | REG_NOSUB)) {
505  ast_log(LOG_ERROR, "Identify '%s' failed to compile match_header regex '%s'.\n",
506  ast_sorcery_object_get_id(identify), c_value);
507  return -1;
508  }
509  identify->is_regex = 1;
510  }
511  }
512 
513  if (!identify->hosts) {
514  /* No match addresses to resolve */
515  return 0;
516  }
517 
518  /* Hosts can produce dynamic content, so mark the identify as such */
520 
521  /* Resolve the match addresses now */
522  i = ao2_iterator_init(identify->hosts, 0);
523  while ((current_string = ao2_iterator_next(&i))) {
524  int results = 0;
525  char *colon = strrchr(current_string, ':');
526 
527  /* We skip SRV lookup if a colon is present, assuming a port was specified */
528  if (!colon) {
529  /* No port, and we know this is not an IP address, so perform SRV resolution on it */
530  if (identify->srv_lookups) {
531  results = ip_identify_match_srv_lookup(identify, "_sip._udp", current_string,
532  results);
533  if (results != -1) {
534  results = ip_identify_match_srv_lookup(identify, "_sip._tcp",
535  current_string, results);
536  }
537  if (results != -1) {
538  results = ip_identify_match_srv_lookup(identify, "_sips._tcp",
539  current_string, results);
540  }
541  }
542  }
543 
544  /* If SRV fails fall back to a normal lookup on the host itself */
545  if (!results) {
546  results = ip_identify_match_host_lookup(identify, current_string);
547  }
548 
549  if (results == 0) {
550  ast_log(LOG_WARNING, "Identify '%s' provided address '%s' did not resolve to any address\n",
551  ast_sorcery_object_get_id(identify), current_string);
552  } else if (results == -1) {
553  ast_log(LOG_ERROR, "Identify '%s' failed when adding resolution results of '%s'\n",
554  ast_sorcery_object_get_id(identify), current_string);
555  ao2_ref(current_string, -1);
557  return -1;
558  }
559 
560  ao2_ref(current_string, -1);
561  }
563 
564  ao2_ref(identify->hosts, -1);
565  identify->hosts = NULL;
566 
567  return 0;
568 }
569 
570 static int match_to_str(const void *obj, const intptr_t *args, char **buf)
571 {
573  const struct ip_identify_match *identify = obj;
574 
575  ast_ha_join(identify->matches, &str);
577  return 0;
578 }
579 
580 static void match_to_var_list_append(struct ast_variable **head, struct ast_ha *ha)
581 {
582  char str[MAX_OBJECT_FIELD];
583  const char *addr;
584 
585  if (ast_sockaddr_port(&ha->addr)) {
587  } else {
589  }
590 
591  snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "",
593 
594  ast_variable_list_append(head, ast_variable_new("match", str, ""));
595 }
596 
597 static int match_to_var_list(const void *obj, struct ast_variable **fields)
598 {
599  const struct ip_identify_match *identify = obj;
600  struct ast_variable *head = NULL;
601  struct ast_ha *ha = identify->matches;
602 
603  for (; ha; ha = ha->next) {
604  match_to_var_list_append(&head, ha);
605  }
606 
607  if (head) {
608  *fields = head;
609  }
610 
611  return 0;
612 }
613 
614 static int sip_identify_to_ami(const struct ip_identify_match *identify,
615  struct ast_str **buf)
616 {
617  return ast_sip_sorcery_object_to_ami(identify, buf);
618 }
619 
620 static int send_identify_ami_event(void *obj, void *arg, void *data, int flags)
621 {
622  struct ip_identify_match *identify = obj;
623  const char *endpoint_name = arg;
624  struct ast_sip_ami *ami = data;
625  struct ast_str *buf;
626 
627  /* Build AMI event */
628  buf = ast_sip_create_ami_event("IdentifyDetail", ami);
629  if (!buf) {
630  return CMP_STOP;
631  }
632  if (sip_identify_to_ami(identify, &buf)) {
633  ast_free(buf);
634  return CMP_STOP;
635  }
636  ast_str_append(&buf, 0, "EndpointName: %s\r\n", endpoint_name);
637 
638  /* Send AMI event */
639  astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
640  ++ami->count;
641 
642  ast_free(buf);
643  return 0;
644 }
645 
646 static int format_ami_endpoint_identify(const struct ast_sip_endpoint *endpoint,
647  struct ast_sip_ami *ami)
648 {
649  struct ao2_container *identifies;
650  struct ast_variable fields = {
651  .name = "endpoint",
652  .value = ast_sorcery_object_get_id(endpoint),
653  };
654 
655  identifies = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
656  AST_RETRIEVE_FLAG_MULTIPLE, &fields);
657  if (!identifies) {
658  return -1;
659  }
660 
661  /* Build and send any found identify object's AMI IdentifyDetail event. */
664  (void *) ast_sorcery_object_get_id(endpoint),
665  ami);
666 
667  ao2_ref(identifies, -1);
668  return 0;
669 }
670 
673 };
674 
675 static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
676 {
677  const struct ast_sip_endpoint *endpoint = container;
678  struct ao2_container *identifies;
679 
680  struct ast_variable fields = {
681  .name = "endpoint",
682  .value = ast_sorcery_object_get_id(endpoint),
683  .next = NULL,
684  };
685 
686  identifies = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
687  AST_RETRIEVE_FLAG_MULTIPLE, &fields);
688  if (!identifies) {
689  return -1;
690  }
691 
692  ao2_callback(identifies, OBJ_NODATA, callback, args);
693  ao2_cleanup(identifies);
694 
695  return 0;
696 }
697 
698 static struct ao2_container *cli_get_container(const char *regex)
699 {
701  struct ao2_container *s_container;
702 
704  if (!container) {
705  return NULL;
706  }
707 
710  if (!s_container) {
711  return NULL;
712  }
713 
714  if (ao2_container_dup(s_container, container, 0)) {
715  ao2_ref(s_container, -1);
716  return NULL;
717  }
718 
719  return s_container;
720 }
721 
722 static void *cli_retrieve_by_id(const char *id)
723 {
724  return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "identify", id);
725 }
726 
727 static int cli_print_header(void *obj, void *arg, int flags)
728 {
729  struct ast_sip_cli_context *context = arg;
730  int indent = CLI_INDENT_TO_SPACES(context->indent_level);
731  int filler = CLI_MAX_WIDTH - indent - 22;
732 
733  ast_assert(context->output_buffer != NULL);
734 
735  ast_str_append(&context->output_buffer, 0,
736  "%*s: <Identify/Endpoint%*.*s>\n",
737  indent, "Identify", filler, filler, CLI_HEADER_FILLER);
738 
739  if (context->recurse) {
740  context->indent_level++;
741  indent = CLI_INDENT_TO_SPACES(context->indent_level);
742  filler = CLI_LAST_TABSTOP - indent - 24;
743 
744  ast_str_append(&context->output_buffer, 0,
745  "%*s: <criteria%*.*s>\n",
746  indent, "Match", filler, filler, CLI_HEADER_FILLER);
747 
748  context->indent_level--;
749  }
750 
751  return 0;
752 }
753 
754 static int cli_print_body(void *obj, void *arg, int flags)
755 {
757  struct ip_identify_match *ident = obj;
758  struct ast_sip_cli_context *context = arg;
759  struct ast_ha *match;
760  int indent;
761 
762  ast_assert(context->output_buffer != NULL);
763 
764  ast_str_append(&context->output_buffer, 0, "%*s: %s/%s\n",
765  CLI_INDENT_TO_SPACES(context->indent_level), "Identify",
767 
768  if (context->recurse) {
769  context->indent_level++;
770  indent = CLI_INDENT_TO_SPACES(context->indent_level);
771 
772  for (match = ident->matches; match; match = match->next) {
773  const char *addr;
774 
775  if (ast_sockaddr_port(&match->addr)) {
777  } else {
779  }
780 
781  ast_str_append(&context->output_buffer, 0, "%*s: %s%s/%d\n",
782  indent,
783  "Match",
784  match->sense == AST_SENSE_ALLOW ? "!" : "",
785  addr, ast_sockaddr_cidr_bits(&match->netmask));
786  }
787 
788  if (!ast_strlen_zero(ident->match_header)) {
789  ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
790  indent,
791  "Match",
792  ident->match_header);
793  }
794 
795  context->indent_level--;
796 
797  if (context->indent_level == 0) {
798  ast_str_append(&context->output_buffer, 0, "\n");
799  }
800  }
801 
802  if (context->show_details
803  || (context->show_details_only_level_0 && context->indent_level == 0)) {
804  ast_str_append(&context->output_buffer, 0, "\n");
806  }
807 
808  return 0;
809 }
810 
811 /*
812  * A function pointer to callback needs to be within the
813  * module in order to avoid problems with an undefined
814  * symbol when the module is loaded.
815  */
816 static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd,
817  struct ast_cli_args *a)
818 {
819  return ast_sip_cli_traverse_objects(e, cmd, a);
820 }
821 
822 static struct ast_cli_entry cli_identify[] = {
823 AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Identifies",
824  .command = "pjsip list identifies",
825  .usage = "Usage: pjsip list identifies [ like <pattern> ]\n"
826  " List the configured PJSIP Identifies\n"
827  " Optional regular expression pattern is used to filter the list.\n"),
828 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identifies",
829  .command = "pjsip show identifies",
830  .usage = "Usage: pjsip show identifies [ like <pattern> ]\n"
831  " Show the configured PJSIP Identifies\n"
832  " Optional regular expression pattern is used to filter the list.\n"),
833 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identify",
834  .command = "pjsip show identify",
835  .usage = "Usage: pjsip show identify <id>\n"
836  " Show the configured PJSIP Identify\n"),
837 };
838 
840 
841 static int load_module(void)
842 {
843  ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_endpoint_identifier_ip");
844  ast_sorcery_apply_default(ast_sip_get_sorcery(), "identify", "config", "pjsip.conf,criteria=type=identify");
845 
848  }
849 
850  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0);
851  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name));
853  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "match_header", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_header));
854  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "srv_lookups", "yes", OPT_BOOL_T, 1, FLDSET(struct ip_identify_match, srv_lookups));
856 
860 
862  if (!cli_formatter) {
863  ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
865  }
866  cli_formatter->name = "identify";
873 
876 
878 }
879 
880 static int reload_module(void)
881 {
883 
884  return 0;
885 }
886 
887 static int unload_module(void)
888 {
894 
895  return 0;
896 }
897 
898 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP IP endpoint identifier",
899  .support_level = AST_MODULE_SUPPORT_CORE,
900  .load = load_module,
902  .unload = unload_module,
903  .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
904  .requires = "res_pjsip",
905 );
Access Control of various sorts.
void ast_free_ha(struct ast_ha *ha)
Free a list of HAs.
Definition: acl.c:222
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:718
@ 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:808
void ast_ha_join(const struct ast_ha *ha, struct ast_str **buf)
Convert HAs to a comma separated string value.
Definition: acl.c:723
const char * str
Definition: app_jack.c:147
#define var
Definition: ast_expr2f.c:614
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
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:120
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:2312
enum ast_cc_service_type service
Definition: chan_sip.c:956
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:3087
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
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
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
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
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_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
static int reload(void)
struct ao2_container * container
Definition: res_fax.c:501
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
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.
void ast_sip_unregister_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier)
Unregister a SIP endpoint identifier.
Definition: res_pjsip.c:308
void ast_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
Register an endpoint formatter.
Definition: res_pjsip.c:474
void ast_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
Unregister an endpoint formatter.
Definition: res_pjsip.c:480
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:226
int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
Unregisters a CLI formatter.
Definition: pjsip_cli.c:326
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_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
#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 void * cli_retrieve_by_id(const char *id)
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 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 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 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)
struct ast_sip_endpoint_formatter endpoint_identify_formatter
static struct ao2_container * cli_get_container(const char *regex)
static int load_module(void)
static int cli_print_body(void *obj, void *arg, int flags)
static struct ast_sip_endpoint * common_identify(ao2_callback_fn *identify_match_cb, void *arg)
static int unload_module(void)
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
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
@ 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_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
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
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:837
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
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition: sorcery.c:2459
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
#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
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
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_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:1117
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:739
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
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:201
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:640
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
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:206
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:2819
struct mansession * s
Definition: res_pjsip.h:2821
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
const char *(* get_id)(const void *obj)
Definition: res_pjsip_cli.h:70
struct ao2_container *(* get_container)(const char *regex)
Definition: res_pjsip_cli.h:64
void *(* retrieve_by_id)(const char *id)
Definition: res_pjsip_cli.h:68
const char * name
Definition: res_pjsip_cli.h:58
ao2_callback_fn * print_body
Definition: res_pjsip_cli.h:62
An entity responsible formatting endpoint information.
Definition: res_pjsip.h:2845
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:2849
An entity responsible for identifying the source of a SIP message.
Definition: res_pjsip.h:1073
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:1078
An entity with which Asterisk communicates.
Definition: res_pjsip.h:854
Socket address structure.
Definition: netsock2.h:97
Full structure for sorcery.
Definition: sorcery.c:230
Support for dynamic strings.
Definition: strings.h:604
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 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:936
#define ast_assert(a)
Definition: utils.h:734
#define ARRAY_LEN(a)
Definition: utils.h:661