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