Add SRV code to SIP, cleanup ENUM and make IAX2 do the right thing on dials
[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
25 #include <asterisk/logger.h>
26 #include <asterisk/options.h>
27 #include <asterisk/enum.h>
28 #include <asterisk/channel.h>
29 #include <asterisk/config.h>
30
31 #define MAX_SIZE 4096
32
33 #define TOPLEV "e164.arpa."
34
35 typedef struct {
36         unsigned        id :16;         /* query identification number */
37 #if BYTE_ORDER == BIG_ENDIAN
38                         /* fields in third byte */
39         unsigned        qr: 1;          /* response flag */
40         unsigned        opcode: 4;      /* purpose of message */
41         unsigned        aa: 1;          /* authoritive answer */
42         unsigned        tc: 1;          /* truncated message */
43         unsigned        rd: 1;          /* recursion desired */
44                         /* fields in fourth byte */
45         unsigned        ra: 1;          /* recursion available */
46         unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
47         unsigned        ad: 1;          /* authentic data from named */
48         unsigned        cd: 1;          /* checking disabled by resolver */
49         unsigned        rcode :4;       /* response code */
50 #endif
51 #if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
52                         /* fields in third byte */
53         unsigned        rd :1;          /* recursion desired */
54         unsigned        tc :1;          /* truncated message */
55         unsigned        aa :1;          /* authoritive answer */
56         unsigned        opcode :4;      /* purpose of message */
57         unsigned        qr :1;          /* response flag */
58                         /* fields in fourth byte */
59         unsigned        rcode :4;       /* response code */
60         unsigned        cd: 1;          /* checking disabled by resolver */
61         unsigned        ad: 1;          /* authentic data from named */
62         unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
63         unsigned        ra :1;          /* recursion available */
64 #endif
65                         /* remaining bytes */
66         unsigned        qdcount :16;    /* number of question entries */
67         unsigned        ancount :16;    /* number of answer entries */
68         unsigned        nscount :16;    /* number of authority entries */
69         unsigned        arcount :16;    /* number of resource entries */
70 } dns_HEADER;
71
72 static struct enum_search {
73         char toplev[80];
74         struct enum_search *next;
75 } *toplevs;
76
77 static int enumver = 0;
78
79 static pthread_mutex_t enumlock = AST_MUTEX_INITIALIZER;
80
81 static int skip_name(unsigned char *s, int len)
82 {
83         /* Shamelessly take from SER */
84         int x = 0;
85         while(x < len) {
86                 if (!*s) {
87                         s++;
88                         x++;
89                         break;
90                 }
91                 if (((*s) & 0xc0) == 0xc0) {
92                         s += 2;
93                         x += 2;
94                         break;
95                 }
96                 x += *s + 1;
97                 s += *s + 1;
98         }
99         if (x >= len)
100                 return -1;
101         return x;
102 }
103
104 struct dn_answer {
105         unsigned short rtype;
106         unsigned short class;
107         unsigned int ttl;
108         unsigned short size;
109 } __attribute__ ((__packed__));
110
111 struct naptr {
112         unsigned short order;
113         unsigned short pref;
114 } __attribute__ ((__packed__));
115
116 static int parse_ie(unsigned char *data, int maxdatalen, unsigned char *src, int srclen)
117 {
118         int len, olen;
119         len = olen = (int)src[0];
120         src++;
121         srclen--;
122         if (len > srclen) {
123                 ast_log(LOG_WARNING, "Want %d, got %d\n", len, srclen);
124                 return -1;
125         }
126         if (len > maxdatalen)
127                 len = maxdatalen;
128         memcpy(data, src, len);
129         return olen + 1;
130 }
131
132 static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len)
133 {
134         unsigned char *oanswer = answer;
135         unsigned char flags[80] = "";
136         unsigned char services[80] = "";
137         unsigned char regexp[80] = "";
138         unsigned char repl[80] = "";
139         int res;
140         
141         if (len < sizeof(struct naptr)) {
142                 printf("Length too short\n");
143                 return -1;
144         }
145         answer += sizeof(struct naptr);
146         len -= sizeof(struct naptr);
147         if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
148                 ast_log(LOG_WARNING, "Failed to get flags\n");
149                 return -1; 
150         } else { answer += res; len -= res; }
151         if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
152                 ast_log(LOG_WARNING, "Failed to get services\n");
153                 return -1; 
154         } else { answer += res; len -= res; }
155         if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0)
156                 return -1; else { answer += res; len -= res; }
157         if ((res = dn_expand(oanswer,answer + len,answer, repl, sizeof(repl) - 1)) < 0) {
158                 ast_log(LOG_WARNING, "Failed to expand hostname\n");
159                 return -1;
160         } 
161 #if 0
162         printf("Flags: %s\n", flags);
163         printf("Services: %s\n", services);
164         printf("Regexp: %s\n", regexp);
165         printf("Repl: %s\n", repl);
166 #endif
167         if (!strncmp(regexp, "!^.*$!", 6)) {
168                 if (!strncmp(services, "E2U+voice:", 10)) {
169                         if (regexp[strlen(regexp) - 1] == '!')
170                                 regexp[strlen(regexp) - 1] = '\0';
171 #if 0
172                         printf("Technology: %s\n", services + 10);
173                         printf("Destination: %s\n", regexp + 6);
174 #endif
175                         strncpy(dst, regexp + 6, dstsize);
176                         strncpy(tech, services + 10, techsize);
177                 }
178         } else
179                 ast_log(LOG_WARNING, "Non-total substitution not yet supported\n");
180         return 0;
181 }
182
183 static int parse_answer(unsigned char *dst, int dstlen, unsigned char *tech, int techlen, unsigned char *answer, int len)
184 {
185         /*
186          * This function is influenced by "ser" the SIP router.
187          */
188         int x;
189         int res;
190         dns_HEADER *h;
191         struct dn_answer *ans;
192         dst[0] = '\0';
193         tech[0] = '\0';
194 #if 0
195         for (x=0;x<len;x++) {
196                 if ((answer[x] < 32) || (answer[x] > 127)) {
197                         if (lastlit)
198                                 printf("\"");
199                         printf(" 0x%02x", answer[x]);
200                         lastlit = 0;
201                 } else {
202                         if (!lastlit) 
203                                 printf(" \"");
204                         printf("%c", answer[x]);
205                         lastlit = 1;
206                 }
207         }
208         printf("\n");
209 #endif  
210         h = (dns_HEADER *)answer;
211         /* Skip over DNS header */
212         answer += sizeof(dns_HEADER);
213         len -= sizeof(dns_HEADER);
214 #if 0
215         printf("Query count: %d\n", ntohs(h->qdcount));
216 #endif
217         for (x=0;x<ntohs(h->qdcount);x++) {
218                 if ((res = skip_name(answer, len)) < 0) {
219                         ast_log(LOG_WARNING, "Couldn't skip over name\n");
220                         return -1;
221                 }
222                 answer += res;
223                 len -= res;
224                 answer += 4;    /* Skip QCODE / QCLASS */
225                 len -= 4;
226                 if (len < 0) {
227                         ast_log(LOG_WARNING, "Strange query size\n");
228                         return -1;
229                 }
230         }
231 #if 0
232         printf("Length remaining: %d\n", len);
233         printf("Answer count: %d\n", ntohs(h->ancount));
234         printf("Looking for %d/%d\n", C_IN, T_NAPTR);
235 #endif
236         for (x=0;x<ntohs(h->ancount);x++) {
237                 if ((res = skip_name(answer, len)) < 0) {
238                         ast_log(LOG_WARNING, "Failed to skip name :(\n");
239                         return -1;
240                 }
241                 answer += res;
242                 len -= res;
243                 ans = (struct dn_answer *)answer;
244                 answer += sizeof(struct dn_answer);
245                 len -= sizeof(struct dn_answer);
246                 if (len < 0)
247                         return -1;
248 #if 0
249                 printf("Type: %d, class: %d, ttl: %d, length: %d\n", ntohs(ans->rtype), ntohs(ans->class),
250                         ntohl(ans->ttl), ntohs(ans->size));
251 #endif                  
252                 len -= ntohs(ans->size);
253                 if (len < 0) {
254                         ast_log(LOG_WARNING, "Length exceeds frame\n");
255                         return -1;
256                 }
257                 if ((ntohs(ans->class) == C_IN) && (ntohs(ans->rtype) == T_NAPTR)) {
258                         if (parse_naptr(dst, dstlen, tech, techlen, answer, ntohs(ans->size)))
259                                 ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
260                         if (strlen(dst))
261                                 return 0;
262                 }
263                 answer += ntohs(ans->size);
264         }
265         return 0;
266 }
267
268 int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen)
269 {
270         unsigned char answer[MAX_SIZE];
271         char tmp[259 + 80];
272         int pos = strlen(number) - 1;
273         int newpos=0;
274         int res = -1;
275         int ret = -1;
276         struct enum_search *s = NULL;
277         int version = -1;
278         struct __res_state enumstate;
279         res_ninit(&enumstate);  
280         if (chan && ast_autoservice_start(chan) < 0)
281                 return -1;
282         
283         if (pos > 128)
284                 pos = 128;
285         while(pos >= 0) {
286                 tmp[newpos++] = number[pos--];
287                 tmp[newpos++] = '.';
288         }
289 #if 0
290         printf("Looking for '%s'\n", tmp);
291 #endif  
292         
293         for(;;) {
294                 ast_pthread_mutex_lock(&enumlock);
295                 if (version != enumver) {
296                         /* Ooh, a reload... */
297                         s = toplevs;
298                 } else {
299                         s = s->next;
300                 }
301                 if (s) {
302                         strcpy(tmp + newpos, s->toplev);
303                 }
304                 ast_pthread_mutex_unlock(&enumlock);
305                 if (!s)
306                         break;
307                 res = res_nsearch(&enumstate, tmp, C_IN, T_NAPTR, answer, sizeof(answer));
308                 if (res > 0)
309                         break;
310         }
311         if (res > 0) {
312                 if ((res = parse_answer(dst, dstlen, tech, techlen, answer, res))) {
313                         ast_log(LOG_WARNING, "Parse error returned %d\n", res);
314                         ret = 0;
315                 } else {
316                         ast_log(LOG_DEBUG, "Found technology '%s', destination '%s'\n", tech, dst);
317                         ret = 1;
318                 }
319         } else {
320                 ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno));
321                 ret = 0;
322         }
323         if (chan)
324                 ret |= ast_autoservice_stop(chan);
325         res_nclose(&enumstate);
326         return ret;
327 }
328
329 static struct enum_search *enum_newtoplev(char *s)
330 {
331         struct enum_search *tmp;
332         tmp = malloc(sizeof(struct enum_search));
333         if (tmp) {
334                 memset(tmp, 0, sizeof(struct enum_search));
335                 strncpy(tmp->toplev, s, sizeof(tmp->toplev) - 1);
336         }
337         return tmp;
338 }
339
340 int ast_enum_init(void)
341 {
342         struct ast_config *cfg;
343         struct enum_search *s, *sl;
344         struct ast_variable *v;
345
346         /* Destroy existing list */
347         ast_pthread_mutex_lock(&enumlock);
348         s = toplevs;
349         while(s) {
350                 sl = s;
351                 s = s->next;
352                 free(sl);
353         }
354         toplevs = NULL;
355         cfg = ast_load("enum.conf");
356         if (cfg) {
357                 sl = NULL;
358                 v = ast_variable_browse(cfg, "general");
359                 while(v) {
360                         if (!strcasecmp(v->name, "search")) {
361                                 s = enum_newtoplev(v->value);
362                                 if (s) {
363                                         if (sl)
364                                                 sl->next = s;
365                                         else
366                                                 toplevs = s;
367                                         sl = s;
368                                 }
369                         }
370                         v = v->next;
371                 }
372                 ast_destroy(cfg);
373         } else {
374                 toplevs = enum_newtoplev(TOPLEV);
375         }
376         enumver++;
377         ast_pthread_mutex_unlock(&enumlock);
378         return 0;
379 }
380
381 int ast_enum_reload(void)
382 {
383         return ast_enum_init();
384 }