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;
62 struct ares_options options;
65 void SockStateCb(void *data, int sock, int read, int write);
68 static void HostByAddrCb(void *data,
71 struct hostent *hostent)
74 IO->DNSStatus = status;
75 if (status != ARES_SUCCESS) {
76 // ResolveError(*cb, status);
80 /// TODO: howto free this??
83 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen)
87 if (IO->VParsedDNSReply != NULL)
88 IO->DNSReplyFree(IO->VParsedDNSReply);
89 IO->VParsedDNSReply = NULL;
91 IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
92 if (IO->DNSStatus != ARES_SUCCESS) {
93 // ResolveError(arg->js_cb, status);
96 IO->VParsedDNSReply = host;
97 IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
101 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
103 struct hostent* host;
105 if (IO->VParsedDNSReply != NULL)
106 IO->DNSReplyFree(IO->VParsedDNSReply);
107 IO->VParsedDNSReply = NULL;
109 IO->DNSStatus = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
110 if (IO->DNSStatus != ARES_SUCCESS) {
111 // ResolveError(arg->js_cb, status);
114 IO->VParsedDNSReply = host;
115 IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
119 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
121 struct hostent* host;
123 if (IO->VParsedDNSReply != NULL)
124 IO->DNSReplyFree(IO->VParsedDNSReply);
125 IO->VParsedDNSReply = NULL;
127 IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
128 if (IO->DNSStatus != ARES_SUCCESS) {
129 // ResolveError(arg->js_cb, status);
133 // a CNAME lookup always returns a single record but
134 IO->VParsedDNSReply = host;
135 IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
139 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
141 struct ares_mx_reply *mx_out;
143 if (IO->VParsedDNSReply != NULL)
144 IO->DNSReplyFree(IO->VParsedDNSReply);
145 IO->VParsedDNSReply = NULL;
147 IO->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
148 if (IO->DNSStatus != ARES_SUCCESS) {
149 // ResolveError(arg->js_cb, status);
153 IO->VParsedDNSReply = mx_out;
154 IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
158 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
160 struct hostent* host;
162 if (IO->VParsedDNSReply != NULL)
163 IO->DNSReplyFree(IO->VParsedDNSReply);
164 IO->VParsedDNSReply = NULL;
166 IO->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
167 if (IO->DNSStatus != ARES_SUCCESS) {
168 // ResolveError(arg->js_cb, status);
171 IO->VParsedDNSReply = host;
172 IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
176 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen)
178 struct ares_srv_reply *srv_out;
180 if (IO->VParsedDNSReply != NULL)
181 IO->DNSReplyFree(IO->VParsedDNSReply);
182 IO->VParsedDNSReply = NULL;
184 IO->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
185 if (IO->DNSStatus != ARES_SUCCESS) {
186 // ResolveError(arg->js_cb, status);
190 IO->VParsedDNSReply = srv_out;
191 IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
195 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen)
197 struct ares_txt_reply *txt_out;
199 if (IO->VParsedDNSReply != NULL)
200 IO->DNSReplyFree(IO->VParsedDNSReply);
201 IO->VParsedDNSReply = NULL;
203 IO->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
204 if (IO->DNSStatus != ARES_SUCCESS) {
205 // ResolveError(arg->js_cb, status);
208 IO->VParsedDNSReply = txt_out;
209 IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
212 void QueryCb(void *arg,
220 IO->DNSStatus = status;
221 if (status == ARES_SUCCESS)
222 IO->DNS_CB(arg, abuf, alen);
223 /// ev_io_stop(event_base, &IO->dns_io_event);
228 int QueueQuery(ns_type Type, char *name, AsyncIO *IO, IO_CallBack PostDNS)
231 char address_b[sizeof(struct in6_addr)];
234 if (IO->DNSChannel == NULL) {
235 optmask |= ARES_OPT_SOCK_STATE_CB;
236 IO->DNSOptions.sock_state_cb = SockStateCb;
237 IO->DNSOptions.sock_state_cb_data = IO;
238 ares_init_options(&IO->DNSChannel, &IO->DNSOptions, optmask);
241 IO->PostDNS = PostDNS;
244 IO->DNS_CB = ParseAnswerA;
248 IO->DNS_CB = ParseAnswerAAAA;
252 IO->DNS_CB = ParseAnswerMX;
256 IO->DNS_CB = ParseAnswerNS;
260 IO->DNS_CB = ParseAnswerTXT;
264 IO->DNS_CB = ParseAnswerSRV;
268 IO->DNS_CB = ParseAnswerCNAME;
274 if (inet_pton(AF_INET, name, &address_b) == 1) {
275 length = sizeof(struct in_addr);
277 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
278 length = sizeof(struct in6_addr);
284 ares_gethostbyaddr(IO->DNSChannel, address_b, length, family, HostByAddrCb, IO);
291 ares_query(IO->DNSChannel, name, ns_c_in, Type, QueryCb, IO);
295 static void DNS_io_callback(struct ev_loop *loop, ev_io *watcher, int revents)
297 AsyncIO *IO = watcher->data;
299 ares_process_fd(IO->DNSChannel, IO->dns_io_event.fd, 0);
302 void SockStateCb(void *data, int sock, int read, int write)
304 struct timeval tvbuf, maxtv, *ret;
308 /* already inside of the event queue. */
310 if ((read == 0) && (write == 0)) {
311 // ev_io_stop(event_base, &IO->dns_io_event);
312 } else if (IO->dns_io_event.fd != sock) {
313 if (IO->dns_io_event.fd != 0) {
314 ev_io_stop(event_base, &IO->dns_io_event);
316 IO->dns_io_event.fd = sock;
317 ev_io_init(&IO->dns_io_event, DNS_io_callback, IO->dns_io_event.fd, EV_READ|EV_WRITE);
318 IO->dns_io_event.data = IO;
320 ev_io_start(event_base, &IO->dns_io_event);
322 maxtv.tv_sec = time/1000;
323 maxtv.tv_usec = (time % 1000) * 1000;
325 ret = ares_timeout(IO->DNSChannel, &maxtv, &tvbuf);
329 CTDL_MODULE_INIT(c_ares_client)
336 int r = ares_library_init(ARES_LIB_INIT_ALL);
339 // ThrowException(Exception::Error(String::New(ares_strerror(r))));
343 optmask |= ARES_OPT_SOCK_STATE_CB;
344 memset(&options, 0, sizeof(struct ares_options));
345 options.sock_state_cb = SockStateCb;
347 ares_init_options(&Channel, &options, optmask);