Asterisk - The Open Source Telephony Project  21.4.1
res_pjsip_endpoint_identifier_ip.c
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"
34 #include "res_pjsip/include/res_pjsip_private.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 */
141  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 */
156  regex_t regex_header_buf;
157  /*! Compiled match_request_uri regular expression when is_request_uri_regex is non-zero */
159  /*! \brief Networks or addresses that should match this */
160  struct ast_ha *matches;
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 */
172 static 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 */
188 static 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 */
201 static 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",
254  ast_sorcery_object_get_id(identify),
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",
261  ast_sorcery_object_get_id(identify),
262  identify->match_header_name);
263  }
264  return 0;
265 }
266 
267 /*! \brief Comparator function for matching an object by request URI */
268 static 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 */
303 static 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 
323 static 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",
347  } else {
348  ast_log(LOG_WARNING, "Identify '%s' points to endpoint '%s' but endpoint could not be found\n",
350  }
351 
352  ao2_ref(match, -1);
353  return endpoint;
354 }
355 
356 static 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 
363  return common_identify(ip_identify_match_check, &addr);
364 }
365 
366 static struct ast_sip_endpoint_identifier ip_identifier = {
367  .identify_endpoint = ip_identify,
368 };
369 
370 static struct ast_sip_endpoint *header_identify(pjsip_rx_data *rdata)
371 {
372  return common_identify(header_identify_match_check, rdata);
373 }
374 
375 static struct ast_sip_endpoint *request_identify(pjsip_rx_data *rdata)
376 {
377  return common_identify(request_identify_match_check, rdata);
378 }
379 
380 static struct ast_sip_endpoint_identifier header_identifier = {
381  .identify_endpoint = header_identify,
382 };
383 
384 static struct ast_sip_endpoint_identifier request_identifier = {
385  .identify_endpoint = request_identify,
386 };
387 
388 /*! \brief Helper function which performs a host lookup and adds result to identify match */
389 static 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 */
423 static 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 
448  ast_srv_cleanup(&context);
449 
450  return results;
451 }
452 
453 /*! \brief Custom handler for match field */
454 static 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 */
510 static 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 
664 static int match_to_str(const void *obj, const intptr_t *args, char **buf)
665 {
666  RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);
667  const struct ip_identify_match *identify = obj;
668 
669  ast_ha_join(identify->matches, &str);
670  *buf = ast_strdup(ast_str_buffer(str));
671  return 0;
672 }
673 
674 static 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)) {
680  addr = ast_strdupa(ast_sockaddr_stringify(&ha->addr));
681  } else {
682  addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr));
683  }
684 
685  snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "",
686  addr, ast_sockaddr_stringify_addr(&ha->netmask));
687 
688  ast_variable_list_append(head, ast_variable_new("match", str, ""));
689 }
690 
691 static 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 
708 static 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 
714 static 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 
740 static 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",
750  AST_RETRIEVE_FLAG_MULTIPLE, &fields);
751  if (!identifies) {
752  return -1;
753  }
754 
755  /* Build and send any found identify object's AMI IdentifyDetail event. */
756  ao2_callback_data(identifies, OBJ_MULTIPLE | OBJ_NODATA,
757  send_identify_ami_event,
758  (void *) ast_sorcery_object_get_id(endpoint),
759  ami);
760 
761  ao2_ref(identifies, -1);
762  return 0;
763 }
764 
765 struct ast_sip_endpoint_formatter endpoint_identify_formatter = {
766  .format_ami = format_ami_endpoint_identify
767 };
768 
769 static 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",
781  AST_RETRIEVE_FLAG_MULTIPLE, &fields);
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 
792 static struct ao2_container *cli_get_container(const char *regex)
793 {
794  RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
795  struct ao2_container *s_container;
796 
797  container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "identify", regex);
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 
816 static void *cli_retrieve_by_id(const char *id)
817 {
818  return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "identify", id);
819 }
820 
821 static 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 
848 static int cli_print_body(void *obj, void *arg, int flags)
849 {
850  RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);
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)) {
870  addr = ast_sockaddr_stringify(&match->addr);
871  } else {
872  addr = ast_sockaddr_stringify_addr(&match->addr);
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");
906  ast_sip_cli_print_sorcery_objectset(ident, context, 0);
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  */
917 static 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 
923 static struct ast_cli_entry cli_identify[] = {
924 AST_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"),
929 AST_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"),
934 AST_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 
940 static struct ast_sip_cli_formatter_entry *cli_formatter;
941 
942 static 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 
947  if (ast_sorcery_object_register(ast_sip_get_sorcery(), "identify", ip_identify_alloc, NULL, ip_identify_apply)) {
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));
953  ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, match_to_str, match_to_var_list, 0, 0);
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));
957  ast_sorcery_load_object(ast_sip_get_sorcery(), "identify");
958 
959  ast_sip_register_endpoint_identifier_with_name(&ip_identifier, "ip");
960  ast_sip_register_endpoint_identifier_with_name(&header_identifier, "header");
961  ast_sip_register_endpoint_identifier_with_name(&request_identifier, "request_uri");
962  ast_sip_register_endpoint_formatter(&endpoint_identify_formatter);
963 
964  cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
965  if (!cli_formatter) {
966  ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
968  }
969  cli_formatter->name = "identify";
970  cli_formatter->print_header = cli_print_header;
971  cli_formatter->print_body = cli_print_body;
972  cli_formatter->get_container = cli_get_container;
973  cli_formatter->iterate = cli_iterator;
974  cli_formatter->get_id = ast_sorcery_object_get_id;
975  cli_formatter->retrieve_by_id = cli_retrieve_by_id;
976 
977  ast_sip_register_cli_formatter(cli_formatter);
978  ast_cli_register_multiple(cli_identify, ARRAY_LEN(cli_identify));
979 
981 }
982 
983 static int reload_module(void)
984 {
985  ast_sorcery_reload_object(ast_sip_get_sorcery(), "identify");
986 
987  return 0;
988 }
989 
990 static int unload_module(void)
991 {
992  ast_cli_unregister_multiple(cli_identify, ARRAY_LEN(cli_identify));
993  ast_sip_unregister_cli_formatter(cli_formatter);
994  ast_sip_unregister_endpoint_formatter(&endpoint_identify_formatter);
995  ast_sip_unregister_endpoint_identifier(&header_identifier);
996  ast_sip_unregister_endpoint_identifier(&request_identifier);
997  ast_sip_unregister_endpoint_identifier(&ip_identifier);
998 
999  return 0;
1000 }
1001 
1002 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP IP endpoint identifier",
1003  .support_level = AST_MODULE_SUPPORT_CORE,
1004  .load = load_module,
1005  .reload = reload_module,
1006  .unload = unload_module,
1007  .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
1008  .requires = "res_pjsip",
1009 );
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
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
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3310
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.
An entity responsible formatting endpoint information.
Definition: res_pjsip.h:3057
CLI Formatter Registry Entry.
Definition: res_pjsip_cli.h:52
int( ao2_callback_fn)(void *obj, void *arg, int flags)
Type of a generic callback function.
Definition: astobj2.h:1226
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
descriptor for a cli entry.
Definition: cli.h:171
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
AMI variable container.
Definition: res_pjsip.h:3031
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
Structure for variables, used for configurations and for channel variables.
void ast_srv_cleanup(struct srv_context **context)
Cleanup resources associated with ast_srv_lookup.
Definition: srv.c:248
Perform no matching, return all objects.
Definition: sorcery.h:123
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition: sorcery.c:2464
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:807
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
#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.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
Return all matching objects.
Definition: sorcery.h:120
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
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:1279
CLI Formatter Context passed to all formatters.
Definition: res_pjsip_cli.h:34
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
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:722
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
const ast_string_field match_request_uri
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_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
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.
#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.
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
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
Access Control of various sorts.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
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
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
void *(* retrieve_by_id)(const char *id)
Definition: res_pjsip_cli.h:68
struct mansession * s
Definition: res_pjsip.h:3033
struct ao2_container * container
Definition: res_fax.c:501
#define ast_debug(level,...)
Log a DEBUG message.
An entity with which Asterisk communicates.
Definition: res_pjsip.h:949
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:837
#define MAX_OBJECT_FIELD
Maximum length of an object field name.
Definition: sorcery.h:110
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
Support for dynamic strings.
Definition: strings.h:623
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:532
Type for default option handler for bools (ast_true/ast_false)
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
char * command
Definition: cli.h:186
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:3061
#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:1274
const char *(* get_id)(const void *obj)
Definition: res_pjsip_cli.h:70
int ast_sockaddr_cidr_bits(const struct ast_sockaddr *sa)
Count the 1 bits in a netmask.
Definition: netsock2.c:130
const char * usage
Definition: cli.h:177
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
#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_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:2384
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
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Type for default option handler for stringfields.
ao2_callback_fn * print_header
Definition: res_pjsip_cli.h:60
Generic container type.
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
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.
const char * name
Definition: res_pjsip_cli.h:58
const ast_string_field endpoint_name
#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
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:374
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:205
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
struct ast_ha * matches
Networks or addresses that should match this.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
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
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
unsigned show_details_only_level_0
Definition: res_pjsip_cli.h:46
const ast_string_field match_header_value