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;
83 /// TODO: howto free this??
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, alen, &host, NULL, NULL);
98 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
99 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
102 IO->DNS.Query->VParsedDNSReply = host;
103 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
107 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
109 struct hostent* host;
111 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __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, alen, &host, NULL, NULL);
119 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
120 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
123 IO->DNS.Query->VParsedDNSReply = host;
124 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
128 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
130 struct hostent* host;
133 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
136 if (IO->DNS.Query->VParsedDNSReply != NULL)
137 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
138 IO->DNS.Query->VParsedDNSReply = NULL;
140 IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
141 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
142 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
146 // a CNAME lookup always returns a single record but
147 IO->DNS.Query->VParsedDNSReply = host;
148 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
152 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
154 struct ares_mx_reply *mx_out;
156 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
159 if (IO->DNS.Query->VParsedDNSReply != NULL)
160 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
161 IO->DNS.Query->VParsedDNSReply = NULL;
163 IO->DNS.Query->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
164 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
165 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
169 IO->DNS.Query->VParsedDNSReply = mx_out;
170 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
174 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
176 struct hostent* host;
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_ns_reply(abuf, alen, &host);
186 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
187 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
190 IO->DNS.Query->VParsedDNSReply = host;
191 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
195 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen)
197 struct ares_srv_reply *srv_out;
199 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
202 if (IO->DNS.Query->VParsedDNSReply != NULL)
203 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
204 IO->DNS.Query->VParsedDNSReply = NULL;
206 IO->DNS.Query->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
207 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
208 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
212 IO->DNS.Query->VParsedDNSReply = srv_out;
213 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
217 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen)
219 struct ares_txt_reply *txt_out;
221 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
224 if (IO->DNS.Query->VParsedDNSReply != NULL)
225 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
226 IO->DNS.Query->VParsedDNSReply = NULL;
228 IO->DNS.Query->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
229 if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
230 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
233 IO->DNS.Query->VParsedDNSReply = txt_out;
234 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
237 void QueryCb(void *arg,
245 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
247 ev_timer_stop (event_base, &IO->DNS.timeout);
249 IO->DNS.Query->DNSStatus = status;
250 if (status == ARES_SUCCESS)
251 IO->DNS.Query->DNS_CB(arg, abuf, alen);
253 EV_syslog(LOG_DEBUG, "C-ARES: Failed by: %s error %s\n",
255 ares_strerror(status));
256 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
257 IO->DNS.Query->DNSStatus = status;
260 ev_idle_init(&IO->unwind_stack,
261 IO_postdns_callback);
262 IO->unwind_stack.data = IO;
263 ev_idle_start(event_base, &IO->unwind_stack);
266 void QueryCbDone(AsyncIO *IO)
269 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
272 ev_idle_stop(event_base, &IO->unwind_stack);
275 void DestructCAres(AsyncIO *IO)
277 ares_destroy_options(&IO->DNS.Options);
281 void InitC_ares_dns(AsyncIO *IO)
285 EV_syslog(LOG_DEBUG, "C-ARES: %s %p\n", __FUNCTION__, IO->DNS.Channel);
288 if (IO->DNS.Channel == NULL) {
289 optmask |= ARES_OPT_SOCK_STATE_CB;
290 IO->DNS.Options.sock_state_cb = SockStateCb;
291 IO->DNS.Options.sock_state_cb_data = IO;
292 ares_init_options(&IO->DNS.Channel, &IO->DNS.Options, optmask);
294 IO->DNS.Query->DNSStatus = 0;
298 DNStimeouttrigger_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
300 AsyncIO *IO = watcher->data;
301 struct timeval tv, MaxTV;
302 struct timeval *NextTV;
304 memset(&MaxTV, 0, sizeof(MaxTV));
305 memset(&tv, 0, sizeof(tv));
307 NextTV = ares_timeout(IO->DNS.Channel, &MaxTV, &tv);
309 if ((NextTV->tv_sec != MaxTV.tv_sec) ||
310 (NextTV->tv_usec != MaxTV.tv_usec))
312 fd_set readers, writers;
314 EV_syslog(LOG_DEBUG, "C-ARES: %s Timeout!\n", __FUNCTION__);
318 ares_fds(IO->DNS.Channel, &readers, &writers);
319 ares_process(IO->DNS.Channel, &readers, &writers);
323 void QueueGetHostByNameDone(void *Ctx,
326 struct hostent *hostent)
328 AsyncIO *IO = (AsyncIO *) Ctx;
330 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
333 IO->DNS.Query->DNSStatus = status;
334 IO->DNS.Query->VParsedDNSReply = hostent;
335 IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
337 ev_idle_init(&IO->unwind_stack,
338 IO_postdns_callback);
339 IO->unwind_stack.data = IO;
340 ev_idle_start(event_base, &IO->unwind_stack);
343 void QueueGetHostByName(AsyncIO *IO, const char *Hostname, DNSQueryParts *QueryParts, IO_CallBack PostDNS)
346 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
347 IO->DNS.SourcePort = 0;
350 IO->DNS.Query = QueryParts;
351 IO->DNS.Query->PostDNS = PostDNS;
355 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
356 IO->DNS.timeout.data = IO;
357 ares_gethostbyname(IO->DNS.Channel,
359 AF_INET6, /* it falls back to ipv4 in doubt... */
360 QueueGetHostByNameDone,
362 ev_timer_start(event_base, &IO->DNS.timeout);
365 int QueueQuery(ns_type Type, const char *name, AsyncIO *IO, DNSQueryParts *QueryParts, IO_CallBack PostDNS)
368 char address_b[sizeof(struct in6_addr)];
371 IO->DNS.SourcePort = 0;
374 IO->DNS.Query = QueryParts;
375 IO->DNS.Query->PostDNS = PostDNS;
379 ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
380 IO->DNS.timeout.data = IO;
384 IO->DNS.Query->DNS_CB = ParseAnswerA;
388 IO->DNS.Query->DNS_CB = ParseAnswerAAAA;
392 IO->DNS.Query->DNS_CB = ParseAnswerMX;
396 IO->DNS.Query->DNS_CB = ParseAnswerNS;
400 IO->DNS.Query->DNS_CB = ParseAnswerTXT;
404 IO->DNS.Query->DNS_CB = ParseAnswerSRV;
408 IO->DNS.Query->DNS_CB = ParseAnswerCNAME;
414 if (inet_pton(AF_INET, name, &address_b) == 1) {
415 length = sizeof(struct in_addr);
417 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
418 length = sizeof(struct in6_addr);
424 ares_gethostbyaddr(IO->DNS.Channel, address_b, length, family, HostByAddrCb, IO);
425 ev_timer_start(event_base, &IO->DNS.timeout);
427 EV_syslog(LOG_DEBUG, "C-ARES: %s X1\n", __FUNCTION__);
433 EV_syslog(LOG_DEBUG, "C-ARES: %sX2\n", __FUNCTION__);
438 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
440 ares_query(IO->DNS.Channel, name, ns_c_in, Type, QueryCb, IO);
441 ev_timer_start(event_base, &IO->DNS.timeout);
449 /*****************************************************************************
450 * libev / c-ares integration *
451 *****************************************************************************/
452 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
454 AsyncIO *IO = watcher->data;
457 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
460 ares_process_fd(IO->DNS.Channel, ARES_SOCKET_BAD, IO->DNS.send_event.fd);
462 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
464 AsyncIO *IO = watcher->data;
467 EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
470 ares_process_fd(IO->DNS.Channel, IO->DNS.recv_event.fd, ARES_SOCKET_BAD);
473 void SockStateCb(void *data, int sock, int read, int write)
476 /* already inside of the event queue. */
479 struct sockaddr_in sin = {};
482 if ((IO->DNS.SourcePort == 0) &&
483 (getsockname(sock, &sin, &slen) == 0))
485 IO->DNS.SourcePort = ntohs(sin.sin_port);
487 EV_syslog(LOG_DEBUG, "C-ARES: %s %d|%d Sock %d port %hu\n",
497 if ((IO->DNS.recv_event.fd != sock) &&
498 (IO->DNS.recv_event.fd != 0)) {
499 ev_io_stop(event_base, &IO->DNS.recv_event);
501 IO->DNS.recv_event.fd = sock;
502 ev_io_init(&IO->DNS.recv_event, DNS_recv_callback, IO->DNS.recv_event.fd, EV_READ);
503 IO->DNS.recv_event.data = IO;
504 ev_io_start(event_base, &IO->DNS.recv_event);
507 if ((IO->DNS.send_event.fd != sock) &&
508 (IO->DNS.send_event.fd != 0)) {
509 ev_io_stop(event_base, &IO->DNS.send_event);
511 IO->DNS.send_event.fd = sock;
512 ev_io_init(&IO->DNS.send_event, DNS_send_callback, IO->DNS.send_event.fd, EV_WRITE);
513 IO->DNS.send_event.data = IO;
514 ev_io_start(event_base, &IO->DNS.send_event);
516 if ((read == 0) && (write == 0)) {
517 ev_io_stop(event_base, &IO->DNS.recv_event);
518 ev_io_stop(event_base, &IO->DNS.send_event);
522 CTDL_MODULE_INIT(c_ares_client)
526 int r = ares_library_init(ARES_LIB_INIT_ALL);
529 // ThrowException(Exception::Error(String::New(ares_strerror(r))));