Wed Jan 8 2020 09:49:47

Asterisk developer's documentation


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