Add H.323, SIP, IAX/IAX2 to ENUM Lookup (bug #202)
[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+h323", 7)) || 
191             (!strncasecmp(services, "h323+e2u", 7))) {
192                 strncpy(tech, "h323", techsize -1); 
193         } else if ((!strncasecmp(services, "e2u+iax", 7)) || 
194             (!strncasecmp(services, "iax+e2u", 7))) {
195                 strncpy(tech, "iax", techsize -1); 
196         } else if ((!strncasecmp(services, "e2u+iax2", 7)) || 
197             (!strncasecmp(services, "iax2+e2u", 7))) {
198                 strncpy(tech, "iax2", techsize -1); 
199         } else if ((!strncasecmp(services, "e2u+tel", 7)) || 
200             (!strncasecmp(services, "tel+e2u", 7))) {
201                 strncpy(tech, "tel", techsize -1); 
202         } else if (strncasecmp(services, "e2u+voice:", 10)) {
203                 ast_log(LOG_WARNING, "Services must be e2u+sip, sip+e2u, e2u+h323, h323+e2u, e2u+iax, iax+e2u, e2u+iax2, iax2+e2u, e2u+tel, tel+e2u or e2u+voice:\n");
204                 return -1;
205         }
206
207         /* DEDBUGGING STUB
208         strcpy(regexp, "!^\\+43(.*)$!\\1@bla.fasel!");
209         */
210
211         regexp_len = strlen(regexp);
212         if (regexp_len < 7) {
213                 ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
214                 return -1;
215         } 
216
217
218         delim = regexp[0];
219         delim2 = strchr(regexp + 1, delim);
220         if ((delim2 == NULL) || (regexp[regexp_len-1] != delim)) {
221                 ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n",regexp);
222                 return -1;
223         }
224
225         pattern = regexp + 1;
226         *delim2 = 0;
227         subst   = delim2 + 1;
228         regexp[regexp_len-1] = 0;
229
230 #if 0
231         printf("Pattern: %s\n", pattern);
232         printf("Subst: %s\n", subst);
233 #endif
234
235 /*
236  * now do the regex wizardry.
237  */
238
239         if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
240                 ast_log(LOG_WARNING, "Regex compilation error (regex = \"%s\").\n",regexp);
241                 return -1;
242         }
243
244         if (preg.re_nsub > 9) {
245                 ast_log(LOG_WARNING, "Regex compilation error: too many subs.\n");
246                 regfree(&preg);
247                 return -1;
248         }
249
250         if (regexec(&preg, naptrinput, 9, pmatch, 0)) {
251                 ast_log(LOG_WARNING, "Regex match failed.\n");
252                 regfree(&preg);
253                 return -1;
254         }
255         regfree(&preg);
256
257         d = temp; d_len--; 
258         while( *subst && (d_len > 0) ) {
259                 if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) {
260                         backref = subst[1]-'0';
261                         size = pmatch[backref].rm_eo - pmatch[backref].rm_so;
262                         if (size > d_len) {
263                                 ast_log(LOG_WARNING, "Not enough space during regex substitution.\n");
264                                 return -1;
265                                 }
266                         memcpy(d, naptrinput + pmatch[backref].rm_so, size);
267                         d += size;
268                         d_len -= size;
269                         subst += 2;
270                 } else if (isprint(*subst)) {
271                         *d++ = *subst++;
272                         d_len--;
273                 } else {
274                         ast_log(LOG_WARNING, "Error during regex substitution.\n");
275                         return -1;
276                 }
277         }
278         *d = 0;
279         strncpy(dst, temp, dstsize);
280         d = strchr(services, ':');
281         if (d) 
282                 strncpy(tech, d+1, techsize -1); 
283         return 0;
284 }
285
286 static int parse_answer(unsigned char *dst, int dstlen, unsigned char *tech, int techlen, unsigned char *answer, int len, char *naptrinput)
287 {
288         /*
289          * This function is influenced by "ser" the SIP router.
290          */
291         int x;
292         int res;
293         dns_HEADER *h;
294         struct dn_answer *ans;
295         dst[0] = '\0';
296         tech[0] = '\0';
297 #if 0
298         for (x=0;x<len;x++) {
299                 if ((answer[x] < 32) || (answer[x] > 127)) {
300                         if (lastlit)
301                                 printf("\"");
302                         printf(" 0x%02x", answer[x]);
303                         lastlit = 0;
304                 } else {
305                         if (!lastlit) 
306                                 printf(" \"");
307                         printf("%c", answer[x]);
308                         lastlit = 1;
309                 }
310         }
311         printf("\n");
312 #endif  
313         h = (dns_HEADER *)answer;
314         /* Skip over DNS header */
315         answer += sizeof(dns_HEADER);
316         len -= sizeof(dns_HEADER);
317 #if 0
318         printf("Query count: %d\n", ntohs(h->qdcount));
319 #endif
320         for (x=0;x<ntohs(h->qdcount);x++) {
321                 if ((res = skip_name(answer, len)) < 0) {
322                         ast_log(LOG_WARNING, "Couldn't skip over name\n");
323                         return -1;
324                 }
325                 answer += res;
326                 len -= res;
327                 answer += 4;    /* Skip QCODE / QCLASS */
328                 len -= 4;
329                 if (len < 0) {
330                         ast_log(LOG_WARNING, "Strange query size\n");
331                         return -1;
332                 }
333         }
334 #if 0
335         printf("Length remaining: %d\n", len);
336         printf("Answer count: %d\n", ntohs(h->ancount));
337         printf("Looking for %d/%d\n", C_IN, T_NAPTR);
338 #endif
339         for (x=0;x<ntohs(h->ancount);x++) {
340                 if ((res = skip_name(answer, len)) < 0) {
341                         ast_log(LOG_WARNING, "Failed to skip name :(\n");
342                         return -1;
343                 }
344                 answer += res;
345                 len -= res;
346                 ans = (struct dn_answer *)answer;
347                 answer += sizeof(struct dn_answer);
348                 len -= sizeof(struct dn_answer);
349                 if (len < 0)
350                         return -1;
351 #if 0
352                 printf("Type: %d, class: %d, ttl: %d, length: %d\n", ntohs(ans->rtype), ntohs(ans->class),
353                         ntohl(ans->ttl), ntohs(ans->size));
354 #endif                  
355                 len -= ntohs(ans->size);
356                 if (len < 0) {
357                         ast_log(LOG_WARNING, "Length exceeds frame\n");
358                         return -1;
359                 }
360                 if ((ntohs(ans->class) == C_IN) && (ntohs(ans->rtype) == T_NAPTR)) {
361                         if (parse_naptr(dst, dstlen, tech, techlen, answer, ntohs(ans->size), naptrinput))
362                                 ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
363                         if (strlen(dst))
364                                 return 0;
365                 }
366                 answer += ntohs(ans->size);
367         }
368         return 0;
369 }
370
371 int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen)
372 {
373         unsigned char answer[MAX_SIZE];
374         char tmp[259 + 80];
375         char naptrinput[80] = "+";
376         int pos = strlen(number) - 1;
377         int newpos=0;
378         int res = -1;
379         int ret = -1;
380         struct enum_search *s = NULL;
381         int version = -1;
382         struct __res_state enumstate;
383         res_ninit(&enumstate);  
384         if (chan && ast_autoservice_start(chan) < 0)
385                 return -1;
386
387         strncat(naptrinput, number, sizeof(naptrinput) - 2);
388
389         if (pos > 128)
390                 pos = 128;
391         while(pos >= 0) {
392                 tmp[newpos++] = number[pos--];
393                 tmp[newpos++] = '.';
394         }
395 #if 0
396         printf("Looking for '%s'\n", tmp);
397 #endif  
398         
399         for(;;) {
400                 ast_mutex_lock(&enumlock);
401                 if (version != enumver) {
402                         /* Ooh, a reload... */
403                         s = toplevs;
404                         version = enumver;
405                 } else {
406                         s = s->next;
407                 }
408                 if (s) {
409                         strcpy(tmp + newpos, s->toplev);
410                 }
411                 ast_mutex_unlock(&enumlock);
412                 if (!s)
413                         break;
414                 res = res_nsearch(&enumstate, tmp, C_IN, T_NAPTR, answer, sizeof(answer));
415                 if (res > 0)
416                         break;
417         }
418         if (res > 0) {
419                 if ((res = parse_answer(dst, dstlen, tech, techlen, answer, res, naptrinput))) {
420                         ast_log(LOG_WARNING, "Parse error returned %d\n", res);
421                         ret = 0;
422                 } else {
423                         ast_log(LOG_DEBUG, "Found technology '%s', destination '%s'\n", tech, dst);
424                         ret = 1;
425                 }
426         } else {
427                 ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno));
428                 ret = 0;
429         }
430         if (chan)
431                 ret |= ast_autoservice_stop(chan);
432         res_nclose(&enumstate);
433         return ret;
434 }
435
436 static struct enum_search *enum_newtoplev(char *s)
437 {
438         struct enum_search *tmp;
439         tmp = malloc(sizeof(struct enum_search));
440         if (tmp) {
441                 memset(tmp, 0, sizeof(struct enum_search));
442                 strncpy(tmp->toplev, s, sizeof(tmp->toplev) - 1);
443         }
444         return tmp;
445 }
446
447 int ast_enum_init(void)
448 {
449         struct ast_config *cfg;
450         struct enum_search *s, *sl;
451         struct ast_variable *v;
452
453         /* Destroy existing list */
454         ast_mutex_lock(&enumlock);
455         s = toplevs;
456         while(s) {
457                 sl = s;
458                 s = s->next;
459                 free(sl);
460         }
461         toplevs = NULL;
462         cfg = ast_load("enum.conf");
463         if (cfg) {
464                 sl = NULL;
465                 v = ast_variable_browse(cfg, "general");
466                 while(v) {
467                         if (!strcasecmp(v->name, "search")) {
468                                 s = enum_newtoplev(v->value);
469                                 if (s) {
470                                         if (sl)
471                                                 sl->next = s;
472                                         else
473                                                 toplevs = s;
474                                         sl = s;
475                                 }
476                         }
477                         v = v->next;
478                 }
479                 ast_destroy(cfg);
480         } else {
481                 toplevs = enum_newtoplev(TOPLEV);
482         }
483         enumver++;
484         ast_mutex_unlock(&enumlock);
485         return 0;
486 }
487
488 int ast_enum_reload(void)
489 {
490         return ast_enum_init();
491 }