ec55ed9e0c1cc4150e5a32fb138312534475547b
[citadel.git] / citadel / modules / c-ares-dns / serv_c-ares-dns.c
1 /*
2  * Copyright (c) 1998-2009 by the citadel.org team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  Inspired by NodeJS.org; thanks for the MX-Parser ;-)
19  */
20
21 #include "sysdep.h"
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <termios.h>
26 #include <fcntl.h>
27 #include <signal.h>
28 #include <pwd.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <syslog.h>
32
33 #if TIME_WITH_SYS_TIME
34 # include <sys/time.h>
35 # include <time.h>
36 #else
37 # if HAVE_SYS_TIME_H
38 #  include <sys/time.h>
39 # else
40 #  include <time.h>
41 # endif
42 #endif
43 #include <sys/wait.h>
44 #include <ctype.h>
45 #include <string.h>
46 #include <limits.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50
51 #include <libcitadel.h>
52 #include "citadel.h"
53 #include "server.h"
54 #include "citserver.h"
55 #include "support.h"
56
57 #include "ctdl_module.h"
58 #include "event_client.h"
59
60
61 extern struct ev_loop *event_base;
62 struct ares_options options;
63 ares_channel Channel;
64
65 void SockStateCb(void *data, int sock, int read, int write);
66
67
68 static void HostByAddrCb(void *data,
69                          int status,
70                          int timeouts,
71                          struct hostent *hostent) 
72 {
73         AsyncIO *IO = data;
74         IO->DNSStatus = status;
75         if  (status != ARES_SUCCESS) {
76 //              ResolveError(*cb, status);
77                 return;
78         }
79         IO->Data = hostent;
80 /// TODO: howto free this??
81 }
82
83 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen) 
84 {
85         struct hostent* host;
86
87         if (IO->VParsedDNSReply != NULL)
88                 IO->DNSReplyFree(IO->VParsedDNSReply);
89         IO->VParsedDNSReply = NULL;
90
91         IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
92         if (IO->DNSStatus != ARES_SUCCESS) {
93 //    ResolveError(arg->js_cb, status);
94                 return;
95         }
96         IO->VParsedDNSReply = host;
97         IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
98 }
99
100
101 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen) 
102 {
103         struct hostent* host;
104
105         if (IO->VParsedDNSReply != NULL)
106                 IO->DNSReplyFree(IO->VParsedDNSReply);
107         IO->VParsedDNSReply = NULL;
108
109         IO->DNSStatus = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
110         if (IO->DNSStatus != ARES_SUCCESS) {
111 //    ResolveError(arg->js_cb, status);
112                 return;
113         }
114         IO->VParsedDNSReply = host;
115         IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
116 }
117
118
119 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen) 
120 {
121         struct hostent* host;
122
123         if (IO->VParsedDNSReply != NULL)
124                 IO->DNSReplyFree(IO->VParsedDNSReply);
125         IO->VParsedDNSReply = NULL;
126
127         IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
128         if (IO->DNSStatus != ARES_SUCCESS) {
129 //    ResolveError(arg->js_cb, status);
130                 return;
131         }
132
133         // a CNAME lookup always returns a single record but
134         IO->VParsedDNSReply = host;
135         IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
136 }
137
138
139 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen) 
140 {
141         struct ares_mx_reply *mx_out;
142
143         if (IO->VParsedDNSReply != NULL)
144                 IO->DNSReplyFree(IO->VParsedDNSReply);
145         IO->VParsedDNSReply = NULL;
146
147         IO->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
148         if (IO->DNSStatus != ARES_SUCCESS) {
149 //    ResolveError(arg->js_cb, status);
150                 return;
151         }
152
153         IO->VParsedDNSReply = mx_out;
154         IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
155 }
156
157
158 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen) 
159 {
160         struct hostent* host;
161
162         if (IO->VParsedDNSReply != NULL)
163                 IO->DNSReplyFree(IO->VParsedDNSReply);
164         IO->VParsedDNSReply = NULL;
165
166         IO->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
167         if (IO->DNSStatus != ARES_SUCCESS) {
168 //    ResolveError(arg->js_cb, status);
169                 return;
170         }
171         IO->VParsedDNSReply = host;
172         IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
173 }
174
175
176 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen) 
177 {
178         struct ares_srv_reply *srv_out;
179
180         if (IO->VParsedDNSReply != NULL)
181                 IO->DNSReplyFree(IO->VParsedDNSReply);
182         IO->VParsedDNSReply = NULL;
183
184         IO->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
185         if (IO->DNSStatus != ARES_SUCCESS) {
186 //    ResolveError(arg->js_cb, status);
187                 return;
188         }
189
190         IO->VParsedDNSReply = srv_out;
191         IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
192 }
193
194
195 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen) 
196 {
197         struct ares_txt_reply *txt_out;
198
199         if (IO->VParsedDNSReply != NULL)
200                 IO->DNSReplyFree(IO->VParsedDNSReply);
201         IO->VParsedDNSReply = NULL;
202
203         IO->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
204         if (IO->DNSStatus != ARES_SUCCESS) {
205 //    ResolveError(arg->js_cb, status);
206                 return;
207         }
208         IO->VParsedDNSReply = txt_out;
209         IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
210 }
211
212 void QueryCb(void *arg,
213              int status,
214              int timeouts,
215              unsigned char* abuf,
216              int alen) 
217 {
218         AsyncIO *IO = arg;
219
220         IO->DNSStatus = status;
221         if (status == ARES_SUCCESS)
222                 IO->DNS_CB(arg, abuf, alen);
223 ///     ev_io_stop(event_base, &IO->dns_io_event);
224                 
225         IO->PostDNS(IO);
226 }
227
228 int QueueQuery(ns_type Type, char *name, AsyncIO *IO, IO_CallBack PostDNS)
229 {
230         int length, family;
231         char address_b[sizeof(struct in6_addr)];
232         int optmask = 0;
233
234         if (IO->DNSChannel == NULL) {
235                 optmask |= ARES_OPT_SOCK_STATE_CB;
236                 IO->DNSOptions.sock_state_cb = SockStateCb;
237                 IO->DNSOptions.sock_state_cb_data = IO;
238                 ares_init_options(&IO->DNSChannel, &IO->DNSOptions, optmask);
239         }
240
241         IO->PostDNS = PostDNS;
242         switch(Type) {
243         case ns_t_a:
244                 IO->DNS_CB = ParseAnswerA;
245                 break;
246
247         case ns_t_aaaa:
248                 IO->DNS_CB = ParseAnswerAAAA;
249                 break;
250
251         case ns_t_mx:
252                 IO->DNS_CB = ParseAnswerMX;
253                 break;
254
255         case ns_t_ns:
256                 IO->DNS_CB = ParseAnswerNS;
257                 break;
258
259         case ns_t_txt:
260                 IO->DNS_CB = ParseAnswerTXT;
261                 break;
262
263         case ns_t_srv:
264                 IO->DNS_CB = ParseAnswerSRV;
265                 break;
266
267         case ns_t_cname:
268                 IO->DNS_CB = ParseAnswerCNAME;
269                 break;
270
271         case ns_t_ptr:
272
273
274                 if (inet_pton(AF_INET, name, &address_b) == 1) {
275                         length = sizeof(struct in_addr);
276                         family = AF_INET;
277                 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
278                         length = sizeof(struct in6_addr);
279                         family = AF_INET6;
280                 } else {
281                         return -1;
282                 }
283
284                 ares_gethostbyaddr(IO->DNSChannel, address_b, length, family, HostByAddrCb, IO);
285
286                 return 1;
287
288         default:
289                 return 0;
290         }
291         ares_query(IO->DNSChannel, name, ns_c_in, Type, QueryCb, IO);
292         return 1;
293 }
294
295 static void DNS_io_callback(struct ev_loop *loop, ev_io *watcher, int revents)
296 {
297         AsyncIO *IO = watcher->data;
298         
299         ares_process_fd(IO->DNSChannel, IO->dns_io_event.fd, 0);
300 }
301
302 void SockStateCb(void *data, int sock, int read, int write) 
303 {
304         struct timeval tvbuf, maxtv, *ret;
305         
306         int64_t time = 10;
307         AsyncIO *IO = data;
308 /* already inside of the event queue. */        
309
310         if ((read == 0) && (write == 0)) {
311 //              ev_io_stop(event_base, &IO->dns_io_event);
312         } else if (IO->dns_io_event.fd != sock) {
313                 if (IO->dns_io_event.fd != 0) {
314                         ev_io_stop(event_base, &IO->dns_io_event);
315                 }
316                 IO->dns_io_event.fd = sock;
317                 ev_io_init(&IO->dns_io_event, DNS_io_callback, IO->dns_io_event.fd, EV_READ|EV_WRITE);
318                 IO->dns_io_event.data = IO;
319
320                 ev_io_start(event_base, &IO->dns_io_event);
321         
322                 maxtv.tv_sec = time/1000;
323                 maxtv.tv_usec = (time % 1000) * 1000;
324                 
325                 ret = ares_timeout(IO->DNSChannel, &maxtv, &tvbuf);
326         }
327 }
328
329 CTDL_MODULE_INIT(c_ares_client)
330 {
331         if (!threading)
332         {
333                 int optmask = 0;
334                 
335
336                 int r = ares_library_init(ARES_LIB_INIT_ALL);
337                 if (0 != r) {
338                         // TODO
339                         // ThrowException(Exception::Error(String::New(ares_strerror(r))));
340 ////                    assert(r == 0);
341                 }
342
343                 optmask |= ARES_OPT_SOCK_STATE_CB;
344                 memset(&options, 0, sizeof(struct ares_options));
345                 options.sock_state_cb = SockStateCb;
346                 
347                 ares_init_options(&Channel, &options, optmask);
348
349         }
350         return "c-ares";
351 }