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