EV: expose current event queue state to the RWHO command
[citadel] / citadel / modules / c-ares-dns / serv_c-ares-dns.c
1 /*
2  * Copyright (c) 1998-2012 by the citadel.org team
3  *
4  *  This program is open source software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License version 3.
6  *  
7  *  
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  *  
15  *  
16  *  
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 int DebugCAres = 0;
61
62 extern struct ev_loop *event_base;
63
64 void SockStateCb(void *data, int sock, int read, int write);
65
66
67 static void HostByAddrCb(void *data,
68                          int status,
69                          int timeouts,
70                          struct hostent *hostent)
71 {
72         AsyncIO *IO = data;
73
74         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
75
76         EV_DNS_LOGT_STOP(DNS.timeout);
77         ev_timer_stop (event_base, &IO->DNS.timeout);
78
79         IO->DNS.Query->DNSStatus = status;
80         if  (status != ARES_SUCCESS) {
81                 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
82                 return;
83         }
84         IO->DNS.Query->Data = hostent;
85 }
86
87 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen)
88 {
89         struct hostent* host = NULL;
90
91         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
92
93         if (IO->DNS.Query->VParsedDNSReply != NULL)
94                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
95         IO->DNS.Query->VParsedDNSReply = NULL;
96
97         IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
98                                                       alen,
99                                                       &host,
100                                                       NULL,
101                                                       NULL);
102         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
103                 if (host != NULL)
104                         ares_free_hostent(host);
105                 StrBufPlain(IO->ErrMsg,
106                             ares_strerror(IO->DNS.Query->DNSStatus), -1);
107                 return;
108         }
109         IO->DNS.Query->VParsedDNSReply = host;
110         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
111 }
112
113
114 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
115 {
116         struct hostent* host = NULL;
117
118         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
119
120         if (IO->DNS.Query->VParsedDNSReply != NULL)
121                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
122         IO->DNS.Query->VParsedDNSReply = NULL;
123
124         IO->DNS.Query->DNSStatus = ares_parse_aaaa_reply(abuf,
125                                                          alen,
126                                                          &host,
127                                                          NULL,
128                                                          NULL);
129         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
130                 if (host != NULL)
131                         ares_free_hostent(host);
132                 StrBufPlain(IO->ErrMsg,
133                             ares_strerror(IO->DNS.Query->DNSStatus), -1);
134                 return;
135         }
136         IO->DNS.Query->VParsedDNSReply = host;
137         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
138 }
139
140
141 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
142 {
143         struct hostent* host = NULL;
144
145         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
146
147         if (IO->DNS.Query->VParsedDNSReply != NULL)
148                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
149         IO->DNS.Query->VParsedDNSReply = NULL;
150
151         IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
152                                                       alen,
153                                                       &host,
154                                                       NULL,
155                                                       NULL);
156         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
157                 if (host != NULL)
158                         ares_free_hostent(host);
159                 StrBufPlain(IO->ErrMsg,
160                             ares_strerror(IO->DNS.Query->DNSStatus), -1);
161                 return;
162         }
163
164         // a CNAME lookup always returns a single record but
165         IO->DNS.Query->VParsedDNSReply = host;
166         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
167 }
168
169
170 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
171 {
172         struct ares_mx_reply *mx_out = NULL;
173
174         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
175
176         if (IO->DNS.Query->VParsedDNSReply != NULL)
177                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
178         IO->DNS.Query->VParsedDNSReply = NULL;
179
180         IO->DNS.Query->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
181         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
182                 if (mx_out != NULL)
183                         ares_free_data(mx_out);
184                 StrBufPlain(IO->ErrMsg,
185                             ares_strerror(IO->DNS.Query->DNSStatus), -1);
186                 return;
187         }
188
189         IO->DNS.Query->VParsedDNSReply = mx_out;
190         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
191 }
192
193
194 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
195 {
196         struct hostent* host = NULL;
197
198         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
199
200         if (IO->DNS.Query->VParsedDNSReply != NULL)
201                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
202         IO->DNS.Query->VParsedDNSReply = NULL;
203
204         IO->DNS.Query->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
205         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
206                 if (host != NULL)
207                         ares_free_hostent(host);
208                 StrBufPlain(IO->ErrMsg,
209                             ares_strerror(IO->DNS.Query->DNSStatus), -1);
210                 return;
211         }
212         IO->DNS.Query->VParsedDNSReply = host;
213         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
214 }
215
216
217 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen)
218 {
219         struct ares_srv_reply *srv_out = NULL;
220
221         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
222
223         if (IO->DNS.Query->VParsedDNSReply != NULL)
224                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
225         IO->DNS.Query->VParsedDNSReply = NULL;
226
227         IO->DNS.Query->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
228         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
229                 if (srv_out != NULL)
230                         ares_free_data(srv_out);
231                 StrBufPlain(IO->ErrMsg,
232                             ares_strerror(IO->DNS.Query->DNSStatus), -1);
233                 return;
234         }
235
236         IO->DNS.Query->VParsedDNSReply = srv_out;
237         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
238 }
239
240
241 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen)
242 {
243         struct ares_txt_reply *txt_out;
244
245         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
246
247         if (IO->DNS.Query->VParsedDNSReply != NULL)
248                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
249         IO->DNS.Query->VParsedDNSReply = NULL;
250
251         IO->DNS.Query->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
252         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
253                 if (txt_out != NULL)
254                         ares_free_data(txt_out);
255                 StrBufPlain(IO->ErrMsg,
256                             ares_strerror(IO->DNS.Query->DNSStatus), -1);
257                 return;
258         }
259         IO->DNS.Query->VParsedDNSReply = txt_out;
260         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
261 }
262
263 void QueryCb(void *arg,
264              int status,
265              int timeouts,
266              unsigned char* abuf,
267              int alen)
268 {
269         AsyncIO *IO = arg;
270
271         SetEVState(IO, eCaresStart);
272         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
273
274         EV_DNS_LOGT_STOP(DNS.timeout);
275         ev_timer_stop (event_base, &IO->DNS.timeout);
276
277         IO->DNS.Query->DNSStatus = status;
278         if (status == ARES_SUCCESS)
279                 IO->DNS.Query->DNS_CB(arg, abuf, alen);
280         else {
281                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: Failed by: %s error %s\n",
282                               __FUNCTION__,
283                               ares_strerror(status));
284                 StrBufPrintf(IO->ErrMsg,
285                              "%s-lookup %s - %s",
286                              IO->DNS.Query->QueryTYPE,
287                              (IO->DNS.Query->QStr != NULL)? IO->DNS.Query->QStr : "",
288                              ares_strerror(status));
289                 IO->DNS.Query->DNSStatus = status;
290         }
291
292         ev_idle_init(&IO->unwind_stack,
293                      IO_postdns_callback);
294         IO->unwind_stack.data = IO;
295         EV_DNS_LOGT_INIT(unwind_stack);
296         EV_DNS_LOGT_START(unwind_stack);
297         ev_idle_start(event_base, &IO->unwind_stack);
298 }
299
300 void QueryCbDone(AsyncIO *IO)
301 {
302         SetEVState(IO, eCaresDoneIO);
303         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
304
305         EV_DNS_LOGT_STOP(DNS.timeout);
306         ev_timer_stop (event_base, &IO->DNS.timeout);
307
308         EV_DNS_LOGT_STOP(unwind_stack);
309         ev_idle_stop(event_base, &IO->unwind_stack);
310 }
311
312 void DestructCAres(AsyncIO *IO)
313 {
314         SetEVState(IO, eCaresX);
315         EVNC_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
316
317         EVNC_syslog(LOG_DEBUG, "C-ARES: - stopping %s %d %p \n", "DNS.recv_event", IO->DNS.recv_event.fd, &IO->DNS.recv_event);
318         ev_io_stop(event_base, &IO->DNS.recv_event);
319
320         EVNC_syslog(LOG_DEBUG, "C-ARES: - stopping %s %d %p\n", "DNS.send_event", IO->DNS.send_event.fd, &IO->DNS.send_event);
321         ev_io_stop(event_base, &IO->DNS.send_event);
322
323         EVNC_syslog(LOG_DEBUG, "C-ARES: - stopping %s %p\n", "DNS.timeout", &IO->DNS.send_event);
324         ev_timer_stop (event_base, &IO->DNS.timeout);
325
326         EVNC_syslog(LOG_DEBUG, "C-ARES: - stopping %s %p\n", "DNS.unwind_stack", &IO->unwind_stack);
327         ev_idle_stop(event_base, &IO->unwind_stack);
328         ares_destroy_options(&IO->DNS.Options);
329 }
330
331
332 void InitC_ares_dns(AsyncIO *IO)
333 {
334         int optmask = 0;
335
336         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s %p\n", __FUNCTION__, IO->DNS.Channel);
337
338         if (IO->DNS.Channel == NULL) {
339                 optmask |= ARES_OPT_SOCK_STATE_CB;
340                 IO->DNS.Options.sock_state_cb = SockStateCb;
341                 IO->DNS.Options.sock_state_cb_data = IO;
342                 ares_init_options(&IO->DNS.Channel, &IO->DNS.Options, optmask);
343         }
344         IO->DNS.Query->DNSStatus = 0;
345 }
346
347 static void
348 DNStimeouttrigger_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
349 {
350         AsyncIO *IO = watcher->data;
351         struct timeval tv, MaxTV;
352         struct timeval *NextTV;
353
354         memset(&MaxTV, 0, sizeof(MaxTV));
355         memset(&tv, 0, sizeof(tv));
356         MaxTV.tv_sec = 30;
357         NextTV = ares_timeout(IO->DNS.Channel, &MaxTV, &tv);
358
359         if ((NextTV->tv_sec != MaxTV.tv_sec) ||
360             (NextTV->tv_usec != MaxTV.tv_usec))
361         {
362                 fd_set readers, writers;
363                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s Timeout!\n", __FUNCTION__);
364
365                 FD_ZERO(&readers);
366                 FD_ZERO(&writers);
367                 ares_fds(IO->DNS.Channel, &readers, &writers);
368                 ares_process(IO->DNS.Channel, &readers, &writers);
369         }
370 }
371
372 void QueueGetHostByNameDone(void *Ctx,
373                             int status,
374                             int timeouts,
375                             struct hostent *hostent)
376 {
377         AsyncIO *IO = (AsyncIO *) Ctx;
378
379         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
380
381
382         IO->DNS.Query->DNSStatus = status;
383         IO->DNS.Query->VParsedDNSReply = hostent;
384         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
385
386         EV_DNS_LOGT_STOP(DNS.timeout);
387         ev_timer_stop (event_base, &IO->DNS.timeout);
388
389         ev_idle_init(&IO->unwind_stack,
390                      IO_postdns_callback);
391         IO->unwind_stack.data = IO;
392         EV_DNS_LOGT_INIT(unwind_stack);
393         EV_DNS_LOGT_START(unwind_stack);
394         ev_idle_start(event_base, &IO->unwind_stack);
395
396 }
397
398 void QueueGetHostByName(AsyncIO *IO,
399                         const char *Hostname,
400                         DNSQueryParts *QueryParts,
401                         IO_CallBack PostDNS)
402 {
403
404         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
405         IO->DNS.SourcePort = 0;
406
407         IO->DNS.Query = QueryParts;
408         IO->DNS.Query->PostDNS = PostDNS;
409
410         InitC_ares_dns(IO);
411
412         ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
413         EV_DNS_LOGT_INIT(DNS.timeout);
414         IO->DNS.timeout.data = IO;
415         ares_gethostbyname(IO->DNS.Channel,
416                            Hostname,
417                            AF_INET6, /* it falls back to ipv4 in doubt... */
418                            QueueGetHostByNameDone,
419                            IO);
420         EV_DNS_LOGT_START(DNS.timeout);
421         ev_timer_start(event_base, &IO->DNS.timeout);
422
423 }
424
425 const char* QT[] = {
426         "A",
427         "AAAA",
428         "MX",
429         "NS",
430         "TXT",
431         "SRV",
432         "CNAME",
433         "PTR"
434 };
435
436 int QueueQuery(ns_type Type,
437                const char *name,
438                AsyncIO *IO,
439                DNSQueryParts *QueryParts,
440                IO_CallBack PostDNS)
441 {
442         int length, family;
443         char address_b[sizeof(struct in6_addr)];
444
445         IO->DNS.SourcePort = 0;
446
447         IO->DNS.Query = QueryParts;
448         IO->DNS.Query->PostDNS = PostDNS;
449         IO->DNS.Start = IO->Now;
450         IO->DNS.Query->QStr = name;
451
452         InitC_ares_dns(IO);
453
454         ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
455         IO->DNS.timeout.data = IO;
456         EV_DNS_LOGT_INIT(DNS.timeout);
457
458         switch(Type) {
459         case ns_t_a:
460                 IO->DNS.Query->DNS_CB = ParseAnswerA;
461                 IO->DNS.Query->QueryTYPE = QT[0];
462                 break;
463
464         case ns_t_aaaa:
465                 IO->DNS.Query->DNS_CB = ParseAnswerAAAA;
466                 IO->DNS.Query->QueryTYPE = QT[1];
467                 break;
468
469         case ns_t_mx:
470                 IO->DNS.Query->DNS_CB = ParseAnswerMX;
471                 IO->DNS.Query->QueryTYPE = QT[2];
472                 break;
473
474         case ns_t_ns:
475                 IO->DNS.Query->DNS_CB = ParseAnswerNS;
476                 IO->DNS.Query->QueryTYPE = QT[3];
477                 break;
478
479         case ns_t_txt:
480                 IO->DNS.Query->DNS_CB = ParseAnswerTXT;
481                 IO->DNS.Query->QueryTYPE = QT[4];
482                 break;
483
484         case ns_t_srv:
485                 IO->DNS.Query->DNS_CB = ParseAnswerSRV;
486                 IO->DNS.Query->QueryTYPE = QT[5];
487                 break;
488
489         case ns_t_cname:
490                 IO->DNS.Query->DNS_CB = ParseAnswerCNAME;
491                 IO->DNS.Query->QueryTYPE = QT[6];
492                 break;
493
494         case ns_t_ptr:
495                 IO->DNS.Query->QueryTYPE = QT[7];
496                 if (inet_pton(AF_INET, name, &address_b) == 1) {
497                         length = sizeof(struct in_addr);
498                         family = AF_INET;
499                 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
500                         length = sizeof(struct in6_addr);
501                         family = AF_INET6;
502                 } else {
503                         return -1;
504                 }
505
506                 ares_gethostbyaddr(IO->DNS.Channel,
507                                    address_b,
508                                    length,
509                                    family,
510                                    HostByAddrCb,
511                                    IO);
512                 EV_DNS_LOGT_START(DNS.timeout);
513                 ev_timer_start(event_base, &IO->DNS.timeout);
514
515                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s X1\n", __FUNCTION__);
516
517                 return 1;
518
519         default:
520
521                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: %sX2\n", __FUNCTION__);
522                 return 0;
523         }
524         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
525
526         ares_query(IO->DNS.Channel, name, ns_c_in, Type, QueryCb, IO);
527         EV_DNS_LOGT_START(DNS.timeout);
528         ev_timer_start(event_base, &IO->DNS.timeout);
529         return 1;
530 }
531
532
533
534
535
536 /*****************************************************************************
537  *                      libev / c-ares integration                           *
538  *****************************************************************************/
539 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
540 {
541         AsyncIO *IO = watcher->data;
542
543         IO->Now = ev_now(event_base);
544
545         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
546
547         ares_process_fd(IO->DNS.Channel,
548                         ARES_SOCKET_BAD,
549                         IO->DNS.send_event.fd);
550 }
551 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
552 {
553         AsyncIO *IO = watcher->data;
554
555         IO->Now = ev_now(event_base);
556
557         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
558
559         ares_process_fd(IO->DNS.Channel,
560                         IO->DNS.recv_event.fd,
561                         ARES_SOCKET_BAD);
562 }
563
564 void SockStateCb(void *data, int sock, int read, int write)
565 {
566         AsyncIO *IO = data;
567 /* already inside of the event queue. */
568         if (DebugCAres)
569         {
570                 struct sockaddr_in sin = {};
571                 socklen_t slen;
572                 slen = sizeof(sin);
573                 if ((IO->DNS.SourcePort == 0) &&
574                     (getsockname(sock, &sin, &slen) == 0))
575                 {
576                         IO->DNS.SourcePort = ntohs(sin.sin_port);
577                 }
578                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s %d|%d Sock %d port %hu\n",
579                               __FUNCTION__,
580                               read,
581                               write,
582                               sock,
583                               IO->DNS.SourcePort);
584         }
585
586         IO->Now = ev_now(event_base);
587
588         if (read) {
589                 if ((IO->DNS.recv_event.fd != sock) &&
590                     (IO->DNS.recv_event.fd != 0)) {
591                         EV_DNS_LOG_STOP(DNS.recv_event);
592                         ev_io_stop(event_base, &IO->DNS.recv_event);
593                 }
594                 IO->DNS.recv_event.fd = sock;
595                 ev_io_init(&IO->DNS.recv_event,
596                            DNS_recv_callback,
597                            IO->DNS.recv_event.fd,
598                            EV_READ);
599                 EV_DNS_LOG_INIT(DNS.recv_event);
600                 IO->DNS.recv_event.data = IO;
601                 EV_DNS_LOG_START(DNS.recv_event);
602                 ev_io_start(event_base, &IO->DNS.recv_event);
603         }
604         if (write) {
605                 if ((IO->DNS.send_event.fd != sock) &&
606                     (IO->DNS.send_event.fd != 0)) {
607                         EV_DNS_LOG_STOP(DNS.send_event);
608                         ev_io_stop(event_base, &IO->DNS.send_event);
609                 }
610                 IO->DNS.send_event.fd = sock;
611                 ev_io_init(&IO->DNS.send_event,
612                            DNS_send_callback,
613                            IO->DNS.send_event.fd,
614                            EV_WRITE);
615                 IO->DNS.send_event.data = IO;
616                 EV_DNS_LOG_INIT(DNS.send_event);
617                 EV_DNS_LOG_START(DNS.send_event);
618                 ev_io_start(event_base, &IO->DNS.send_event);
619         }
620         if ((read == 0) && (write == 0)) {
621                 EV_DNS_LOG_STOP(DNS.recv_event);
622                 EV_DNS_LOG_STOP(DNS.send_event);
623                 ev_io_stop(event_base, &IO->DNS.recv_event);
624                 ev_io_stop(event_base, &IO->DNS.send_event);
625         }
626 }
627 void EnableDebugCAres(const int n)
628 {
629         DebugCAres = n;
630 }
631
632 CTDL_MODULE_INIT(c_ares_client)
633 {
634         if (!threading)
635         {
636                 CtdlRegisterDebugFlagHook(HKEY("cares"), EnableDebugCAres, &DebugCAres);
637                 int r = ares_library_init(ARES_LIB_INIT_ALL);
638                 if (0 != r) {
639                         
640                 }
641         }
642         return "c-ares";
643 }