ae26751b068da5db78985235d1c61bca1c6e414d
[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
213 void QueryCb(void *arg,
214              int status,
215              int timeouts,
216              unsigned char* abuf,
217              int alen) 
218 {
219         AsyncIO *IO = arg;
220
221         IO->DNSStatus = status;
222         if (status == ARES_SUCCESS)
223                 IO->DNS_CB(arg, abuf, alen);
224         IO->PostDNS(IO);
225 }
226
227 int QueueQuery(ns_type Type, char *name, AsyncIO *IO, IO_CallBack PostDNS)
228 {
229         int length, family;
230         char address_b[sizeof(struct in6_addr)];
231         int optmask = 0;
232         fd_set rfd, wfd;
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         ares_fds(IO->DNSChannel, &rfd, &wfd);
293         return 1;
294 }
295
296 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
297 {
298         AsyncIO *IO = watcher->data;
299         
300         ares_process_fd(IO->DNSChannel, IO->sock, 0);
301 }
302
303 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
304 {
305         AsyncIO *IO = watcher->data;
306         
307         ares_process_fd(IO->DNSChannel, 0, IO->sock);
308 }
309
310 void SockStateCb(void *data, int sock, int read, int write) 
311 {
312         struct timeval tvbuf, maxtv, *ret;
313         
314         int64_t time = 10;
315         AsyncIO *IO = data;
316 /* already inside of the event queue. */        
317         IO->sock = sock;
318         ev_io_init(&IO->recv_event, DNS_recv_callback, IO->sock, EV_READ);
319         IO->recv_event.data = IO;
320         ev_io_init(&IO->send_event, DNS_send_callback, IO->sock, EV_WRITE);
321         IO->send_event.data = IO;
322         if (write)
323                 ev_io_start(event_base, &IO->send_event);
324         else
325                 ev_io_start(event_base, &IO->recv_event);
326         
327
328         maxtv.tv_sec = time/1000;
329         maxtv.tv_usec = (time % 1000) * 1000;
330
331         ret = ares_timeout(IO->DNSChannel, &maxtv, &tvbuf);
332
333         
334 }
335
336 CTDL_MODULE_INIT(c_ares_client)
337 {
338         if (!threading)
339         {
340                 int optmask = 0;
341                 
342
343                 int r = ares_library_init(ARES_LIB_INIT_ALL);
344                 if (0 != r) {
345                         // TODO
346                         // ThrowException(Exception::Error(String::New(ares_strerror(r))));
347 ////                    assert(r == 0);
348                 }
349
350                 optmask |= ARES_OPT_SOCK_STATE_CB;
351                 memset(&options, 0, sizeof(struct ares_options));
352                 options.sock_state_cb = SockStateCb;
353                 
354                 ares_init_options(&Channel, &options, optmask);
355
356         }
357         return "c-ares";
358 }