Include fixes for portability
[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                 /* XXX Why am I adding 2 here? XXX */
244                 answer += 2;
245                 len -= 2;
246                 ans = (struct dn_answer *)answer;
247                 answer += sizeof(struct dn_answer);
248                 len -= sizeof(struct dn_answer);
249                 if (len < 0)
250                         return -1;
251 #if 0
252                 printf("Type: %d, class: %d, ttl: %d, length: %d\n", ntohs(ans->rtype), ntohs(ans->class),
253                         ntohl(ans->ttl), ntohs(ans->size));
254 #endif                  
255                 len -= ntohs(ans->size);
256                 if (len < 0) {
257                         ast_log(LOG_WARNING, "Length exceeds frame\n");
258                         return -1;
259                 }
260                 if ((ntohs(ans->class) == C_IN) && (ntohs(ans->rtype) == T_NAPTR)) {
261                         if (parse_naptr(dst, dstlen, tech, techlen, answer, ntohs(ans->size)))
262                                 ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
263                         if (strlen(dst))
264                                 return 0;
265                 }
266                 answer += ntohs(ans->size);
267         }
268         return 0;
269 }
270
271 int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen)
272 {
273         unsigned char answer[MAX_SIZE];
274         char tmp[259 + 80];
275         int pos = strlen(number) - 1;
276         int newpos=0;
277         int res = -1;
278         int ret = -1;
279         struct enum_search *s = NULL;
280         int version = -1;
281         struct __res_state enumstate;
282         res_ninit(&enumstate);  
283         if (chan && ast_autoservice_start(chan) < 0)
284                 return -1;
285         
286         if (pos > 128)
287                 pos = 128;
288         while(pos >= 0) {
289                 tmp[newpos++] = number[pos--];
290                 tmp[newpos++] = '.';
291         }
292 #if 0
293         printf("Looking for '%s'\n", tmp);
294 #endif  
295         
296         for(;;) {
297                 ast_pthread_mutex_lock(&enumlock);
298                 if (version != enumver) {
299                         /* Ooh, a reload... */
300                         s = toplevs;
301                 } else {
302                         s = s->next;
303                 }
304                 if (s) {
305                         strcpy(tmp + newpos, s->toplev);
306                 }
307                 ast_pthread_mutex_unlock(&enumlock);
308                 if (!s)
309                         break;
310                 res = res_nsearch(&enumstate, tmp, C_IN, T_NAPTR, answer, sizeof(answer));
311                 if (res > 0)
312                         break;
313         }
314         if (res > 0) {
315                 if ((res = parse_answer(dst, dstlen, tech, techlen, answer, res))) {
316                         ast_log(LOG_WARNING, "Parse error returned %d\n", res);
317                         ret = 0;
318                 } else {
319                         ast_log(LOG_DEBUG, "Found technology '%s', destination '%s'\n", tech, dst);
320                         ret = 1;
321                 }
322         } else {
323                 ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno));
324                 ret = 0;
325         }
326         if (chan)
327                 ret |= ast_autoservice_stop(chan);
328         res_nclose(&enumstate);
329         return ret;
330 }
331
332 static struct enum_search *enum_newtoplev(char *s)
333 {
334         struct enum_search *tmp;
335         tmp = malloc(sizeof(struct enum_search));
336         if (tmp) {
337                 memset(tmp, 0, sizeof(struct enum_search));
338                 strncpy(tmp->toplev, s, sizeof(tmp->toplev) - 1);
339         }
340         return tmp;
341 }
342
343 int ast_enum_init(void)
344 {
345         struct ast_config *cfg;
346         struct enum_search *s, *sl;
347         struct ast_variable *v;
348
349         /* Destroy existing list */
350         ast_pthread_mutex_lock(&enumlock);
351         s = toplevs;
352         while(s) {
353                 sl = s;
354                 s = s->next;
355                 free(sl);
356         }
357         toplevs = NULL;
358         cfg = ast_load("enum.conf");
359         if (cfg) {
360                 sl = NULL;
361                 v = ast_variable_browse(cfg, "general");
362                 while(v) {
363                         if (!strcasecmp(v->name, "search")) {
364                                 s = enum_newtoplev(v->value);
365                                 if (s) {
366                                         if (sl)
367                                                 sl->next = s;
368                                         else
369                                                 toplevs = s;
370                                         sl = s;
371                                 }
372                         }
373                         v = v->next;
374                 }
375                 ast_destroy(cfg);
376         } else {
377                 toplevs = enum_newtoplev(TOPLEV);
378         }
379         enumver++;
380         ast_pthread_mutex_unlock(&enumlock);
381         return 0;
382 }
383
384 int ast_enum_reload(void)
385 {
386         return ast_enum_init();
387 }