libev/libc-ares migration
[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   c-ares beim connect zum nameserver:   if (channel->sock_create_cb)
69
70   SOCK_STATE_CALLBACK(channel, s, 1, 0); -> void Channel::SockStateCb(void *data, int sock, int read, int write) {
71
72
73
74 lesen der antwort:
75 #0  node::Channel::QueryCb (arg=0x8579268, status=0x0, timeouts=0x0, abuf=0xbffff0ef "\356|\201\200", alen=0x48) at ../src/node_cares.cc:453
76 #1  0x08181884 in qcallback (arg=0x8579278, status=0x857b5a0, timeouts=0x0, abuf=0xbffff0ef "\356|\201\200", alen=0x48) at ../deps/c-ares/ares_query.c:180
77 #2  0x0817fbf5 in end_query (channel=<value optimized out>, query=0x85790b0, status=0x1, abuf=0xbffff0ef "\356|\201\200", alen=0x48) at ../deps/c-ares/ares_process.c:1233
78 #3  0x08180898 in process_answer (channel=<value optimized out>, abuf=<value optimized out>, alen=<value optimized out>, whichserver=0x0, tcp=0x0, now=0xbffff388) at ../deps/c-ares/ares_process.c:612
79 #4  0x08180cf8 in read_udp_packets (channel=<value optimized out>, read_fds=<value optimized out>, read_fd=<value optimized out>, now=0xbffff388) at ../deps/c-ares/ares_process.c:498
80 #5  0x08181021 in processfds (channel=0x85a9888, read_fds=<value optimized out>, read_fd=<value optimized out>, write_fds=0x0, write_fd=0xffffffff) at ../deps/c-ares/ares_process.c:153
81
82 -> 
83 static void ParseAnswerMX(QueryArg *arg, unsigned char* abuf, int alen) {
84   HandleScope scope;
85
86
87
88  */
89
90 /*
91
92 static Local<Array> HostEntToAddresses(struct hostent* hostent) {
93   Local<Array> addresses = Array::New();
94
95
96   char ip[INET6_ADDRSTRLEN];
97   for (int i = 0; hostent->h_addr_list[i]; ++i) {
98     inet_ntop(hostent->h_addrtype, hostent->h_addr_list[i], ip, sizeof(ip));
99
100     Local<String> address = String::New(ip);
101     addresses->Set(Integer::New(i), address);
102   }
103
104   return addresses;
105 }
106
107
108 static Local<Array> HostEntToNames(struct hostent* hostent) {
109   Local<Array> names = Array::New();
110
111   for (int i = 0; hostent->h_aliases[i]; ++i) {
112     Local<String> address = String::New(hostent->h_aliases[i]);
113     names->Set(Integer::New(i), address);
114   }
115
116   return names;
117 }
118
119 static inline const char *ares_errno_string(int errorno) {
120 #define ERRNO_CASE(e)  case ARES_##e: return #e;
121   switch (errorno) {
122     ERRNO_CASE(SUCCESS)
123     ERRNO_CASE(ENODATA)
124     ERRNO_CASE(EFORMERR)
125     ERRNO_CASE(ESERVFAIL)
126     ERRNO_CASE(ENOTFOUND)
127     ERRNO_CASE(ENOTIMP)
128     ERRNO_CASE(EREFUSED)
129     ERRNO_CASE(EBADQUERY)
130     ERRNO_CASE(EBADNAME)
131     ERRNO_CASE(EBADFAMILY)
132     ERRNO_CASE(EBADRESP)
133     ERRNO_CASE(ECONNREFUSED)
134     ERRNO_CASE(ETIMEOUT)
135     ERRNO_CASE(EOF)
136     ERRNO_CASE(EFILE)
137     ERRNO_CASE(ENOMEM)
138     ERRNO_CASE(EDESTRUCTION)
139     ERRNO_CASE(EBADSTR)
140     ERRNO_CASE(EBADFLAGS)
141     ERRNO_CASE(ENONAME)
142     ERRNO_CASE(EBADHINTS)
143     ERRNO_CASE(ENOTINITIALIZED)
144     ERRNO_CASE(ELOADIPHLPAPI)
145     ERRNO_CASE(EADDRGETNETWORKPARAMS)
146     ERRNO_CASE(ECANCELLED)
147     default:
148       assert(0 && "Unhandled c-ares errno");
149       return "(UNKNOWN)";
150   }
151 }
152
153
154 static void ResolveError(Persistent<Function> &cb, int status) {
155   HandleScope scope;
156
157   Local<String> code = String::NewSymbol(ares_errno_string(status));
158   Local<String> message = String::NewSymbol(ares_strerror(status));
159
160   Local<String> cons1 = String::Concat(code, String::NewSymbol(", "));
161   Local<String> cons2 = String::Concat(cons1, message);
162
163   Local<Value> e = Exception::Error(cons2);
164
165   Local<Object> obj = e->ToObject();
166   obj->Set(String::NewSymbol("errno"), Integer::New(status));
167
168   TryCatch try_catch;
169
170   cb->Call(v8::Context::GetCurrent()->Global(), 1, &e);
171
172   if (try_catch.HasCaught()) {
173     FatalException(try_catch);
174   }
175 }
176
177 static void HostByNameCb(void *data,
178                          int status,
179                          int timeouts,
180                          struct hostent *hostent) {
181   HandleScope scope;
182
183   Persistent<Function> *cb = cb_unwrap(data);
184
185   if  (status != ARES_SUCCESS) {
186     ResolveError(*cb, status);
187     cb_destroy(cb);
188     return;
189   }
190
191   TryCatch try_catch;
192
193   Local<Array> addresses = HostEntToAddresses(hostent);
194
195   Local<Value> argv[2] = { Local<Value>::New(Null()), addresses};
196
197   (*cb)->Call(v8::Context::GetCurrent()->Global(), 2, argv);
198
199   if (try_catch.HasCaught()) {
200     FatalException(try_catch);
201   }
202
203   cb_destroy(cb);
204 }
205
206
207
208
209 static void cb_call(Persistent<Function> &cb, int argc, Local<Value> *argv) {
210   TryCatch try_catch;
211
212   cb->Call(v8::Context::GetCurrent()->Global(), argc, argv);
213
214   if (try_catch.HasCaught()) {
215     FatalException(try_catch);
216   }
217 }
218
219 Handle<Value> Channel::GetHostByAddr(const Arguments& args) {
220   HandleScope scope;
221   Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder());
222   assert(c);
223
224   if (!args[0]->IsString()) {
225     return ThrowException(Exception::Error(
226           String::New("First argument must be a address")));
227   }
228
229   if (!args[1]->IsInt32()) {
230     return ThrowException(Exception::Error(
231           String::New("Second argument must be an address family")));
232   }
233
234   if (!args[2]->IsFunction()) {
235     return ThrowException(Exception::Error(
236           String::New("Third argument must be a callback")));
237   }
238
239   int family = args[1]->Int32Value();
240   if (family != AF_INET6 && family != AF_INET) {
241     return ThrowException(Exception::Error(
242           String::New("Unsupported address family")));
243   }
244
245   String::Utf8Value address_s(args[0]->ToString());
246
247   char address_b[sizeof(struct in6_addr)];
248   int r = inet_pton(family, *address_s, address_b);
249   if (r != 1) {
250     return ThrowException(Exception::Error(
251           String::New("Invalid network address")));
252   }
253
254   int length;
255   if (family == AF_INET6)
256     length = sizeof(struct in6_addr);
257   else
258     length = sizeof(struct in_addr);
259
260   ares_gethostbyaddr(c->channel, address_b, length, family, HostByAddrCb, cb_persist(args[2]));
261
262   return Undefined();
263 }
264
265
266
267 Handle<Value> Channel::GetHostByName(const Arguments& args) {
268   HandleScope scope;
269   Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder());
270   assert(c);
271
272   if (!args[0]->IsString()) {
273     return ThrowException(Exception::Error(
274           String::New("First argument must be a name")));
275   }
276
277   if (!args[1]->IsInt32()) {
278     return ThrowException(Exception::Error(
279           String::New("Second argument must be a family")));
280   }
281
282   if (!args[2]->IsFunction()) {
283     return ThrowException(Exception::Error(
284           String::New("Third argument must be a callback")));
285   }
286
287   int family = args[1]->Int32Value();
288   if (family != AF_INET6 && family != AF_INET) {
289     return ThrowException(Exception::Error(
290           String::New("Unsupported address family")));
291   }
292
293   String::Utf8Value name(args[0]->ToString());
294
295   ares_gethostbyname(c->channel, *name, family, HostByNameCb, cb_persist(args[2]));
296
297   return Undefined();
298 }
299
300 */
301
302
303
304 static void HostByAddrCb(void *data,
305                          int status,
306                          int timeouts,
307                          struct hostent *hostent) 
308 {
309         AsyncIO *IO = data;
310         IO->DNSStatus = status;
311         if  (status != ARES_SUCCESS) {
312 //              ResolveError(*cb, status);
313                 return;
314         }
315         IO->Data = hostent;
316 /// TODO: howto free this??
317 }
318
319 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen) 
320 {
321         struct hostent* host;
322
323         if (IO->VParsedDNSReply != NULL)
324                 IO->DNSReplyFree(IO->VParsedDNSReply);
325         IO->VParsedDNSReply = NULL;
326
327         IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
328         if (IO->DNSStatus != ARES_SUCCESS) {
329 //    ResolveError(arg->js_cb, status);
330                 return;
331         }
332         IO->VParsedDNSReply = host;
333         IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
334 }
335
336
337 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen) 
338 {
339         struct hostent* host;
340
341         if (IO->VParsedDNSReply != NULL)
342                 IO->DNSReplyFree(IO->VParsedDNSReply);
343         IO->VParsedDNSReply = NULL;
344
345         IO->DNSStatus = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
346         if (IO->DNSStatus != ARES_SUCCESS) {
347 //    ResolveError(arg->js_cb, status);
348                 return;
349         }
350         IO->VParsedDNSReply = host;
351         IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
352 }
353
354
355 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen) 
356 {
357         struct hostent* host;
358
359         if (IO->VParsedDNSReply != NULL)
360                 IO->DNSReplyFree(IO->VParsedDNSReply);
361         IO->VParsedDNSReply = NULL;
362
363         IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
364         if (IO->DNSStatus != ARES_SUCCESS) {
365 //    ResolveError(arg->js_cb, status);
366                 return;
367         }
368
369         // a CNAME lookup always returns a single record but
370         IO->VParsedDNSReply = host;
371         IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
372 }
373
374
375 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen) 
376 {
377         struct ares_mx_reply *mx_out;
378
379         if (IO->VParsedDNSReply != NULL)
380                 IO->DNSReplyFree(IO->VParsedDNSReply);
381         IO->VParsedDNSReply = NULL;
382
383         IO->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
384         if (IO->DNSStatus != ARES_SUCCESS) {
385 //    ResolveError(arg->js_cb, status);
386                 return;
387         }
388
389         IO->VParsedDNSReply = mx_out;
390         IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
391 }
392
393
394 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen) 
395 {
396         struct hostent* host;
397
398         if (IO->VParsedDNSReply != NULL)
399                 IO->DNSReplyFree(IO->VParsedDNSReply);
400         IO->VParsedDNSReply = NULL;
401
402         IO->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
403         if (IO->DNSStatus != ARES_SUCCESS) {
404 //    ResolveError(arg->js_cb, status);
405                 return;
406         }
407         IO->VParsedDNSReply = host;
408         IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
409 }
410
411
412 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen) 
413 {
414         struct ares_srv_reply *srv_out;
415
416         if (IO->VParsedDNSReply != NULL)
417                 IO->DNSReplyFree(IO->VParsedDNSReply);
418         IO->VParsedDNSReply = NULL;
419
420         IO->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
421         if (IO->DNSStatus != ARES_SUCCESS) {
422 //    ResolveError(arg->js_cb, status);
423                 return;
424         }
425
426         IO->VParsedDNSReply = srv_out;
427         IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
428 }
429
430
431 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen) 
432 {
433         struct ares_txt_reply *txt_out;
434
435         if (IO->VParsedDNSReply != NULL)
436                 IO->DNSReplyFree(IO->VParsedDNSReply);
437         IO->VParsedDNSReply = NULL;
438
439         IO->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
440         if (IO->DNSStatus != ARES_SUCCESS) {
441 //    ResolveError(arg->js_cb, status);
442                 return;
443         }
444         IO->VParsedDNSReply = txt_out;
445         IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
446 }
447
448
449 void QueryCb(void *arg,
450              int status,
451              int timeouts,
452              unsigned char* abuf,
453              int alen) 
454 {
455         AsyncIO *IO = arg;
456
457         IO->DNSStatus = status;
458         if (status == ARES_SUCCESS)
459                 IO->DNS_CB(arg, abuf, alen);
460         IO->PostDNS(IO);
461 }
462
463 int QueueQuery(ns_type Type, char *name, AsyncIO *IO, IO_CallBack PostDNS)
464 {
465         int length, family;
466         char address_b[sizeof(struct in6_addr)];
467         int optmask = 0;
468         int rfd, wfd;
469
470         optmask |= ARES_OPT_SOCK_STATE_CB;
471         IO->DNSOptions.sock_state_cb = SockStateCb;
472         IO->DNSOptions.sock_state_cb_data = IO;
473         ares_init_options(&IO->DNSChannel, &IO->DNSOptions, optmask);
474
475         IO->PostDNS = PostDNS;
476         switch(Type) {
477         case ns_t_a:
478                 IO->DNS_CB = ParseAnswerA;
479                 break;
480
481         case ns_t_aaaa:
482                 IO->DNS_CB = ParseAnswerAAAA;
483                 break;
484
485         case ns_t_mx:
486                 IO->DNS_CB = ParseAnswerMX;
487                 break;
488
489         case ns_t_ns:
490                 IO->DNS_CB = ParseAnswerNS;
491                 break;
492
493         case ns_t_txt:
494                 IO->DNS_CB = ParseAnswerTXT;
495                 break;
496
497         case ns_t_srv:
498                 IO->DNS_CB = ParseAnswerSRV;
499                 break;
500
501         case ns_t_cname:
502                 IO->DNS_CB = ParseAnswerCNAME;
503                 break;
504
505         case ns_t_ptr:
506
507
508                 if (inet_pton(AF_INET, name, &address_b) == 1) {
509                         length = sizeof(struct in_addr);
510                         family = AF_INET;
511                 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
512                         length = sizeof(struct in6_addr);
513                         family = AF_INET6;
514                 } else {
515                         return -1;
516                 }
517
518                 ares_gethostbyaddr(IO->DNSChannel, address_b, length, family, HostByAddrCb, IO);
519
520                 return 1;
521
522         default:
523                 return 0;
524         }
525         ares_query(IO->DNSChannel, name, ns_c_in, Type, QueryCb, IO);
526         ares_fds(IO->DNSChannel, &rfd, &wfd);
527         return 1;
528 }
529
530 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
531 {
532 }
533
534 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
535 {
536 }
537
538 void SockStateCb(void *data, int sock, int read, int write) 
539 {
540         struct timeval tvbuf, maxtv, *ret;
541         
542         int64_t time = 10;
543         AsyncIO *IO = data;
544 /* already inside of the event queue. */        
545         IO->sock = sock;
546         ev_io_init(&IO->recv_event, DNS_recv_callback, IO->sock, EV_READ);
547         IO->recv_event.data = IO;
548         ev_io_init(&IO->send_event, DNS_send_callback, IO->sock, EV_WRITE);
549         IO->send_event.data = IO;
550         if (write)
551                 ev_io_start(event_base, &IO->send_event);
552         else
553                 ev_io_start(event_base, &IO->recv_event);
554         
555
556         maxtv.tv_sec = time/1000;
557         maxtv.tv_usec = (time % 1000) * 1000;
558
559         ret = ares_timeout(IO->DNSChannel, &maxtv, &tvbuf);
560
561         
562 }
563
564 CTDL_MODULE_INIT(c_ares_client)
565 {
566         if (!threading)
567         {
568                 int optmask = 0;
569                 
570
571                 int r = ares_library_init(ARES_LIB_INIT_ALL);
572                 if (0 != r) {
573                         // TODO
574                         // ThrowException(Exception::Error(String::New(ares_strerror(r))));
575 ////                    assert(r == 0);
576                 }
577
578                 optmask |= ARES_OPT_SOCK_STATE_CB;
579                 memset(&options, 0, sizeof(struct ares_options));
580                 options.sock_state_cb = SockStateCb;
581                 
582                 ares_init_options(&Channel, &options, optmask);
583
584         }
585         return "c-ares";
586 }