846720b35a2c3f3513e21570eac1042d9e02f0a4
[citadel.git] / 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         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
272
273         EV_DNS_LOGT_STOP(DNS.timeout);
274         ev_timer_stop (event_base, &IO->DNS.timeout);
275
276         IO->DNS.Query->DNSStatus = status;
277         if (status == ARES_SUCCESS)
278                 IO->DNS.Query->DNS_CB(arg, abuf, alen);
279         else {
280                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: Failed by: %s error %s\n",
281                               __FUNCTION__,
282                               ares_strerror(status));
283                 StrBufPrintf(IO->ErrMsg,
284                              "%s-lookup %s - %s",
285                              IO->DNS.Query->QueryTYPE,
286                              (IO->DNS.Query->QStr != NULL)? IO->DNS.Query->QStr : "",
287                              ares_strerror(status));
288                 IO->DNS.Query->DNSStatus = status;
289         }
290
291         ev_idle_init(&IO->unwind_stack,
292                      IO_postdns_callback);
293         IO->unwind_stack.data = IO;
294         EV_DNS_LOGT_INIT(unwind_stack);
295         EV_DNS_LOGT_START(unwind_stack);
296         ev_idle_start(event_base, &IO->unwind_stack);
297 }
298
299 void QueryCbDone(AsyncIO *IO)
300 {
301         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
302
303         EV_DNS_LOGT_STOP(DNS.timeout);
304         ev_timer_stop (event_base, &IO->DNS.timeout);
305
306         EV_DNS_LOGT_STOP(unwind_stack);
307         ev_idle_stop(event_base, &IO->unwind_stack);
308 }
309
310 void DestructCAres(AsyncIO *IO)
311 {
312         EVNC_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
313
314         EVNC_syslog(LOG_DEBUG, "C-ARES: - stopping %s %d %p \n", "DNS.recv_event", IO->DNS.recv_event.fd, &IO->DNS.recv_event);
315         ev_io_stop(event_base, &IO->DNS.recv_event);
316
317         EVNC_syslog(LOG_DEBUG, "C-ARES: - stopping %s %d %p\n", "DNS.send_event", IO->DNS.send_event.fd, &IO->DNS.send_event);
318         ev_io_stop(event_base, &IO->DNS.send_event);
319
320         EVNC_syslog(LOG_DEBUG, "C-ARES: - stopping %s %p\n", "DNS.timeout", &IO->DNS.send_event);
321         ev_timer_stop (event_base, &IO->DNS.timeout);
322
323         EVNC_syslog(LOG_DEBUG, "C-ARES: - stopping %s %p\n", "DNS.unwind_stack", &IO->unwind_stack);
324         ev_idle_stop(event_base, &IO->unwind_stack);
325         ares_destroy_options(&IO->DNS.Options);
326 }
327
328
329 void InitC_ares_dns(AsyncIO *IO)
330 {
331         int optmask = 0;
332
333         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s %p\n", __FUNCTION__, IO->DNS.Channel);
334
335         if (IO->DNS.Channel == NULL) {
336                 optmask |= ARES_OPT_SOCK_STATE_CB;
337                 IO->DNS.Options.sock_state_cb = SockStateCb;
338                 IO->DNS.Options.sock_state_cb_data = IO;
339                 ares_init_options(&IO->DNS.Channel, &IO->DNS.Options, optmask);
340         }
341         IO->DNS.Query->DNSStatus = 0;
342 }
343
344 static void
345 DNStimeouttrigger_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
346 {
347         AsyncIO *IO = watcher->data;
348         struct timeval tv, MaxTV;
349         struct timeval *NextTV;
350
351         memset(&MaxTV, 0, sizeof(MaxTV));
352         memset(&tv, 0, sizeof(tv));
353         MaxTV.tv_sec = 30;
354         NextTV = ares_timeout(IO->DNS.Channel, &MaxTV, &tv);
355
356         if ((NextTV->tv_sec != MaxTV.tv_sec) ||
357             (NextTV->tv_usec != MaxTV.tv_usec))
358         {
359                 fd_set readers, writers;
360                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s Timeout!\n", __FUNCTION__);
361
362                 FD_ZERO(&readers);
363                 FD_ZERO(&writers);
364                 ares_fds(IO->DNS.Channel, &readers, &writers);
365                 ares_process(IO->DNS.Channel, &readers, &writers);
366         }
367 }
368
369 void QueueGetHostByNameDone(void *Ctx,
370                             int status,
371                             int timeouts,
372                             struct hostent *hostent)
373 {
374         AsyncIO *IO = (AsyncIO *) Ctx;
375
376         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
377
378
379         IO->DNS.Query->DNSStatus = status;
380         IO->DNS.Query->VParsedDNSReply = hostent;
381         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
382
383         EV_DNS_LOGT_STOP(DNS.timeout);
384         ev_timer_stop (event_base, &IO->DNS.timeout);
385
386         ev_idle_init(&IO->unwind_stack,
387                      IO_postdns_callback);
388         IO->unwind_stack.data = IO;
389         EV_DNS_LOGT_INIT(unwind_stack);
390         EV_DNS_LOGT_START(unwind_stack);
391         ev_idle_start(event_base, &IO->unwind_stack);
392
393 }
394
395 void QueueGetHostByName(AsyncIO *IO,
396                         const char *Hostname,
397                         DNSQueryParts *QueryParts,
398                         IO_CallBack PostDNS)
399 {
400
401         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
402         IO->DNS.SourcePort = 0;
403
404         IO->DNS.Query = QueryParts;
405         IO->DNS.Query->PostDNS = PostDNS;
406
407         InitC_ares_dns(IO);
408
409         ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
410         EV_DNS_LOGT_INIT(DNS.timeout);
411         IO->DNS.timeout.data = IO;
412         ares_gethostbyname(IO->DNS.Channel,
413                            Hostname,
414                            AF_INET6, /* it falls back to ipv4 in doubt... */
415                            QueueGetHostByNameDone,
416                            IO);
417         EV_DNS_LOGT_START(DNS.timeout);
418         ev_timer_start(event_base, &IO->DNS.timeout);
419
420 }
421
422 const char* QT[] = {
423         "A",
424         "AAAA",
425         "MX",
426         "NS",
427         "TXT",
428         "SRV",
429         "CNAME",
430         "PTR"
431 };
432
433 int QueueQuery(ns_type Type,
434                const char *name,
435                AsyncIO *IO,
436                DNSQueryParts *QueryParts,
437                IO_CallBack PostDNS)
438 {
439         int length, family;
440         char address_b[sizeof(struct in6_addr)];
441
442         IO->DNS.SourcePort = 0;
443
444         IO->DNS.Query = QueryParts;
445         IO->DNS.Query->PostDNS = PostDNS;
446         IO->DNS.Start = IO->Now;
447         IO->DNS.Query->QStr = name;
448
449         InitC_ares_dns(IO);
450
451         ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
452         IO->DNS.timeout.data = IO;
453         EV_DNS_LOGT_INIT(DNS.timeout);
454
455         switch(Type) {
456         case ns_t_a:
457                 IO->DNS.Query->DNS_CB = ParseAnswerA;
458                 IO->DNS.Query->QueryTYPE = QT[0];
459                 break;
460
461         case ns_t_aaaa:
462                 IO->DNS.Query->DNS_CB = ParseAnswerAAAA;
463                 IO->DNS.Query->QueryTYPE = QT[1];
464                 break;
465
466         case ns_t_mx:
467                 IO->DNS.Query->DNS_CB = ParseAnswerMX;
468                 IO->DNS.Query->QueryTYPE = QT[2];
469                 break;
470
471         case ns_t_ns:
472                 IO->DNS.Query->DNS_CB = ParseAnswerNS;
473                 IO->DNS.Query->QueryTYPE = QT[3];
474                 break;
475
476         case ns_t_txt:
477                 IO->DNS.Query->DNS_CB = ParseAnswerTXT;
478                 IO->DNS.Query->QueryTYPE = QT[4];
479                 break;
480
481         case ns_t_srv:
482                 IO->DNS.Query->DNS_CB = ParseAnswerSRV;
483                 IO->DNS.Query->QueryTYPE = QT[5];
484                 break;
485
486         case ns_t_cname:
487                 IO->DNS.Query->DNS_CB = ParseAnswerCNAME;
488                 IO->DNS.Query->QueryTYPE = QT[6];
489                 break;
490
491         case ns_t_ptr:
492                 IO->DNS.Query->QueryTYPE = QT[7];
493                 if (inet_pton(AF_INET, name, &address_b) == 1) {
494                         length = sizeof(struct in_addr);
495                         family = AF_INET;
496                 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
497                         length = sizeof(struct in6_addr);
498                         family = AF_INET6;
499                 } else {
500                         return -1;
501                 }
502
503                 ares_gethostbyaddr(IO->DNS.Channel,
504                                    address_b,
505                                    length,
506                                    family,
507                                    HostByAddrCb,
508                                    IO);
509                 EV_DNS_LOGT_START(DNS.timeout);
510                 ev_timer_start(event_base, &IO->DNS.timeout);
511
512                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s X1\n", __FUNCTION__);
513
514                 return 1;
515
516         default:
517
518                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: %sX2\n", __FUNCTION__);
519                 return 0;
520         }
521         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
522
523         ares_query(IO->DNS.Channel, name, ns_c_in, Type, QueryCb, IO);
524         EV_DNS_LOGT_START(DNS.timeout);
525         ev_timer_start(event_base, &IO->DNS.timeout);
526         return 1;
527 }
528
529
530
531
532
533 /*****************************************************************************
534  *                      libev / c-ares integration                           *
535  *****************************************************************************/
536 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
537 {
538         AsyncIO *IO = watcher->data;
539
540         IO->Now = ev_now(event_base);
541
542         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
543
544         ares_process_fd(IO->DNS.Channel,
545                         ARES_SOCKET_BAD,
546                         IO->DNS.send_event.fd);
547 }
548 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
549 {
550         AsyncIO *IO = watcher->data;
551
552         IO->Now = ev_now(event_base);
553
554         EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
555
556         ares_process_fd(IO->DNS.Channel,
557                         IO->DNS.recv_event.fd,
558                         ARES_SOCKET_BAD);
559 }
560
561 void SockStateCb(void *data, int sock, int read, int write)
562 {
563         AsyncIO *IO = data;
564 /* already inside of the event queue. */
565         if (DebugCAres)
566         {
567                 struct sockaddr_in sin = {};
568                 socklen_t slen;
569                 slen = sizeof(sin);
570                 if ((IO->DNS.SourcePort == 0) &&
571                     (getsockname(sock, &sin, &slen) == 0))
572                 {
573                         IO->DNS.SourcePort = ntohs(sin.sin_port);
574                 }
575                 EV_DNS_syslog(LOG_DEBUG, "C-ARES: %s %d|%d Sock %d port %hu\n",
576                               __FUNCTION__,
577                               read,
578                               write,
579                               sock,
580                               IO->DNS.SourcePort);
581         }
582
583         IO->Now = ev_now(event_base);
584
585         if (read) {
586                 if ((IO->DNS.recv_event.fd != sock) &&
587                     (IO->DNS.recv_event.fd != 0)) {
588                         EV_DNS_LOG_STOP(DNS.recv_event);
589                         ev_io_stop(event_base, &IO->DNS.recv_event);
590                 }
591                 IO->DNS.recv_event.fd = sock;
592                 ev_io_init(&IO->DNS.recv_event,
593                            DNS_recv_callback,
594                            IO->DNS.recv_event.fd,
595                            EV_READ);
596                 EV_DNS_LOG_INIT(DNS.recv_event);
597                 IO->DNS.recv_event.data = IO;
598                 EV_DNS_LOG_START(DNS.recv_event);
599                 ev_io_start(event_base, &IO->DNS.recv_event);
600         }
601         if (write) {
602                 if ((IO->DNS.send_event.fd != sock) &&
603                     (IO->DNS.send_event.fd != 0)) {
604                         EV_DNS_LOG_STOP(DNS.send_event);
605                         ev_io_stop(event_base, &IO->DNS.send_event);
606                 }
607                 IO->DNS.send_event.fd = sock;
608                 ev_io_init(&IO->DNS.send_event,
609                            DNS_send_callback,
610                            IO->DNS.send_event.fd,
611                            EV_WRITE);
612                 IO->DNS.send_event.data = IO;
613                 EV_DNS_LOG_INIT(DNS.send_event);
614                 EV_DNS_LOG_START(DNS.send_event);
615                 ev_io_start(event_base, &IO->DNS.send_event);
616         }
617         if ((read == 0) && (write == 0)) {
618                 EV_DNS_LOG_STOP(DNS.recv_event);
619                 EV_DNS_LOG_STOP(DNS.send_event);
620                 ev_io_stop(event_base, &IO->DNS.recv_event);
621                 ev_io_stop(event_base, &IO->DNS.send_event);
622         }
623 }
624 void EnableDebugCAres(const int n)
625 {
626         DebugCAres = n;
627 }
628
629 CTDL_MODULE_INIT(c_ares_client)
630 {
631         if (!threading)
632         {
633                 CtdlRegisterDebugFlagHook(HKEY("cares"), EnableDebugCAres, &DebugCAres);
634                 int r = ares_library_init(ARES_LIB_INIT_ALL);
635                 if (0 != r) {
636                         
637                 }
638         }
639         return "c-ares";
640 }