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