Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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",
289  } else {
290  ast_log(LOG_WARNING, "Identify '%s' points to endpoint '%s' but endpoint could not be found\n",
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 
381  ast_srv_cleanup(&context);
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);
576  *buf = ast_strdup(ast_str_buffer(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)) {
776  addr = ast_sockaddr_stringify(&match->addr);
777  } else {
778  addr = ast_sockaddr_stringify_addr(&match->addr);
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");
805  ast_sip_cli_print_sorcery_objectset(ident, context, 0);
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);
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 
858  ast_sip_register_endpoint_identifier_with_name(&header_identifier, "header");
859  ast_sip_register_endpoint_formatter(&endpoint_identify_formatter);
860 
861  cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
862  if (!cli_formatter) {
863  ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
865  }
866  cli_formatter->name = "identify";
867  cli_formatter->print_header = cli_print_header;
868  cli_formatter->print_body = cli_print_body;
869  cli_formatter->get_container = cli_get_container;
870  cli_formatter->iterate = cli_iterator;
871  cli_formatter->get_id = ast_sorcery_object_get_id;
872  cli_formatter->retrieve_by_id = cli_retrieve_by_id;
873 
874  ast_sip_register_cli_formatter(cli_formatter);
875  ast_cli_register_multiple(cli_identify, ARRAY_LEN(cli_identify));
876 
878 }
879 
880 static int reload_module(void)
881 {
883 
884  return 0;
885 }
886 
887 static int unload_module(void)
888 {
889  ast_cli_unregister_multiple(cli_identify, ARRAY_LEN(cli_identify));
890  ast_sip_unregister_cli_formatter(cli_formatter);
891  ast_sip_unregister_endpoint_formatter(&endpoint_identify_formatter);
892  ast_sip_unregister_endpoint_identifier(&header_identifier);
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 );
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:290
struct ast_ha * next
Definition: acl.h:56
struct ast_str * output_buffer
Definition: res_pjsip_cli.h:36
struct ao2_container *(* get_container)(const char *regex)
Definition: res_pjsip_cli.h:64
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
static struct ast_cli_entry cli_identify[]
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3080
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
An entity responsible formatting endpoint information.
Definition: res_pjsip.h:2763
Definition: res_pjsip_cli.h:52
#define CLI_LAST_TABSTOP
Definition: res_pjsip_cli.h:27
static void match_to_var_list_append(struct ast_variable **head, struct ast_ha *ha)
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
struct ast_sockaddr addr
Definition: acl.h:53
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
descriptor for a cli entry.
Definition: cli.h:171
#define LOG_WARNING
Definition: logger.h:274
static struct ast_sip_endpoint * common_identify(ao2_callback_fn *identify_match_cb, void *arg)
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
static int load_module(void)
AMI variable container.
Definition: res_pjsip.h:2737
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Definition: astobj2.h:1335
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
void ast_srv_cleanup(struct srv_context **context)
Cleanup resources associated with ast_srv_lookup.
Definition: srv.c:248
static int header_identify_match_check(void *obj, void *arg, int flags)
Comparator function for matching an object by header.
Perform no matching, return all objects.
Definition: sorcery.h:123
enum ast_cc_service_type service
Definition: chan_sip.c:949
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition: sorcery.c:2459
Full structure for sorcery.
Definition: sorcery.c:230
int(* iterate)(void *container, ao2_callback_fn callback, void *args)
Definition: res_pjsip_cli.h:66
Type for a default handler that should do nothing.
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
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:1091
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
enum ast_acl_sense sense
Definition: acl.h:55
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:337
#define ast_sorcery_apply_config(sorcery, name)
Definition: sorcery.h:456
Return all matching objects.
Definition: sorcery.h:120
#define ast_assert(a)
Definition: utils.h:695
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:2315
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
const char * str
Definition: app_jack.c:147
void ast_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
Unregister an endpoint formatter.
Definition: res_pjsip.c:3685
const char * args
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:998
#define NULL
Definition: resample.c:96
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
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.
Socket address structure.
Definition: netsock2.h:97
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
char * ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pjsip_cli.c:109
static char * my_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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
static struct ast_sip_endpoint_identifier ip_identifier
static int unload_module(void)
#define ast_strlen_zero(foo)
Definition: strings.h:52
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
int ast_sip_register_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
Registers a CLI formatter.
Definition: pjsip_cli.c:310
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:521
internal representation of ACL entries In principle user applications would have no need for this...
Definition: acl.h:51
struct ao2_container * hosts
Hosts to be resolved when applying configuration.
void ast_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj)
Register an endpoint formatter.
Definition: res_pjsip.c:3679
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.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
int() ao2_callback_fn(void *obj, void *arg, int flags)
Type of a generic callback function.
Definition: astobj2.h:1230
#define ast_log
Definition: astobj2.c:42
#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
unsigned int srv_lookups
Perform SRV resolution of hostnames.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
static struct ast_sip_endpoint * header_identify(pjsip_rx_data *rdata)
static char host[256]
Definition: muted.c:77
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
struct ast_sockaddr netmask
Definition: acl.h:54
#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:911
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
#define CLI_INDENT_TO_SPACES(x)
Definition: res_pjsip_cli.h:29
Access Control of various sorts.
static struct ast_sip_endpoint_identifier header_identifier
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:299
#define ao2_ref(o, delta)
Definition: astobj2.h:464
static int format_ami_endpoint_identify(const struct ast_sip_endpoint *endpoint, struct ast_sip_ami *ami)
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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 HOSTS_BUCKETS
The number of buckets for storing hosts for resolution.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
void *(* retrieve_by_id)(const char *id)
Definition: res_pjsip_cli.h:68
struct mansession * s
Definition: res_pjsip.h:2739
static struct ast_sip_cli_formatter_entry * cli_formatter
struct ao2_container * container
Definition: res_fax.c:502
#define ast_variable_new(name, value, filename)
int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
Unregisters a CLI formatter.
Definition: pjsip_cli.c:326
An entity with which Asterisk communicates.
Definition: res_pjsip.h:812
static struct ast_sip_endpoint * ip_identify(pjsip_rx_data *rdata)
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:838
#define MAX_OBJECT_FIELD
Maximum length of an object field name.
Definition: sorcery.h:110
static int send_identify_ami_event(void *obj, void *arg, void *data, int flags)
#define CLI_MAX_WIDTH
Definition: res_pjsip_cli.h:26
#define CLI_HEADER_FILLER
Definition: res_pjsip_cli.h:24
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
char * usage
Definition: utils/frame.c:37
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 ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:537
Type for default option handler for bools (ast_true/ast_false)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition: sorcery.h:477
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1743
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:260
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
static int match_to_str(const void *obj, const intptr_t *args, char **buf)
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
static void * ip_identify_alloc(const char *name)
Allocator function for a matching object.
struct ast_sip_endpoint_formatter endpoint_identify_formatter
static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
Definition: func_strings.c:948
static int reload(void)
Definition: cdr_mysql.c:741
const ast_string_field match_header_name
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:2767
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
An entity responsible for identifying the source of a SIP message.
Definition: res_pjsip.h:993
const char *(* get_id)(const void *obj)
Definition: res_pjsip_cli.h:70
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.
int ast_sockaddr_cidr_bits(const struct ast_sockaddr *sa)
Count the 1 bits in a netmask.
Definition: netsock2.c:130
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
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
static int ip_identify_apply(const struct ast_sorcery *sorcery, void *obj)
Apply handler for identify type.
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
static int cli_print_header(void *obj, void *arg, int flags)
void ast_free_ha(struct ast_ha *ha)
Free a list of HAs.
Definition: acl.c:222
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
SORCERY_OBJECT(details)
Sorcery object details.
void ast_sorcery_object_set_has_dynamic_contents(const void *object)
Set the dynamic contents flag on a sorcery object.
Definition: sorcery.c:2379
static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
static struct ao2_container * cli_get_container(const char *regex)
char * strsep(char **str, const char *delims)
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
static struct ast_sorcery * sorcery
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
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:3431
int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf)
Converts a sorcery object to a string of object properties.
static void * cli_retrieve_by_id(const char *id)
void ast_sip_unregister_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier)
Unregister a SIP endpoint identifier.
Definition: res_pjsip.c:3513
Type for default option handler for stringfields.
ao2_callback_fn * print_header
Definition: res_pjsip_cli.h:60
int error(const char *format,...)
Definition: utils/frame.c:999
static int match_to_var_list(const void *obj, struct ast_variable **fields)
static int ip_identify_match_check(void *obj, void *arg, int flags)
Comparator function for matching an object by IP address.
#define PATH_MAX
Definition: asterisk.h:40
Generic container type.
static int cli_print_body(void *obj, void *arg, int flags)
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
Custom handler for match field.
static int reload_module(void)
ao2_callback_fn * print_body
Definition: res_pjsip_cli.h:62
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
int ast_sip_cli_print_sorcery_objectset(void *obj, void *arg, int flags)
Prints a sorcery object&#39;s ast_variable list.
Definition: pjsip_cli.c:36
const char * name
Definition: res_pjsip_cli.h:58
const ast_string_field endpoint_name
Structure for an IP identification matching object.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
const ast_string_field match_header
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
#define ast_variable_list_append(head, new_var)
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
static void ip_identify_destroy(void *obj)
Destructor function for a matching object.
struct ast_ha * matches
Networks or addresses that should match this.
static int sip_identify_to_ami(const struct ip_identify_match *identify, struct ast_str **buf)
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
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
static char prefix[MAX_PREFIX]
Definition: http.c:141
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
unsigned show_details_only_level_0
Definition: res_pjsip_cli.h:46
static struct test_val a
const ast_string_field match_header_value