Minor enum improvements for iax/iax2
[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         strcpy(dst, "");
92         
93         if (len < sizeof(struct naptr)) {
94                 ast_log(LOG_WARNING, "Length too short\n");
95                 return -1;
96         }
97         answer += sizeof(struct naptr);
98         len -= sizeof(struct naptr);
99         if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
100                 ast_log(LOG_WARNING, "Failed to get flags\n");
101                 return -1; 
102         } else { answer += res; len -= res; }
103         if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
104                 ast_log(LOG_WARNING, "Failed to get services\n");
105                 return -1; 
106         } else { answer += res; len -= res; }
107         if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0)
108                 return -1; else { answer += res; len -= res; }
109         if ((res = dn_expand(oanswer,answer + len,answer, repl, sizeof(repl) - 1)) < 0) {
110                 ast_log(LOG_WARNING, "Failed to expand hostname\n");
111                 return -1;
112         } 
113
114         ast_log(LOG_DEBUG, "input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
115                     naptrinput, flags, services, regexp, repl);
116
117         if (tolower(flags[0]) != 'u') {
118                 ast_log(LOG_WARNING, "Flag must be 'U' or 'u'.\n");
119                 return -1;
120         }
121
122         if ((!strncasecmp(services, "e2u+sip", 7)) || 
123             (!strncasecmp(services, "sip+e2u", 7))) {
124                 strncpy(tech, "sip", techsize -1); 
125         } else if ((!strncasecmp(services, "e2u+h323", 8)) || 
126             (!strncasecmp(services, "h323+e2u", 8))) {
127                 strncpy(tech, "h323", techsize -1); 
128         } else if ((!strncasecmp(services, "e2u+x-iax2", 10)) || 
129             (!strncasecmp(services, "e2u+iax2", 8)) ||
130             (!strncasecmp(services, "iax2+e2u", 8))) {
131                 strncpy(tech, "iax2", techsize -1); 
132         } else if ((!strncasecmp(services, "e2u+x-iax", 9)) ||
133             (!strncasecmp(services, "e2u+iax", 7)) ||
134             (!strncasecmp(services, "iax+e2u", 7))) {
135                 strncpy(tech, "iax", techsize -1); 
136         } else if ((!strncasecmp(services, "e2u+tel", 7)) || 
137             (!strncasecmp(services, "tel+e2u", 7))) {
138                 strncpy(tech, "tel", techsize -1); 
139         } else if (!strncasecmp(services, "e2u+voice:", 10)) {
140                 strncpy(tech, services+10, techsize -1); 
141         } else {
142                 ast_log(LOG_DEBUG, 
143                 "Services must be e2u+${tech}, ${tech}+e2u, or e2u+voice: where $tech is from (sip, h323, tel, iax, iax2). \n");
144                 return 0;
145         }
146
147         /* DEDBUGGING STUB
148         strcpy(regexp, "!^\\+43(.*)$!\\1@bla.fasel!");
149         */
150
151         regexp_len = strlen(regexp);
152         if (regexp_len < 7) {
153                 ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
154                 return -1;
155         } 
156
157
158         delim = regexp[0];
159         delim2 = strchr(regexp + 1, delim);
160         if ((delim2 == NULL) || (regexp[regexp_len-1] != delim)) {
161                 ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n",regexp);
162                 return -1;
163         }
164
165         pattern = regexp + 1;
166         *delim2 = 0;
167         subst   = delim2 + 1;
168         regexp[regexp_len-1] = 0;
169
170 #if 0
171         printf("Pattern: %s\n", pattern);
172         printf("Subst: %s\n", subst);
173 #endif
174
175 /*
176  * now do the regex wizardry.
177  */
178
179         if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
180                 ast_log(LOG_WARNING, "Regex compilation error (regex = \"%s\").\n",regexp);
181                 return -1;
182         }
183
184         if (preg.re_nsub > 9) {
185                 ast_log(LOG_WARNING, "Regex compilation error: too many subs.\n");
186                 regfree(&preg);
187                 return -1;
188         }
189
190         if (regexec(&preg, naptrinput, 9, pmatch, 0)) {
191                 ast_log(LOG_WARNING, "Regex match failed.\n");
192                 regfree(&preg);
193                 return -1;
194         }
195         regfree(&preg);
196
197         d = temp; d_len--; 
198         while( *subst && (d_len > 0) ) {
199                 if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) {
200                         backref = subst[1]-'0';
201                         size = pmatch[backref].rm_eo - pmatch[backref].rm_so;
202                         if (size > d_len) {
203                                 ast_log(LOG_WARNING, "Not enough space during regex substitution.\n");
204                                 return -1;
205                                 }
206                         memcpy(d, naptrinput + pmatch[backref].rm_so, size);
207                         d += size;
208                         d_len -= size;
209                         subst += 2;
210                 } else if (isprint(*subst)) {
211                         *d++ = *subst++;
212                         d_len--;
213                 } else {
214                         ast_log(LOG_WARNING, "Error during regex substitution.\n");
215                         return -1;
216                 }
217         }
218         *d = 0;
219         strncpy(dst, temp, dstsize);
220         return 0;
221 }
222
223 struct enum_context {
224         char *dst;
225         int dstlen;
226         char *tech;
227         int techlen;
228         char *naptrinput;
229 };
230
231 static int enum_callback(void *context, u_char *answer, int len, u_char *fullanswer)
232 {
233         struct enum_context *c = (struct enum_context *)context;
234
235         if (parse_naptr(c->dst, c->dstlen, c->tech, c->techlen, answer, len, c->naptrinput))
236                 ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
237
238         if (strlen(c->dst))
239                 return 1;
240
241         return 0;
242 }
243
244 int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen)
245 {
246         struct enum_context context;
247         char tmp[259 + 80];
248         char naptrinput[80] = "+";
249         int pos = strlen(number) - 1;
250         int newpos = 0;
251         int ret = -1;
252         struct enum_search *s = NULL;
253         int version = -1;
254
255         strncat(naptrinput, number, sizeof(naptrinput) - 2);
256
257         context.naptrinput = naptrinput;
258         context.dst = dst;
259         context.dstlen = dstlen;
260         context.tech = tech;
261         context.techlen = techlen;
262
263         if (pos > 128)
264                 pos = 128;
265         while(pos >= 0) {
266                 tmp[newpos++] = number[pos--];
267                 tmp[newpos++] = '.';
268         }
269         
270         if (chan && ast_autoservice_start(chan) < 0)
271                 return -1;
272
273         for(;;) {
274                 ast_mutex_lock(&enumlock);
275                 if (version != enumver) {
276                         /* Ooh, a reload... */
277                         s = toplevs;
278                         version = enumver;
279                 } else {
280                         s = s->next;
281                 }
282                 if (s) {
283                         strcpy(tmp + newpos, s->toplev);
284                 }
285                 ast_mutex_unlock(&enumlock);
286                 if (!s)
287                         break;
288                 ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback);
289                 if (ret > 0)
290                         break;
291         }
292         if (ret < 0) {
293                 ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno));
294                 ret = 0;
295         }
296         if (chan)
297                 ret |= ast_autoservice_stop(chan);
298         return ret;
299 }
300
301 static struct enum_search *enum_newtoplev(char *s)
302 {
303         struct enum_search *tmp;
304         tmp = malloc(sizeof(struct enum_search));
305         if (tmp) {
306                 memset(tmp, 0, sizeof(struct enum_search));
307                 strncpy(tmp->toplev, s, sizeof(tmp->toplev) - 1);
308         }
309         return tmp;
310 }
311
312 int ast_enum_init(void)
313 {
314         struct ast_config *cfg;
315         struct enum_search *s, *sl;
316         struct ast_variable *v;
317
318         /* Destroy existing list */
319         ast_mutex_lock(&enumlock);
320         s = toplevs;
321         while(s) {
322                 sl = s;
323                 s = s->next;
324                 free(sl);
325         }
326         toplevs = NULL;
327         cfg = ast_load("enum.conf");
328         if (cfg) {
329                 sl = NULL;
330                 v = ast_variable_browse(cfg, "general");
331                 while(v) {
332                         if (!strcasecmp(v->name, "search")) {
333                                 s = enum_newtoplev(v->value);
334                                 if (s) {
335                                         if (sl)
336                                                 sl->next = s;
337                                         else
338                                                 toplevs = s;
339                                         sl = s;
340                                 }
341                         }
342                         v = v->next;
343                 }
344                 ast_destroy(cfg);
345         } else {
346                 toplevs = enum_newtoplev(TOPLEV);
347         }
348         enumver++;
349         ast_mutex_unlock(&enumlock);
350         return 0;
351 }
352
353 int ast_enum_reload(void)
354 {
355         return ast_enum_init();
356 }