0dead9aa5b853a63696e870fb88cf9dc46b6bacb
[asterisk/asterisk.git] / main / enum.c
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$")
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
90 static int ebl_alg = ENUMLOOKUP_BLR_CC;
91
92 /* EBL record provisional type code */
93 #define T_EBL      65300
94
95 AST_MUTEX_DEFINE_STATIC(enumlock);
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
202         ast_mutex_lock(&enumlock);
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)) {
207                 ast_mutex_unlock(&enumlock);
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);
213         ast_mutex_unlock(&enumlock);
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
331         ast_mutex_lock(&enumlock);
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)) {
336                 ast_mutex_unlock(&enumlock);
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);
342         ast_mutex_unlock(&enumlock);
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 %d characters, got %d\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));
618                                 c->naptr_rrs[c->naptr_rrs_count].result = ast_strdup(c->dst);
619                                 c->naptr_rrs[c->naptr_rrs_count].tech = ast_strdup(c->tech);
620                                 c->naptr_rrs[c->naptr_rrs_count].sort_pos = c->naptr_rrs_count;
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=%d\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')) {
709                         context->options |= ENUMLOOKUP_OPTIONS_DIRECT;
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;
764                 ast_mutex_lock(&enumlock);
765                 ast_copy_string(sep, ienum_branchlabel, sizeof(sep)); /* default */
766                 ast_mutex_unlock(&enumlock);
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
802                 ast_mutex_lock(&enumlock);
803                 ast_copy_string(middle, sep, sizeof(middle) - 1);
804                 strcat(middle, ".");
805                 ast_mutex_unlock(&enumlock);
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                 context->naptr_rrs_count = -1;
867                 strcpy(dst, "0");
868                 ret = 0;
869         }
870
871         if (context->naptr_rrs_count >= context->position && ! (context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
872                 /* sort array by NAPTR order/preference */
873                 for (k = 0; k < context->naptr_rrs_count; k++) {
874                         for (i = 0; i < context->naptr_rrs_count; i++) {
875                                 /* use order first and then preference to compare */
876                                 if ((ntohs(context->naptr_rrs[k].naptr.order) < ntohs(context->naptr_rrs[i].naptr.order)
877                                      && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
878                                      || (ntohs(context->naptr_rrs[k].naptr.order) > ntohs(context->naptr_rrs[i].naptr.order)
879                                      && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
880                                         z = context->naptr_rrs[k].sort_pos;
881                                         context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
882                                         context->naptr_rrs[i].sort_pos = z;
883                                         continue;
884                                 }
885                                 if (ntohs(context->naptr_rrs[k].naptr.order) == ntohs(context->naptr_rrs[i].naptr.order)) {
886                                         if ((ntohs(context->naptr_rrs[k].naptr.pref) < ntohs(context->naptr_rrs[i].naptr.pref)
887                                              && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
888                                              || (ntohs(context->naptr_rrs[k].naptr.pref) > ntohs(context->naptr_rrs[i].naptr.pref)
889                                              && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) {
890                                                 z = context->naptr_rrs[k].sort_pos;
891                                                 context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
892                                                 context->naptr_rrs[i].sort_pos = z;
893                                         }
894                                 }
895                         }
896                 }
897                 for (k = 0; k < context->naptr_rrs_count; k++) {
898                         if (context->naptr_rrs[k].sort_pos == context->position - 1) {
899                                 ast_copy_string(context->dst, context->naptr_rrs[k].result, dstlen);
900                                 ast_copy_string(context->tech, context->naptr_rrs[k].tech, techlen);
901                                 break;
902                         }
903                 }
904         } else if (!(context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
905                 context->dst[0] = 0;
906         } else if ((context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
907                 snprintf(context->dst, context->dstlen, "%d", context->naptr_rrs_count + context->count);
908         }
909
910         if (chan) {
911                 ret |= ast_autoservice_stop(chan);
912         }
913
914         if (!argcontext) {
915                 for (k = 0; k < context->naptr_rrs_count; k++) {
916                         ast_free(context->naptr_rrs[k].result);
917                         ast_free(context->naptr_rrs[k].tech);
918                 }
919                 ast_free(context->naptr_rrs);
920                 ast_free(context);
921         } else {
922                 *argcontext = context;
923         }
924
925         return ret;
926 }
927
928 int ast_get_txt(struct ast_channel *chan, const char *number, char *txt, int txtlen, char *suffix)
929 {
930         struct txt_context context;
931         char tmp[259 + 512];
932         int pos = strlen(number) - 1;
933         int newpos = 0;
934         int ret = -1;
935
936         ast_debug(4, "ast_get_txt: Number = '%s', suffix = '%s'\n", number, suffix);
937
938         if (chan && ast_autoservice_start(chan) < 0) {
939                 return -1;
940         }
941
942         if (pos > 128) {
943                 pos = 128;
944         }
945
946         while (pos >= 0) {
947                 if (isdigit(number[pos])) {
948                         tmp[newpos++] = number[pos];
949                         tmp[newpos++] = '.';
950                 }
951                 pos--;
952         }
953
954         ast_copy_string(&tmp[newpos], suffix, sizeof(tmp) - newpos);
955
956         if (ret < 0) {
957                 ast_debug(2, "No such number found in ENUM: %s (%s)\n", tmp, strerror(errno));
958                 ret = 0;
959         } else {
960                 ast_copy_string(txt, context.txt, txtlen);
961         }
962         if (chan) {
963                 ret |= ast_autoservice_stop(chan);
964         }
965         return ret;
966 }
967
968 /*! \brief Initialize the ENUM support subsystem */
969 static int private_enum_init(int reload)
970 {
971         struct ast_config *cfg;
972         const char *string;
973         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
974
975         if ((cfg = ast_config_load2("enum.conf", "enum", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
976                 return 0;
977         if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
978                 return 0;
979         }
980
981         /* Destroy existing list */
982         ast_mutex_lock(&enumlock);
983         if (cfg) {
984                 if ((string = ast_variable_retrieve(cfg, "ienum", "branchlabel"))) {
985                         ast_copy_string(ienum_branchlabel, string, sizeof(ienum_branchlabel));
986                 }
987
988                 if ((string = ast_variable_retrieve(cfg, "ienum", "ebl_alg"))) {
989                         ebl_alg = ENUMLOOKUP_BLR_CC; /* default */
990
991                         if (!strcasecmp(string, "txt"))
992                                 ebl_alg = ENUMLOOKUP_BLR_TXT;
993                         else if (!strcasecmp(string, "ebl"))
994                                 ebl_alg = ENUMLOOKUP_BLR_EBL;
995                         else if (!strcasecmp(string, "cc"))
996                                 ebl_alg = ENUMLOOKUP_BLR_CC;
997                         else
998                                 ast_log(LOG_WARNING, "No valid parameter for ienum/ebl_alg.\n");
999                 }
1000                 ast_config_destroy(cfg);
1001         }
1002         ast_mutex_unlock(&enumlock);
1003         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Enum\r\nStatus: Enabled\r\nMessage: ENUM reload Requested\r\n");
1004         return 0;
1005 }
1006
1007 int ast_enum_init(void)
1008 {
1009         return private_enum_init(0);
1010 }
1011
1012 int ast_enum_reload(void)
1013 {
1014         return private_enum_init(1);
1015 }