2 * Copyright (c) 1998-2009 by the citadel.org team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
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.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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__);
75 ev_timer_stop (event_base, &IO->DNS.timeout);
77 IO->DNS.Query->DNSStatus = status;
78 if (status != ARES_SUCCESS) {
79 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
82 IO->DNS.Query->Data = hostent;
85 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen)
89 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
92 if (IO->DNS.Query->VParsedDNSReply != NULL)
93 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
94 IO->DNS.Query->VParsedDNSReply = NULL;
96 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
101 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
102 StrBufPlain(IO->ErrMsg,
103 ares_strerror(IO->DNS.Query->DNSStatus), -1);
106 IO->DNS.Query->VParsedDNSReply = host;
107 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
111 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
113 struct hostent* host;
115 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
118 if (IO->DNS.Query->VParsedDNSReply != NULL)
119 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
120 IO->DNS.Query->VParsedDNSReply = NULL;
122 IO->DNS.Query->DNSStatus = ares_parse_aaaa_reply(abuf,
127 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
128 StrBufPlain(IO->ErrMsg,
129 ares_strerror(IO->DNS.Query->DNSStatus), -1);
132 IO->DNS.Query->VParsedDNSReply = host;
133 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
137 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
139 struct hostent* host;
142 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
145 if (IO->DNS.Query->VParsedDNSReply != NULL)
146 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
147 IO->DNS.Query->VParsedDNSReply = NULL;
149 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf,
154 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
155 StrBufPlain(IO->ErrMsg,
156 ares_strerror(IO->DNS.Query->DNSStatus), -1);
160 // a CNAME lookup always returns a single record but
161 IO->DNS.Query->VParsedDNSReply = host;
162 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
166 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
168 struct ares_mx_reply *mx_out;
170 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
173 if (IO->DNS.Query->VParsedDNSReply != NULL)
174 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
175 IO->DNS.Query->VParsedDNSReply = NULL;
177 IO->DNS.Query->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
178 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
179 StrBufPlain(IO->ErrMsg,
180 ares_strerror(IO->DNS.Query->DNSStatus), -1);
184 IO->DNS.Query->VParsedDNSReply = mx_out;
185 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
189 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
191 struct hostent* host;
193 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
196 if (IO->DNS.Query->VParsedDNSReply != NULL)
197 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
198 IO->DNS.Query->VParsedDNSReply = NULL;
200 IO->DNS.Query->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
201 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
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;
215 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
218 if (IO->DNS.Query->VParsedDNSReply != NULL)
219 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
220 IO->DNS.Query->VParsedDNSReply = NULL;
222 IO->DNS.Query->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
223 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
224 StrBufPlain(IO->ErrMsg,
225 ares_strerror(IO->DNS.Query->DNSStatus), -1);
229 IO->DNS.Query->VParsedDNSReply = srv_out;
230 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
234 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen)
236 struct ares_txt_reply *txt_out;
238 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __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) {
247 StrBufPlain(IO->ErrMsg,
248 ares_strerror(IO->DNS.Query->DNSStatus), -1);
251 IO->DNS.Query->VParsedDNSReply = txt_out;
252 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
255 void QueryCb(void *arg,
263 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
265 ev_timer_stop (event_base, &IO->DNS.timeout);
267 IO->DNS.Query->DNSStatus = status;
268 if (status == ARES_SUCCESS)
269 IO->DNS.Query->DNS_CB(arg, abuf, alen);
271 EV_syslog(LOG_DEBUG, "C-ARES: Failed by: %s error %s\n",
273 ares_strerror(status));
274 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
275 IO->DNS.Query->DNSStatus = status;
278 ev_idle_init(&IO->unwind_stack,
279 IO_postdns_callback);
280 IO->unwind_stack.data = IO;
281 ev_idle_start(event_base, &IO->unwind_stack);
284 void QueryCbDone(AsyncIO *IO)
287 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
290 ev_idle_stop(event_base, &IO->unwind_stack);
293 void DestructCAres(AsyncIO *IO)
295 ares_destroy_options(&IO->DNS.Options);
299 void InitC_ares_dns(AsyncIO *IO)
303 EV_syslog(LOG_DEBUG, "C-ARES: %s %p\n", __FUNCTION__, IO->DNS.Channel);
306 if (IO->DNS.Channel == NULL) {
307 optmask |= ARES_OPT_SOCK_STATE_CB;
308 IO->DNS.Options.sock_state_cb = SockStateCb;
309 IO->DNS.Options.sock_state_cb_data = IO;
310 ares_init_options(&IO->DNS.Channel, &IO->DNS.Options, optmask);
312 IO->DNS.Query->DNSStatus = 0;
316 DNStimeouttrigger_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
318 AsyncIO *IO = watcher->data;
319 struct timeval tv, MaxTV;
320 struct timeval *NextTV;
322 memset(&MaxTV, 0, sizeof(MaxTV));
323 memset(&tv, 0, sizeof(tv));
325 NextTV = ares_timeout(IO->DNS.Channel, &MaxTV, &tv);
327 if ((NextTV->tv_sec != MaxTV.tv_sec) ||
328 (NextTV->tv_usec != MaxTV.tv_usec))
330 fd_set readers, writers;
332 EV_syslog(LOG_DEBUG, "C-ARES: %s Timeout!\n", __FUNCTION__);
336 ares_fds(IO->DNS.Channel, &readers, &writers);
337 ares_process(IO->DNS.Channel, &readers, &writers);
341 void QueueGetHostByNameDone(void *Ctx,
344 struct hostent *hostent)
346 AsyncIO *IO = (AsyncIO *) Ctx;
348 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
351 IO->DNS.Query->DNSStatus = status;
352 IO->DNS.Query->VParsedDNSReply = hostent;
353 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
355 ev_idle_init(&IO->unwind_stack,
356 IO_postdns_callback);
357 IO->unwind_stack.data = IO;
358 ev_idle_start(event_base, &IO->unwind_stack);
361 void QueueGetHostByName(AsyncIO *IO,
362 const char *Hostname,
363 DNSQueryParts *QueryParts,
367 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
368 IO->DNS.SourcePort = 0;
371 IO->DNS.Query = QueryParts;
372 IO->DNS.Query->PostDNS = PostDNS;
376 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
377 IO->DNS.timeout.data = IO;
378 ares_gethostbyname(IO->DNS.Channel,
380 AF_INET6, /* it falls back to ipv4 in doubt... */
381 QueueGetHostByNameDone,
383 ev_timer_start(event_base, &IO->DNS.timeout);
387 int QueueQuery(ns_type Type,
390 DNSQueryParts *QueryParts,
394 char address_b[sizeof(struct in6_addr)];
397 IO->DNS.SourcePort = 0;
400 IO->DNS.Query = QueryParts;
401 IO->DNS.Query->PostDNS = PostDNS;
405 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
406 IO->DNS.timeout.data = IO;
410 IO->DNS.Query->DNS_CB = ParseAnswerA;
414 IO->DNS.Query->DNS_CB = ParseAnswerAAAA;
418 IO->DNS.Query->DNS_CB = ParseAnswerMX;
422 IO->DNS.Query->DNS_CB = ParseAnswerNS;
426 IO->DNS.Query->DNS_CB = ParseAnswerTXT;
430 IO->DNS.Query->DNS_CB = ParseAnswerSRV;
434 IO->DNS.Query->DNS_CB = ParseAnswerCNAME;
440 if (inet_pton(AF_INET, name, &address_b) == 1) {
441 length = sizeof(struct in_addr);
443 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
444 length = sizeof(struct in6_addr);
450 ares_gethostbyaddr(IO->DNS.Channel,
456 ev_timer_start(event_base, &IO->DNS.timeout);
458 EV_syslog(LOG_DEBUG, "C-ARES: %s X1\n", __FUNCTION__);
464 EV_syslog(LOG_DEBUG, "C-ARES: %sX2\n", __FUNCTION__);
469 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
471 ares_query(IO->DNS.Channel, name, ns_c_in, Type, QueryCb, IO);
472 ev_timer_start(event_base, &IO->DNS.timeout);
480 /*****************************************************************************
481 * libev / c-ares integration *
482 *****************************************************************************/
483 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
485 AsyncIO *IO = watcher->data;
488 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
491 ares_process_fd(IO->DNS.Channel,
493 IO->DNS.send_event.fd);
495 static void DNS_recv_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,
504 IO->DNS.recv_event.fd,
508 void SockStateCb(void *data, int sock, int read, int write)
511 /* already inside of the event queue. */
514 struct sockaddr_in sin = {};
517 if ((IO->DNS.SourcePort == 0) &&
518 (getsockname(sock, &sin, &slen) == 0))
520 IO->DNS.SourcePort = ntohs(sin.sin_port);
522 EV_syslog(LOG_DEBUG, "C-ARES: %s %d|%d Sock %d port %hu\n",
532 if ((IO->DNS.recv_event.fd != sock) &&
533 (IO->DNS.recv_event.fd != 0)) {
534 ev_io_stop(event_base, &IO->DNS.recv_event);
536 IO->DNS.recv_event.fd = sock;
537 ev_io_init(&IO->DNS.recv_event,
539 IO->DNS.recv_event.fd,
541 IO->DNS.recv_event.data = IO;
542 ev_io_start(event_base, &IO->DNS.recv_event);
545 if ((IO->DNS.send_event.fd != sock) &&
546 (IO->DNS.send_event.fd != 0)) {
547 ev_io_stop(event_base, &IO->DNS.send_event);
549 IO->DNS.send_event.fd = sock;
550 ev_io_init(&IO->DNS.send_event,
552 IO->DNS.send_event.fd,
554 IO->DNS.send_event.data = IO;
555 ev_io_start(event_base, &IO->DNS.send_event);
557 if ((read == 0) && (write == 0)) {
558 ev_io_stop(event_base, &IO->DNS.recv_event);
559 ev_io_stop(event_base, &IO->DNS.send_event);
563 CTDL_MODULE_INIT(c_ares_client)
567 int r = ares_library_init(ARES_LIB_INIT_ALL);