2 * Copyright (c) 1998-2017 by the citadel.org team
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.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * Inspired by NodeJS.org; thanks for the MX-Parser ;-)
24 #include <sys/types.h>
27 #if TIME_WITH_SYS_TIME
28 # include <sys/time.h>
32 # include <sys/time.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
45 #include <libcitadel.h>
48 #include "citserver.h"
51 #include "ctdl_module.h"
52 #include "event_client.h"
56 extern struct ev_loop *event_base;
58 void SockStateCb(void *data, int sock, int read, int write);
61 static void HostByAddrCb(void *data,
64 struct hostent *hostent)
68 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
70 EV_DNS_LOGT_STOP(DNS.timeout);
71 ev_timer_stop (event_base, &IO->DNS.timeout);
73 IO->DNS.Query->DNSStatus = status;
74 if (status != ARES_SUCCESS) {
75 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
78 IO->DNS.Query->Data = hostent;
81 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen)
83 struct hostent* host = NULL;
85 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
87 if (IO->DNS.Query->VParsedDNSReply != NULL)
88 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
89 IO->DNS.Query->VParsedDNSReply = NULL;
91 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
96 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
98 ares_free_hostent(host);
99 StrBufPlain(IO->ErrMsg,
100 ares_strerror(IO->DNS.Query->DNSStatus), -1);
103 IO->DNS.Query->VParsedDNSReply = host;
104 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
108 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
110 struct hostent* host = NULL;
112 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
114 if (IO->DNS.Query->VParsedDNSReply != NULL)
115 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
116 IO->DNS.Query->VParsedDNSReply = NULL;
118 IO->DNS.Query->DNSStatus = ares_parse_aaaa_reply(abuf,
123 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
125 ares_free_hostent(host);
126 StrBufPlain(IO->ErrMsg,
127 ares_strerror(IO->DNS.Query->DNSStatus), -1);
130 IO->DNS.Query->VParsedDNSReply = host;
131 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
135 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
137 struct hostent* host = NULL;
139 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
141 if (IO->DNS.Query->VParsedDNSReply != NULL)
142 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
143 IO->DNS.Query->VParsedDNSReply = NULL;
145 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
150 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
152 ares_free_hostent(host);
153 StrBufPlain(IO->ErrMsg,
154 ares_strerror(IO->DNS.Query->DNSStatus), -1);
158 // a CNAME lookup always returns a single record but
159 IO->DNS.Query->VParsedDNSReply = host;
160 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
164 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
166 struct ares_mx_reply *mx_out = NULL;
168 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
170 if (IO->DNS.Query->VParsedDNSReply != NULL)
171 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
172 IO->DNS.Query->VParsedDNSReply = NULL;
174 IO->DNS.Query->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
175 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
177 ares_free_data(mx_out);
178 StrBufPlain(IO->ErrMsg,
179 ares_strerror(IO->DNS.Query->DNSStatus), -1);
183 IO->DNS.Query->VParsedDNSReply = mx_out;
184 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
188 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
190 struct hostent* host = NULL;
192 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
194 if (IO->DNS.Query->VParsedDNSReply != NULL)
195 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
196 IO->DNS.Query->VParsedDNSReply = NULL;
198 IO->DNS.Query->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
199 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
201 ares_free_hostent(host);
202 StrBufPlain(IO->ErrMsg,
203 ares_strerror(IO->DNS.Query->DNSStatus), -1);
206 IO->DNS.Query->VParsedDNSReply = host;
207 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
211 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen)
213 struct ares_srv_reply *srv_out = NULL;
215 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
217 if (IO->DNS.Query->VParsedDNSReply != NULL)
218 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
219 IO->DNS.Query->VParsedDNSReply = NULL;
221 IO->DNS.Query->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
222 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
224 ares_free_data(srv_out);
225 StrBufPlain(IO->ErrMsg,
226 ares_strerror(IO->DNS.Query->DNSStatus), -1);
230 IO->DNS.Query->VParsedDNSReply = srv_out;
231 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
235 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen)
237 struct ares_txt_reply *txt_out;
239 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
241 if (IO->DNS.Query->VParsedDNSReply != NULL)
242 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
243 IO->DNS.Query->VParsedDNSReply = NULL;
245 IO->DNS.Query->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
246 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
248 ares_free_data(txt_out);
249 StrBufPlain(IO->ErrMsg,
250 ares_strerror(IO->DNS.Query->DNSStatus), -1);
253 IO->DNS.Query->VParsedDNSReply = txt_out;
254 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
257 void QueryCb(void *arg,
265 SetEVState(IO, eCaresStart);
266 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
268 EV_DNS_LOGT_STOP(DNS.timeout);
269 ev_timer_stop (event_base, &IO->DNS.timeout);
271 IO->DNS.Query->DNSStatus = status;
272 if (status == ARES_SUCCESS)
273 IO->DNS.Query->DNS_CB(arg, abuf, alen);
275 syslog(LOG_DEBUG, "c-ares: Failed by: %s error %s", __FUNCTION__, ares_strerror(status));
276 StrBufPrintf(IO->ErrMsg,
278 IO->DNS.Query->QueryTYPE,
279 (IO->DNS.Query->QStr != NULL)? IO->DNS.Query->QStr : "",
280 ares_strerror(status));
281 IO->DNS.Query->DNSStatus = status;
284 ev_idle_init(&IO->unwind_stack,
285 IO_postdns_callback);
286 IO->unwind_stack.data = IO;
287 EV_DNS_LOGT_INIT(unwind_stack);
288 EV_DNS_LOGT_START(unwind_stack);
289 ev_idle_start(event_base, &IO->unwind_stack);
292 void QueryCbDone(AsyncIO *IO)
294 SetEVState(IO, eCaresDoneIO);
295 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
297 EV_DNS_LOGT_STOP(DNS.timeout);
298 ev_timer_stop (event_base, &IO->DNS.timeout);
300 EV_DNS_LOGT_STOP(unwind_stack);
301 ev_idle_stop(event_base, &IO->unwind_stack);
304 void DestructCAres(AsyncIO *IO)
306 SetEVState(IO, eCaresX);
307 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
308 syslog(LOG_DEBUG, "c-ares: stopping %s %d %p", "DNS.recv_event", IO->DNS.recv_event.fd, &IO->DNS.recv_event);
309 ev_io_stop(event_base, &IO->DNS.recv_event);
310 syslog(LOG_DEBUG, "c-ares: stopping %s %d %p", "DNS.send_event", IO->DNS.send_event.fd, &IO->DNS.send_event);
311 ev_io_stop(event_base, &IO->DNS.send_event);
312 syslog(LOG_DEBUG, "c-ares: stopping %s %p", "DNS.timeout", &IO->DNS.send_event);
313 ev_timer_stop (event_base, &IO->DNS.timeout);
314 syslog(LOG_DEBUG, "c-ares: stopping %s %p", "DNS.unwind_stack", &IO->unwind_stack);
315 ev_idle_stop(event_base, &IO->unwind_stack);
316 ares_destroy_options(&IO->DNS.Options);
320 void InitC_ares_dns(AsyncIO *IO)
324 syslog(LOG_DEBUG, "c-ares: %s %p", __FUNCTION__, IO->DNS.Channel);
326 if (IO->DNS.Channel == NULL) {
327 optmask |= ARES_OPT_SOCK_STATE_CB;
328 IO->DNS.Options.sock_state_cb = SockStateCb;
329 IO->DNS.Options.sock_state_cb_data = IO;
330 ares_init_options(&IO->DNS.Channel, &IO->DNS.Options, optmask);
332 IO->DNS.Query->DNSStatus = 0;
336 DNStimeouttrigger_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
338 AsyncIO *IO = watcher->data;
339 struct timeval tv, MaxTV;
340 struct timeval *NextTV;
342 memset(&MaxTV, 0, sizeof(MaxTV));
343 memset(&tv, 0, sizeof(tv));
345 NextTV = ares_timeout(IO->DNS.Channel, &MaxTV, &tv);
347 if ((NextTV->tv_sec != MaxTV.tv_sec) ||
348 (NextTV->tv_usec != MaxTV.tv_usec))
350 fd_set readers, writers;
351 syslog(LOG_DEBUG, "c-ares: %s Timeout!", __FUNCTION__);
355 ares_fds(IO->DNS.Channel, &readers, &writers);
356 ares_process(IO->DNS.Channel, &readers, &writers);
360 void QueueGetHostByNameDone(void *Ctx,
363 struct hostent *hostent)
365 AsyncIO *IO = (AsyncIO *) Ctx;
367 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
370 IO->DNS.Query->DNSStatus = status;
371 IO->DNS.Query->VParsedDNSReply = hostent;
372 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
374 EV_DNS_LOGT_STOP(DNS.timeout);
375 ev_timer_stop (event_base, &IO->DNS.timeout);
377 ev_idle_init(&IO->unwind_stack,
378 IO_postdns_callback);
379 IO->unwind_stack.data = IO;
380 EV_DNS_LOGT_INIT(unwind_stack);
381 EV_DNS_LOGT_START(unwind_stack);
382 ev_idle_start(event_base, &IO->unwind_stack);
386 void QueueGetHostByName(AsyncIO *IO,
387 const char *Hostname,
388 DNSQueryParts *QueryParts,
392 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
393 IO->DNS.SourcePort = 0;
395 IO->DNS.Query = QueryParts;
396 IO->DNS.Query->PostDNS = PostDNS;
400 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
401 EV_DNS_LOGT_INIT(DNS.timeout);
402 IO->DNS.timeout.data = IO;
403 ares_gethostbyname(IO->DNS.Channel,
405 AF_INET6, /* it falls back to ipv4 in doubt... */
406 QueueGetHostByNameDone,
408 EV_DNS_LOGT_START(DNS.timeout);
409 ev_timer_start(event_base, &IO->DNS.timeout);
424 int QueueQuery(ns_type Type,
427 DNSQueryParts *QueryParts,
431 char address_b[sizeof(struct in6_addr)];
433 IO->DNS.SourcePort = 0;
435 IO->DNS.Query = QueryParts;
436 IO->DNS.Query->PostDNS = PostDNS;
437 IO->DNS.Start = IO->Now;
438 IO->DNS.Query->QStr = name;
442 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
443 IO->DNS.timeout.data = IO;
444 EV_DNS_LOGT_INIT(DNS.timeout);
448 IO->DNS.Query->DNS_CB = ParseAnswerA;
449 IO->DNS.Query->QueryTYPE = QT[0];
453 IO->DNS.Query->DNS_CB = ParseAnswerAAAA;
454 IO->DNS.Query->QueryTYPE = QT[1];
458 IO->DNS.Query->DNS_CB = ParseAnswerMX;
459 IO->DNS.Query->QueryTYPE = QT[2];
463 IO->DNS.Query->DNS_CB = ParseAnswerNS;
464 IO->DNS.Query->QueryTYPE = QT[3];
468 IO->DNS.Query->DNS_CB = ParseAnswerTXT;
469 IO->DNS.Query->QueryTYPE = QT[4];
473 IO->DNS.Query->DNS_CB = ParseAnswerSRV;
474 IO->DNS.Query->QueryTYPE = QT[5];
478 IO->DNS.Query->DNS_CB = ParseAnswerCNAME;
479 IO->DNS.Query->QueryTYPE = QT[6];
483 IO->DNS.Query->QueryTYPE = QT[7];
484 if (inet_pton(AF_INET, name, &address_b) == 1) {
485 length = sizeof(struct in_addr);
487 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
488 length = sizeof(struct in6_addr);
494 ares_gethostbyaddr(IO->DNS.Channel,
500 EV_DNS_LOGT_START(DNS.timeout);
501 ev_timer_start(event_base, &IO->DNS.timeout);
503 syslog(LOG_DEBUG, "c-ares: %s X1", __FUNCTION__);
508 syslog(LOG_DEBUG, "c-ares: %sX2", __FUNCTION__);
511 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
513 ares_query(IO->DNS.Channel, name, ns_c_in, Type, QueryCb, IO);
514 EV_DNS_LOGT_START(DNS.timeout);
515 ev_timer_start(event_base, &IO->DNS.timeout);
523 /*****************************************************************************
524 * libev / c-ares integration *
525 *****************************************************************************/
526 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
528 AsyncIO *IO = watcher->data;
530 IO->Now = ev_now(event_base);
532 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
534 ares_process_fd(IO->DNS.Channel,
536 IO->DNS.send_event.fd);
538 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
540 AsyncIO *IO = watcher->data;
542 IO->Now = ev_now(event_base);
544 syslog(LOG_DEBUG, "c-ares: %s", __FUNCTION__);
546 ares_process_fd(IO->DNS.Channel,
547 IO->DNS.recv_event.fd,
551 void SockStateCb(void *data, int sock, int read, int write)
554 /* already inside of the event queue. */
557 struct sockaddr_in sin;
559 memset(&sin, 0, sizeof(sin));
561 if ((IO->DNS.SourcePort == 0) &&
562 (getsockname(sock, &sin, &slen) == 0))
564 IO->DNS.SourcePort = ntohs(sin.sin_port);
566 syslog(LOG_DEBUG, "c-ares: %s %d|%d Sock %d port %hu", __FUNCTION__, read, write, sock, IO->DNS.SourcePort);
569 IO->Now = ev_now(event_base);
572 if ((IO->DNS.recv_event.fd != sock) &&
573 (IO->DNS.recv_event.fd != 0)) {
574 EV_DNS_LOG_STOP(DNS.recv_event);
575 ev_io_stop(event_base, &IO->DNS.recv_event);
577 IO->DNS.recv_event.fd = sock;
578 ev_io_init(&IO->DNS.recv_event,
580 IO->DNS.recv_event.fd,
582 EV_DNS_LOG_INIT(DNS.recv_event);
583 IO->DNS.recv_event.data = IO;
584 EV_DNS_LOG_START(DNS.recv_event);
585 ev_io_start(event_base, &IO->DNS.recv_event);
588 if ((IO->DNS.send_event.fd != sock) &&
589 (IO->DNS.send_event.fd != 0)) {
590 EV_DNS_LOG_STOP(DNS.send_event);
591 ev_io_stop(event_base, &IO->DNS.send_event);
593 IO->DNS.send_event.fd = sock;
594 ev_io_init(&IO->DNS.send_event,
596 IO->DNS.send_event.fd,
598 IO->DNS.send_event.data = IO;
599 EV_DNS_LOG_INIT(DNS.send_event);
600 EV_DNS_LOG_START(DNS.send_event);
601 ev_io_start(event_base, &IO->DNS.send_event);
603 if ((read == 0) && (write == 0)) {
604 EV_DNS_LOG_STOP(DNS.recv_event);
605 EV_DNS_LOG_STOP(DNS.send_event);
606 ev_io_stop(event_base, &IO->DNS.recv_event);
607 ev_io_stop(event_base, &IO->DNS.send_event);
610 void EnableDebugCAres(const int n)
615 CTDL_MODULE_INIT(c_ares_client)
619 CtdlRegisterDebugFlagHook(HKEY("cares"), EnableDebugCAres, &DebugCAres);
620 int r = ares_library_init(ARES_LIB_INIT_ALL);