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)
88 struct hostent* host = NULL;
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) {
104 ares_free_hostent(host);
105 StrBufPlain(IO->ErrMsg,
106 ares_strerror(IO->DNS.Query->DNSStatus), -1);
109 IO->DNS.Query->VParsedDNSReply = host;
110 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
114 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
116 struct hostent* host = NULL;
118 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
121 if (IO->DNS.Query->VParsedDNSReply != NULL)
122 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
123 IO->DNS.Query->VParsedDNSReply = NULL;
125 IO->DNS.Query->DNSStatus = ares_parse_aaaa_reply(abuf,
130 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
132 ares_free_hostent(host);
133 StrBufPlain(IO->ErrMsg,
134 ares_strerror(IO->DNS.Query->DNSStatus), -1);
137 IO->DNS.Query->VParsedDNSReply = host;
138 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
142 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
144 struct hostent* host = NULL;
147 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
150 if (IO->DNS.Query->VParsedDNSReply != NULL)
151 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
152 IO->DNS.Query->VParsedDNSReply = NULL;
154 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
159 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
161 ares_free_hostent(host);
162 StrBufPlain(IO->ErrMsg,
163 ares_strerror(IO->DNS.Query->DNSStatus), -1);
167 // a CNAME lookup always returns a single record but
168 IO->DNS.Query->VParsedDNSReply = host;
169 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
173 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
175 struct ares_mx_reply *mx_out = NULL;
177 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
180 if (IO->DNS.Query->VParsedDNSReply != NULL)
181 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
182 IO->DNS.Query->VParsedDNSReply = NULL;
184 IO->DNS.Query->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
185 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
187 ares_free_data(mx_out);
188 StrBufPlain(IO->ErrMsg,
189 ares_strerror(IO->DNS.Query->DNSStatus), -1);
193 IO->DNS.Query->VParsedDNSReply = mx_out;
194 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
198 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
200 struct hostent* host = NULL;
202 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
205 if (IO->DNS.Query->VParsedDNSReply != NULL)
206 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
207 IO->DNS.Query->VParsedDNSReply = NULL;
209 IO->DNS.Query->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
210 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
212 ares_free_hostent(host);
213 StrBufPlain(IO->ErrMsg,
214 ares_strerror(IO->DNS.Query->DNSStatus), -1);
217 IO->DNS.Query->VParsedDNSReply = host;
218 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
222 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen)
224 struct ares_srv_reply *srv_out = NULL;
226 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
229 if (IO->DNS.Query->VParsedDNSReply != NULL)
230 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
231 IO->DNS.Query->VParsedDNSReply = NULL;
233 IO->DNS.Query->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
234 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
236 ares_free_data(srv_out);
237 StrBufPlain(IO->ErrMsg,
238 ares_strerror(IO->DNS.Query->DNSStatus), -1);
242 IO->DNS.Query->VParsedDNSReply = srv_out;
243 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
247 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen)
249 struct ares_txt_reply *txt_out;
251 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
254 if (IO->DNS.Query->VParsedDNSReply != NULL)
255 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
256 IO->DNS.Query->VParsedDNSReply = NULL;
258 IO->DNS.Query->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
259 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
261 ares_free_data(txt_out);
262 StrBufPlain(IO->ErrMsg,
263 ares_strerror(IO->DNS.Query->DNSStatus), -1);
266 IO->DNS.Query->VParsedDNSReply = txt_out;
267 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
270 void QueryCb(void *arg,
278 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
279 EV_DNS_LOGT_STOP(DNS.timeout);
281 ev_timer_stop (event_base, &IO->DNS.timeout);
283 IO->DNS.Query->DNSStatus = status;
284 if (status == ARES_SUCCESS)
285 IO->DNS.Query->DNS_CB(arg, abuf, alen);
287 EV_syslog(LOG_DEBUG, "C-ARES: Failed by: %s error %s\n",
289 ares_strerror(status));
290 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
291 IO->DNS.Query->DNSStatus = status;
294 ev_idle_init(&IO->unwind_stack,
295 IO_postdns_callback);
296 IO->unwind_stack.data = IO;
297 EV_DNS_LOGT_INIT(unwind_stack);
298 EV_DNS_LOGT_START(unwind_stack);
299 ev_idle_start(event_base, &IO->unwind_stack);
302 void QueryCbDone(AsyncIO *IO)
305 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
306 EV_DNS_LOGT_STOP(DNS.timeout);
309 ev_idle_stop(event_base, &IO->unwind_stack);
312 void DestructCAres(AsyncIO *IO)
315 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
316 EV_DNS_LOGT_STOP(DNS.timeout);
318 EV_DNS_LOG_STOP(DNS.recv_event);
319 ev_io_stop(event_base, &IO->DNS.recv_event);
320 EV_DNS_LOG_STOP(DNS.send_event);
321 ev_io_stop(event_base, &IO->DNS.send_event);
322 ev_timer_stop (event_base, &IO->DNS.timeout);
323 ev_idle_stop(event_base, &IO->unwind_stack);
324 ares_destroy_options(&IO->DNS.Options);
328 void InitC_ares_dns(AsyncIO *IO)
332 EV_syslog(LOG_DEBUG, "C-ARES: %s %p\n", __FUNCTION__, IO->DNS.Channel);
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);
341 IO->DNS.Query->DNSStatus = 0;
345 DNStimeouttrigger_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
347 AsyncIO *IO = watcher->data;
348 struct timeval tv, MaxTV;
349 struct timeval *NextTV;
351 memset(&MaxTV, 0, sizeof(MaxTV));
352 memset(&tv, 0, sizeof(tv));
354 NextTV = ares_timeout(IO->DNS.Channel, &MaxTV, &tv);
356 if ((NextTV->tv_sec != MaxTV.tv_sec) ||
357 (NextTV->tv_usec != MaxTV.tv_usec))
359 fd_set readers, writers;
361 EV_syslog(LOG_DEBUG, "C-ARES: %s Timeout!\n", __FUNCTION__);
365 ares_fds(IO->DNS.Channel, &readers, &writers);
366 ares_process(IO->DNS.Channel, &readers, &writers);
370 void QueueGetHostByNameDone(void *Ctx,
373 struct hostent *hostent)
375 AsyncIO *IO = (AsyncIO *) Ctx;
377 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
380 IO->DNS.Query->DNSStatus = status;
381 IO->DNS.Query->VParsedDNSReply = hostent;
382 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
384 ev_idle_init(&IO->unwind_stack,
385 IO_postdns_callback);
386 IO->unwind_stack.data = IO;
387 EV_DNS_LOGT_INIT(unwind_stack);
388 EV_DNS_LOGT_START(unwind_stack);
389 ev_idle_start(event_base, &IO->unwind_stack);
390 ev_timer_stop (event_base, &IO->DNS.timeout);
393 void QueueGetHostByName(AsyncIO *IO,
394 const char *Hostname,
395 DNSQueryParts *QueryParts,
399 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
400 IO->DNS.SourcePort = 0;
403 IO->DNS.Query = QueryParts;
404 IO->DNS.Query->PostDNS = PostDNS;
408 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
409 EV_DNS_LOGT_INIT(DNS.timeout);
410 IO->DNS.timeout.data = IO;
411 ares_gethostbyname(IO->DNS.Channel,
413 AF_INET6, /* it falls back to ipv4 in doubt... */
414 QueueGetHostByNameDone,
416 EV_DNS_LOGT_START(DNS.timeout);
417 ev_timer_start(event_base, &IO->DNS.timeout);
421 int QueueQuery(ns_type Type,
424 DNSQueryParts *QueryParts,
428 char address_b[sizeof(struct in6_addr)];
431 IO->DNS.SourcePort = 0;
434 IO->DNS.Query = QueryParts;
435 IO->DNS.Query->PostDNS = PostDNS;
436 IO->DNS.Start = IO->Now;
440 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
441 IO->DNS.timeout.data = IO;
442 EV_DNS_LOGT_INIT(DNS.timeout);
446 IO->DNS.Query->DNS_CB = ParseAnswerA;
450 IO->DNS.Query->DNS_CB = ParseAnswerAAAA;
454 IO->DNS.Query->DNS_CB = ParseAnswerMX;
458 IO->DNS.Query->DNS_CB = ParseAnswerNS;
462 IO->DNS.Query->DNS_CB = ParseAnswerTXT;
466 IO->DNS.Query->DNS_CB = ParseAnswerSRV;
470 IO->DNS.Query->DNS_CB = ParseAnswerCNAME;
476 if (inet_pton(AF_INET, name, &address_b) == 1) {
477 length = sizeof(struct in_addr);
479 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
480 length = sizeof(struct in6_addr);
486 ares_gethostbyaddr(IO->DNS.Channel,
492 EV_DNS_LOGT_START(DNS.timeout);
493 ev_timer_start(event_base, &IO->DNS.timeout);
495 EV_syslog(LOG_DEBUG, "C-ARES: %s X1\n", __FUNCTION__);
501 EV_syslog(LOG_DEBUG, "C-ARES: %sX2\n", __FUNCTION__);
506 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
508 ares_query(IO->DNS.Channel, name, ns_c_in, Type, QueryCb, IO);
509 EV_DNS_LOGT_START(DNS.timeout);
510 ev_timer_start(event_base, &IO->DNS.timeout);
518 /*****************************************************************************
519 * libev / c-ares integration *
520 *****************************************************************************/
521 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
523 AsyncIO *IO = watcher->data;
525 IO->Now = ev_now(event_base);
527 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
530 ares_process_fd(IO->DNS.Channel,
532 IO->DNS.send_event.fd);
534 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
536 AsyncIO *IO = watcher->data;
538 IO->Now = ev_now(event_base);
541 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
544 ares_process_fd(IO->DNS.Channel,
545 IO->DNS.recv_event.fd,
549 void SockStateCb(void *data, int sock, int read, int write)
552 /* already inside of the event queue. */
555 struct sockaddr_in sin = {};
558 if ((IO->DNS.SourcePort == 0) &&
559 (getsockname(sock, &sin, &slen) == 0))
561 IO->DNS.SourcePort = ntohs(sin.sin_port);
563 EV_syslog(LOG_DEBUG, "C-ARES: %s %d|%d Sock %d port %hu\n",
571 IO->Now = ev_now(event_base);
574 if ((IO->DNS.recv_event.fd != sock) &&
575 (IO->DNS.recv_event.fd != 0)) {
576 EV_DNS_LOG_STOP(DNS.recv_event);
577 ev_io_stop(event_base, &IO->DNS.recv_event);
579 IO->DNS.recv_event.fd = sock;
580 ev_io_init(&IO->DNS.recv_event,
582 IO->DNS.recv_event.fd,
584 EV_DNS_LOG_INIT(DNS.recv_event);
585 IO->DNS.recv_event.data = IO;
586 EV_DNS_LOG_START(DNS.recv_event);
587 ev_io_start(event_base, &IO->DNS.recv_event);
590 if ((IO->DNS.send_event.fd != sock) &&
591 (IO->DNS.send_event.fd != 0)) {
592 EV_DNS_LOG_STOP(DNS.send_event);
593 ev_io_stop(event_base, &IO->DNS.send_event);
595 IO->DNS.send_event.fd = sock;
596 ev_io_init(&IO->DNS.send_event,
598 IO->DNS.send_event.fd,
600 IO->DNS.send_event.data = IO;
601 EV_DNS_LOG_INIT(DNS.send_event);
602 EV_DNS_LOG_START(DNS.send_event);
603 ev_io_start(event_base, &IO->DNS.send_event);
605 if ((read == 0) && (write == 0)) {
606 EV_DNS_LOG_STOP(DNS.recv_event);
607 EV_DNS_LOG_STOP(DNS.send_event);
608 ev_io_stop(event_base, &IO->DNS.recv_event);
609 ev_io_stop(event_base, &IO->DNS.send_event);
613 CTDL_MODULE_INIT(c_ares_client)
617 int r = ares_library_init(ARES_LIB_INIT_ALL);