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