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"
61 extern struct ev_loop *event_base;
63 void SockStateCb(void *data, int sock, int read, int write);
66 static void HostByAddrCb(void *data,
69 struct hostent *hostent)
73 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
74 EV_DNS_LOGT_STOP(DNS.timeout);
76 ev_timer_stop (event_base, &IO->DNS.timeout);
78 IO->DNS.Query->DNSStatus = status;
79 if (status != ARES_SUCCESS) {
80 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
83 IO->DNS.Query->Data = hostent;
86 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen)
90 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
93 if (IO->DNS.Query->VParsedDNSReply != NULL)
94 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
95 IO->DNS.Query->VParsedDNSReply = NULL;
97 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
102 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
103 StrBufPlain(IO->ErrMsg,
104 ares_strerror(IO->DNS.Query->DNSStatus), -1);
107 IO->DNS.Query->VParsedDNSReply = host;
108 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
112 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
114 struct hostent* host;
116 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
119 if (IO->DNS.Query->VParsedDNSReply != NULL)
120 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
121 IO->DNS.Query->VParsedDNSReply = NULL;
123 IO->DNS.Query->DNSStatus = ares_parse_aaaa_reply(abuf,
128 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
129 StrBufPlain(IO->ErrMsg,
130 ares_strerror(IO->DNS.Query->DNSStatus), -1);
133 IO->DNS.Query->VParsedDNSReply = host;
134 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
138 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
140 struct hostent* host;
143 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
146 if (IO->DNS.Query->VParsedDNSReply != NULL)
147 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
148 IO->DNS.Query->VParsedDNSReply = NULL;
150 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
155 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
156 StrBufPlain(IO->ErrMsg,
157 ares_strerror(IO->DNS.Query->DNSStatus), -1);
161 // a CNAME lookup always returns a single record but
162 IO->DNS.Query->VParsedDNSReply = host;
163 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
167 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
169 struct ares_mx_reply *mx_out;
171 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
174 if (IO->DNS.Query->VParsedDNSReply != NULL)
175 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
176 IO->DNS.Query->VParsedDNSReply = NULL;
178 IO->DNS.Query->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
179 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
180 StrBufPlain(IO->ErrMsg,
181 ares_strerror(IO->DNS.Query->DNSStatus), -1);
185 IO->DNS.Query->VParsedDNSReply = mx_out;
186 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
190 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
192 struct hostent* host;
194 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
197 if (IO->DNS.Query->VParsedDNSReply != NULL)
198 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
199 IO->DNS.Query->VParsedDNSReply = NULL;
201 IO->DNS.Query->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
202 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
203 StrBufPlain(IO->ErrMsg,
204 ares_strerror(IO->DNS.Query->DNSStatus), -1);
207 IO->DNS.Query->VParsedDNSReply = host;
208 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
212 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen)
214 struct ares_srv_reply *srv_out;
216 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
219 if (IO->DNS.Query->VParsedDNSReply != NULL)
220 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
221 IO->DNS.Query->VParsedDNSReply = NULL;
223 IO->DNS.Query->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
224 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
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 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
242 if (IO->DNS.Query->VParsedDNSReply != NULL)
243 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
244 IO->DNS.Query->VParsedDNSReply = NULL;
246 IO->DNS.Query->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
247 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
248 StrBufPlain(IO->ErrMsg,
249 ares_strerror(IO->DNS.Query->DNSStatus), -1);
252 IO->DNS.Query->VParsedDNSReply = txt_out;
253 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
256 void QueryCb(void *arg,
264 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
265 EV_DNS_LOGT_STOP(DNS.timeout);
267 ev_timer_stop (event_base, &IO->DNS.timeout);
269 IO->DNS.Query->DNSStatus = status;
270 if (status == ARES_SUCCESS)
271 IO->DNS.Query->DNS_CB(arg, abuf, alen);
273 EV_syslog(LOG_DEBUG, "C-ARES: Failed by: %s error %s\n",
275 ares_strerror(status));
276 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
277 IO->DNS.Query->DNSStatus = status;
280 ev_idle_init(&IO->unwind_stack,
281 IO_postdns_callback);
282 IO->unwind_stack.data = IO;
283 EV_DNS_LOGT_INIT(unwind_stack);
284 EV_DNS_LOGT_START(unwind_stack);
285 ev_idle_start(event_base, &IO->unwind_stack);
288 void QueryCbDone(AsyncIO *IO)
291 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
292 EV_DNS_LOGT_STOP(DNS.timeout);
295 ev_idle_stop(event_base, &IO->unwind_stack);
298 void DestructCAres(AsyncIO *IO)
300 ares_destroy_options(&IO->DNS.Options);
304 void InitC_ares_dns(AsyncIO *IO)
308 EV_syslog(LOG_DEBUG, "C-ARES: %s %p\n", __FUNCTION__, IO->DNS.Channel);
311 if (IO->DNS.Channel == NULL) {
312 optmask |= ARES_OPT_SOCK_STATE_CB;
313 IO->DNS.Options.sock_state_cb = SockStateCb;
314 IO->DNS.Options.sock_state_cb_data = IO;
315 ares_init_options(&IO->DNS.Channel, &IO->DNS.Options, optmask);
317 IO->DNS.Query->DNSStatus = 0;
321 DNStimeouttrigger_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
323 AsyncIO *IO = watcher->data;
324 struct timeval tv, MaxTV;
325 struct timeval *NextTV;
327 memset(&MaxTV, 0, sizeof(MaxTV));
328 memset(&tv, 0, sizeof(tv));
330 NextTV = ares_timeout(IO->DNS.Channel, &MaxTV, &tv);
332 if ((NextTV->tv_sec != MaxTV.tv_sec) ||
333 (NextTV->tv_usec != MaxTV.tv_usec))
335 fd_set readers, writers;
337 EV_syslog(LOG_DEBUG, "C-ARES: %s Timeout!\n", __FUNCTION__);
341 ares_fds(IO->DNS.Channel, &readers, &writers);
342 ares_process(IO->DNS.Channel, &readers, &writers);
346 void QueueGetHostByNameDone(void *Ctx,
349 struct hostent *hostent)
351 AsyncIO *IO = (AsyncIO *) Ctx;
353 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
356 IO->DNS.Query->DNSStatus = status;
357 IO->DNS.Query->VParsedDNSReply = hostent;
358 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
360 ev_idle_init(&IO->unwind_stack,
361 IO_postdns_callback);
362 IO->unwind_stack.data = IO;
363 EV_DNS_LOGT_INIT(unwind_stack);
364 EV_DNS_LOGT_START(unwind_stack);
365 ev_idle_start(event_base, &IO->unwind_stack);
368 void QueueGetHostByName(AsyncIO *IO,
369 const char *Hostname,
370 DNSQueryParts *QueryParts,
374 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
375 IO->DNS.SourcePort = 0;
378 IO->DNS.Query = QueryParts;
379 IO->DNS.Query->PostDNS = PostDNS;
383 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
384 EV_DNS_LOGT_INIT(DNS.timeout);
385 IO->DNS.timeout.data = IO;
386 ares_gethostbyname(IO->DNS.Channel,
388 AF_INET6, /* it falls back to ipv4 in doubt... */
389 QueueGetHostByNameDone,
391 EV_DNS_LOGT_START(DNS.timeout);
392 ev_timer_start(event_base, &IO->DNS.timeout);
396 int QueueQuery(ns_type Type,
399 DNSQueryParts *QueryParts,
403 char address_b[sizeof(struct in6_addr)];
406 IO->DNS.SourcePort = 0;
409 IO->DNS.Query = QueryParts;
410 IO->DNS.Query->PostDNS = PostDNS;
414 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
415 IO->DNS.timeout.data = IO;
416 EV_DNS_LOGT_INIT(DNS.timeout);
420 IO->DNS.Query->DNS_CB = ParseAnswerA;
424 IO->DNS.Query->DNS_CB = ParseAnswerAAAA;
428 IO->DNS.Query->DNS_CB = ParseAnswerMX;
432 IO->DNS.Query->DNS_CB = ParseAnswerNS;
436 IO->DNS.Query->DNS_CB = ParseAnswerTXT;
440 IO->DNS.Query->DNS_CB = ParseAnswerSRV;
444 IO->DNS.Query->DNS_CB = ParseAnswerCNAME;
450 if (inet_pton(AF_INET, name, &address_b) == 1) {
451 length = sizeof(struct in_addr);
453 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
454 length = sizeof(struct in6_addr);
460 ares_gethostbyaddr(IO->DNS.Channel,
466 EV_DNS_LOGT_START(DNS.timeout);
467 ev_timer_start(event_base, &IO->DNS.timeout);
469 EV_syslog(LOG_DEBUG, "C-ARES: %s X1\n", __FUNCTION__);
475 EV_syslog(LOG_DEBUG, "C-ARES: %sX2\n", __FUNCTION__);
480 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
482 ares_query(IO->DNS.Channel, name, ns_c_in, Type, QueryCb, IO);
483 EV_DNS_LOGT_START(DNS.timeout);
484 ev_timer_start(event_base, &IO->DNS.timeout);
492 /*****************************************************************************
493 * libev / c-ares integration *
494 *****************************************************************************/
495 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
497 AsyncIO *IO = watcher->data;
500 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
503 ares_process_fd(IO->DNS.Channel,
505 IO->DNS.send_event.fd);
507 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
509 AsyncIO *IO = watcher->data;
512 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
515 ares_process_fd(IO->DNS.Channel,
516 IO->DNS.recv_event.fd,
520 void SockStateCb(void *data, int sock, int read, int write)
523 /* already inside of the event queue. */
526 struct sockaddr_in sin = {};
529 if ((IO->DNS.SourcePort == 0) &&
530 (getsockname(sock, &sin, &slen) == 0))
532 IO->DNS.SourcePort = ntohs(sin.sin_port);
534 EV_syslog(LOG_DEBUG, "C-ARES: %s %d|%d Sock %d port %hu\n",
544 if ((IO->DNS.recv_event.fd != sock) &&
545 (IO->DNS.recv_event.fd != 0)) {
546 EV_DNS_LOG_STOP(DNS.recv_event);
547 ev_io_stop(event_base, &IO->DNS.recv_event);
549 IO->DNS.recv_event.fd = sock;
550 ev_io_init(&IO->DNS.recv_event,
552 IO->DNS.recv_event.fd,
554 EV_DNS_LOG_INIT(DNS.recv_event);
555 IO->DNS.recv_event.data = IO;
556 EV_DNS_LOG_START(DNS.recv_event);
557 ev_io_start(event_base, &IO->DNS.recv_event);
560 if ((IO->DNS.send_event.fd != sock) &&
561 (IO->DNS.send_event.fd != 0)) {
562 EV_DNS_LOG_STOP(DNS.send_event);
563 ev_io_stop(event_base, &IO->DNS.send_event);
565 IO->DNS.send_event.fd = sock;
566 ev_io_init(&IO->DNS.send_event,
568 IO->DNS.send_event.fd,
570 IO->DNS.send_event.data = IO;
571 EV_DNS_LOG_INIT(DNS.send_event);
572 EV_DNS_LOG_START(DNS.send_event);
573 ev_io_start(event_base, &IO->DNS.send_event);
575 if ((read == 0) && (write == 0)) {
576 EV_DNS_LOG_STOP(DNS.recv_event);
577 EV_DNS_LOG_STOP(DNS.send_event);
578 ev_io_stop(event_base, &IO->DNS.recv_event);
579 ev_io_stop(event_base, &IO->DNS.send_event);
583 CTDL_MODULE_INIT(c_ares_client)
587 int r = ares_library_init(ARES_LIB_INIT_ALL);