2 * Copyright (c) 1998-2012 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.
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.
18 * Inspired by NodeJS.org; thanks for the MX-Parser ;-)
30 #include <sys/types.h>
33 #if TIME_WITH_SYS_TIME
34 # include <sys/time.h>
38 # include <sys/time.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
51 #include <libcitadel.h>
54 #include "citserver.h"
57 #include "ctdl_module.h"
58 #include "event_client.h"
62 extern struct ev_loop *event_base;
64 void SockStateCb(void *data, int sock, int read, int write);
67 static void HostByAddrCb(void *data,
70 struct hostent *hostent)
74 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
75 EV_DNS_LOGT_STOP(DNS.timeout);
77 ev_timer_stop (event_base, &IO->DNS.timeout);
79 IO->DNS.Query->DNSStatus = status;
80 if (status != ARES_SUCCESS) {
81 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
84 IO->DNS.Query->Data = hostent;
87 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen)
89 struct hostent* host = NULL;
91 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
94 if (IO->DNS.Query->VParsedDNSReply != NULL)
95 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
96 IO->DNS.Query->VParsedDNSReply = NULL;
98 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
103 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
105 ares_free_hostent(host);
106 StrBufPlain(IO->ErrMsg,
107 ares_strerror(IO->DNS.Query->DNSStatus), -1);
110 IO->DNS.Query->VParsedDNSReply = host;
111 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
115 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
117 struct hostent* host = NULL;
119 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
122 if (IO->DNS.Query->VParsedDNSReply != NULL)
123 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
124 IO->DNS.Query->VParsedDNSReply = NULL;
126 IO->DNS.Query->DNSStatus = ares_parse_aaaa_reply(abuf,
131 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
133 ares_free_hostent(host);
134 StrBufPlain(IO->ErrMsg,
135 ares_strerror(IO->DNS.Query->DNSStatus), -1);
138 IO->DNS.Query->VParsedDNSReply = host;
139 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
143 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
145 struct hostent* host = NULL;
148 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
151 if (IO->DNS.Query->VParsedDNSReply != NULL)
152 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
153 IO->DNS.Query->VParsedDNSReply = NULL;
155 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
160 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
162 ares_free_hostent(host);
163 StrBufPlain(IO->ErrMsg,
164 ares_strerror(IO->DNS.Query->DNSStatus), -1);
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;
174 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
176 struct ares_mx_reply *mx_out = NULL;
178 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
181 if (IO->DNS.Query->VParsedDNSReply != NULL)
182 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
183 IO->DNS.Query->VParsedDNSReply = NULL;
185 IO->DNS.Query->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
186 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
188 ares_free_data(mx_out);
189 StrBufPlain(IO->ErrMsg,
190 ares_strerror(IO->DNS.Query->DNSStatus), -1);
194 IO->DNS.Query->VParsedDNSReply = mx_out;
195 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
199 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
201 struct hostent* host = NULL;
203 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
206 if (IO->DNS.Query->VParsedDNSReply != NULL)
207 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
208 IO->DNS.Query->VParsedDNSReply = NULL;
210 IO->DNS.Query->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
211 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
213 ares_free_hostent(host);
214 StrBufPlain(IO->ErrMsg,
215 ares_strerror(IO->DNS.Query->DNSStatus), -1);
218 IO->DNS.Query->VParsedDNSReply = host;
219 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
223 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen)
225 struct ares_srv_reply *srv_out = NULL;
227 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
230 if (IO->DNS.Query->VParsedDNSReply != NULL)
231 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
232 IO->DNS.Query->VParsedDNSReply = NULL;
234 IO->DNS.Query->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
235 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
237 ares_free_data(srv_out);
238 StrBufPlain(IO->ErrMsg,
239 ares_strerror(IO->DNS.Query->DNSStatus), -1);
243 IO->DNS.Query->VParsedDNSReply = srv_out;
244 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
248 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen)
250 struct ares_txt_reply *txt_out;
252 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
255 if (IO->DNS.Query->VParsedDNSReply != NULL)
256 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
257 IO->DNS.Query->VParsedDNSReply = NULL;
259 IO->DNS.Query->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
260 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
262 ares_free_data(txt_out);
263 StrBufPlain(IO->ErrMsg,
264 ares_strerror(IO->DNS.Query->DNSStatus), -1);
267 IO->DNS.Query->VParsedDNSReply = txt_out;
268 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
271 void QueryCb(void *arg,
279 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
280 EV_DNS_LOGT_STOP(DNS.timeout);
282 ev_timer_stop (event_base, &IO->DNS.timeout);
284 IO->DNS.Query->DNSStatus = status;
285 if (status == ARES_SUCCESS)
286 IO->DNS.Query->DNS_CB(arg, abuf, alen);
288 EV_syslog(LOG_DEBUG, "C-ARES: Failed by: %s error %s\n",
290 ares_strerror(status));
291 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
292 IO->DNS.Query->DNSStatus = status;
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);
303 void QueryCbDone(AsyncIO *IO)
306 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
307 EV_DNS_LOGT_STOP(DNS.timeout);
310 ev_idle_stop(event_base, &IO->unwind_stack);
313 void DestructCAres(AsyncIO *IO)
316 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
317 EV_DNS_LOGT_STOP(DNS.timeout);
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);
329 void InitC_ares_dns(AsyncIO *IO)
333 EV_syslog(LOG_DEBUG, "C-ARES: %s %p\n", __FUNCTION__, IO->DNS.Channel);
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);
342 IO->DNS.Query->DNSStatus = 0;
346 DNStimeouttrigger_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
348 AsyncIO *IO = watcher->data;
349 struct timeval tv, MaxTV;
350 struct timeval *NextTV;
352 memset(&MaxTV, 0, sizeof(MaxTV));
353 memset(&tv, 0, sizeof(tv));
355 NextTV = ares_timeout(IO->DNS.Channel, &MaxTV, &tv);
357 if ((NextTV->tv_sec != MaxTV.tv_sec) ||
358 (NextTV->tv_usec != MaxTV.tv_usec))
360 fd_set readers, writers;
362 EV_syslog(LOG_DEBUG, "C-ARES: %s Timeout!\n", __FUNCTION__);
366 ares_fds(IO->DNS.Channel, &readers, &writers);
367 ares_process(IO->DNS.Channel, &readers, &writers);
371 void QueueGetHostByNameDone(void *Ctx,
374 struct hostent *hostent)
376 AsyncIO *IO = (AsyncIO *) Ctx;
378 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
381 IO->DNS.Query->DNSStatus = status;
382 IO->DNS.Query->VParsedDNSReply = hostent;
383 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
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);
394 void QueueGetHostByName(AsyncIO *IO,
395 const char *Hostname,
396 DNSQueryParts *QueryParts,
400 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
401 IO->DNS.SourcePort = 0;
404 IO->DNS.Query = QueryParts;
405 IO->DNS.Query->PostDNS = PostDNS;
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,
414 AF_INET6, /* it falls back to ipv4 in doubt... */
415 QueueGetHostByNameDone,
417 EV_DNS_LOGT_START(DNS.timeout);
418 ev_timer_start(event_base, &IO->DNS.timeout);
422 int QueueQuery(ns_type Type,
425 DNSQueryParts *QueryParts,
429 char address_b[sizeof(struct in6_addr)];
432 IO->DNS.SourcePort = 0;
435 IO->DNS.Query = QueryParts;
436 IO->DNS.Query->PostDNS = PostDNS;
437 IO->DNS.Start = IO->Now;
441 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
442 IO->DNS.timeout.data = IO;
443 EV_DNS_LOGT_INIT(DNS.timeout);
447 IO->DNS.Query->DNS_CB = ParseAnswerA;
451 IO->DNS.Query->DNS_CB = ParseAnswerAAAA;
455 IO->DNS.Query->DNS_CB = ParseAnswerMX;
459 IO->DNS.Query->DNS_CB = ParseAnswerNS;
463 IO->DNS.Query->DNS_CB = ParseAnswerTXT;
467 IO->DNS.Query->DNS_CB = ParseAnswerSRV;
471 IO->DNS.Query->DNS_CB = ParseAnswerCNAME;
477 if (inet_pton(AF_INET, name, &address_b) == 1) {
478 length = sizeof(struct in_addr);
480 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
481 length = sizeof(struct in6_addr);
487 ares_gethostbyaddr(IO->DNS.Channel,
493 EV_DNS_LOGT_START(DNS.timeout);
494 ev_timer_start(event_base, &IO->DNS.timeout);
496 EV_syslog(LOG_DEBUG, "C-ARES: %s X1\n", __FUNCTION__);
502 EV_syslog(LOG_DEBUG, "C-ARES: %sX2\n", __FUNCTION__);
507 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
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);
519 /*****************************************************************************
520 * libev / c-ares integration *
521 *****************************************************************************/
522 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
524 AsyncIO *IO = watcher->data;
526 IO->Now = ev_now(event_base);
528 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
531 ares_process_fd(IO->DNS.Channel,
533 IO->DNS.send_event.fd);
535 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
537 AsyncIO *IO = watcher->data;
539 IO->Now = ev_now(event_base);
542 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
545 ares_process_fd(IO->DNS.Channel,
546 IO->DNS.recv_event.fd,
550 void SockStateCb(void *data, int sock, int read, int write)
553 /* already inside of the event queue. */
556 struct sockaddr_in sin = {};
559 if ((IO->DNS.SourcePort == 0) &&
560 (getsockname(sock, &sin, &slen) == 0))
562 IO->DNS.SourcePort = ntohs(sin.sin_port);
564 EV_syslog(LOG_DEBUG, "C-ARES: %s %d|%d Sock %d port %hu\n",
572 IO->Now = ev_now(event_base);
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);
580 IO->DNS.recv_event.fd = sock;
581 ev_io_init(&IO->DNS.recv_event,
583 IO->DNS.recv_event.fd,
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);
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);
596 IO->DNS.send_event.fd = sock;
597 ev_io_init(&IO->DNS.send_event,
599 IO->DNS.send_event.fd,
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);
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);
613 void EnableDebugCAres(void)
618 CTDL_MODULE_INIT(c_ares_client)
622 CtdlRegisterDebugFlagHook(HKEY("cares"), EnableDebugCAres);
623 int r = ares_library_init(ARES_LIB_INIT_ALL);