extensive ENUM support update, including ENUMLOOKUP() dialplan function (issue #5201...
[asterisk/asterisk.git] / enum.c
1 /*
2  * ENUM Support for Asterisk
3  *
4  * Copyright (C) 2003-2005, Digium, inc
5  *
6  * Written by Mark Spencer <markster@digium.com>
7  *
8  * Funding provided by nic.at
9  *
10  * Distributed under the terms of the GNU GPL
11  *
12  */
13
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <arpa/nameser.h>
18 #if __APPLE_CC__ >= 1495
19 #include <arpa/nameser_compat.h>
20 #endif
21 #include <resolv.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <regex.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include "asterisk/logger.h"
34 #include "asterisk/options.h"
35 #include "asterisk/enum.h"
36 #include "asterisk/dns.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/config.h"
39 #include "asterisk/utils.h"
40
41 #ifdef __APPLE__
42 #undef T_NAPTR
43 #define T_NAPTR 35
44 #endif
45
46 #ifdef __APPLE__
47 #undef T_TXT
48 #define T_TXT 16
49 #endif
50
51 /* The IETF Enum standard root, managed by the ITU */
52 #define TOPLEV "e164.arpa."
53
54 /* Linked list from config file */
55 static struct enum_search {
56         char toplev[512];
57         struct enum_search *next;
58 } *toplevs;
59
60 static int enumver = 0;
61
62 AST_MUTEX_DEFINE_STATIC(enumlock);
63
64 struct naptr {
65         unsigned short order;
66         unsigned short pref;
67 } __attribute__ ((__packed__));
68
69 /*--- parse_ie: Parse NAPTR record information elements */
70 static int parse_ie(char *data, int maxdatalen, char *src, int srclen)
71 {
72         int len, olen;
73
74         len = olen = (int)src[0];
75         src++;
76         srclen--;
77         if (len > srclen) {
78                 ast_log(LOG_WARNING, "Want %d, got %d\n", len, srclen);
79                 return -1;
80         }
81         if (len > maxdatalen)
82                 len = maxdatalen;
83         memcpy(data, src, len);
84         return olen + 1;
85 }
86
87 /*--- parse_naptr: Parse DNS NAPTR record used in ENUM ---*/
88 static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, char *answer, int len, char *naptrinput)
89 {
90
91        char tech_return[80];
92         char *oanswer = answer;
93         char flags[512] = "";
94         char services[512] = "";
95        unsigned char *p;
96         char regexp[512] = "";
97         char repl[512] = "";
98         char temp[512] = "";
99         char delim;
100         char *delim2;
101         char *pattern, *subst, *d;
102         int res;
103         int regexp_len, size, backref;
104         int d_len = sizeof(temp) - 1;
105         regex_t preg;
106         regmatch_t pmatch[9];
107
108        tech_return[0] = '\0';
109
110         dst[0] = '\0';
111
112         if (len < sizeof(struct naptr)) {
113                 ast_log(LOG_WARNING, "NAPTR record length too short\n");
114                 return -1;
115         }
116         answer += sizeof(struct naptr);
117         len -= sizeof(struct naptr);
118         if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
119                 ast_log(LOG_WARNING, "Failed to get flags from NAPTR record\n");
120                return -1;
121        } else {
122                answer += res;
123                len -= res;
124         }
125         if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
126                 ast_log(LOG_WARNING, "Failed to get services from NAPTR record\n");
127                return -1;
128        } else {
129                answer += res;
130                len -= res;
131         }
132         if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0) {
133                 ast_log(LOG_WARNING, "Failed to get regexp from NAPTR record\n");
134                return -1;
135        } else {
136                answer += res;
137                len -= res;
138         }
139
140         if ((res = dn_expand((unsigned char *)oanswer, (unsigned char *)answer + len, (unsigned char *)answer, repl, sizeof(repl) - 1)) < 0) {
141                 ast_log(LOG_WARNING, "Failed to expand hostname\n");
142                 return -1;
143        }
144
145         if (option_debug > 2)   /* Advanced NAPTR debugging */
146                 ast_log(LOG_DEBUG, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
147                         naptrinput, flags, services, regexp, repl);
148
149         if (tolower(flags[0]) != 'u') {
150                 ast_log(LOG_WARNING, "NAPTR Flag must be 'U' or 'u'.\n");
151                 return -1;
152         }
153
154        p = strstr(services, "e2u+");
155        if(p == NULL)
156                p = strstr(services, "E2U+");
157        if(p){
158                p = p + 4;
159                if(strchr(p, ':')){
160                        p = strchr(p, ':') + 1;
161                }
162                ast_copy_string(tech_return, p, sizeof(tech_return));
163         } else {
164
165                p = strstr(services, "+e2u");
166                if(p == NULL)
167                        p = strstr(services, "+E2U");
168                if(p){
169                        *p = 0;
170                        p = strchr(services, ':');
171                        if(p)
172                                *p = 0;
173                        ast_copy_string(tech_return, services, sizeof(tech_return));
174                }
175         }
176
177         /* DEDBUGGING STUB
178         ast_copy_string(regexp, "!^\\+43(.*)$!\\1@bla.fasel!", sizeof(regexp) - 1);
179         */
180
181         regexp_len = strlen(regexp);
182         if (regexp_len < 7) {
183                 ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
184                 return -1;
185        }
186
187
188         delim = regexp[0];
189         delim2 = strchr(regexp + 1, delim);
190         if ((delim2 == NULL) || (regexp[regexp_len-1] != delim)) {
191                 ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n",regexp);
192                 return -1;
193         }
194
195         pattern = regexp + 1;
196         *delim2 = 0;
197         subst   = delim2 + 1;
198         regexp[regexp_len-1] = 0;
199
200 #if 0
201         printf("Pattern: %s\n", pattern);
202         printf("Subst: %s\n", subst);
203        printf("Input: %s\n", naptrinput);
204 #endif
205
206 /*
207  * now do the regex wizardry.
208  */
209
210         if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
211                 ast_log(LOG_WARNING, "NAPTR Regex compilation error (regex = \"%s\").\n",regexp);
212                 return -1;
213         }
214
215         if (preg.re_nsub > 9) {
216                 ast_log(LOG_WARNING, "NAPTR Regex compilation error: too many subs.\n");
217                 regfree(&preg);
218                 return -1;
219         }
220
221         if (regexec(&preg, naptrinput, 9, pmatch, 0)) {
222                 ast_log(LOG_WARNING, "NAPTR Regex match failed.\n");
223                 regfree(&preg);
224                 return -1;
225         }
226         regfree(&preg);
227
228        d = temp;
229        d_len--;
230         while( *subst && (d_len > 0) ) {
231                 if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) {
232                         backref = subst[1]-'0';
233                         size = pmatch[backref].rm_eo - pmatch[backref].rm_so;
234                         if (size > d_len) {
235                                 ast_log(LOG_WARNING, "Not enough space during NAPTR regex substitution.\n");
236                                 return -1;
237                                 }
238                         memcpy(d, naptrinput + pmatch[backref].rm_so, size);
239                         d += size;
240                         d_len -= size;
241                         subst += 2;
242                 } else if (isprint(*subst)) {
243                         *d++ = *subst++;
244                         d_len--;
245                 } else {
246                         ast_log(LOG_WARNING, "Error during regex substitution.\n");
247                         return -1;
248                 }
249         }
250         *d = 0;
251         ast_copy_string(dst, temp, dstsize);
252         dst[dstsize - 1] = '\0';
253
254        if(*tech != '\0'){ /* check if it is requested NAPTR */
255                if(!strncasecmp(tech, "ALL", techsize)){
256                        return 1; /* return or count any RR */
257                }
258                if(!strncasecmp(tech_return, tech, sizeof(tech_return)<techsize?sizeof(tech_return):techsize)){
259                        ast_copy_string(tech, tech_return, techsize);
260                        return 1; /* we got out RR */
261                } else { /* go to the next RR in the DNS answer */
262                        return 0;
263                }
264        }
265
266        /* tech was not specified, return first parsed RR */
267        ast_copy_string(tech, tech_return, techsize);
268
269        return 1;
270 }
271
272 /* do not return requested value, just count RRs and return thei number in dst */
273 #define ENUMLOOKUP_OPTIONS_COUNT       1
274
275 struct enum_naptr_rr {
276        struct naptr naptr; /* order and preference of RR */
277        char *result; /* result of naptr parsing,e.g.: tel:+5553 */
278        char *tech; /* Technology (from URL scheme) */
279        int sort_pos; /* sort position */
280 };
281
282 struct enum_context {
283         char *dst;      /* Destination part of URL from ENUM */
284         int dstlen;     /* Length */
285         char *tech;     /* Technology (from URL scheme) */
286         int techlen;    /* Length */
287         char *txt;      /* TXT record in TXT lookup */
288         int txtlen;     /* Length */
289         char *naptrinput;       /* The number to lookup */
290        int position; /* used as counter for RRs or specifies position of required RR */
291        int options; /* options , see ENUMLOOKUP_OPTIONS_* defined above */
292        struct enum_naptr_rr *naptr_rrs; /* array of parsed NAPTR RRs */
293        int naptr_rrs_count; /* Size of array naptr_rrs */
294 };
295
296 /*--- txt_callback: Callback for TXT record lookup */
297 static int txt_callback(void *context, char *answer, int len, char *fullanswer)
298 {
299         struct enum_context *c = (struct enum_context *)context;
300 #if 0
301         printf("ENUMTXT Called\n");
302 #endif
303
304         if (answer == NULL) {
305                 c->txt = NULL;
306                 c->txtlen = 0;
307                 return 0;
308         }
309
310         /* skip over first byte, as for some reason it's a vertical tab character */
311         answer += 1;
312         len -= 1;
313
314         /* answer is not null-terminated, but should be */
315        /* this is safe to do, as answer has extra bytes on the end we can
316            safely overwrite with a null */
317         answer[len] = '\0';
318         /* now increment len so that len includes the null, so that we can
319            compare apples to apples */
320         len +=1;
321
322         /* finally, copy the answer into c->txt */
323         ast_copy_string(c->txt, answer, len < c->txtlen ? len : (c->txtlen));
324
325         /* just to be safe, let's make sure c->txt is null terminated */
326         c->txt[(c->txtlen)-1] = '\0';
327
328         return 1;
329 }
330
331 /*--- enum_callback: Callback from ENUM lookup function */
332 static int enum_callback(void *context, char *answer, int len, char *fullanswer)
333 {
334         struct enum_context *c = (struct enum_context *)context;
335        void *p = NULL;
336        int res;
337
338        res = parse_naptr(c->dst, c->dstlen, c->tech, c->techlen, answer, len, c->naptrinput);
339
340        if(res < 0){
341                 ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
342                 return -1;
343        } else if(res > 0 && !ast_strlen_zero(c->dst)){ /* ok, we got needed NAPTR */
344                if(c->options & ENUMLOOKUP_OPTIONS_COUNT){ /* counting RRs */
345                        c->position++;
346                        snprintf(c->dst, c->dstlen, "%d", c->position);
347                } else  {
348                        p = realloc(c->naptr_rrs, sizeof(struct enum_naptr_rr)*(c->naptr_rrs_count+1));
349                        if(p){
350                                c->naptr_rrs = (struct enum_naptr_rr*)p;
351                                memcpy(&c->naptr_rrs[c->naptr_rrs_count].naptr, answer, sizeof(struct naptr));
352                                /* printf("order=%d, pref=%d\n", ntohs(c->naptr_rrs[c->naptr_rrs_count].naptr.order), ntohs(c->naptr_rrs[c->naptr_rrs_count].naptr.pref)); */
353                                c->naptr_rrs[c->naptr_rrs_count].result = strdup(c->dst);
354                                c->naptr_rrs[c->naptr_rrs_count].tech = strdup(c->tech);
355                                c->naptr_rrs[c->naptr_rrs_count].sort_pos = c->naptr_rrs_count;
356                                c->naptr_rrs_count++;
357                        }
358                        c->dst[0] = 0;
359                }
360                return 0;
361         }
362
363        if(c->options & ENUMLOOKUP_OPTIONS_COUNT){ /* counting RRs */
364                snprintf(c->dst, c->dstlen, "%d", c->position);
365        }
366
367         return 0;
368 }
369
370 /*--- ast_get_enum: ENUM lookup */
371 int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char* suffix, char* options)
372 {
373         struct enum_context context;
374         char tmp[259 + 512];
375        char naptrinput[512];
376         int pos = strlen(number) - 1;
377         int newpos = 0;
378         int ret = -1;
379         struct enum_search *s = NULL;
380         int version = -1;
381        /* for ISN rewrite */
382        char *p1 = NULL;
383        char *p2 = NULL;
384        int k = 0;
385        int i = 0;
386        int z = 0;
387
388        if(number[0] == 'n'){
389                strncpy(naptrinput, number+1, sizeof(naptrinput));
390        } else {
391                strncpy(naptrinput, number, sizeof(naptrinput));
392        }
393
394         context.naptrinput = naptrinput;        /* The number */
395         context.dst = dst;                      /* Return string */
396         context.dstlen = dstlen;
397        context.tech = tech;
398         context.techlen = techlen;
399        context.options = 0;
400        context.position = 1;
401        context.naptr_rrs = NULL;
402        context.naptr_rrs_count = 0;
403
404        if(options != NULL){
405                if(*options == 'c'){
406                        context.options = ENUMLOOKUP_OPTIONS_COUNT;
407                        context.position = 0;
408                } else {
409                        context.position = atoi(options);
410                        if(context.position < 1)
411                                context.position = 1;
412                }
413        }
414
415         if (pos > 128)
416                 pos = 128;
417
418        /* ISN rewrite */
419        p1 = strchr(number, '*');
420
421        if(number[0] == 'n'){ /* do not perform ISN rewrite ('n' is testing flag) */
422                p1 = NULL;
423                k = 1; /* strip 'n' from number */
424        }
425
426        if(p1 != NULL){
427                p2 = p1+1;
428                while(p1 > number){
429                        p1--;
430                        tmp[newpos++] = *p1;
431                        tmp[newpos++] = '.';
432                }
433                if(*p2){
434                        while(*p2 && newpos < 128){
435                                tmp[newpos++] = *p2;
436                                p2++;
437                        }
438                        tmp[newpos++] = '.';
439                }
440
441        } else {
442                while(pos >= k) {
443                        if(isdigit(number[pos])){
444                                tmp[newpos++] = number[pos];
445                                tmp[newpos++] = '.';
446                        }
447                        pos--;
448                }
449         }
450
451         if (chan && ast_autoservice_start(chan) < 0)
452                 return -1;
453
454         for(;;) {
455                 ast_mutex_lock(&enumlock);
456                 if (version != enumver) {
457                         /* Ooh, a reload... */
458                         s = toplevs;
459                         version = enumver;
460                 } else {
461                         s = s->next;
462                 }
463                if(suffix != NULL){
464                        strncpy(tmp + newpos, suffix, sizeof(tmp) - newpos - 1);
465                } else if (s) {
466                         strncpy(tmp + newpos, s->toplev, sizeof(tmp) - newpos - 1);
467                 }
468                 ast_mutex_unlock(&enumlock);
469                 if (!s)
470                         break;
471                 ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback);
472                 if (ret > 0)
473                         break;
474                if(suffix != NULL)
475                        break;
476         }
477         if (ret < 0) {
478                 ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno));
479                 ret = 0;
480         }
481
482        if(context.naptr_rrs_count >= context.position && ! (context.options & ENUMLOOKUP_OPTIONS_COUNT)){
483                /* sort array by NAPTR order/preference */
484                for(k=0; k<context.naptr_rrs_count; k++){
485                        for(i=0; i<context.naptr_rrs_count; i++){
486                                /* use order first and then preference to compare */
487                                if((ntohs(context.naptr_rrs[k].naptr.order) < ntohs(context.naptr_rrs[i].naptr.order)
488                                                && context.naptr_rrs[k].sort_pos > context.naptr_rrs[i].sort_pos)
489                                        || (ntohs(context.naptr_rrs[k].naptr.order) > ntohs(context.naptr_rrs[i].naptr.order)
490                                                && context.naptr_rrs[k].sort_pos < context.naptr_rrs[i].sort_pos)){
491                                        z = context.naptr_rrs[k].sort_pos;
492                                        context.naptr_rrs[k].sort_pos = context.naptr_rrs[i].sort_pos;
493                                        context.naptr_rrs[i].sort_pos = z;
494                                        continue;
495                                }
496                                if(ntohs(context.naptr_rrs[k].naptr.order) == ntohs(context.naptr_rrs[i].naptr.order)){
497                                        if((ntohs(context.naptr_rrs[k].naptr.pref) < ntohs(context.naptr_rrs[i].naptr.pref)
498                                                        && context.naptr_rrs[k].sort_pos > context.naptr_rrs[i].sort_pos)
499                                                || (ntohs(context.naptr_rrs[k].naptr.pref) > ntohs(context.naptr_rrs[i].naptr.pref)
500                                                        && context.naptr_rrs[k].sort_pos < context.naptr_rrs[i].sort_pos)){
501                                                z = context.naptr_rrs[k].sort_pos;
502                                                context.naptr_rrs[k].sort_pos = context.naptr_rrs[i].sort_pos;
503                                                context.naptr_rrs[i].sort_pos = z;
504                                        }
505                                }
506                        }
507                }
508                for(k=0; k<context.naptr_rrs_count; k++){
509                        if(context.naptr_rrs[k].sort_pos == context.position-1){
510                                ast_copy_string(context.dst, context.naptr_rrs[k].result, dstlen);
511                                ast_copy_string(context.tech, context.naptr_rrs[k].tech, techlen);
512                                break;
513                        }
514                }
515        } else if( !(context.options & ENUMLOOKUP_OPTIONS_COUNT) ) {
516                context.dst[0] = 0;
517        }
518
519         if (chan)
520                 ret |= ast_autoservice_stop(chan);
521
522        for(k=0; k<context.naptr_rrs_count; k++){
523                free(context.naptr_rrs[k].result);
524                free(context.naptr_rrs[k].tech);
525        }
526
527        free(context.naptr_rrs);
528
529         return ret;
530 }
531
532 /*--- ast_get_txt: Get TXT record from DNS.
533         Really has nothing to do with enum, but anyway...
534  */
535 int ast_get_txt(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char *txt, int txtlen)
536 {
537         struct enum_context context;
538         char tmp[259 + 512];
539         char naptrinput[512] = "+";
540         int pos = strlen(number) - 1;
541         int newpos = 0;
542         int ret = -1;
543         struct enum_search *s = NULL;
544         int version = -1;
545
546         strncat(naptrinput, number, sizeof(naptrinput) - 2);
547
548         context.naptrinput = naptrinput;
549         context.dst = dst;
550         context.dstlen = dstlen;
551         context.tech = tech;
552         context.techlen = techlen;
553         context.txt = txt;
554         context.txtlen = txtlen;
555
556         if (pos > 128)
557                 pos = 128;
558         while(pos >= 0) {
559                 tmp[newpos++] = number[pos--];
560                 tmp[newpos++] = '.';
561         }
562
563         if (chan && ast_autoservice_start(chan) < 0)
564                 return -1;
565
566         for(;;) {
567                 ast_mutex_lock(&enumlock);
568                 if (version != enumver) {
569                         /* Ooh, a reload... */
570                         s = toplevs;
571                         version = enumver;
572                 } else {
573                         s = s->next;
574                 }
575                 if (s) {
576                         strncpy(tmp + newpos, s->toplev, sizeof(tmp) - newpos - 1);
577                 }
578                 ast_mutex_unlock(&enumlock);
579                 if (!s)
580                         break;
581
582                 ret = ast_search_dns(&context, tmp, C_IN, T_TXT, txt_callback);
583                 if (ret > 0)
584                         break;
585         }
586         if (ret < 0) {
587                 ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno));
588                 ret = 0;
589         }
590         if (chan)
591                 ret |= ast_autoservice_stop(chan);
592         return ret;
593 }
594
595 /*--- enum_newtoplev: Add enum tree to linked list ---*/
596 static struct enum_search *enum_newtoplev(char *s)
597 {
598         struct enum_search *tmp;
599
600         tmp = malloc(sizeof(struct enum_search));
601         if (tmp) {
602                 memset(tmp, 0, sizeof(struct enum_search));
603                 ast_copy_string(tmp->toplev, s, sizeof(tmp->toplev));
604         }
605         return tmp;
606 }
607
608 /*--- ast_enum_init: Initialize the ENUM support subsystem */
609 int ast_enum_init(void)
610 {
611         struct ast_config *cfg;
612         struct enum_search *s, *sl;
613         struct ast_variable *v;
614
615         /* Destroy existing list */
616         ast_mutex_lock(&enumlock);
617         s = toplevs;
618         while(s) {
619                 sl = s;
620                 s = s->next;
621                 free(sl);
622         }
623         toplevs = NULL;
624         cfg = ast_config_load("enum.conf");
625         if (cfg) {
626                 sl = NULL;
627                 v = ast_variable_browse(cfg, "general");
628                 while(v) {
629                         if (!strcasecmp(v->name, "search")) {
630                                 s = enum_newtoplev(v->value);
631                                 if (s) {
632                                         if (sl)
633                                                 sl->next = s;
634                                         else
635                                                 toplevs = s;
636                                         sl = s;
637                                 }
638                         }
639                         v = v->next;
640                 }
641                 ast_config_destroy(cfg);
642         } else {
643                 toplevs = enum_newtoplev(TOPLEV);
644         }
645         enumver++;
646         ast_mutex_unlock(&enumlock);
647         return 0;
648 }
649
650 int ast_enum_reload(void)
651 {
652         return ast_enum_init();
653 }