Asterisk - The Open Source Telephony Project  GIT-master-44aef04
enum.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Funding provided by nic.at
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20 
21 /*! \file
22  *
23  * \brief ENUM Support for Asterisk
24  *
25  * \author Mark Spencer <markster@digium.com>
26  *
27  * \arg Funding provided by nic.at
28  *
29  * \par Enum standards
30  *
31  * - NAPTR records: http://ietf.nri.reston.va.us/rfc/rfc2915.txt
32  * - DNS SRV records: http://www.ietf.org/rfc/rfc2782.txt
33  * - ENUM http://www.ietf.org/rfc/rfc3761.txt
34  * - ENUM for H.323: http://www.ietf.org/rfc/rfc3762.txt
35  * - ENUM SIP: http://www.ietf.org/rfc/rfc3764.txt
36  * - IANA ENUM Services: http://www.iana.org/assignments/enum-services
37  *
38  * - I-ENUM:
39  * http://tools.ietf.org/wg/enum/draft-ietf-enum-combined/
40  * http://tools.ietf.org/wg/enum/draft-ietf-enum-branch-location-record/
41  *
42  * \par Possible improvement
43  * \todo Implement a caching mechanism for multile enum lookups
44  * - See https://issues.asterisk.org/view.php?id=6739
45  * \todo The service type selection needs to be redone.
46  */
47 
48 /*! \li \ref enum.c uses the configuration file \ref enum.conf
49  * \addtogroup configuration_file Configuration Files
50  */
51 
52 /*!
53  * \page enum.conf enum.conf
54  * \verbinclude enum.conf.sample
55  */
56 
57 /*** MODULEINFO
58  <support_level>core</support_level>
59  ***/
60 
61 #include "asterisk.h"
62 
63 #include <sys/socket.h>
64 #include <netinet/in.h>
65 #include <arpa/nameser.h>
66 #ifdef __APPLE__
67 #include <arpa/nameser_compat.h>
68 #endif
69 #include <resolv.h>
70 #include <ctype.h>
71 #include <regex.h>
72 
73 #include "asterisk/module.h"
74 #include "asterisk/enum.h"
75 #include "asterisk/dns.h"
76 #include "asterisk/channel.h"
77 #include "asterisk/config.h"
78 #include "asterisk/utils.h"
79 #include "asterisk/manager.h"
80 
81 #ifdef __APPLE__
82 #undef T_NAPTR
83 #define T_NAPTR 35
84 #endif
85 
86 #ifdef __APPLE__
87 #undef T_TXT
88 #define T_TXT 16
89 #endif
90 
91 static char ienum_branchlabel[32] = "i";
92 /* how to do infrastructure enum branch location resolution? */
93 #define ENUMLOOKUP_BLR_CC 0
94 #define ENUMLOOKUP_BLR_TXT 1
95 #define ENUMLOOKUP_BLR_EBL 2
97 
98 /* EBL record provisional type code */
99 #define T_EBL 65300
100 
102 
103 /*! \brief Determine the length of a country code when given an E.164 string */
104 /*
105  * Input: E.164 number w/o leading +
106  *
107  * Output: number of digits in the country code
108  * 0 on invalid number
109  *
110  * Algorithm:
111  * 3 digits is the default length of a country code.
112  * country codes 1 and 7 are a single digit.
113  * the following country codes are two digits: 20, 27, 30-34, 36, 39,
114  * 40, 41, 43-49, 51-58, 60-66, 81, 82, 84, 86, 90-95, 98.
115  */
116 static int cclen(const char *number)
117 {
118  int cc;
119  char digits[3] = "";
120 
121  if (!number || (strlen(number) < 3)) {
122  return 0;
123  }
124 
125  strncpy(digits, number, 2);
126 
127  if (!sscanf(digits, "%30d", &cc)) {
128  return 0;
129  }
130 
131  if (cc / 10 == 1 || cc / 10 == 7)
132  return 1;
133 
134  if (cc == 20 || cc == 27 || (cc >= 30 && cc <= 34) || cc == 36 ||
135  cc == 39 || cc == 40 || cc == 41 || (cc >= 40 && cc <= 41) ||
136  (cc >= 43 && cc <= 49) || (cc >= 51 && cc <= 58) ||
137  (cc >= 60 && cc <= 66) || cc == 81 || cc == 82 || cc == 84 ||
138  cc == 86 || (cc >= 90 && cc <= 95) || cc == 98) {
139  return 2;
140  }
141 
142  return 3;
143 }
144 
145 struct txt_context {
146  char txt[1024]; /* TXT record in TXT lookup */
147  int txtlen; /* Length */
148 };
149 
150 /*! \brief Callback for TXT record lookup, /ol version */
151 static int txt_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
152 {
153  struct txt_context *c = context;
154  unsigned int i;
155 
156  c->txt[0] = 0; /* default to empty */
157  c->txtlen = 0;
158 
159  if (answer == NULL) {
160  return 0;
161  }
162 
163  /* RFC1035:
164  *
165  * <character-string> is a single length octet followed by that number of characters.
166  * TXT-DATA One or more <character-string>s.
167  *
168  * We only take the first string here.
169  */
170 
171  i = *answer++;
172  len -= 1;
173 
174  if (i > len) { /* illegal packet */
175  ast_log(LOG_WARNING, "txt_callback: malformed TXT record.\n");
176  return 0;
177  }
178 
179  if (i >= sizeof(c->txt)) { /* too long? */
180  ast_log(LOG_WARNING, "txt_callback: TXT record too long.\n");
181  i = sizeof(c->txt) - 1;
182  }
183 
184  ast_copy_string(c->txt, (char *)answer, i + 1); /* this handles the \0 termination */
185  c->txtlen = i;
186 
187  return 1;
188 }
189 
190 /*! \brief Determine the branch location record as stored in a TXT record */
191 /*
192  * Input: CC code
193  *
194  * Output: number of digits in the number before the i-enum branch
195  *
196  * Algorithm: Build <ienum_branchlabel>.c.c.<suffix> and look for a TXT lookup.
197  * Return atoi(TXT-record).
198  * Return -1 on not found.
199  *
200  */
201 static int blr_txt(const char *cc, const char *suffix)
202 {
203  struct txt_context context;
204  char domain[128] = "";
205  char *p1, *p2;
206  int ret;
207 
209 
210  ast_verb(4, "blr_txt() cc='%s', suffix='%s', c_bl='%s'\n", cc, suffix, ienum_branchlabel);
211 
212  if (sizeof(domain) < (strlen(cc) * 2 + strlen(ienum_branchlabel) + strlen(suffix) + 2)) {
214  ast_log(LOG_WARNING, "ERROR: string sizing in blr_txt.\n");
215  return -1;
216  }
217 
218  p1 = domain + snprintf(domain, sizeof(domain), "%s.", ienum_branchlabel);
220 
221  for (p2 = (char *) cc + strlen(cc) - 1; p2 >= cc; p2--) {
222  if (isdigit(*p2)) {
223  *p1++ = *p2;
224  *p1++ = '.';
225  }
226  }
227  strcat(p1, suffix);
228 
229  ast_verb(4, "blr_txt() FQDN for TXT record: %s, cc was %s\n", domain, cc);
230 
231  ret = ast_search_dns(&context, domain, C_IN, T_TXT, txt_callback);
232 
233  if (ret > 0) {
234  ret = atoi(context.txt);
235 
236  if ((ret >= 0) && (ret < 20)) {
237  ast_verb(3, "blr_txt() BLR TXT record for %s is %d (apex: %s)\n", cc, ret, suffix);
238  return ret;
239  }
240  }
241 
242  ast_verb(3, "blr_txt() BLR TXT record for %s not found (apex: %s)\n", cc, suffix);
243 
244  return -1;
245 }
246 
247 struct ebl_context {
248  unsigned char pos;
249  char separator[256]; /* label to insert */
250  int sep_len; /* Length */
251  char apex[256]; /* new Apex */
252  int apex_len; /* Length */
253 };
254 
255 /*! \brief Callback for EBL record lookup */
256 static int ebl_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
257 {
258  struct ebl_context *c = context;
259  int i;
260 
261  c->pos = 0; /* default to empty */
262  c->separator[0] = 0;
263  c->sep_len = 0;
264  c->apex[0] = 0;
265  c->apex_len = 0;
266 
267  if (answer == NULL) {
268  return 0;
269  }
270 
271  /* draft-lendl-enum-branch-location-record-00
272  *
273  * 0 1 2 3 4 5 6 7
274  * +--+--+--+--+--+--+--+--+
275  * | POSITION |
276  * +--+--+--+--+--+--+--+--+
277  * / SEPARATOR /
278  * +--+--+--+--+--+--+--+--+
279  * / APEX /
280  * +--+--+--+--+--+--+--+--+
281  *
282  * where POSITION is a single byte, SEPARATOR is a <character-string>
283  * and APEX is a <domain-name>.
284  *
285  */
286 
287  c->pos = *answer++;
288  len -= 1;
289 
290  if ((c->pos > 15) || len < 2) { /* illegal packet */
291  ast_log(LOG_WARNING, "ebl_callback: malformed EBL record.\n");
292  return 0;
293  }
294 
295  i = *answer++;
296  len -= 1;
297  if (i > len) { /* illegal packet */
298  ast_log(LOG_WARNING, "ebl_callback: malformed EBL record.\n");
299  return 0;
300  }
301 
302  ast_copy_string(c->separator, (char *)answer, i + 1);
303  c->sep_len = i;
304 
305  answer += i;
306  len -= i;
307 
308  if ((i = dn_expand((unsigned char *)fullanswer, (unsigned char *)answer + len,
309  (unsigned char *)answer, c->apex, sizeof(c->apex) - 1)) < 0) {
310  ast_log(LOG_WARNING, "Failed to expand hostname\n");
311  return 0;
312  }
313  c->apex[i] = 0;
314  c->apex_len = i;
315 
316  return 1;
317 }
318 
319 /*! \brief Evaluate the I-ENUM branch as stored in an EBL record */
320 /*
321  * Input: CC code
322  *
323  * Output: number of digits in the number before the i-enum branch
324  *
325  * Algorithm: Build <ienum_branchlabel>.c.c.<suffix> and look for an EBL record
326  * Return pos and fill in separator and apex.
327  * Return -1 on not found.
328  *
329  */
330 static int blr_ebl(const char *cc, const char *suffix, char *separator, int sep_len, char* apex, int apex_len)
331 {
332  struct ebl_context context;
333  char domain[128] = "";
334  char *p1,*p2;
335  int ret;
336 
338 
339  ast_verb(4, "blr_ebl() cc='%s', suffix='%s', c_bl='%s'\n", cc, suffix, ienum_branchlabel);
340 
341  if (sizeof(domain) < (strlen(cc) * 2 + strlen(ienum_branchlabel) + strlen(suffix) + 2)) {
343  ast_log(LOG_WARNING, "ERROR: string sizing in blr_EBL.\n");
344  return -1;
345  }
346 
347  p1 = domain + snprintf(domain, sizeof(domain), "%s.", ienum_branchlabel);
349 
350  for (p2 = (char *) cc + strlen(cc) - 1; p2 >= cc; p2--) {
351  if (isdigit(*p2)) {
352  *p1++ = *p2;
353  *p1++ = '.';
354  }
355  }
356  strcat(p1, suffix);
357 
358  ast_verb(4, "blr_ebl() FQDN for EBL record: %s, cc was %s\n", domain, cc);
359 
360  ret = ast_search_dns(&context, domain, C_IN, T_EBL, ebl_callback);
361  if (ret > 0) {
362  ret = context.pos;
363 
364  if ((ret >= 0) && (ret < 20)) {
365  ast_verb(3, "blr_txt() BLR EBL record for %s is %d/%s/%s)\n", cc, ret, context.separator, context.apex);
366  ast_copy_string(separator, context.separator, sep_len);
367  ast_copy_string(apex, context.apex, apex_len);
368  return ret;
369  }
370  }
371  ast_verb(3, "blr_txt() BLR EBL record for %s not found (apex: %s)\n", cc, suffix);
372  return -1;
373 }
374 
375 /*! \brief Parse NAPTR record information elements */
376 static unsigned int parse_ie(char *data, unsigned int maxdatalen, unsigned char *src, unsigned int srclen)
377 {
378  unsigned int len, olen;
379 
380  len = olen = (unsigned int) src[0];
381  src++;
382  srclen--;
383 
384  if (len > srclen) {
385  ast_log(LOG_WARNING, "ENUM parsing failed: Wanted %u characters, got %u\n", len, srclen);
386  return -1;
387  }
388 
389  if (len > maxdatalen)
390  len = maxdatalen;
391  memcpy(data, src, len);
392 
393  return olen + 1;
394 }
395 
396 /*! \brief Parse DNS NAPTR record used in ENUM ---*/
397 static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, unsigned char *naptrinput)
398 {
399  char tech_return[80];
400  char *oanswer = (char *)answer;
401  char flags[512] = "";
402  char services[512] = "";
403  char *p;
404  char regexp[512] = "";
405  char repl[512] = "";
406  char tempdst[512] = "";
407  char errbuff[512] = "";
408  char delim;
409  char *delim2;
410  char *pattern, *subst, *d;
411  int res;
412  int regexp_len, rc;
413  static const int max_bt = 10; /* max num of regexp backreference allowed, must remain 10 to guarantee a valid backreference index */
414  int size, matchindex; /* size is the size of the backreference sub. */
415  size_t d_len = sizeof(tempdst) - 1;
416  regex_t preg;
417  int re_flags = REG_EXTENDED | REG_NEWLINE;
418  regmatch_t pmatch[max_bt];
419 
420  tech_return[0] = '\0';
421  dst[0] = '\0';
422 
423  if (len < sizeof(struct naptr)) {
424  ast_log(LOG_WARNING, "NAPTR record length too short\n");
425  return -1;
426  }
427  answer += sizeof(struct naptr);
428  len -= sizeof(struct naptr);
429  if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
430  ast_log(LOG_WARNING, "Failed to get flags from NAPTR record\n");
431  return -1;
432  } else {
433  answer += res;
434  len -= res;
435  }
436 
437  if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
438  ast_log(LOG_WARNING, "Failed to get services from NAPTR record\n");
439  return -1;
440  } else {
441  answer += res;
442  len -= res;
443  }
444  if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0) {
445  ast_log(LOG_WARNING, "Failed to get regexp from NAPTR record\n");
446  return -1;
447  } else {
448  answer += res;
449  len -= res;
450  }
451 
452  if ((res = dn_expand((unsigned char *)oanswer, (unsigned char *)answer + len, (unsigned char *)answer, repl, sizeof(repl) - 1)) < 0) {
453  ast_log(LOG_WARNING, "Failed to expand hostname\n");
454  return -1;
455  }
456 
457  ast_debug(3, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
458  naptrinput, flags, services, regexp, repl);
459 
460 
461  if (tolower(flags[0]) != 'u') {
462  ast_log(LOG_WARNING, "NAPTR Flag must be 'U' or 'u'.\n");
463  return -1;
464  }
465 
466  p = strstr(services, "e2u+");
467  if (p == NULL)
468  p = strstr(services, "E2U+");
469  if (p){
470  p = p + 4;
471  if (strchr(p, ':')){
472  p = strchr(p, ':') + 1;
473  }
474  ast_copy_string(tech_return, p, sizeof(tech_return));
475  } else {
476 
477  p = strstr(services, "+e2u");
478  if (p == NULL)
479  p = strstr(services, "+E2U");
480  if (p) {
481  *p = 0;
482  p = strchr(services, ':');
483  if (p)
484  *p = 0;
485  ast_copy_string(tech_return, services, sizeof(tech_return));
486  }
487  }
488 
489  regexp_len = strlen(regexp);
490  if (regexp_len < 7) {
491  ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
492  return -1;
493  }
494 
495  /* this takes the first character of the regexp (which is a delimiter)
496  * and uses that character to find the index of the second delimiter */
497  delim = regexp[0];
498  delim2 = strchr(regexp + 1, delim);
499  if ((delim2 == NULL)
500  || ((regexp[regexp_len - 1] != 'i' || regexp[regexp_len - 2] != delim)
501  && regexp[regexp_len - 1] != delim)) {
502  ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n", regexp);
503  return -1;
504  } else if (strchr((delim2 + 1), delim) == NULL) { /* if the second delimiter is found, make sure there is a third instance. this could be the end one instead of the middle */
505  ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n", regexp);
506  return -1;
507  }
508 
509  /* Make the regex case-insensitive if the 'i' flag is present. This assumes you
510  * aren't using 'i' as a delimiter which, altough dubious, does not appear to be
511  * explicitly non-compliant */
512  if (regexp[regexp_len - 1] == 'i') {
513  re_flags |= REG_ICASE;
514  }
515 
516  pattern = regexp + 1; /* pattern is the regex without the begining and ending delimiter */
517  *delim2 = 0; /* zero out the middle delimiter */
518  subst = delim2 + 1; /* dst substring is everything after the second delimiter. */
519  regexp[regexp_len - 1] = 0; /* zero out the last delimiter */
520 
521 /*
522  * now do the regex wizardry.
523  */
524 
525  if (regcomp(&preg, pattern, re_flags)) {
526  ast_log(LOG_WARNING, "NAPTR Regex compilation error (regex = \"%s\").\n", regexp);
527  return -1;
528  }
529 
530  if (preg.re_nsub > ARRAY_LEN(pmatch)) {
531  ast_log(LOG_WARNING, "NAPTR Regex compilation error: too many subs.\n");
532  regfree(&preg);
533  return -1;
534  }
535  /* pmatch is an array containing the substring indexes for the regex backreference sub.
536  * max_bt is the maximum number of backreferences allowed to be stored in pmatch */
537  if ((rc = regexec(&preg, (char *) naptrinput, max_bt, pmatch, 0))) {
538  regerror(rc, &preg, errbuff, sizeof(errbuff));
539  ast_log(LOG_WARNING, "NAPTR Regex match failed. Reason: %s\n", errbuff);
540  regfree(&preg);
541  return -1;
542  }
543  regfree(&preg);
544 
545  d = tempdst;
546  d_len--;
547 
548  /* perform the backreference sub. Search the subst for backreferences,
549  * when a backreference is found, retrieve the backreferences number.
550  * use the backreference number as an index for pmatch to retrieve the
551  * beginning and ending indexes of the substring to insert as the backreference.
552  * if no backreference is found, continue copying the subst into tempdst */
553  while (*subst && (d_len > 0)) {
554  if ((subst[0] == '\\') && isdigit(subst[1])) { /* is this character the beginning of a backreference */
555  matchindex = (int) (subst[1] - '0');
556  if (matchindex >= ARRAY_LEN(pmatch)) {
557  ast_log(LOG_WARNING, "Error during regex substitution. Invalid pmatch index.\n");
558  return -1;
559  }
560  /* pmatch len is 10. we are garanteed a single char 0-9 is a valid index */
561  size = pmatch[matchindex].rm_eo - pmatch[matchindex].rm_so;
562  if (size > d_len) {
563  ast_log(LOG_WARNING, "Not enough space during NAPTR regex substitution.\n");
564  return -1;
565  }
566  /* are the pmatch indexes valid for the input length */
567  if ((strlen((char *) naptrinput) >= pmatch[matchindex].rm_eo) && (pmatch[matchindex].rm_so <= pmatch[matchindex].rm_eo)) {
568  memcpy(d, (naptrinput + (int) pmatch[matchindex].rm_so), size); /* copy input substring into backreference marker */
569  d_len -= size;
570  subst += 2; /* skip over backreference characters to next valid character */
571  d += size;
572  } else {
573  ast_log(LOG_WARNING, "Error during regex substitution. Invalid backreference index.\n");
574  return -1;
575  }
576  } else if (isprint(*subst)) {
577  *d++ = *subst++;
578  d_len--;
579  } else {
580  ast_log(LOG_WARNING, "Error during regex substitution.\n");
581  return -1;
582  }
583  }
584  *d = 0;
585  ast_copy_string((char *) dst, tempdst, dstsize);
586  dst[dstsize - 1] = '\0';
587 
588  if (*tech != '\0'){ /* check if it is requested NAPTR */
589  if (!strncasecmp(tech, "ALL", techsize)){
590  return 0; /* return or count any RR */
591  }
592  if (!strncasecmp(tech_return, tech, sizeof(tech_return) < techsize ? sizeof(tech_return): techsize)){
593  ast_copy_string(tech, tech_return, techsize);
594  return 0; /* we got our RR */
595  } else { /* go to the next RR in the DNS answer */
596  return 1;
597  }
598  }
599 
600  /* tech was not specified, return first parsed RR */
601  ast_copy_string(tech, tech_return, techsize);
602 
603  return 0;
604 }
605 
606 /* do not return requested value, just count RRs and return thei number in dst */
607 #define ENUMLOOKUP_OPTIONS_COUNT 1
608 /* do an ISN style lookup */
609 #define ENUMLOOKUP_OPTIONS_ISN 2
610 /* do a infrastructure ENUM lookup */
611 #define ENUMLOOKUP_OPTIONS_IENUM 4
612 /* do a direct DNS lookup: no reversal */
613 #define ENUMLOOKUP_OPTIONS_DIRECT 8
614 
615 /*! \brief Callback from ENUM lookup function */
616 static int enum_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
617 {
618  struct enum_context *c = context;
619  void *p = NULL;
620  int res;
621 
622  res = parse_naptr((unsigned char *)c->dst, c->dstlen, c->tech, c->techlen, answer, len, (unsigned char *)c->naptrinput);
623 
624  if (res < 0) {
625  ast_log(LOG_WARNING, "Failed to parse naptr\n");
626  return -1;
627  } else if ((res == 0) && !ast_strlen_zero(c->dst)) { /* ok, we got needed NAPTR */
628  if (c->options & ENUMLOOKUP_OPTIONS_COUNT) { /* counting RRs */
629  c->count++;
630  snprintf(c->dst, c->dstlen, "%d", c->count);
631  } else {
632  if ((p = ast_realloc(c->naptr_rrs, sizeof(*c->naptr_rrs) * (c->naptr_rrs_count + 1)))) {
633  c->naptr_rrs = p;
634  memcpy(&c->naptr_rrs[c->naptr_rrs_count].naptr, answer, sizeof(c->naptr_rrs->naptr));
638  c->naptr_rrs_count++;
639  }
640  c->dst[0] = 0;
641  }
642  return 0;
643  }
644 
645  return 0;
646 }
647 
648 /* ENUM lookup */
649 int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char* suffix, char* options, unsigned int record, struct enum_context **argcontext)
650 {
651  struct enum_context *context;
652  char tmp[512];
653  char domain[256];
654  char left[128];
655  char middle[128];
656  char naptrinput[128];
657  char apex[128] = "";
658  int ret = -1;
659  /* for ISN rewrite */
660  char *p1 = NULL;
661  char *p2 = NULL;
662  char *p3 = NULL;
663  int k = 0;
664  int i = 0;
665  int z = 0;
666  int spaceleft = 0;
667  struct timeval time_start, time_end;
668 
669  if (ast_strlen_zero(suffix)) {
670  ast_log(LOG_WARNING, "ast_get_enum need a suffix parameter now.\n");
671  return -1;
672  }
673 
674  ast_debug(2, "num='%s', tech='%s', suffix='%s', options='%s', record=%u\n", number, tech, suffix, options, record);
675 
676 /*
677  We don't need that any more, that "n" preceding the number has been replaced by a flag
678  in the options paramter.
679  ast_copy_string(naptrinput, number, sizeof(naptrinput));
680 */
681 /*
682  * The "number" parameter includes a leading '+' if it's a full E.164 number (and not ISN)
683  * We need to preserve that as the regex inside NAPTRs expect the +.
684  *
685  * But for the domain generation, the '+' is a nuissance, so we get rid of it.
686 */
687  ast_copy_string(naptrinput, number[0] == 'n' ? number + 1 : number, sizeof(naptrinput));
688  if (number[0] == '+') {
689  number++;
690  }
691 
692  if (!(context = ast_calloc(1, sizeof(*context)))) {
693  return -1;
694  }
695 
696  if ((p3 = strchr(naptrinput, '*'))) {
697  *p3='\0';
698  }
699 
700  context->naptrinput = naptrinput; /* The number */
701  context->dst = dst; /* Return string */
702  context->dstlen = dstlen;
703  context->tech = tech;
704  context->techlen = techlen;
705  context->options = 0;
706  context->position = record > 0 ? record : 1;
707  context->count = 0;
708  context->naptr_rrs = NULL;
709  context->naptr_rrs_count = 0;
710 
711  /*
712  * Process options:
713  *
714  * c Return count, not URI
715  * i Use infrastructure ENUM
716  * s Do ISN transformation
717  * d Direct DNS query: no reversing.
718  *
719  */
720  if (options != NULL) {
721  if (strchr(options,'s')) {
722  context->options |= ENUMLOOKUP_OPTIONS_ISN;
723  } else if (strchr(options,'i')) {
724  context->options |= ENUMLOOKUP_OPTIONS_IENUM;
725  } else if (strchr(options,'d')) {
727  }
728  if (strchr(options,'c')) {
729  context->options |= ENUMLOOKUP_OPTIONS_COUNT;
730  }
731  if (strchr(number,'*')) {
732  context->options |= ENUMLOOKUP_OPTIONS_ISN;
733  }
734  }
735  ast_debug(2, "ENUM options(%s): pos=%d, options='%d'\n", options, context->position, context->options);
736  ast_debug(1, "n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n",
737  number, tech, suffix, context->options, context->position);
738 
739  /*
740  * This code does more than simple RFC3261 ENUM. All these rewriting
741  * schemes have in common that they build the FQDN for the NAPTR lookup
742  * by concatenating
743  * - a number which needs be flipped and "."-seperated (left)
744  * - some fixed string (middle)
745  * - an Apex. (apex)
746  *
747  * The RFC3261 ENUM is: left=full number, middle="", apex=from args.
748  * ISN: number = "middle*left", apex=from args
749  * I-ENUM: EBL parameters build the split, can change apex
750  * Direct: left="", middle=argument, apex=from args
751  *
752  */
753 
754  /* default: the whole number will be flipped, no middle domain component */
755  ast_copy_string(left, number, sizeof(left));
756  middle[0] = '\0';
757  /*
758  * I-ENUM can change the apex, thus we copy it
759  */
760  ast_copy_string(apex, suffix, sizeof(apex));
761  /* ISN rewrite */
762  if ((context->options & ENUMLOOKUP_OPTIONS_ISN) && (p1 = strchr(number, '*'))) {
763  *p1++ = '\0';
764  ast_copy_string(left, number, sizeof(left));
765  ast_copy_string(middle, p1, sizeof(middle) - 1);
766  strcat(middle, ".");
767  ast_debug(2, "ISN ENUM: left=%s, middle='%s'\n", left, middle);
768  /* Direct DNS lookup rewrite */
769  } else if (context->options & ENUMLOOKUP_OPTIONS_DIRECT) {
770  left[0] = 0; /* nothing to flip around */
771  ast_copy_string(middle, number, sizeof(middle) - 1);
772  strcat(middle, ".");
773  ast_debug(2, "DIRECT ENUM: middle='%s'\n", middle);
774  /* Infrastructure ENUM rewrite */
775  } else if (context->options & ENUMLOOKUP_OPTIONS_IENUM) {
776  int sdl = 0;
777  char cc[8];
778  char sep[256], n_apex[256];
779  int cc_len = cclen(number);
780  sdl = cc_len;
782  ast_copy_string(sep, ienum_branchlabel, sizeof(sep)); /* default */
784 
785  switch (ebl_alg) {
786  case ENUMLOOKUP_BLR_EBL:
787  ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */
788  sdl = blr_ebl(cc, suffix, sep, sizeof(sep) - 1, n_apex, sizeof(n_apex) - 1);
789 
790  if (sdl >= 0) {
791  ast_copy_string(apex, n_apex, sizeof(apex));
792  ast_debug(2, "EBL ENUM: sep=%s, apex='%s'\n", sep, n_apex);
793  } else {
794  sdl = cc_len;
795  }
796  break;
797  case ENUMLOOKUP_BLR_TXT:
798  ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */
799  sdl = blr_txt(cc, suffix);
800 
801  if (sdl < 0) {
802  sdl = cc_len;
803  }
804  break;
805 
806  case ENUMLOOKUP_BLR_CC: /* BLR is at the country-code level */
807  default:
808  sdl = cc_len;
809  break;
810  }
811 
812  if (sdl > strlen(number)) { /* Number too short for this sdl? */
813  ast_log(LOG_WARNING, "I-ENUM: subdomain location %d behind number %s\n", sdl, number);
814  ast_free(context);
815  return 0;
816  }
817  ast_copy_string(left, number + sdl, sizeof(left));
818 
820  ast_copy_string(middle, sep, sizeof(middle) - 1);
821  strcat(middle, ".");
823 
824  /* check the space we need for middle */
825  if ((sdl * 2 + strlen(middle) + 2) > sizeof(middle)) {
826  ast_log(LOG_WARNING, "ast_get_enum: not enough space for I-ENUM rewrite.\n");
827  ast_free(context);
828  return -1;
829  }
830 
831  p1 = middle + strlen(middle);
832  for (p2 = (char *) number + sdl - 1; p2 >= number; p2--) {
833  if (isdigit(*p2)) {
834  *p1++ = *p2;
835  *p1++ = '.';
836  }
837  }
838  *p1 = '\0';
839 
840  ast_debug(2, "I-ENUM: cclen=%d, left=%s, middle='%s', apex='%s'\n", cc_len, left, middle, apex);
841  }
842 
843  if (strlen(left) * 2 + 2 > sizeof(domain)) {
844  ast_log(LOG_WARNING, "string to long in ast_get_enum\n");
845  ast_free(context);
846  return -1;
847  }
848 
849  /* flip left into domain */
850  p1 = domain;
851  for (p2 = left + strlen(left); p2 >= left; p2--) {
852  if (isdigit(*p2)) {
853  *p1++ = *p2;
854  *p1++ = '.';
855  }
856  }
857  *p1 = '\0';
858 
859  if (chan && ast_autoservice_start(chan) < 0) {
860  ast_free(context);
861  return -1;
862  }
863 
864  spaceleft = sizeof(tmp) - 2;
865  ast_copy_string(tmp, domain, spaceleft);
866  spaceleft -= strlen(domain);
867 
868  if (*middle) {
869  strncat(tmp, middle, spaceleft);
870  spaceleft -= strlen(middle);
871  }
872 
873  strncat(tmp,apex,spaceleft);
874  time_start = ast_tvnow();
875  ret = ast_search_dns(context, tmp, C_IN, T_NAPTR, enum_callback);
876  time_end = ast_tvnow();
877 
878  ast_debug(2, "profiling: %s, %s, %" PRIi64 " ms\n",
879  (ret == 0) ? "OK" : "FAIL", tmp, ast_tvdiff_ms(time_end, time_start));
880 
881  if (ret < 0) {
882  ast_debug(1, "No such number found: %s (%s)\n", tmp, strerror(errno));
883  context->naptr_rrs_count = -1;
884  strcpy(dst, "0");
885  ret = 0;
886  }
887 
888  if (context->naptr_rrs_count >= context->position && ! (context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
889  /* sort array by NAPTR order/preference */
890  for (k = 0; k < context->naptr_rrs_count; k++) {
891  for (i = 0; i < context->naptr_rrs_count; i++) {
892  /* use order first and then preference to compare */
893  if ((ntohs(context->naptr_rrs[k].naptr.order) < ntohs(context->naptr_rrs[i].naptr.order)
894  && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
895  || (ntohs(context->naptr_rrs[k].naptr.order) > ntohs(context->naptr_rrs[i].naptr.order)
896  && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
897  z = context->naptr_rrs[k].sort_pos;
898  context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
899  context->naptr_rrs[i].sort_pos = z;
900  continue;
901  }
902  if (ntohs(context->naptr_rrs[k].naptr.order) == ntohs(context->naptr_rrs[i].naptr.order)) {
903  if ((ntohs(context->naptr_rrs[k].naptr.pref) < ntohs(context->naptr_rrs[i].naptr.pref)
904  && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
905  || (ntohs(context->naptr_rrs[k].naptr.pref) > ntohs(context->naptr_rrs[i].naptr.pref)
906  && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
907  z = context->naptr_rrs[k].sort_pos;
908  context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
909  context->naptr_rrs[i].sort_pos = z;
910  }
911  }
912  }
913  }
914  for (k = 0; k < context->naptr_rrs_count; k++) {
915  if (context->naptr_rrs[k].sort_pos == context->position - 1) {
916  ast_copy_string(context->dst, context->naptr_rrs[k].result, dstlen);
917  ast_copy_string(context->tech, context->naptr_rrs[k].tech, techlen);
918  break;
919  }
920  }
921  } else if (!(context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
922  context->dst[0] = 0;
923  } else if ((context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
924  snprintf(context->dst, context->dstlen, "%d", context->naptr_rrs_count + context->count);
925  }
926 
927  if (chan) {
928  ret |= ast_autoservice_stop(chan);
929  }
930 
931  if (!argcontext) {
932  for (k = 0; k < context->naptr_rrs_count; k++) {
933  ast_free(context->naptr_rrs[k].result);
934  ast_free(context->naptr_rrs[k].tech);
935  }
936  ast_free(context->naptr_rrs);
937  ast_free(context);
938  } else {
939  *argcontext = context;
940  }
941 
942  return ret;
943 }
944 
945 /*! \internal
946  * \brief Format a phone number as a domain in an ENUM-adjacent way.
947  *
948  * Creates a domain name suitable for query by:
949  *
950  * 1. Removing non-digits
951  * 2. Adding a '.' between adjacent digits
952  * 3. Reversing the string
953  * 4. Appending the specified suffix (or e164.arpa if none is specified)
954  */
955 static char *format_numeric_domain(const char *number, const char *suffix)
956 {
957  char *buffer, *dst;
958  size_t suffix_length;
959  size_t number_length = strlen(number);
960  const char *src = number + number_length - 1;
961 
962  if (!suffix) {
963  suffix = "e164.arpa";
964  }
965 
966  suffix_length = strlen(suffix);
967 
968  dst = buffer = ast_malloc(
969  (number_length * 2) /* We need 2 bytes per input digit */
970  + suffix_length /* ... plus however long the suffix is */
971  + 1 /* ... plus room for the '.' separator */
972  + 1 /* ... and room for the \0 byte at the end */);
973 
974  if (buffer) {
975  while (src >= number) {
976  if (isdigit(*src)) {
977  *dst++ = *src;
978  *dst++ = '.';
979  }
980  src--;
981  }
982 
983  /* The length arguments below make sure that the \0 byte is copied into
984  the final string */
985  if (*suffix == '.') {
986  memcpy(dst, &suffix[1], suffix_length);
987  } else {
988  memcpy(dst, suffix, suffix_length + 1);
989  }
990  }
991 
992  return buffer;
993 }
994 
995 int ast_get_txt(struct ast_channel *chan, const char *number, char *txt, int txtlen, char *suffix)
996 {
997  struct txt_context context;
998  char *domain;
999  int ret;
1000  int autoservice = 0;
1001 
1002  ast_debug(4, "ast_get_txt: Number = '%s', suffix = '%s'\n", number, suffix);
1003 
1004  domain = format_numeric_domain(number, suffix);
1005  if (!domain) {
1006  return -1;
1007  }
1008 
1009  if (chan) {
1010  /* DNS might take a while, so service the channel while we're blocked */
1011  autoservice = !ast_autoservice_start(chan);
1012  }
1013 
1014  ret = ast_search_dns(&context, domain, C_IN, T_TXT, txt_callback);
1015  if (ret > 0) {
1016  ast_copy_string(txt, context.txt, txtlen);
1017  } else {
1018  ast_debug(2, "No such number found in ENUM: %s (%s)\n", domain, strerror(errno));
1019  }
1020 
1021  if (autoservice) {
1022  ast_autoservice_stop(chan);
1023  }
1024 
1025  ast_free(domain);
1026  return 0;
1027 }
1028 
1029 /*! \brief Initialize the ENUM support subsystem */
1030 static int private_enum_init(int reload)
1031 {
1032  struct ast_config *cfg;
1033  const char *string;
1034  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1035 
1036  if ((cfg = ast_config_load2("enum.conf", "enum", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
1037  return 0;
1039  return 0;
1040  }
1041 
1042  /* Destroy existing list */
1044  if (cfg) {
1045  if ((string = ast_variable_retrieve(cfg, "ienum", "branchlabel"))) {
1047  }
1048 
1049  if ((string = ast_variable_retrieve(cfg, "ienum", "ebl_alg"))) {
1050  ebl_alg = ENUMLOOKUP_BLR_CC; /* default */
1051 
1052  if (!strcasecmp(string, "txt"))
1054  else if (!strcasecmp(string, "ebl"))
1056  else if (!strcasecmp(string, "cc"))
1058  else
1059  ast_log(LOG_WARNING, "No valid parameter for ienum/ebl_alg.\n");
1060  }
1061  ast_config_destroy(cfg);
1062  }
1064  return 0;
1065 }
1066 
1067 static int load_module(void)
1068 {
1070 }
1071 
1072 static int unload_module(void)
1073 {
1074  return 0;
1075 }
1076 
1077 static int reload_module(void)
1078 {
1079  return private_enum_init(1);
1080 }
1081 
1083  .support_level = AST_MODULE_SUPPORT_CORE,
1084  .load = load_module,
1085  .unload = unload_module,
1086  .reload = reload_module,
1087  .load_pri = AST_MODPRI_CORE,
1088  .requires = "extconfig",
1089 );
static int enum_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Callback from ENUM lookup function.
Definition: enum.c:616
Main Channel structure associated with a channel.
#define ENUMLOOKUP_OPTIONS_COUNT
Definition: enum.c:607
Asterisk main include file. File version handling, generic pbx functions.
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:228
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
char txt[1024]
Definition: enum.c:146
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
int count
Definition: enum.h:49
static char ienum_branchlabel[32]
Definition: enum.c:91
struct enum_naptr_rr * naptr_rrs
Definition: enum.h:51
int apex_len
Definition: enum.c:252
#define LOG_WARNING
Definition: logger.h:274
static int private_enum_init(int reload)
Initialize the ENUM support subsystem.
Definition: enum.c:1030
int ast_search_dns(void *context, const char *dname, int class, int type, int(*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
Perform DNS lookup (used by DNS, enum and SRV lookups)
Definition: dns.c:493
static int cclen(const char *number)
Determine the length of a country code when given an E.164 string.
Definition: enum.c:116
static struct test_val d
#define CONFIG_STATUS_FILEINVALID
static ast_mutex_t enumlock
Definition: enum.c:101
static int ebl_alg
Definition: enum.c:96
static int tmp()
Definition: bt_open.c:389
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: main/config.c:3154
unsigned short pref
Definition: enum.h:30
int dstlen
Definition: enum.h:42
int position
Definition: enum.h:48
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
const char * string
Definition: presencestate.c:71
#define ast_mutex_lock(a)
Definition: lock.h:187
static struct test_val c
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:98
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
Domain data structure.
Definition: sip.h:888
char separator[256]
Definition: enum.c:249
#define ENUMLOOKUP_OPTIONS_DIRECT
Definition: enum.c:613
int txtlen
Definition: enum.c:147
#define ast_verb(level,...)
Definition: logger.h:455
Utility functions.
unsigned char pos
Definition: enum.c:248
#define ENUMLOOKUP_BLR_CC
Definition: enum.c:93
Number structure.
Definition: app_followme.c:154
int sep_len
Definition: enum.c:250
char apex[256]
Definition: enum.c:251
Configuration File Parser.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#define ast_log
Definition: astobj2.c:42
#define ENUMLOOKUP_BLR_EBL
Definition: enum.c:95
int techlen
Definition: enum.h:44
General Asterisk PBX channel definitions.
DNS and ENUM functions.
char * dst
Definition: enum.h:41
static char * format_numeric_domain(const char *number, const char *suffix)
Definition: enum.c:955
unsigned short order
Definition: enum.h:29
struct naptr naptr
Definition: enum.h:34
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
char * naptrinput
Definition: enum.h:47
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
char * tech
Definition: enum.h:36
int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char *suffix, char *options, unsigned int record, struct enum_context **argcontext)
Lookup entry in ENUM.
Definition: enum.c:649
static int answer(void *data)
Definition: chan_pjsip.c:682
#define T_EBL
Definition: enum.c:99
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
#define CONFIG_STATUS_FILEUNCHANGED
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
static int load_module(void)
Definition: enum.c:1067
static int txt_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Callback for TXT record lookup, /ol version.
Definition: enum.c:151
static unsigned int parse_ie(char *data, unsigned int maxdatalen, unsigned char *src, unsigned int srclen)
Parse NAPTR record information elements.
Definition: enum.c:376
#define ast_strlen_zero(a)
Definition: muted.c:73
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static int reload(void)
Definition: cdr_mysql.c:741
int ast_get_txt(struct ast_channel *chan, const char *number, char *txt, int txtlen, char *suffix)
Lookup DNS TXT record (used by app TXTCIDnum)
Definition: enum.c:995
static int ebl_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
Callback for EBL record lookup.
Definition: enum.c:256
Module could not be loaded properly.
Definition: module.h:102
Definition: enum.h:28
Structure used to handle boolean flags.
Definition: utils.h:199
static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, unsigned char *naptrinput)
Parse DNS NAPTR record used in ENUM —.
Definition: enum.c:397
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",)
#define CONFIG_STATUS_FILEMISSING
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
char * result
Definition: enum.h:35
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static int reload_module(void)
Definition: enum.c:1077
int naptr_rrs_count
Definition: enum.h:52
#define ENUMLOOKUP_OPTIONS_IENUM
Definition: enum.c:611
static struct test_options options
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116
char * tech
Definition: enum.h:43
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int sort_pos
Definition: enum.h:37
Asterisk module definitions.
#define ENUMLOOKUP_OPTIONS_ISN
Definition: enum.c:609
int options
Definition: enum.h:50
#define ENUMLOOKUP_BLR_TXT
Definition: enum.c:94
static int blr_txt(const char *cc, const char *suffix)
Determine the branch location record as stored in a TXT record.
Definition: enum.c:201
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:518
DNS support for Asterisk.
static int blr_ebl(const char *cc, const char *suffix, char *separator, int sep_len, char *apex, int apex_len)
Evaluate the I-ENUM branch as stored in an EBL record.
Definition: enum.c:330
#define ast_mutex_unlock(a)
Definition: lock.h:188
static int unload_module(void)
Definition: enum.c:1072