libc-ares migration - first implementation draft
authorWilfried Goesgens <dothebart@citadel.org>
Mon, 3 Jan 2011 15:36:43 +0000 (16:36 +0100)
committerWilfried Goesgens <dothebart@citadel.org>
Mon, 3 Jan 2011 15:36:43 +0000 (16:36 +0100)
 - dns queries are going out, but the reply isn't read so far.

citadel/modules/c-ares-dns/serv_c-ares-dns.c [new file with mode: 0644]

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 (file)
index 0000000..737f404
--- /dev/null
@@ -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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <syslog.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+#include <sys/wait.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <libcitadel.h>
+#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=<value optimized out>, 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=<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
+#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
+#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
+
+-> 
+static void ParseAnswerMX(QueryArg *arg, unsigned char* abuf, int alen) {
+  HandleScope scope;
+
+
+
+ */
+
+/*
+
+static Local<Array> HostEntToAddresses(struct hostent* hostent) {
+  Local<Array> 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<String> address = String::New(ip);
+    addresses->Set(Integer::New(i), address);
+  }
+
+  return addresses;
+}
+
+
+static Local<Array> HostEntToNames(struct hostent* hostent) {
+  Local<Array> names = Array::New();
+
+  for (int i = 0; hostent->h_aliases[i]; ++i) {
+    Local<String> 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<Function> &cb, int status) {
+  HandleScope scope;
+
+  Local<String> code = String::NewSymbol(ares_errno_string(status));
+  Local<String> message = String::NewSymbol(ares_strerror(status));
+
+  Local<String> cons1 = String::Concat(code, String::NewSymbol(", "));
+  Local<String> cons2 = String::Concat(cons1, message);
+
+  Local<Value> e = Exception::Error(cons2);
+
+  Local<Object> 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<Function> *cb = cb_unwrap(data);
+
+  if  (status != ARES_SUCCESS) {
+    ResolveError(*cb, status);
+    cb_destroy(cb);
+    return;
+  }
+
+  TryCatch try_catch;
+
+  Local<Array> addresses = HostEntToAddresses(hostent);
+
+  Local<Value> argv[2] = { Local<Value>::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<Function> &cb, int argc, Local<Value> *argv) {
+  TryCatch try_catch;
+
+  cb->Call(v8::Context::GetCurrent()->Global(), argc, argv);
+
+  if (try_catch.HasCaught()) {
+    FatalException(try_catch);
+  }
+}
+
+Handle<Value> Channel::GetHostByAddr(const Arguments& args) {
+  HandleScope scope;
+  Channel *c = ObjectWrap::Unwrap<Channel>(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<Value> Channel::GetHostByName(const Arguments& args) {
+  HandleScope scope;
+  Channel *c = ObjectWrap::Unwrap<Channel>(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";
+}