Merge ENUM fixes (bug #99)
[asterisk/asterisk.git] / enum.c
1 /*
2  * ENUM Support for Asterisk
3  *
4  * Copyright (C) 2003 Digium
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 <string.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <stdlib.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/nameser.h>
22 #include <resolv.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <regex.h>
26
27
28 #include <asterisk/logger.h>
29 #include <asterisk/options.h>
30 #include <asterisk/enum.h>
31 #include <asterisk/channel.h>
32 #include <asterisk/config.h>
33
34 #define MAX_SIZE 4096
35
36 #define TOPLEV "e164.arpa."
37
38 typedef struct {
39         unsigned        id :16;         /* query identification number */
40 #if BYTE_ORDER == BIG_ENDIAN
41                         /* fields in third byte */
42         unsigned        qr: 1;          /* response flag */
43         unsigned        opcode: 4;      /* purpose of message */
44         unsigned        aa: 1;          /* authoritive answer */
45         unsigned        tc: 1;          /* truncated message */
46         unsigned        rd: 1;          /* recursion desired */
47                         /* fields in fourth byte */
48         unsigned        ra: 1;          /* recursion available */
49         unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
50         unsigned        ad: 1;          /* authentic data from named */
51         unsigned        cd: 1;          /* checking disabled by resolver */
52         unsigned        rcode :4;       /* response code */
53 #endif
54 #if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
55                         /* fields in third byte */
56         unsigned        rd :1;          /* recursion desired */
57         unsigned        tc :1;          /* truncated message */
58         unsigned        aa :1;          /* authoritive answer */
59         unsigned        opcode :4;      /* purpose of message */
60         unsigned        qr :1;          /* response flag */
61                         /* fields in fourth byte */
62         unsigned        rcode :4;       /* response code */
63         unsigned        cd: 1;          /* checking disabled by resolver */
64         unsigned        ad: 1;          /* authentic data from named */
65         unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
66         unsigned        ra :1;          /* recursion available */
67 #endif
68                         /* remaining bytes */
69         unsigned        qdcount :16;    /* number of question entries */
70         unsigned        ancount :16;    /* number of answer entries */
71         unsigned        nscount :16;    /* number of authority entries */
72         unsigned        arcount :16;    /* number of resource entries */
73 } dns_HEADER;
74
75 static struct enum_search {
76         char toplev[80];
77         struct enum_search *next;
78 } *toplevs;
79
80 static int enumver = 0;
81
82 static ast_mutex_t enumlock = AST_MUTEX_INITIALIZER;
83
84 static int skip_name(unsigned char *s, int len)
85 {
86         /* Shamelessly take from SER */
87         int x = 0;
88         while(x < len) {
89                 if (!*s) {
90                         s++;
91                         x++;
92                         break;
93                 }
94                 if (((*s) & 0xc0) == 0xc0) {
95                         s += 2;
96                         x += 2;
97                         break;
98                 }
99                 x += *s + 1;
100                 s += *s + 1;
101         }
102         if (x >= len)
103                 return -1;
104         return x;
105 }
106
107 struct dn_answer {
108         unsigned short rtype;
109         unsigned short class;
110         unsigned int ttl;
111         unsigned short size;
112 } __attribute__ ((__packed__));
113
114 struct naptr {
115         unsigned short order;
116         unsigned short pref;
117 } __attribute__ ((__packed__));
118
119 static int parse_ie(unsigned char *data, int maxdatalen, unsigned char *src, int srclen)
120 {
121         int len, olen;
122         len = olen = (int)src[0];
123         src++;
124         srclen--;
125         if (len > srclen) {
126                 ast_log(LOG_WARNING, "Want %d, got %d\n", len, srclen);
127                 return -1;
128         }
129         if (len > maxdatalen)
130                 len = maxdatalen;
131         memcpy(data, src, len);
132         return olen + 1;
133 }
134
135 static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, char *naptrinput)
136 {
137         unsigned char *oanswer = answer;
138         unsigned char flags[80] = "";
139         unsigned char services[80] = "";
140         unsigned char regexp[80] = "";
141         unsigned char repl[80] = "";
142         unsigned char temp[80] = "";
143         unsigned char delim;
144         unsigned char *delim2;
145         unsigned char *pattern, *subst, *d;
146         int res;
147         int regexp_len, size, backref;
148         int d_len = sizeof(temp) - 1;
149         regex_t preg;
150         regmatch_t pmatch[9];
151
152         
153         if (len < sizeof(struct naptr)) {
154                 printf("Length too short\n");
155                 return -1;
156         }
157         answer += sizeof(struct naptr);
158         len -= sizeof(struct naptr);
159         if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
160                 ast_log(LOG_WARNING, "Failed to get flags\n");
161                 return -1; 
162         } else { answer += res; len -= res; }
163         if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
164                 ast_log(LOG_WARNING, "Failed to get services\n");
165                 return -1; 
166         } else { answer += res; len -= res; }
167         if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0)
168                 return -1; else { answer += res; len -= res; }
169         if ((res = dn_expand(oanswer,answer + len,answer, repl, sizeof(repl) - 1)) < 0) {
170                 ast_log(LOG_WARNING, "Failed to expand hostname\n");
171                 return -1;
172         } 
173
174 #if 0
175         printf("Input: %s\n", naptrinput);
176         printf("Flags: %s\n", flags);
177         printf("Services: %s\n", services);
178         printf("Regexp: %s\n", regexp);
179         printf("Repl: %s\n", repl);
180 #endif
181
182         if (tolower(flags[0]) != 'u') {
183                 ast_log(LOG_WARNING, "Flag must be 'U' or 'u'.\n");
184                 return -1;
185         }
186
187         if ((!strncasecmp(services, "e2u+sip", 7)) || 
188             (!strncasecmp(services, "sip+e2u", 7))) {
189                 strncpy(tech, "sip", techsize -1); 
190         } else if ((!strncasecmp(services, "e2u+tel", 7)) || 
191             (!strncasecmp(services, "tel+e2u", 7))) {
192                 strncpy(tech, "tel", techsize -1); 
193         } else if (strncasecmp(services, "e2u+voice:", 10)) {
194                 ast_log(LOG_WARNING, "Services must be e2u+sip, sip+e2u, e2u+tel, tel+e2u or e2u+voice:\n");
195                 return -1;
196         }
197
198         /* DEDBUGGING STUB
199         strcpy(regexp, "!^\\+43(.*)$!\\1@bla.fasel!");
200         */
201
202         regexp_len = strlen(regexp);
203         if (regexp_len < 7) {
204                 ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
205                 return -1;
206         } 
207
208
209         delim = regexp[0];
210         delim2 = strchr(regexp + 1, delim);
211         if ((delim2 == NULL) || (regexp[regexp_len-1] != delim)) {
212                 ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n",regexp);
213                 return -1;
214         }
215
216         pattern = regexp + 1;
217         *delim2 = 0;
218         subst   = delim2 + 1;
219         regexp[regexp_len-1] = 0;
220
221 #if 0
222         printf("Pattern: %s\n", pattern);
223         printf("Subst: %s\n", subst);
224 #endif
225
226 /*
227  * now do the regex wizardry.
228  */
229
230         if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
231                 ast_log(LOG_WARNING, "Regex compilation error (regex = \"%s\").\n",regexp);
232                 return -1;
233         }
234
235         if (preg.re_nsub > 9) {
236                 ast_log(LOG_WARNING, "Regex compilation error: too many subs.\n");
237                 regfree(&preg);
238                 return -1;
239         }
240
241         if (regexec(&preg, naptrinput, 9, pmatch, 0)) {
242                 ast_log(LOG_WARNING, "Regex match failed.\n");
243                 regfree(&preg);
244                 return -1;
245         }
246         regfree(&preg);
247
248         d = temp; d_len--; 
249         while( *subst && (d_len > 0) ) {
250                 if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) {
251                         backref = subst[1]-'0';
252                         size = pmatch[backref].rm_eo - pmatch[backref].rm_so;
253                         if (size > d_len) {
254                                 ast_log(LOG_WARNING, "Not enough space during regex substitution.\n");
255                                 return -1;
256                                 }
257                         memcpy(d, naptrinput + pmatch[backref].rm_so, size);
258                         d += size;
259                         d_len -= size;
260                         subst += 2;
261                 } else if (isprint(*subst)) {
262                         *d++ = *subst++;
263                         d_len--;
264                 } else {
265                         ast_log(LOG_WARNING, "Error during regex substitution.\n");
266                         return -1;
267                 }
268         }
269         *d = 0;
270         strncpy(dst, temp, dstsize);
271         d = strchr(services, ':');
272         if (d) 
273                 strncpy(tech, d+1, techsize -1); 
274         return 0;
275 }
276
277 static int parse_answer(unsigned char *dst, int dstlen, unsigned char *tech, int techlen, unsigned char *answer, int len, char *naptrinput)
278 {
279         /*
280          * This function is influenced by "ser" the SIP router.
281          */
282         int x;
283         int res;
284         dns_HEADER *h;
285         struct dn_answer *ans;
286         dst[0] = '\0';
287         tech[0] = '\0';
288 #if 0
289         for (x=0;x<len;x++) {
290                 if ((answer[x] < 32) || (answer[x] > 127)) {
291                         if (lastlit)
292                                 printf("\"");
293                         printf(" 0x%02x", answer[x]);
294                         lastlit = 0;
295                 } else {
296                         if (!lastlit) 
297                                 printf(" \"");
298                         printf("%c", answer[x]);
299                         lastlit = 1;
300                 }
301         }
302         printf("\n");
303 #endif  
304         h = (dns_HEADER *)answer;
305         /* Skip over DNS header */
306         answer += sizeof(dns_HEADER);
307         len -= sizeof(dns_HEADER);
308 #if 0
309         printf("Query count: %d\n", ntohs(h->qdcount));
310 #endif
311         for (x=0;x<ntohs(h->qdcount);x++) {
312                 if ((res = skip_name(answer, len)) < 0) {
313                         ast_log(LOG_WARNING, "Couldn't skip over name\n");
314                         return -1;
315                 }
316                 answer += res;
317                 len -= res;
318                 answer += 4;    /* Skip QCODE / QCLASS */
319                 len -= 4;
320                 if (len < 0) {
321                         ast_log(LOG_WARNING, "Strange query size\n");
322                         return -1;
323                 }
324         }
325 #if 0
326         printf("Length remaining: %d\n", len);
327         printf("Answer count: %d\n", ntohs(h->ancount));
328         printf("Looking for %d/%d\n", C_IN, T_NAPTR);
329 #endif
330         for (x=0;x<ntohs(h->ancount);x++) {
331                 if ((res = skip_name(answer, len)) < 0) {
332                         ast_log(LOG_WARNING, "Failed to skip name :(\n");
333                         return -1;
334                 }
335                 answer += res;
336                 len -= res;
337                 ans = (struct dn_answer *)answer;
338                 answer += sizeof(struct dn_answer);
339                 len -= sizeof(struct dn_answer);
340                 if (len < 0)
341                         return -1;
342 #if 0
343                 printf("Type: %d, class: %d, ttl: %d, length: %d\n", ntohs(ans->rtype), ntohs(ans->class),
344                         ntohl(ans->ttl), ntohs(ans->size));
345 #endif                  
346                 len -= ntohs(ans->size);
347                 if (len < 0) {
348                         ast_log(LOG_WARNING, "Length exceeds frame\n");
349                         return -1;
350                 }
351                 if ((ntohs(ans->class) == C_IN) && (ntohs(ans->rtype) == T_NAPTR)) {
352                         if (parse_naptr(dst, dstlen, tech, techlen, answer, ntohs(ans->size), naptrinput))
353                                 ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
354                         if (strlen(dst))
355                                 return 0;
356                 }
357                 answer += ntohs(ans->size);
358         }
359         return 0;
360 }
361
362 int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen)
363 {
364         unsigned char answer[MAX_SIZE];
365         char tmp[259 + 80];
366         char naptrinput[80] = "+";
367         int pos = strlen(number) - 1;
368         int newpos=0;
369         int res = -1;
370         int ret = -1;
371         struct enum_search *s = NULL;
372         int version = -1;
373         struct __res_state enumstate;
374         res_ninit(&enumstate);  
375         if (chan && ast_autoservice_start(chan) < 0)
376                 return -1;
377
378         strncat(naptrinput, number, sizeof(naptrinput) - 2);
379
380         if (pos > 128)
381                 pos = 128;
382         while(pos >= 0) {
383                 tmp[newpos++] = number[pos--];
384                 tmp[newpos++] = '.';
385         }
386 #if 0
387         printf("Looking for '%s'\n", tmp);
388 #endif  
389         
390         for(;;) {
391                 ast_mutex_lock(&enumlock);
392                 if (version != enumver) {
393                         /* Ooh, a reload... */
394                         s = toplevs;
395                         version = enumver;
396                 } else {
397                         s = s->next;
398                 }
399                 if (s) {
400                         strcpy(tmp + newpos, s->toplev);
401                 }
402                 ast_mutex_unlock(&enumlock);
403                 if (!s)
404                         break;
405                 res = res_nsearch(&enumstate, tmp, C_IN, T_NAPTR, answer, sizeof(answer));
406                 if (res > 0)
407                         break;
408         }
409         if (res > 0) {
410                 if ((res = parse_answer(dst, dstlen, tech, techlen, answer, res, naptrinput))) {
411                         ast_log(LOG_WARNING, "Parse error returned %d\n", res);
412                         ret = 0;
413                 } else {
414                         ast_log(LOG_DEBUG, "Found technology '%s', destination '%s'\n", tech, dst);
415                         ret = 1;
416                 }
417         } else {
418                 ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno));
419                 ret = 0;
420         }
421         if (chan)
422                 ret |= ast_autoservice_stop(chan);
423         res_nclose(&enumstate);
424         return ret;
425 }
426
427 static struct enum_search *enum_newtoplev(char *s)
428 {
429         struct enum_search *tmp;
430         tmp = malloc(sizeof(struct enum_search));
431         if (tmp) {
432                 memset(tmp, 0, sizeof(struct enum_search));
433                 strncpy(tmp->toplev, s, sizeof(tmp->toplev) - 1);
434         }
435         return tmp;
436 }
437
438 int ast_enum_init(void)
439 {
440         struct ast_config *cfg;
441         struct enum_search *s, *sl;
442         struct ast_variable *v;
443
444         /* Destroy existing list */
445         ast_mutex_lock(&enumlock);
446         s = toplevs;
447         while(s) {
448                 sl = s;
449                 s = s->next;
450                 free(sl);
451         }
452         toplevs = NULL;
453         cfg = ast_load("enum.conf");
454         if (cfg) {
455                 sl = NULL;
456                 v = ast_variable_browse(cfg, "general");
457                 while(v) {
458                         if (!strcasecmp(v->name, "search")) {
459                                 s = enum_newtoplev(v->value);
460                                 if (s) {
461                                         if (sl)
462                                                 sl->next = s;
463                                         else
464                                                 toplevs = s;
465                                         sl = s;
466                                 }
467                         }
468                         v = v->next;
469                 }
470                 ast_destroy(cfg);
471         } else {
472                 toplevs = enum_newtoplev(TOPLEV);
473         }
474         enumver++;
475         ast_mutex_unlock(&enumlock);
476         return 0;
477 }
478
479 int ast_enum_reload(void)
480 {
481         return ast_enum_init();
482 }