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 c-ares beim connect zum nameserver: if (channel->sock_create_cb)
70 SOCK_STATE_CALLBACK(channel, s, 1, 0); -> void Channel::SockStateCb(void *data, int sock, int read, int write) {
75 #0 node::Channel::QueryCb (arg=0x8579268, status=0x0, timeouts=0x0, abuf=0xbffff0ef "\356|\201\200", alen=0x48) at ../src/node_cares.cc:453
76 #1 0x08181884 in qcallback (arg=0x8579278, status=0x857b5a0, timeouts=0x0, abuf=0xbffff0ef "\356|\201\200", alen=0x48) at ../deps/c-ares/ares_query.c:180
77 #2 0x0817fbf5 in end_query (channel=<value optimized out>, query=0x85790b0, status=0x1, abuf=0xbffff0ef "\356|\201\200", alen=0x48) at ../deps/c-ares/ares_process.c:1233
78 #3 0x08180898 in process_answer (channel=<value optimized out>, abuf=<value optimized out>, alen=<value optimized out>, whichserver=0x0, tcp=0x0, now=0xbffff388) at ../deps/c-ares/ares_process.c:612
79 #4 0x08180cf8 in read_udp_packets (channel=<value optimized out>, read_fds=<value optimized out>, read_fd=<value optimized out>, now=0xbffff388) at ../deps/c-ares/ares_process.c:498
80 #5 0x08181021 in processfds (channel=0x85a9888, read_fds=<value optimized out>, read_fd=<value optimized out>, write_fds=0x0, write_fd=0xffffffff) at ../deps/c-ares/ares_process.c:153
83 static void ParseAnswerMX(QueryArg *arg, unsigned char* abuf, int alen) {
92 static Local<Array> HostEntToAddresses(struct hostent* hostent) {
93 Local<Array> addresses = Array::New();
96 char ip[INET6_ADDRSTRLEN];
97 for (int i = 0; hostent->h_addr_list[i]; ++i) {
98 inet_ntop(hostent->h_addrtype, hostent->h_addr_list[i], ip, sizeof(ip));
100 Local<String> address = String::New(ip);
101 addresses->Set(Integer::New(i), address);
108 static Local<Array> HostEntToNames(struct hostent* hostent) {
109 Local<Array> names = Array::New();
111 for (int i = 0; hostent->h_aliases[i]; ++i) {
112 Local<String> address = String::New(hostent->h_aliases[i]);
113 names->Set(Integer::New(i), address);
119 static inline const char *ares_errno_string(int errorno) {
120 #define ERRNO_CASE(e) case ARES_##e: return #e;
125 ERRNO_CASE(ESERVFAIL)
126 ERRNO_CASE(ENOTFOUND)
129 ERRNO_CASE(EBADQUERY)
131 ERRNO_CASE(EBADFAMILY)
133 ERRNO_CASE(ECONNREFUSED)
138 ERRNO_CASE(EDESTRUCTION)
140 ERRNO_CASE(EBADFLAGS)
142 ERRNO_CASE(EBADHINTS)
143 ERRNO_CASE(ENOTINITIALIZED)
144 ERRNO_CASE(ELOADIPHLPAPI)
145 ERRNO_CASE(EADDRGETNETWORKPARAMS)
146 ERRNO_CASE(ECANCELLED)
148 assert(0 && "Unhandled c-ares errno");
154 static void ResolveError(Persistent<Function> &cb, int status) {
157 Local<String> code = String::NewSymbol(ares_errno_string(status));
158 Local<String> message = String::NewSymbol(ares_strerror(status));
160 Local<String> cons1 = String::Concat(code, String::NewSymbol(", "));
161 Local<String> cons2 = String::Concat(cons1, message);
163 Local<Value> e = Exception::Error(cons2);
165 Local<Object> obj = e->ToObject();
166 obj->Set(String::NewSymbol("errno"), Integer::New(status));
170 cb->Call(v8::Context::GetCurrent()->Global(), 1, &e);
172 if (try_catch.HasCaught()) {
173 FatalException(try_catch);
177 static void HostByNameCb(void *data,
180 struct hostent *hostent) {
183 Persistent<Function> *cb = cb_unwrap(data);
185 if (status != ARES_SUCCESS) {
186 ResolveError(*cb, status);
193 Local<Array> addresses = HostEntToAddresses(hostent);
195 Local<Value> argv[2] = { Local<Value>::New(Null()), addresses};
197 (*cb)->Call(v8::Context::GetCurrent()->Global(), 2, argv);
199 if (try_catch.HasCaught()) {
200 FatalException(try_catch);
209 static void cb_call(Persistent<Function> &cb, int argc, Local<Value> *argv) {
212 cb->Call(v8::Context::GetCurrent()->Global(), argc, argv);
214 if (try_catch.HasCaught()) {
215 FatalException(try_catch);
219 Handle<Value> Channel::GetHostByAddr(const Arguments& args) {
221 Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder());
224 if (!args[0]->IsString()) {
225 return ThrowException(Exception::Error(
226 String::New("First argument must be a address")));
229 if (!args[1]->IsInt32()) {
230 return ThrowException(Exception::Error(
231 String::New("Second argument must be an address family")));
234 if (!args[2]->IsFunction()) {
235 return ThrowException(Exception::Error(
236 String::New("Third argument must be a callback")));
239 int family = args[1]->Int32Value();
240 if (family != AF_INET6 && family != AF_INET) {
241 return ThrowException(Exception::Error(
242 String::New("Unsupported address family")));
245 String::Utf8Value address_s(args[0]->ToString());
247 char address_b[sizeof(struct in6_addr)];
248 int r = inet_pton(family, *address_s, address_b);
250 return ThrowException(Exception::Error(
251 String::New("Invalid network address")));
255 if (family == AF_INET6)
256 length = sizeof(struct in6_addr);
258 length = sizeof(struct in_addr);
260 ares_gethostbyaddr(c->channel, address_b, length, family, HostByAddrCb, cb_persist(args[2]));
267 Handle<Value> Channel::GetHostByName(const Arguments& args) {
269 Channel *c = ObjectWrap::Unwrap<Channel>(args.Holder());
272 if (!args[0]->IsString()) {
273 return ThrowException(Exception::Error(
274 String::New("First argument must be a name")));
277 if (!args[1]->IsInt32()) {
278 return ThrowException(Exception::Error(
279 String::New("Second argument must be a family")));
282 if (!args[2]->IsFunction()) {
283 return ThrowException(Exception::Error(
284 String::New("Third argument must be a callback")));
287 int family = args[1]->Int32Value();
288 if (family != AF_INET6 && family != AF_INET) {
289 return ThrowException(Exception::Error(
290 String::New("Unsupported address family")));
293 String::Utf8Value name(args[0]->ToString());
295 ares_gethostbyname(c->channel, *name, family, HostByNameCb, cb_persist(args[2]));
304 static void HostByAddrCb(void *data,
307 struct hostent *hostent)
310 IO->DNSStatus = status;
311 if (status != ARES_SUCCESS) {
312 // ResolveError(*cb, status);
316 /// TODO: howto free this??
319 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen)
321 struct hostent* host;
323 if (IO->VParsedDNSReply != NULL)
324 IO->DNSReplyFree(IO->VParsedDNSReply);
325 IO->VParsedDNSReply = NULL;
327 IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
328 if (IO->DNSStatus != ARES_SUCCESS) {
329 // ResolveError(arg->js_cb, status);
332 IO->VParsedDNSReply = host;
333 IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
337 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen)
339 struct hostent* host;
341 if (IO->VParsedDNSReply != NULL)
342 IO->DNSReplyFree(IO->VParsedDNSReply);
343 IO->VParsedDNSReply = NULL;
345 IO->DNSStatus = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
346 if (IO->DNSStatus != ARES_SUCCESS) {
347 // ResolveError(arg->js_cb, status);
350 IO->VParsedDNSReply = host;
351 IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
355 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen)
357 struct hostent* host;
359 if (IO->VParsedDNSReply != NULL)
360 IO->DNSReplyFree(IO->VParsedDNSReply);
361 IO->VParsedDNSReply = NULL;
363 IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
364 if (IO->DNSStatus != ARES_SUCCESS) {
365 // ResolveError(arg->js_cb, status);
369 // a CNAME lookup always returns a single record but
370 IO->VParsedDNSReply = host;
371 IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
375 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen)
377 struct ares_mx_reply *mx_out;
379 if (IO->VParsedDNSReply != NULL)
380 IO->DNSReplyFree(IO->VParsedDNSReply);
381 IO->VParsedDNSReply = NULL;
383 IO->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
384 if (IO->DNSStatus != ARES_SUCCESS) {
385 // ResolveError(arg->js_cb, status);
389 IO->VParsedDNSReply = mx_out;
390 IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
394 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen)
396 struct hostent* host;
398 if (IO->VParsedDNSReply != NULL)
399 IO->DNSReplyFree(IO->VParsedDNSReply);
400 IO->VParsedDNSReply = NULL;
402 IO->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
403 if (IO->DNSStatus != ARES_SUCCESS) {
404 // ResolveError(arg->js_cb, status);
407 IO->VParsedDNSReply = host;
408 IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
412 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen)
414 struct ares_srv_reply *srv_out;
416 if (IO->VParsedDNSReply != NULL)
417 IO->DNSReplyFree(IO->VParsedDNSReply);
418 IO->VParsedDNSReply = NULL;
420 IO->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
421 if (IO->DNSStatus != ARES_SUCCESS) {
422 // ResolveError(arg->js_cb, status);
426 IO->VParsedDNSReply = srv_out;
427 IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
431 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen)
433 struct ares_txt_reply *txt_out;
435 if (IO->VParsedDNSReply != NULL)
436 IO->DNSReplyFree(IO->VParsedDNSReply);
437 IO->VParsedDNSReply = NULL;
439 IO->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
440 if (IO->DNSStatus != ARES_SUCCESS) {
441 // ResolveError(arg->js_cb, status);
444 IO->VParsedDNSReply = txt_out;
445 IO->DNSReplyFree = (FreeDNSReply) ares_free_data;
449 void QueryCb(void *arg,
457 IO->DNSStatus = status;
458 if (status == ARES_SUCCESS)
459 IO->DNS_CB(arg, abuf, alen);
463 int QueueQuery(ns_type Type, char *name, AsyncIO *IO, IO_CallBack PostDNS)
466 char address_b[sizeof(struct in6_addr)];
470 optmask |= ARES_OPT_SOCK_STATE_CB;
471 IO->DNSOptions.sock_state_cb = SockStateCb;
472 IO->DNSOptions.sock_state_cb_data = IO;
473 ares_init_options(&IO->DNSChannel, &IO->DNSOptions, optmask);
475 IO->PostDNS = PostDNS;
478 IO->DNS_CB = ParseAnswerA;
482 IO->DNS_CB = ParseAnswerAAAA;
486 IO->DNS_CB = ParseAnswerMX;
490 IO->DNS_CB = ParseAnswerNS;
494 IO->DNS_CB = ParseAnswerTXT;
498 IO->DNS_CB = ParseAnswerSRV;
502 IO->DNS_CB = ParseAnswerCNAME;
508 if (inet_pton(AF_INET, name, &address_b) == 1) {
509 length = sizeof(struct in_addr);
511 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
512 length = sizeof(struct in6_addr);
518 ares_gethostbyaddr(IO->DNSChannel, address_b, length, family, HostByAddrCb, IO);
525 ares_query(IO->DNSChannel, name, ns_c_in, Type, QueryCb, IO);
526 ares_fds(IO->DNSChannel, &rfd, &wfd);
530 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
534 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
538 void SockStateCb(void *data, int sock, int read, int write)
540 struct timeval tvbuf, maxtv, *ret;
544 /* already inside of the event queue. */
546 ev_io_init(&IO->recv_event, DNS_recv_callback, IO->sock, EV_READ);
547 IO->recv_event.data = IO;
548 ev_io_init(&IO->send_event, DNS_send_callback, IO->sock, EV_WRITE);
549 IO->send_event.data = IO;
551 ev_io_start(event_base, &IO->send_event);
553 ev_io_start(event_base, &IO->recv_event);
556 maxtv.tv_sec = time/1000;
557 maxtv.tv_usec = (time % 1000) * 1000;
559 ret = ares_timeout(IO->DNSChannel, &maxtv, &tvbuf);
564 CTDL_MODULE_INIT(c_ares_client)
571 int r = ares_library_init(ARES_LIB_INIT_ALL);
574 // ThrowException(Exception::Error(String::New(ares_strerror(r))));
578 optmask |= ARES_OPT_SOCK_STATE_CB;
579 memset(&options, 0, sizeof(struct ares_options));
580 options.sock_state_cb = SockStateCb;
582 ares_init_options(&Channel, &options, optmask);