More cleanups and OSX fixes for 10.3
[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 <sys/types.h>
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <arpa/nameser.h>
18 #if __APPLE_CC__ >= 1495
19 #include <arpa/nameser_compat.h>
20 #endif
21 #include <resolv.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <regex.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #include <asterisk/logger.h>
30 #include <asterisk/options.h>
31 #include <asterisk/enum.h>
32 #include <asterisk/dns.h>
33 #include <asterisk/channel.h>
34 #include <asterisk/config.h>
35
36 #ifdef __APPLE__
37 #undef T_NAPTR
38 #define T_NAPTR 35
39 #endif
40
41 #define TOPLEV "e164.arpa."
42
43 static struct enum_search {
44         char toplev[80];
45         struct enum_search *next;
46 } *toplevs;
47
48 static int enumver = 0;
49
50 static ast_mutex_t enumlock = AST_MUTEX_INITIALIZER;
51
52 struct naptr {
53         unsigned short order;
54         unsigned short pref;
55 } __attribute__ ((__packed__));
56
57 static int parse_ie(unsigned char *data, int maxdatalen, unsigned char *src, int srclen)
58 {
59         int len, olen;
60         len = olen = (int)src[0];
61         src++;
62         srclen--;
63         if (len > srclen) {
64                 ast_log(LOG_WARNING, "Want %d, got %d\n", len, srclen);
65                 return -1;
66         }
67         if (len > maxdatalen)
68                 len = maxdatalen;
69         memcpy(data, src, len);
70         return olen + 1;
71 }
72
73 static int parse_naptr(unsigned char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, char *naptrinput)
74 {
75         unsigned char *oanswer = answer;
76         unsigned char flags[80] = "";
77         unsigned char services[80] = "";
78         unsigned char regexp[80] = "";
79         unsigned char repl[80] = "";
80         unsigned char temp[80] = "";
81         unsigned char delim;
82         unsigned char *delim2;
83         unsigned char *pattern, *subst, *d;
84         int res;
85         int regexp_len, size, backref;
86         int d_len = sizeof(temp) - 1;
87         regex_t preg;
88         regmatch_t pmatch[9];
89
90         
91         if (len < sizeof(struct naptr)) {
92                 printf("Length too short\n");
93                 return -1;
94         }
95         answer += sizeof(struct naptr);
96         len -= sizeof(struct naptr);
97         if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
98                 ast_log(LOG_WARNING, "Failed to get flags\n");
99                 return -1; 
100         } else { answer += res; len -= res; }
101         if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
102                 ast_log(LOG_WARNING, "Failed to get services\n");
103                 return -1; 
104         } else { answer += res; len -= res; }
105         if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0)
106                 return -1; else { answer += res; len -= res; }
107         if ((res = dn_expand(oanswer,answer + len,answer, repl, sizeof(repl) - 1)) < 0) {
108                 ast_log(LOG_WARNING, "Failed to expand hostname\n");
109                 return -1;
110         } 
111
112         ast_log(LOG_DEBUG, "input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
113                     naptrinput, flags, services, regexp, repl);
114
115         if (tolower(flags[0]) != 'u') {
116                 ast_log(LOG_WARNING, "Flag must be 'U' or 'u'.\n");
117                 return -1;
118         }
119
120         if ((!strncasecmp(services, "e2u+sip", 7)) || 
121             (!strncasecmp(services, "sip+e2u", 7))) {
122                 strncpy(tech, "sip", techsize -1); 
123         } else if ((!strncasecmp(services, "e2u+h323", 8)) || 
124             (!strncasecmp(services, "h323+e2u", 8))) {
125                 strncpy(tech, "h323", techsize -1); 
126         } else if ((!strncasecmp(services, "e2u+iax", 7)) || 
127             (!strncasecmp(services, "iax+e2u", 7))) {
128                 strncpy(tech, "iax", techsize -1); 
129         } else if ((!strncasecmp(services, "e2u+iax2", 8)) || 
130             (!strncasecmp(services, "iax2+e2u", 8))) {
131                 strncpy(tech, "iax2", techsize -1); 
132         } else if ((!strncasecmp(services, "e2u+tel", 7)) || 
133             (!strncasecmp(services, "tel+e2u", 7))) {
134                 strncpy(tech, "tel", techsize -1); 
135         } else if (!strncasecmp(services, "e2u+voice:", 10)) {
136                 strncpy(tech, services+10, techsize -1); 
137         } else {
138                 ast_log(LOG_WARNING, 
139                 "Services must be e2u+${tech}, ${tech}+e2u, or e2u+voice: where $tech is from (sip, h323, tel, iax, iax2). \n");
140                 return -1;
141         }
142
143         /* DEDBUGGING STUB
144         strcpy(regexp, "!^\\+43(.*)$!\\1@bla.fasel!");
145         */
146
147         regexp_len = strlen(regexp);
148         if (regexp_len < 7) {
149                 ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
150                 return -1;
151         } 
152
153
154         delim = regexp[0];
155         delim2 = strchr(regexp + 1, delim);
156         if ((delim2 == NULL) || (regexp[regexp_len-1] != delim)) {
157                 ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n",regexp);
158                 return -1;
159         }
160
161         pattern = regexp + 1;
162         *delim2 = 0;
163         subst   = delim2 + 1;
164         regexp[regexp_len-1] = 0;
165
166 #if 0
167         printf("Pattern: %s\n", pattern);
168         printf("Subst: %s\n", subst);
169 #endif
170
171 /*
172  * now do the regex wizardry.
173  */
174
175         if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
176                 ast_log(LOG_WARNING, "Regex compilation error (regex = \"%s\").\n",regexp);
177                 return -1;
178         }
179
180         if (preg.re_nsub > 9) {
181                 ast_log(LOG_WARNING, "Regex compilation error: too many subs.\n");
182                 regfree(&preg);
183                 return -1;
184         }
185
186         if (regexec(&preg, naptrinput, 9, pmatch, 0)) {
187                 ast_log(LOG_WARNING, "Regex match failed.\n");
188                 regfree(&preg);
189                 return -1;
190         }
191         regfree(&preg);
192
193         d = temp; d_len--; 
194         while( *subst && (d_len > 0) ) {
195                 if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) {
196                         backref = subst[1]-'0';
197                         size = pmatch[backref].rm_eo - pmatch[backref].rm_so;
198                         if (size > d_len) {
199                                 ast_log(LOG_WARNING, "Not enough space during regex substitution.\n");
200                                 return -1;
201                                 }
202                         memcpy(d, naptrinput + pmatch[backref].rm_so, size);
203                         d += size;
204                         d_len -= size;
205                         subst += 2;
206                 } else if (isprint(*subst)) {
207                         *d++ = *subst++;
208                         d_len--;
209                 } else {
210                         ast_log(LOG_WARNING, "Error during regex substitution.\n");
211                         return -1;
212                 }
213         }
214         *d = 0;
215         strncpy(dst, temp, dstsize);
216         return 0;
217 }
218
219 struct enum_context {
220         char *dst;
221         int dstlen;
222         char *tech;
223         int techlen;
224         char *naptrinput;
225 };
226
227 static int enum_callback(void *context, u_char *answer, int len, u_char *fullanswer)
228 {
229         struct enum_context *c = (struct enum_context *)context;
230
231         if (parse_naptr(c->dst, c->dstlen, c->tech, c->techlen, answer, len, c->naptrinput))
232                 ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
233
234         if (strlen(c->dst))
235                 return 1;
236
237         return 0;
238 }
239
240 int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen)
241 {
242         struct enum_context context;
243         char tmp[259 + 80];
244         char naptrinput[80] = "+";
245         int pos = strlen(number) - 1;
246         int newpos = 0;
247         int ret = -1;
248         struct enum_search *s = NULL;
249         int version = -1;
250
251         strncat(naptrinput, number, sizeof(naptrinput) - 2);
252
253         context.naptrinput = naptrinput;
254         context.dst = dst;
255         context.dstlen = dstlen;
256         context.tech = tech;
257         context.techlen = techlen;
258
259         if (pos > 128)
260                 pos = 128;
261         while(pos >= 0) {
262                 tmp[newpos++] = number[pos--];
263                 tmp[newpos++] = '.';
264         }
265         
266         if (chan && ast_autoservice_start(chan) < 0)
267                 return -1;
268
269         for(;;) {
270                 ast_mutex_lock(&enumlock);
271                 if (version != enumver) {
272                         /* Ooh, a reload... */
273                         s = toplevs;
274                         version = enumver;
275                 } else {
276                         s = s->next;
277                 }
278                 if (s) {
279                         strcpy(tmp + newpos, s->toplev);
280                 }
281                 ast_mutex_unlock(&enumlock);
282                 if (!s)
283                         break;
284                 ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback);
285                 if (ret > 0)
286                         break;
287         }
288         if (ret < 0) {
289                 ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno));
290                 ret = 0;
291         }
292         if (chan)
293                 ret |= ast_autoservice_stop(chan);
294         return ret;
295 }
296
297 static struct enum_search *enum_newtoplev(char *s)
298 {
299         struct enum_search *tmp;
300         tmp = malloc(sizeof(struct enum_search));
301         if (tmp) {
302                 memset(tmp, 0, sizeof(struct enum_search));
303                 strncpy(tmp->toplev, s, sizeof(tmp->toplev) - 1);
304         }
305         return tmp;
306 }
307
308 int ast_enum_init(void)
309 {
310         struct ast_config *cfg;
311         struct enum_search *s, *sl;
312         struct ast_variable *v;
313
314         /* Destroy existing list */
315         ast_mutex_lock(&enumlock);
316         s = toplevs;
317         while(s) {
318                 sl = s;
319                 s = s->next;
320                 free(sl);
321         }
322         toplevs = NULL;
323         cfg = ast_load("enum.conf");
324         if (cfg) {
325                 sl = NULL;
326                 v = ast_variable_browse(cfg, "general");
327                 while(v) {
328                         if (!strcasecmp(v->name, "search")) {
329                                 s = enum_newtoplev(v->value);
330                                 if (s) {
331                                         if (sl)
332                                                 sl->next = s;
333                                         else
334                                                 toplevs = s;
335                                         sl = s;
336                                 }
337                         }
338                         v = v->next;
339                 }
340                 ast_destroy(cfg);
341         } else {
342                 toplevs = enum_newtoplev(TOPLEV);
343         }
344         enumver++;
345         ast_mutex_unlock(&enumlock);
346         return 0;
347 }
348
349 int ast_enum_reload(void)
350 {
351         return ast_enum_init();
352 }