From 91d42549e96cb21ab3fee9b481c70ee0729d1b79 Mon Sep 17 00:00:00 2001 From: Wilfried Goesgens Date: Mon, 3 Jan 2011 16:36:43 +0100 Subject: [PATCH] libc-ares migration - first implementation draft - dns queries are going out, but the reply isn't read so far. --- citadel/modules/c-ares-dns/serv_c-ares-dns.c | 584 +++++++++++++++++++ 1 file changed, 584 insertions(+) create mode 100644 citadel/modules/c-ares-dns/serv_c-ares-dns.c diff --git a/citadel/modules/c-ares-dns/serv_c-ares-dns.c b/citadel/modules/c-ares-dns/serv_c-ares-dns.c new file mode 100644 index 000000000..737f40452 --- /dev/null +++ b/citadel/modules/c-ares-dns/serv_c-ares-dns.c @@ -0,0 +1,584 @@ +/* + * Copyright (c) 1998-2009 by the citadel.org team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Inspired by NodeJS.org; thanks for the MX-Parser ;-) + */ + +#include "sysdep.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include + +#include +#include "citadel.h" +#include "server.h" +#include "citserver.h" +#include "support.h" + +#include "ctdl_module.h" +#include "event_client.h" + + +extern struct ev_loop *event_base; +struct ares_options options; +ares_channel Channel; + +void SockStateCb(void *data, int sock, int read, int write); + +/* + c-ares beim connect zum nameserver: if (channel->sock_create_cb) + + SOCK_STATE_CALLBACK(channel, s, 1, 0); -> void Channel::SockStateCb(void *data, int sock, int read, int write) { + + + +lesen der antwort: +#0 node::Channel::QueryCb (arg=0x8579268, status=0x0, timeouts=0x0, abuf=0xbffff0ef "\356|\201\200", alen=0x48) at ../src/node_cares.cc:453 +#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 +#2 0x0817fbf5 in end_query (channel=, query=0x85790b0, status=0x1, abuf=0xbffff0ef "\356|\201\200", alen=0x48) at ../deps/c-ares/ares_process.c:1233 +#3 0x08180898 in process_answer (channel=, abuf=, alen=, whichserver=0x0, tcp=0x0, now=0xbffff388) at ../deps/c-ares/ares_process.c:612 +#4 0x08180cf8 in read_udp_packets (channel=, read_fds=, read_fd=, now=0xbffff388) at ../deps/c-ares/ares_process.c:498 +#5 0x08181021 in processfds (channel=0x85a9888, read_fds=, read_fd=, write_fds=0x0, write_fd=0xffffffff) at ../deps/c-ares/ares_process.c:153 + +-> +static void ParseAnswerMX(QueryArg *arg, unsigned char* abuf, int alen) { + HandleScope scope; + + + + */ + +/* + +static Local HostEntToAddresses(struct hostent* hostent) { + Local addresses = Array::New(); + + + char ip[INET6_ADDRSTRLEN]; + for (int i = 0; hostent->h_addr_list[i]; ++i) { + inet_ntop(hostent->h_addrtype, hostent->h_addr_list[i], ip, sizeof(ip)); + + Local address = String::New(ip); + addresses->Set(Integer::New(i), address); + } + + return addresses; +} + + +static Local HostEntToNames(struct hostent* hostent) { + Local names = Array::New(); + + for (int i = 0; hostent->h_aliases[i]; ++i) { + Local address = String::New(hostent->h_aliases[i]); + names->Set(Integer::New(i), address); + } + + return names; +} + +static inline const char *ares_errno_string(int errorno) { +#define ERRNO_CASE(e) case ARES_##e: return #e; + switch (errorno) { + ERRNO_CASE(SUCCESS) + ERRNO_CASE(ENODATA) + ERRNO_CASE(EFORMERR) + ERRNO_CASE(ESERVFAIL) + ERRNO_CASE(ENOTFOUND) + ERRNO_CASE(ENOTIMP) + ERRNO_CASE(EREFUSED) + ERRNO_CASE(EBADQUERY) + ERRNO_CASE(EBADNAME) + ERRNO_CASE(EBADFAMILY) + ERRNO_CASE(EBADRESP) + ERRNO_CASE(ECONNREFUSED) + ERRNO_CASE(ETIMEOUT) + ERRNO_CASE(EOF) + ERRNO_CASE(EFILE) + ERRNO_CASE(ENOMEM) + ERRNO_CASE(EDESTRUCTION) + ERRNO_CASE(EBADSTR) + ERRNO_CASE(EBADFLAGS) + ERRNO_CASE(ENONAME) + ERRNO_CASE(EBADHINTS) + ERRNO_CASE(ENOTINITIALIZED) + ERRNO_CASE(ELOADIPHLPAPI) + ERRNO_CASE(EADDRGETNETWORKPARAMS) + ERRNO_CASE(ECANCELLED) + default: + assert(0 && "Unhandled c-ares errno"); + return "(UNKNOWN)"; + } +} + + +static void ResolveError(Persistent &cb, int status) { + HandleScope scope; + + Local code = String::NewSymbol(ares_errno_string(status)); + Local message = String::NewSymbol(ares_strerror(status)); + + Local cons1 = String::Concat(code, String::NewSymbol(", ")); + Local cons2 = String::Concat(cons1, message); + + Local e = Exception::Error(cons2); + + Local obj = e->ToObject(); + obj->Set(String::NewSymbol("errno"), Integer::New(status)); + + TryCatch try_catch; + + cb->Call(v8::Context::GetCurrent()->Global(), 1, &e); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } +} + +static void HostByNameCb(void *data, + int status, + int timeouts, + struct hostent *hostent) { + HandleScope scope; + + Persistent *cb = cb_unwrap(data); + + if (status != ARES_SUCCESS) { + ResolveError(*cb, status); + cb_destroy(cb); + return; + } + + TryCatch try_catch; + + Local addresses = HostEntToAddresses(hostent); + + Local argv[2] = { Local::New(Null()), addresses}; + + (*cb)->Call(v8::Context::GetCurrent()->Global(), 2, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + cb_destroy(cb); +} + + + + +static void cb_call(Persistent &cb, int argc, Local *argv) { + TryCatch try_catch; + + cb->Call(v8::Context::GetCurrent()->Global(), argc, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } +} + +Handle Channel::GetHostByAddr(const Arguments& args) { + HandleScope scope; + Channel *c = ObjectWrap::Unwrap(args.Holder()); + assert(c); + + if (!args[0]->IsString()) { + return ThrowException(Exception::Error( + String::New("First argument must be a address"))); + } + + if (!args[1]->IsInt32()) { + return ThrowException(Exception::Error( + String::New("Second argument must be an address family"))); + } + + if (!args[2]->IsFunction()) { + return ThrowException(Exception::Error( + String::New("Third argument must be a callback"))); + } + + int family = args[1]->Int32Value(); + if (family != AF_INET6 && family != AF_INET) { + return ThrowException(Exception::Error( + String::New("Unsupported address family"))); + } + + String::Utf8Value address_s(args[0]->ToString()); + + char address_b[sizeof(struct in6_addr)]; + int r = inet_pton(family, *address_s, address_b); + if (r != 1) { + return ThrowException(Exception::Error( + String::New("Invalid network address"))); + } + + int length; + if (family == AF_INET6) + length = sizeof(struct in6_addr); + else + length = sizeof(struct in_addr); + + ares_gethostbyaddr(c->channel, address_b, length, family, HostByAddrCb, cb_persist(args[2])); + + return Undefined(); +} + + + +Handle Channel::GetHostByName(const Arguments& args) { + HandleScope scope; + Channel *c = ObjectWrap::Unwrap(args.Holder()); + assert(c); + + if (!args[0]->IsString()) { + return ThrowException(Exception::Error( + String::New("First argument must be a name"))); + } + + if (!args[1]->IsInt32()) { + return ThrowException(Exception::Error( + String::New("Second argument must be a family"))); + } + + if (!args[2]->IsFunction()) { + return ThrowException(Exception::Error( + String::New("Third argument must be a callback"))); + } + + int family = args[1]->Int32Value(); + if (family != AF_INET6 && family != AF_INET) { + return ThrowException(Exception::Error( + String::New("Unsupported address family"))); + } + + String::Utf8Value name(args[0]->ToString()); + + ares_gethostbyname(c->channel, *name, family, HostByNameCb, cb_persist(args[2])); + + return Undefined(); +} + +*/ + + + +static void HostByAddrCb(void *data, + int status, + int timeouts, + struct hostent *hostent) +{ + AsyncIO *IO = data; + IO->DNSStatus = status; + if (status != ARES_SUCCESS) { +// ResolveError(*cb, status); + return; + } + IO->Data = hostent; +/// TODO: howto free this?? +} + +static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen) +{ + struct hostent* host; + + if (IO->VParsedDNSReply != NULL) + IO->DNSReplyFree(IO->VParsedDNSReply); + IO->VParsedDNSReply = NULL; + + IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL); + if (IO->DNSStatus != ARES_SUCCESS) { +// ResolveError(arg->js_cb, status); + return; + } + IO->VParsedDNSReply = host; + IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent; +} + + +static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen) +{ + struct hostent* host; + + if (IO->VParsedDNSReply != NULL) + IO->DNSReplyFree(IO->VParsedDNSReply); + IO->VParsedDNSReply = NULL; + + IO->DNSStatus = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL); + if (IO->DNSStatus != ARES_SUCCESS) { +// ResolveError(arg->js_cb, status); + return; + } + IO->VParsedDNSReply = host; + IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent; +} + + +static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen) +{ + struct hostent* host; + + if (IO->VParsedDNSReply != NULL) + IO->DNSReplyFree(IO->VParsedDNSReply); + IO->VParsedDNSReply = NULL; + + IO->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL); + if (IO->DNSStatus != ARES_SUCCESS) { +// ResolveError(arg->js_cb, status); + return; + } + + // a CNAME lookup always returns a single record but + IO->VParsedDNSReply = host; + IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent; +} + + +static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen) +{ + struct ares_mx_reply *mx_out; + + if (IO->VParsedDNSReply != NULL) + IO->DNSReplyFree(IO->VParsedDNSReply); + IO->VParsedDNSReply = NULL; + + IO->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out); + if (IO->DNSStatus != ARES_SUCCESS) { +// ResolveError(arg->js_cb, status); + return; + } + + IO->VParsedDNSReply = mx_out; + IO->DNSReplyFree = (FreeDNSReply) ares_free_data; +} + + +static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen) +{ + struct hostent* host; + + if (IO->VParsedDNSReply != NULL) + IO->DNSReplyFree(IO->VParsedDNSReply); + IO->VParsedDNSReply = NULL; + + IO->DNSStatus = ares_parse_ns_reply(abuf, alen, &host); + if (IO->DNSStatus != ARES_SUCCESS) { +// ResolveError(arg->js_cb, status); + return; + } + IO->VParsedDNSReply = host; + IO->DNSReplyFree = (FreeDNSReply) ares_free_hostent; +} + + +static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen) +{ + struct ares_srv_reply *srv_out; + + if (IO->VParsedDNSReply != NULL) + IO->DNSReplyFree(IO->VParsedDNSReply); + IO->VParsedDNSReply = NULL; + + IO->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out); + if (IO->DNSStatus != ARES_SUCCESS) { +// ResolveError(arg->js_cb, status); + return; + } + + IO->VParsedDNSReply = srv_out; + IO->DNSReplyFree = (FreeDNSReply) ares_free_data; +} + + +static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen) +{ + struct ares_txt_reply *txt_out; + + if (IO->VParsedDNSReply != NULL) + IO->DNSReplyFree(IO->VParsedDNSReply); + IO->VParsedDNSReply = NULL; + + IO->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out); + if (IO->DNSStatus != ARES_SUCCESS) { +// ResolveError(arg->js_cb, status); + return; + } + IO->VParsedDNSReply = txt_out; + IO->DNSReplyFree = (FreeDNSReply) ares_free_data; +} + + +void QueryCb(void *arg, + int status, + int timeouts, + unsigned char* abuf, + int alen) +{ + AsyncIO *IO = arg; + + IO->DNSStatus = status; + if (status == ARES_SUCCESS) + IO->DNS_CB(arg, abuf, alen); + IO->PostDNS(IO); +} + +int QueueQuery(ns_type Type, char *name, AsyncIO *IO, IO_CallBack PostDNS) +{ + int length, family; + char address_b[sizeof(struct in6_addr)]; + int optmask = 0; + + optmask |= ARES_OPT_SOCK_STATE_CB; + IO->DNSOptions.sock_state_cb = SockStateCb; + IO->DNSOptions.sock_state_cb_data = IO; + ares_init_options(&IO->DNSChannel, &IO->DNSOptions, optmask); + + IO->PostDNS = PostDNS; + switch(Type) { + case ns_t_a: + IO->DNS_CB = ParseAnswerA; + break; + + case ns_t_aaaa: + IO->DNS_CB = ParseAnswerAAAA; + break; + + case ns_t_mx: + IO->DNS_CB = ParseAnswerMX; + break; + + case ns_t_ns: + IO->DNS_CB = ParseAnswerNS; + break; + + case ns_t_txt: + IO->DNS_CB = ParseAnswerTXT; + break; + + case ns_t_srv: + IO->DNS_CB = ParseAnswerSRV; + break; + + case ns_t_cname: + IO->DNS_CB = ParseAnswerCNAME; + break; + + case ns_t_ptr: + + + if (inet_pton(AF_INET, name, &address_b) == 1) { + length = sizeof(struct in_addr); + family = AF_INET; + } else if (inet_pton(AF_INET6, name, &address_b) == 1) { + length = sizeof(struct in6_addr); + family = AF_INET6; + } else { + return -1; + } + + ares_gethostbyaddr(IO->DNSChannel, address_b, length, family, HostByAddrCb, IO); + + return 1; + + default: + return 0; + } + ares_query(IO->DNSChannel, name, ns_c_in, Type, QueryCb, IO); + return 1; +} + +static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents) +{ +} + +static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents) +{ +} + +void SockStateCb(void *data, int sock, int read, int write) +{ + struct timeval tvbuf, maxtv, *ret; + + int64_t time = 10; + AsyncIO *IO = data; +/* already inside of the event queue. */ + IO->sock = sock; + ev_io_init(&IO->recv_event, DNS_recv_callback, IO->sock, EV_READ); + IO->recv_event.data = IO; + ev_io_init(&IO->send_event, DNS_send_callback, IO->sock, EV_WRITE); + IO->send_event.data = IO; + if (write) + ev_io_start(event_base, &IO->send_event); + else + ev_io_start(event_base, &IO->recv_event); + + + maxtv.tv_sec = time/1000; + maxtv.tv_usec = (time % 1000) * 1000; + + ret = ares_timeout(IO->DNSChannel, &maxtv, &tvbuf); + + +} + +CTDL_MODULE_INIT(c_ares_client) +{ + if (!threading) + { + int optmask = 0; + + + int r = ares_library_init(ARES_LIB_INIT_ALL); + if (0 != r) { + // TODO + // ThrowException(Exception::Error(String::New(ares_strerror(r)))); +//// assert(r == 0); + } + + optmask |= ARES_OPT_SOCK_STATE_CB; + memset(&options, 0, sizeof(struct ares_options)); + options.sock_state_cb = SockStateCb; + + ares_init_options(&Channel, &options, optmask); + + } + return "c-ares"; +} -- 2.30.2