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)
72 IO->DNSQuery->DNSStatus = status;
73 if (status != ARES_SUCCESS) {
74 // ResolveError(*cb, status);
77 IO->DNSQuery->Data = hostent;
78 /// TODO: howto free this??
81 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen)
85 if (IO->DNSQuery->VParsedDNSReply != NULL)
86 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
87 IO->DNSQuery->VParsedDNSReply = NULL;
89 IO->DNSQuery->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
90 if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
91 // ResolveError(arg->js_cb, status);
94 IO->DNSQuery->VParsedDNSReply = host;
95 IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
99 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
101 struct hostent* host;
103 if (IO->DNSQuery->VParsedDNSReply != NULL)
104 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
105 IO->DNSQuery->VParsedDNSReply = NULL;
107 IO->DNSQuery->DNSStatus = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
108 if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
109 // ResolveError(arg->js_cb, status);
112 IO->DNSQuery->VParsedDNSReply = host;
113 IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
117 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
119 struct hostent* host;
121 if (IO->DNSQuery->VParsedDNSReply != NULL)
122 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
123 IO->DNSQuery->VParsedDNSReply = NULL;
125 IO->DNSQuery->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
126 if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
127 // ResolveError(arg->js_cb, status);
131 // a CNAME lookup always returns a single record but
132 IO->DNSQuery->VParsedDNSReply = host;
133 IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
137 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
139 struct ares_mx_reply *mx_out;
141 if (IO->DNSQuery->VParsedDNSReply != NULL)
142 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
143 IO->DNSQuery->VParsedDNSReply = NULL;
145 IO->DNSQuery->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
146 if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
147 // ResolveError(arg->js_cb, status);
151 IO->DNSQuery->VParsedDNSReply = mx_out;
152 IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_data;
156 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
158 struct hostent* host;
160 if (IO->DNSQuery->VParsedDNSReply != NULL)
161 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
162 IO->DNSQuery->VParsedDNSReply = NULL;
164 IO->DNSQuery->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
165 if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
166 // ResolveError(arg->js_cb, status);
169 IO->DNSQuery->VParsedDNSReply = host;
170 IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
174 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen)
176 struct ares_srv_reply *srv_out;
178 if (IO->DNSQuery->VParsedDNSReply != NULL)
179 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
180 IO->DNSQuery->VParsedDNSReply = NULL;
182 IO->DNSQuery->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
183 if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
184 // ResolveError(arg->js_cb, status);
188 IO->DNSQuery->VParsedDNSReply = srv_out;
189 IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_data;
193 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen)
195 struct ares_txt_reply *txt_out;
197 if (IO->DNSQuery->VParsedDNSReply != NULL)
198 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
199 IO->DNSQuery->VParsedDNSReply = NULL;
201 IO->DNSQuery->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
202 if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
203 // ResolveError(arg->js_cb, status);
206 IO->DNSQuery->VParsedDNSReply = txt_out;
207 IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_data;
210 void QueryCb(void *arg,
218 IO->DNSQuery->DNSStatus = status;
219 if (status == ARES_SUCCESS)
220 IO->DNSQuery->DNS_CB(arg, abuf, alen);
222 IO->DNSQuery->DNSStatus = status;
223 /// ev_io_stop(event_base, &IO->DNSQuery->dns_io_event);
225 ev_idle_init(&IO->unwind_stack,
226 IO_postdns_callback);
227 IO->unwind_stack.data = IO;
228 ev_idle_start(event_base, &IO->unwind_stack);
229 CtdlLogPrintf(CTDL_DEBUG, "C-ARES: %s\n", __FUNCTION__);
232 void QueryCbDone(AsyncIO *IO)
234 ev_idle_stop(event_base, &IO->unwind_stack);
238 void InitC_ares_dns(AsyncIO *IO)
241 if (IO->DNSChannel == NULL) {
242 optmask |= ARES_OPT_SOCK_STATE_CB;
243 IO->DNSOptions.sock_state_cb = SockStateCb;
244 IO->DNSOptions.sock_state_cb_data = IO;
245 ares_init_options(&IO->DNSChannel, &IO->DNSOptions, optmask);
249 void QueueGetHostByNameDone(void *Ctx,
252 struct hostent *hostent)
254 AsyncIO *IO = (AsyncIO *) Ctx;
256 IO->DNSQuery->DNSStatus = status;
257 IO->DNSQuery->VParsedDNSReply = hostent;
258 IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
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);
264 CtdlLogPrintf(CTDL_DEBUG, "C-ARES: %s\n", __FUNCTION__);
267 void QueueGetHostByName(AsyncIO *IO, const char *Hostname, DNSQueryParts *QueryParts, IO_CallBack PostDNS)
269 IO->DNSQuery = QueryParts;
270 IO->DNSQuery->PostDNS = PostDNS;
274 ares_gethostbyname(IO->DNSChannel,
276 AF_INET6, /* it falls back to ipv4 in doubt... */
277 QueueGetHostByNameDone,
279 //get_one_mx_host_ip_done);
281 int QueueQuery(ns_type Type, const char *name, AsyncIO *IO, DNSQueryParts *QueryParts, IO_CallBack PostDNS)
284 char address_b[sizeof(struct in6_addr)];
286 IO->DNSQuery = QueryParts;
287 IO->DNSQuery->PostDNS = PostDNS;
293 IO->DNSQuery->DNS_CB = ParseAnswerA;
297 IO->DNSQuery->DNS_CB = ParseAnswerAAAA;
301 IO->DNSQuery->DNS_CB = ParseAnswerMX;
305 IO->DNSQuery->DNS_CB = ParseAnswerNS;
309 IO->DNSQuery->DNS_CB = ParseAnswerTXT;
313 IO->DNSQuery->DNS_CB = ParseAnswerSRV;
317 IO->DNSQuery->DNS_CB = ParseAnswerCNAME;
323 if (inet_pton(AF_INET, name, &address_b) == 1) {
324 length = sizeof(struct in_addr);
326 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
327 length = sizeof(struct in6_addr);
333 ares_gethostbyaddr(IO->DNSChannel, address_b, length, family, HostByAddrCb, IO);
340 ares_query(IO->DNSChannel, name, ns_c_in, Type, QueryCb, IO);
345 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
347 AsyncIO *IO = watcher->data;
349 ares_process_fd(IO->DNSChannel, ARES_SOCKET_BAD, IO->dns_send_event.fd);
351 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
353 AsyncIO *IO = watcher->data;
355 ares_process_fd(IO->DNSChannel, IO->dns_recv_event.fd, ARES_SOCKET_BAD);
358 void SockStateCb(void *data, int sock, int read, int write)
361 struct timeval tvbuf, maxtv, *ret;
366 /* already inside of the event queue. */
369 if ((IO->dns_recv_event.fd != sock) &&
370 (IO->dns_recv_event.fd != 0)) {
371 ev_io_stop(event_base, &IO->dns_recv_event);
373 IO->dns_recv_event.fd = sock;
374 ev_io_init(&IO->dns_recv_event, DNS_recv_callback, IO->dns_recv_event.fd, EV_READ);
375 IO->dns_recv_event.data = IO;
376 ev_io_start(event_base, &IO->dns_recv_event);
379 if ((IO->dns_send_event.fd != sock) &&
380 (IO->dns_send_event.fd != 0)) {
381 ev_io_stop(event_base, &IO->dns_send_event);
383 IO->dns_send_event.fd = sock;
384 ev_io_init(&IO->dns_send_event, DNS_send_callback, IO->dns_send_event.fd, EV_WRITE);
385 IO->dns_send_event.data = IO;
386 ev_io_start(event_base, &IO->dns_send_event);
390 ev_io_start(event_base, &IO->dns_io_event);
392 maxtv.tv_sec = time/1000;
393 maxtv.tv_usec = (time % 1000) * 1000;
395 ret = ares_timeout(IO->DNSChannel, &maxtv, &tvbuf);
398 if ((read == 0) && (write == 0)) {
399 ev_io_stop(event_base, &IO->dns_recv_event);
400 ev_io_stop(event_base, &IO->dns_send_event);
404 CTDL_MODULE_INIT(c_ares_client)
408 int r = ares_library_init(ARES_LIB_INIT_ALL);
411 // ThrowException(Exception::Error(String::New(ares_strerror(r))));