gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [gnunet] branch master updated: support multiple DNS resolv


From: gnunet
Subject: [GNUnet-SVN] [gnunet] branch master updated: support multiple DNS resolvers for queries (in DNSSTUB and GNS2DNS resolution for now)
Date: Thu, 19 Apr 2018 18:38:52 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository gnunet.

The following commit(s) were added to refs/heads/master by this push:
     new ff4d7b51f support multiple DNS resolvers for queries (in DNSSTUB and 
GNS2DNS resolution for now)
ff4d7b51f is described below

commit ff4d7b51f37f61633766664647e9b148af1e4f0a
Author: Christian Grothoff <address@hidden>
AuthorDate: Thu Apr 19 18:38:41 2018 +0200

    support multiple DNS resolvers for queries (in DNSSTUB and GNS2DNS 
resolution for now)
---
 src/dns/dnsstub.c                     |  683 +++++++++++----------
 src/dns/gnunet-service-dns.c          |   43 +-
 src/dns/gnunet-zoneimport.c           |   24 +-
 src/exit/gnunet-daemon-exit.c         |   29 +-
 src/gns/gnunet-dns2gns.c              |   19 +-
 src/gns/gnunet-service-gns_resolver.c | 1072 ++++++++++++++++++---------------
 src/gns/test_gns_lookup.conf          |    2 +-
 src/include/gnunet_dnsstub_lib.h      |   85 +--
 src/namestore/gnunet-zoneimport.c     |   35 +-
 9 files changed, 1107 insertions(+), 885 deletions(-)

diff --git a/src/dns/dnsstub.c b/src/dns/dnsstub.c
index 6aa2d7b8f..6eb3612c2 100644
--- a/src/dns/dnsstub.c
+++ b/src/dns/dnsstub.c
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     Copyright (C) 2012 GNUnet e.V.
+     Copyright (C) 2012, 2018 GNUnet e.V.
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -28,20 +28,15 @@
 #include "gnunet_dnsstub_lib.h"
 
 /**
- * Timeout for an external (Internet-DNS) DNS resolution
- */
-#define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_SECONDS, 5)
-
-/**
  * Timeout for retrying DNS queries.
  */
 #define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_MILLISECONDS, 250)
 
+
 /**
- * How many DNS sockets do we open at most at the same time?
- * (technical socket maximum is this number x2 for IPv4+IPv6)
+ * DNS Server used for resolution.
  */
-#define DNS_SOCKET_MAX 128
+struct DnsServer;
 
 
 /**
@@ -81,19 +76,14 @@ struct GNUNET_DNSSTUB_RequestSocket
   struct GNUNET_SCHEDULER_Task *retry_task;
 
   /**
-   * When should this request time out?
-   */
-  struct GNUNET_TIME_Absolute timeout;
-
-  /**
-   * Address we sent the DNS request to.
+   * Next address we sent the DNS request to.
    */
-  struct sockaddr_storage addr;
+  struct DnsServer *ds_pos;
 
   /**
-   * Number of bytes in @e addr.
+   * Context this request executes in.
    */
-  socklen_t addrlen;
+  struct GNUNET_DNSSTUB_Context *ctx;
 
   /**
    * Query we sent to @e addr.
@@ -109,6 +99,29 @@ struct GNUNET_DNSSTUB_RequestSocket
 
 
 /**
+ * DNS Server used for resolution.
+ */
+struct DnsServer
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct DnsServer *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct DnsServer *prev;
+
+  /**
+   * IP address of the DNS resolver.
+   */
+  struct sockaddr_storage ss;
+};
+
+
+/**
  * Handle to the stub resolver.
  */
 struct GNUNET_DNSSTUB_Context
@@ -117,13 +130,28 @@ struct GNUNET_DNSSTUB_Context
   /**
    * Array of all open sockets for DNS requests.
    */
-  struct GNUNET_DNSSTUB_RequestSocket sockets[DNS_SOCKET_MAX];
+  struct GNUNET_DNSSTUB_RequestSocket *sockets;
+
+  /**
+   * DLL of DNS resolvers we use.
+   */
+  struct DnsServer *dns_head;
+
+  /**
+   * DLL of DNS resolvers we use.
+   */
+  struct DnsServer *dns_tail;
+
+  /**
+   * How frequently do we retry requests?
+   */
+  struct GNUNET_TIME_Relative retry_freq;
 
   /**
-   * IP address to use for the DNS server if we are a DNS exit service
-   * (for VPN via cadet); otherwise NULL.
+   * Length of @e sockets array.
    */
-  char *dns_exit;
+  unsigned int num_sockets;
+
 };
 
 
@@ -212,33 +240,21 @@ open_socket (int af)
 
 
 /**
- * Read a DNS response from the (unhindered) UDP-Socket
- *
- * @param cls socket to read from
- */
-static void
-read_response (void *cls);
-
-
-/**
  * Get a socket of the specified address family to send out a
  * UDP DNS request to the Internet.
  *
  * @param ctx the DNSSTUB context
- * @param af desired address family
- * @return NULL on error (given AF not "supported")
+ * @return NULL on error
  */
 static struct GNUNET_DNSSTUB_RequestSocket *
-get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
-                   int af)
+get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
 {
   struct GNUNET_DNSSTUB_RequestSocket *rs;
-  struct GNUNET_NETWORK_FDSet *rset;
 
   for (unsigned int i=0;i<256;i++)
   {
     rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
-                                                 DNS_SOCKET_MAX)];
+                                                 ctx->num_sockets)];
     if (NULL == rs->rc)
       break;
   }
@@ -246,25 +262,10 @@ get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
   {
     /* signal "failure" */
     rs->rc (rs->rc_cls,
-            rs,
             NULL,
             0);
     rs->rc = NULL;
   }
-  rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
-  switch (af)
-  {
-  case AF_INET:
-    if (NULL == rs->dnsout4)
-      rs->dnsout4 = open_socket (AF_INET);
-    break;
-  case AF_INET6:
-    if (NULL == rs->dnsout6)
-      rs->dnsout6 = open_socket (AF_INET6);
-    break;
-  default:
-    return NULL;
-  }
   if (NULL != rs->read_task)
   {
     GNUNET_SCHEDULER_cancel (rs->read_task);
@@ -280,194 +281,7 @@ get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
     GNUNET_free (rs->request);
     rs->request = NULL;
   }
-  if ( (NULL == rs->dnsout4) &&
-       (NULL == rs->dnsout6) )
-    return NULL;
-  rset = GNUNET_NETWORK_fdset_create ();
-  if (NULL != rs->dnsout4)
-    GNUNET_NETWORK_fdset_set (rset,
-                              rs->dnsout4);
-  if (NULL != rs->dnsout6)
-    GNUNET_NETWORK_fdset_set (rset,
-                              rs->dnsout6);
-  rs->read_task = GNUNET_SCHEDULER_add_select 
(GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                              REQUEST_TIMEOUT,
-                                              rset,
-                                              NULL,
-                                              &read_response,
-                                               rs);
-  GNUNET_NETWORK_fdset_destroy (rset);
-  return rs;
-}
-
-
-/**
- * Task to (re)transmit the DNS query, possibly repeatedly until
- * we succeed.
- *
- * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
- */
-static void
-transmit_query (void *cls)
-{
-  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
-  struct GNUNET_NETWORK_Handle *ret;
-
-  rs->retry_task = NULL;
-  ret = (NULL != rs->dnsout4) ? rs->dnsout4 : rs->dnsout6;
-  GNUNET_assert (NULL != ret);
-  if (GNUNET_SYSERR ==
-      GNUNET_NETWORK_socket_sendto (ret,
-                                   rs->request,
-                                   rs->request_len,
-                                   (struct sockaddr *) &rs->addr,
-                                   rs->addrlen))
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               _("Failed to send DNS request to %s\n"),
-               GNUNET_a2s ((struct sockaddr *) &rs->addr,
-                            rs->addrlen));
-  else
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-               _("Sent DNS request to %s\n"),
-               GNUNET_a2s ((struct sockaddr *) &rs->addr,
-                            rs->addrlen));
-  rs->retry_task = GNUNET_SCHEDULER_add_delayed (DNS_RETRANSMIT_DELAY,
-                                                 &transmit_query,
-                                                 rs);
-}
-
-
-/**
- * Perform DNS resolution.
- *
- * @param ctx stub resolver to use
- * @param sa the socket address
- * @param sa_len the length of @a sa
- * @param request DNS request to transmit
- * @param request_len number of bytes in @a request
- * @param rc function to call with result
- * @param rc_cls closure for @a rc
- * @return socket used for the request, NULL on error
- */
-struct GNUNET_DNSSTUB_RequestSocket *
-GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
-                       const struct sockaddr *sa,
-                       socklen_t sa_len,
-                       const void *request,
-                       size_t request_len,
-                       GNUNET_DNSSTUB_ResultCallback rc,
-                       void *rc_cls)
-{
-  struct GNUNET_DNSSTUB_RequestSocket *rs;
-
-  if (NULL == (rs = get_request_socket (ctx,
-                                        sa->sa_family)))
-    return NULL;
-  GNUNET_assert (NULL == rs->rc);
-  GNUNET_memcpy (&rs->addr,
-                 sa,
-                 sa_len);
-  rs->addrlen = sa_len;
-  rs->rc = rc;
-  rs->rc_cls = rc_cls;
-  rs->request = GNUNET_memdup (request,
-                               request_len);
-  rs->request_len = request_len;
-  rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
-                                             rs);
-  return rs;
-}
-
-
-/**
- * Perform DNS resolution using our default IP from init.
- *
- * @param ctx stub resolver to use
- * @param request DNS request to transmit
- * @param request_len number of bytes in msg
- * @param rc function to call with result
- * @param rc_cls closure for 'rc'
- * @return socket used for the request, NULL on error
- */
-struct GNUNET_DNSSTUB_RequestSocket *
-GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
-                        const void *request,
-                        size_t request_len,
-                        GNUNET_DNSSTUB_ResultCallback rc,
-                        void *rc_cls)
-{
-  int af;
-  struct sockaddr_in v4;
-  struct sockaddr_in6 v6;
-  struct sockaddr *sa;
-  socklen_t salen;
-  struct GNUNET_NETWORK_Handle *dnsout;
-  struct GNUNET_DNSSTUB_RequestSocket *rs;
-
-  memset (&v4, 0, sizeof (v4));
-  memset (&v6, 0, sizeof (v6));
-  if (1 == inet_pton (AF_INET,
-                      ctx->dns_exit,
-                      &v4.sin_addr))
-  {
-    salen = sizeof (v4);
-    v4.sin_family = AF_INET;
-    v4.sin_port = htons (53);
-#if HAVE_SOCKADDR_IN_SIN_LEN
-    v4.sin_len = (u_char) salen;
-#endif
-    sa = (struct sockaddr *) &v4;
-    af = AF_INET;
-  }
-  else if (1 == inet_pton (AF_INET6,
-                           ctx->dns_exit,
-                           &v6.sin6_addr))
-  {
-    salen = sizeof (v6);
-    v6.sin6_family = AF_INET6;
-    v6.sin6_port = htons (53);
-#if HAVE_SOCKADDR_IN_SIN_LEN
-    v6.sin6_len = (u_char) salen;
-#endif
-    sa = (struct sockaddr *) &v6;
-    af = AF_INET6;
-  }
-  else
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  if (NULL == (rs = get_request_socket (ctx,
-                                        af)))
-    return NULL;
-  GNUNET_assert (NULL == rs->rc);
-  if (NULL != rs->dnsout4)
-    dnsout = rs->dnsout4;
-  else
-    dnsout = rs->dnsout6;
-  if (NULL == dnsout)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               _("Configured DNS exit `%s' is not working / valid.\n"),
-               ctx->dns_exit);
-    return NULL;
-  }
-  GNUNET_memcpy (&rs->addr,
-                 sa,
-                 salen);
-  rs->addrlen = salen;
-  rs->rc = rc;
-  rs->rc_cls = rc_cls;
-  if (GNUNET_SYSERR ==
-      GNUNET_NETWORK_socket_sendto (dnsout,
-                                   request,
-                                   request_len,
-                                    sa,
-                                    salen))
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               _("Failed to send DNS request to %s\n"),
-               GNUNET_a2s (sa, salen));
-  rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
+  rs->ctx = ctx;
   return rs;
 }
 
@@ -484,9 +298,7 @@ static int
 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
             struct GNUNET_NETWORK_Handle *dnsout)
 {
-  struct sockaddr_storage addr;
-  socklen_t addrlen;
-  struct GNUNET_TUN_DnsHeader *dns;
+  struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
   ssize_t r;
   int len;
 
@@ -507,9 +319,15 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
              len);
   {
     unsigned char buf[len] GNUNET_ALIGN;
+    int found;
+    struct sockaddr_storage addr;
+    socklen_t addrlen;
+    struct GNUNET_TUN_DnsHeader *dns;
 
     addrlen = sizeof (addr);
-    memset (&addr, 0, sizeof (addr));
+    memset (&addr,
+            0,
+            sizeof (addr));
     r = GNUNET_NETWORK_socket_recvfrom (dnsout,
                                        buf,
                                         sizeof (buf),
@@ -522,6 +340,24 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
       GNUNET_NETWORK_socket_close (dnsout);
       return GNUNET_SYSERR;
     }
+    found = GNUNET_NO;
+    for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
+    {
+      if (0 == memcmp (&addr,
+                       &ds->ss,
+                       GNUNET_MIN (sizeof (struct sockaddr_storage),
+                                   addrlen)))
+      {
+        found = GNUNET_YES;
+        break;
+      }
+    }
+    if (GNUNET_NO == found)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Received DNS response from server we never asked (ignored)");
+      return GNUNET_NO;
+    }
     if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -530,22 +366,15 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
       return GNUNET_NO;
     }
     dns = (struct GNUNET_TUN_DnsHeader *) buf;
-    if ( (addrlen != rs->addrlen) ||
-        (GNUNET_YES !=
-          GNUNET_TUN_sockaddr_cmp ((struct sockaddr *) &rs->addr,
-                                   (struct sockaddr *) &addr,
-                                   GNUNET_YES)) ||
-       (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value_us) )
+    if (NULL == rs->rc)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Request timeout or invalid sender address; ignoring 
reply\n");
+                 "Request timeout or cancelled; ignoring reply\n");
       return GNUNET_NO;
     }
-    if (NULL != rs->rc)
-      rs->rc (rs->rc_cls,
-             rs,
-             dns,
-             r);
+    rs->rc (rs->rc_cls,
+            dns,
+            r);
   }
   return GNUNET_OK;
 }
@@ -557,44 +386,21 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
  * @param cls socket to read from
  */
 static void
-read_response (void *cls)
+read_response (void *cls);
+
+
+/**
+ * Schedule #read_response() task for @a rs.
+ *
+ * @param rs request to schedule read operation for
+ */
+static void
+schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
 {
-  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
   struct GNUNET_NETWORK_FDSet *rset;
-  const struct GNUNET_SCHEDULER_TaskContext *tc;
-
-  rs->read_task = NULL;
-  tc = GNUNET_SCHEDULER_get_task_context ();
-  if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
-  {
-    /* signal "failure" (from timeout) */
-    if (NULL != rs->rc)
-    {
-      rs->rc (rs->rc_cls,
-              rs,
-              NULL,
-              0);
-      rs->rc = NULL;
-    }
-    /* timeout */
-    cleanup_rs (rs);
-    return;
-  }
-  /* read and process ready sockets */
-  if ((NULL != rs->dnsout4) &&
-      (GNUNET_NETWORK_fdset_isset (tc->read_ready,
-                                   rs->dnsout4)) &&
-      (GNUNET_SYSERR == do_dns_read (rs,
-                                     rs->dnsout4)))
-    rs->dnsout4 = NULL;
-  if ((NULL != rs->dnsout6) &&
-      (GNUNET_NETWORK_fdset_isset (tc->read_ready,
-                                   rs->dnsout6)) &&
-      (GNUNET_SYSERR == do_dns_read (rs,
-                                     rs->dnsout6)))
-    rs->dnsout6 = NULL;
 
-  /* re-schedule read task */
+  if (NULL != rs->read_task)
+    GNUNET_SCHEDULER_cancel (rs->read_task);
   rset = GNUNET_NETWORK_fdset_create ();
   if (NULL != rs->dnsout4)
     GNUNET_NETWORK_fdset_set (rset,
@@ -603,7 +409,7 @@ read_response (void *cls)
     GNUNET_NETWORK_fdset_set (rset,
                               rs->dnsout6);
   rs->read_task = GNUNET_SCHEDULER_add_select 
(GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                              
GNUNET_TIME_absolute_get_remaining (rs->timeout),
+                                              GNUNET_TIME_UNIT_FOREVER_REL,
                                               rset,
                                               NULL,
                                               &read_response,
@@ -613,6 +419,151 @@ read_response (void *cls)
 
 
 /**
+ * Read a DNS response from the (unhindered) UDP-Socket
+ *
+ * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
+ */
+static void
+read_response (void *cls)
+{
+  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
+  const struct GNUNET_SCHEDULER_TaskContext *tc;
+
+  rs->read_task = NULL;
+  tc = GNUNET_SCHEDULER_get_task_context ();
+  /* read and process ready sockets */
+  if ( (NULL != rs->dnsout4) &&
+       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+                                    rs->dnsout4)) &&
+       (GNUNET_SYSERR ==
+        do_dns_read (rs,
+                     rs->dnsout4)) )
+    rs->dnsout4 = NULL;
+  if ( (NULL != rs->dnsout6) &&
+       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+                                    rs->dnsout6)) &&
+       (GNUNET_SYSERR ==
+        do_dns_read (rs,
+                     rs->dnsout6)) )
+    rs->dnsout6 = NULL;
+  /* re-schedule read task */
+  schedule_read (rs);
+}
+
+
+/**
+ * Task to (re)transmit the DNS query, possibly repeatedly until
+ * we succeed.
+ *
+ * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
+ */
+static void
+transmit_query (void *cls)
+{
+  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
+  struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
+  const struct sockaddr *sa;
+  socklen_t salen;
+  struct DnsServer *ds;
+  struct GNUNET_NETWORK_Handle *dnsout;
+
+  rs->retry_task = GNUNET_SCHEDULER_add_delayed (ctx->retry_freq,
+                                                 &transmit_query,
+                                                 rs);
+  ds = rs->ds_pos;
+  rs->ds_pos = ds->next;
+  if (NULL == rs->ds_pos)
+    rs->ds_pos = ctx->dns_head;
+  GNUNET_assert (NULL != ds);
+  dnsout = NULL;
+  switch (ds->ss.ss_family)
+  {
+  case AF_INET:
+    if (NULL == rs->dnsout4)
+      rs->dnsout4 = open_socket (AF_INET);
+    dnsout = rs->dnsout4;
+    sa = (const struct sockaddr *) &ds->ss;
+    salen = sizeof (struct sockaddr_in);
+    break;
+  case AF_INET6:
+    if (NULL == rs->dnsout6)
+      rs->dnsout6 = open_socket (AF_INET6);
+    dnsout = rs->dnsout6;
+    sa = (const struct sockaddr *) &ds->ss;
+    salen = sizeof (struct sockaddr_in6);
+    break;
+  default:
+    return;
+  }
+  if (NULL == dnsout)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unable to use configure DNS server, skipping\n");
+    return;
+  }
+  if (GNUNET_SYSERR ==
+      GNUNET_NETWORK_socket_sendto (dnsout,
+                                   rs->request,
+                                   rs->request_len,
+                                    sa,
+                                    salen))
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Failed to send DNS request to %s\n"),
+               GNUNET_a2s (sa,
+                            salen));
+  else
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               _("Sent DNS request to %s\n"),
+               GNUNET_a2s (sa,
+                            salen));
+  schedule_read (rs);
+}
+
+
+/**
+ * Perform DNS resolution using our default IP from init.
+ *
+ * @param ctx stub resolver to use
+ * @param request DNS request to transmit
+ * @param request_len number of bytes in msg
+ * @param rc function to call with result
+ * @param rc_cls closure for 'rc'
+ * @return socket used for the request, NULL on error
+ */
+struct GNUNET_DNSSTUB_RequestSocket *
+GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
+                        const void *request,
+                        size_t request_len,
+                        GNUNET_DNSSTUB_ResultCallback rc,
+                        void *rc_cls)
+{
+  struct GNUNET_DNSSTUB_RequestSocket *rs;
+
+  if (NULL == ctx->dns_head)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No DNS server configured for resolution\n");
+    return NULL;
+  }
+  if (NULL == (rs = get_request_socket (ctx)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No request socket available for DNS resolution\n");
+    return NULL;
+  }
+  rs->ds_pos = ctx->dns_head;
+  rs->rc = rc;
+  rs->rc_cls = rc_cls;
+  rs->request = GNUNET_memdup (request,
+                               request_len);
+  rs->request_len = request_len;
+  rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
+                                             rs);
+  return rs;
+}
+
+
+/**
  * Cancel DNS resolution.
  *
  * @param rs resolution to cancel
@@ -626,28 +577,153 @@ GNUNET_DNSSTUB_resolve_cancel (struct 
GNUNET_DNSSTUB_RequestSocket *rs)
     GNUNET_SCHEDULER_cancel (rs->retry_task);
     rs->retry_task = NULL;
   }
+  if (NULL != rs->read_task)
+  {
+    GNUNET_SCHEDULER_cancel (rs->read_task);
+    rs->read_task = NULL;
+  }
 }
 
 
 /**
  * Start a DNS stub resolver.
  *
- * @param dns_ip target IP address to use
+ * @param num_sockets how many sockets should we open
+ *        in parallel for DNS queries for this stub?
  * @return NULL on error
  */
 struct GNUNET_DNSSTUB_Context *
-GNUNET_DNSSTUB_start (const char *dns_ip)
+GNUNET_DNSSTUB_start (unsigned int num_sockets)
 {
   struct GNUNET_DNSSTUB_Context *ctx;
 
+  if (0 == num_sockets)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
   ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
-  if (NULL != dns_ip)
-    ctx->dns_exit = GNUNET_strdup (dns_ip);
+  ctx->num_sockets = num_sockets;
+  ctx->sockets = GNUNET_new_array (num_sockets,
+                                   struct GNUNET_DNSSTUB_RequestSocket);
+  ctx->retry_freq = DNS_RETRANSMIT_DELAY;
   return ctx;
 }
 
 
 /**
+ * Add nameserver for use by the DNSSTUB.  We will use
+ * all provided nameservers for resolution (round-robin).
+ *
+ * @param ctx resolver context to modify
+ * @param dns_ip target IP address to use (as string)
+ * @return #GNUNET_OK on success
+ */
+int
+GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
+                           const char *dns_ip)
+{
+  struct DnsServer *ds;
+  struct in_addr i4;
+  struct in6_addr i6;
+
+  ds = GNUNET_new (struct DnsServer);
+  if (1 == inet_pton (AF_INET,
+                      dns_ip,
+                      &i4))
+  {
+    struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
+
+    s4->sin_family = AF_INET;
+    s4->sin_port = htons (53);
+    s4->sin_addr = i4;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    s4->sin_len = (u_char) sizeof (struct sockaddr_in);
+#endif
+  }
+  else if (1 == inet_pton (AF_INET6,
+                           dns_ip,
+                           &i6))
+  {
+    struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
+
+    s6->sin6_family = AF_INET6;
+    s6->sin6_port = htons (53);
+    s6->sin6_addr = i6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
+#endif
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Malformed IP address `%s' for DNS server\n",
+                dns_ip);
+    GNUNET_free (ds);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
+                               ctx->dns_tail,
+                               ds);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add nameserver for use by the DNSSTUB.  We will use
+ * all provided nameservers for resolution (round-robin).
+ *
+ * @param ctx resolver context to modify
+ * @param sa socket address of DNS resolver to use
+ * @return #GNUNET_OK on success
+ */
+int
+GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
+                           const struct sockaddr *sa)
+{
+  struct DnsServer *ds;
+
+  ds = GNUNET_new (struct DnsServer);
+  switch (sa->sa_family)
+  {
+  case AF_INET:
+    memcpy (&ds->ss,
+            sa,
+            sizeof (struct sockaddr_in));
+    break;
+  case AF_INET6:
+    memcpy (&ds->ss,
+            sa,
+            sizeof (struct sockaddr_in6));
+    break;
+  default:
+    GNUNET_break (0);
+    GNUNET_free (ds);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
+                               ctx->dns_tail,
+                               ds);
+  return GNUNET_OK;
+}
+
+
+/**
+ * How long should we try requests before timing out?
+ * Only effective for requests issued after this call.
+ *
+ * @param ctx resolver context to modify
+ * @param retry_freq how long to wait between retries
+ */
+void
+GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
+                          struct GNUNET_TIME_Relative retry_freq)
+{
+  ctx->retry_freq = retry_freq;
+}
+
+
+/**
  * Cleanup DNSSTUB resolver.
  *
  * @param ctx stub resolver to clean up
@@ -655,15 +731,18 @@ GNUNET_DNSSTUB_start (const char *dns_ip)
 void
 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
 {
-  unsigned int i;
+  struct DnsServer *ds;
 
-  for (i=0;i<DNS_SOCKET_MAX;i++)
-    cleanup_rs (&ctx->sockets[i]);
-  if (NULL != ctx->dns_exit)
+  while (NULL != (ds = ctx->dns_head))
   {
-    GNUNET_free (ctx->dns_exit);
-    ctx->dns_exit = NULL;
+    GNUNET_CONTAINER_DLL_remove (ctx->dns_head,
+                                 ctx->dns_tail,
+                                 ds);
+    GNUNET_free (ds);
   }
+  for (unsigned int i=0;i<ctx->num_sockets;i++)
+    cleanup_rs (&ctx->sockets[i]);
+  GNUNET_free (ctx->sockets);
   GNUNET_free (ctx);
 }
 
diff --git a/src/dns/gnunet-service-dns.c b/src/dns/gnunet-service-dns.c
index 9feaa8413..39ce7f6e5 100644
--- a/src/dns/gnunet-service-dns.c
+++ b/src/dns/gnunet-service-dns.c
@@ -508,13 +508,11 @@ send_request_to_client (struct RequestRecord *rr,
  * succeeded.
  *
  * @param cls NULL
- * @param rs the socket that received the response
  * @param dns the response itself
  * @param r number of bytes in dns
  */
 static void
 process_dns_result (void *cls,
-                   struct GNUNET_DNSSTUB_RequestSocket *rs,
                    const struct GNUNET_TUN_DnsHeader *dns,
                    size_t r);
 
@@ -530,7 +528,6 @@ next_phase (struct RequestRecord *rr)
 {
   struct ClientRecord *cr;
   int nz;
-  socklen_t salen;
 
   if (rr->phase == RP_DROP)
   {
@@ -582,22 +579,27 @@ next_phase (struct RequestRecord *rr)
     next_phase (rr);
     return;
   case RP_QUERY:
+#if 0
+    /* TODO: optionally, use this to forward DNS requests to the
+       *original* DNS server instead of the one we have configured...
+       (but then we need to create a fresh dnsstub for each request
+       *and* manage the timeout) */
     switch (rr->dst_addr.ss_family)
     {
     case AF_INET:
       salen = sizeof (struct sockaddr_in);
+      sa = (const struct sockaddr *) &rr->dst_addr;
       break;
     case AF_INET6:
       salen = sizeof (struct sockaddr_in6);
+      sa = (const struct sockaddr *) &rr->dst_addr;
       break;
     default:
       GNUNET_assert (0);
     }
-
+#endif
     rr->phase = RP_INTERNET_DNS;
     rr->rs = GNUNET_DNSSTUB_resolve (dnsstub,
-                                    (struct sockaddr*) &rr->dst_addr,
-                                    salen,
                                     rr->payload,
                                     rr->payload_length,
                                     &process_dns_result,
@@ -714,13 +716,11 @@ client_disconnect_cb (void *cls,
  * succeeded.
  *
  * @param cls NULL
- * @param rs the socket that received the response
  * @param dns the response itself
  * @param r number of bytes in dns
  */
 static void
 process_dns_result (void *cls,
-                   struct GNUNET_DNSSTUB_RequestSocket *rs,
                    const struct GNUNET_TUN_DnsHeader *dns,
                    size_t r)
 {
@@ -733,8 +733,7 @@ process_dns_result (void *cls,
     return; /* ignore */
 
   rr = &requests[dns->id];
-  if ( (rr->phase != RP_INTERNET_DNS) ||
-       (rr->rs != rs) )
+  if (rr->phase != RP_INTERNET_DNS)
   {
     /* unexpected / bogus reply */
     GNUNET_STATISTICS_update (stats,
@@ -1055,8 +1054,6 @@ run (void *cls,
   char *ipv4mask;
   char *ipv6addr;
   char *ipv6prefix;
-  struct in_addr dns_exit4;
-  struct in6_addr dns_exit6;
   char *dns_exit;
   char *binary;
   int nortsetup;
@@ -1065,24 +1062,26 @@ run (void *cls,
   stats = GNUNET_STATISTICS_create ("dns", cfg);
   GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
                                 cls);
+  dnsstub = GNUNET_DNSSTUB_start (128);
+  /* TODO: support multiple DNS_EXIT servers being configured */
+  /* TODO: see above TODO on using DNS server from original packet.
+     Not sure which is best... */
   dns_exit = NULL;
-  if ( ( (GNUNET_OK !=
-         GNUNET_CONFIGURATION_get_value_string (cfg,
-                                                 "dns",
-                                                "DNS_EXIT",
-                                                &dns_exit)) ||
-        ( (1 != inet_pton (AF_INET, dns_exit, &dns_exit4)) &&
-          (1 != inet_pton (AF_INET6, dns_exit, &dns_exit6)) ) ) )
+  if ( (GNUNET_OK !=
+        GNUNET_CONFIGURATION_get_value_string (cfg,
+                                               "dns",
+                                               "DNS_EXIT",
+                                               &dns_exit)) ||
+       (GNUNET_OK !=
+        GNUNET_DNSSTUB_add_dns_ip (dnsstub,
+                                   dns_exit)) )
   {
     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
                                "dns",
                                "DNS_EXIT",
                               _("need a valid IPv4 or IPv6 address\n"));
     GNUNET_free_non_null (dns_exit);
-    dns_exit = NULL;
   }
-  dnsstub = GNUNET_DNSSTUB_start (dns_exit);
-  GNUNET_free_non_null (dns_exit);
   binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-dns");
   if (GNUNET_YES !=
       GNUNET_OS_check_helper_binary (binary,
diff --git a/src/dns/gnunet-zoneimport.c b/src/dns/gnunet-zoneimport.c
index 914868af4..860672e7a 100644
--- a/src/dns/gnunet-zoneimport.c
+++ b/src/dns/gnunet-zoneimport.c
@@ -290,13 +290,11 @@ process_record (struct Request *req,
  * Function called with the result of a DNS resolution.
  *
  * @param cls closure with the `struct Request`
- * @param rs socket that received the response
  * @param dns dns response, never NULL
  * @param dns_len number of bytes in @a dns
  */
 static void
 process_result (void *cls,
-                struct GNUNET_DNSSTUB_RequestSocket *rs,
                 const struct GNUNET_TUN_DnsHeader *dns,
                 size_t dns_len)
 {
@@ -407,11 +405,11 @@ submit_req (struct Request *req)
        (pending >= THRESH) )
     return GNUNET_SYSERR;
   GNUNET_assert (NULL == req->rs);
-  req->rs = GNUNET_DNSSTUB_resolve2 (ctx,
-                                     req->raw,
-                                     req->raw_len,
-                                     &process_result,
-                                     req);
+  req->rs = GNUNET_DNSSTUB_resolve (ctx,
+                                    req->raw,
+                                    req->raw_len,
+                                    &process_result,
+                                    req);
   GNUNET_assert (NULL != req->rs);
   req->issue_num++;
   last_request = now;
@@ -561,13 +559,23 @@ main (int argc,
              "Missing required configuration argument\n");
     return -1;
   }
-  ctx = GNUNET_DNSSTUB_start (argv[1]);
+  ctx = GNUNET_DNSSTUB_start (256);
   if (NULL == ctx)
   {
     fprintf (stderr,
              "Failed to initialize GNUnet DNS STUB\n");
     return 1;
   }
+  if (GNUNET_OK !=
+      GNUNET_DNSSTUB_add_dns_ip (ctx,
+                                 argv[1]))
+  {
+    fprintf (stderr,
+             "Failed to use `%s' for DNS resolver\n",
+             argv[1]);
+    return 1;
+  }
+
   while (NULL !=
          fgets (hn,
                 sizeof (hn),
diff --git a/src/exit/gnunet-daemon-exit.c b/src/exit/gnunet-daemon-exit.c
index 0b3cc505a..5cb1ebfd9 100644
--- a/src/exit/gnunet-daemon-exit.c
+++ b/src/exit/gnunet-daemon-exit.c
@@ -458,13 +458,11 @@ GNUNET_NETWORK_STRUCT_END
  * succeeded.
  *
  * @param cls NULL
- * @param rs the socket that received the response
  * @param dns the response itself
  * @param r number of bytes in @a dns
  */
 static void
 process_dns_result (void *cls,
-                   struct GNUNET_DNSSTUB_RequestSocket *rs,
                    const struct GNUNET_TUN_DnsHeader *dns,
                    size_t r)
 {
@@ -479,8 +477,7 @@ process_dns_result (void *cls,
     return;
   /* Handle case that this is a reply to a request from a CADET DNS channel */
   ts = channels[dns->id];
-  if ( (NULL == ts) ||
-       (ts->specifics.dns.rs != rs) )
+  if (NULL == ts)
     return;
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Got a response from the stub resolver for DNS request received via 
CADET!\n");
@@ -557,11 +554,11 @@ handle_dns_request (void *cls,
                  dlen);
   dout = (struct GNUNET_TUN_DnsHeader *) buf;
   dout->id = ts->specifics.dns.my_id;
-  ts->specifics.dns.rs = GNUNET_DNSSTUB_resolve2 (dnsstub,
-                                                 buf,
-                                                  dlen,
-                                                 &process_dns_result,
-                                                 NULL);
+  ts->specifics.dns.rs = GNUNET_DNSSTUB_resolve (dnsstub,
+                                                 buf,
+                                                 dlen,
+                                                 &process_dns_result,
+                                                 NULL);
   if (NULL == ts->specifics.dns.rs)
   {
     GNUNET_break_op (0);
@@ -3545,25 +3542,23 @@ advertise_dns_exit ()
   };
   char *dns_exit;
   struct GNUNET_HashCode port;
-  struct in_addr dns_exit4;
-  struct in6_addr dns_exit6;
 
   if (GNUNET_YES !=
       GNUNET_CONFIGURATION_get_value_yesno (cfg,
                                             "exit",
                                             "EXIT_DNS"))
     return;
+  GNUNET_assert (NULL != (dnsstub = GNUNET_DNSSTUB_start (128)));
+  dns_exit = NULL;
+  /* TODO: support using multiple DNS resolvers */
   if ( (GNUNET_OK !=
         GNUNET_CONFIGURATION_get_value_string (cfg,
                                                "exit",
                                                "DNS_RESOLVER",
                                                &dns_exit)) ||
-       ( (1 != inet_pton (AF_INET,
-                          dns_exit,
-                          &dns_exit4)) &&
-         (1 != inet_pton (AF_INET6,
-                          dns_exit,
-                          &dns_exit6)) ) )
+       (GNUNET_OK !=
+        GNUNET_DNSSTUB_add_dns_ip (dnsstub,
+                                   dns_exit)) )
   {
     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
                               "dns",
diff --git a/src/gns/gnunet-dns2gns.c b/src/gns/gnunet-dns2gns.c
index 47cc6dde0..bc66f1325 100644
--- a/src/gns/gnunet-dns2gns.c
+++ b/src/gns/gnunet-dns2gns.c
@@ -247,19 +247,16 @@ do_timeout (void *cls)
  * Iterator called on obtained result for a DNS lookup
  *
  * @param cls closure
- * @param rs the request socket
  * @param dns the DNS udp payload
  * @param r size of the DNS payload
  */
 static void
 dns_result_processor (void *cls,
-                     struct GNUNET_DNSSTUB_RequestSocket *rs,
                      const struct GNUNET_TUN_DnsHeader *dns,
                      size_t r)
 {
   struct Request *request = cls;
 
-  (void) rs;
   if (NULL == dns)
   {
     /* DNSSTUB gave up, so we trigger timeout early */
@@ -307,11 +304,11 @@ result_processor (void *cls,
     request->original_request_id = request->packet->id;
     GNUNET_DNSPARSER_free_packet (request->packet);
     request->packet = NULL;
-    request->dns_lookup = GNUNET_DNSSTUB_resolve2 (dns_stub,
-                                                  request->udp_msg,
-                                                  request->udp_msg_size,
-                                                  &dns_result_processor,
-                                                  request);
+    request->dns_lookup = GNUNET_DNSSTUB_resolve (dns_stub,
+                                                  request->udp_msg,
+                                                  request->udp_msg_size,
+                                                  &dns_result_processor,
+                                                  request);
     return;
   }
   packet = request->packet;
@@ -594,8 +591,12 @@ run (void *cls,
                                 NULL);
   if (NULL == (gns = GNUNET_GNS_connect (cfg)))
     return;
-  if (NULL == (dns_stub = GNUNET_DNSSTUB_start (dns_ip)))
+  GNUNET_assert (NULL != (dns_stub = GNUNET_DNSSTUB_start (128)));
+  if (GNUNET_OK !=
+      GNUNET_DNSSTUB_add_dns_ip (dns_stub,
+                                 dns_ip))
   {
+    GNUNET_DNSSTUB_stop (dns_stub);
     GNUNET_GNS_disconnect (gns);
     gns = NULL;
     return;
diff --git a/src/gns/gnunet-service-gns_resolver.c 
b/src/gns/gnunet-service-gns_resolver.c
index 94819b040..74eb47f29 100644
--- a/src/gns/gnunet-service-gns_resolver.c
+++ b/src/gns/gnunet-service-gns_resolver.c
@@ -72,6 +72,57 @@
  * DLL to hold the authority chain we had to pass in the resolution
  * process.
  */
+struct AuthorityChain;
+
+
+/**
+ * Element of a resolution process for looking up the
+ * responsible DNS server hostname in a GNS2DNS recursive
+ * resolution.
+ */
+struct Gns2DnsPending
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct Gns2DnsPending *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct Gns2DnsPending *prev;
+
+  /**
+   * Context this activity belongs with.
+   */
+  struct AuthorityChain *ac;
+
+  /**
+   * Handle for the resolution of the IP part of the
+   * GNS2DNS record.  Will return to us the addresses
+   * of the DNS resolver to use.
+   */
+  struct GNS_ResolverHandle *rh;
+
+  /**
+   * Handle for DNS resolution of the DNS nameserver.
+   */
+  struct GNUNET_RESOLVER_RequestHandle *dns_rh;
+};
+
+
+/**
+ * Handle to a currenty pending resolution.  On result (positive or
+ * negative) the #GNS_ResultProcessor is called.
+ */
+struct GNS_ResolverHandle;
+
+
+/**
+ * DLL to hold the authority chain we had to pass in the resolution
+ * process.
+ */
 struct AuthorityChain
 {
   /**
@@ -131,11 +182,32 @@ struct AuthorityChain
       char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
 
       /**
-       * IP address of the DNS resolver that is authoritative.
-       * (this implementation currently only supports one
-       * IP at a time).
+       * List of resolutions of the 'ip' of the name server that
+       * are still pending.
+       */
+      struct Gns2DnsPending *gp_head;
+
+      /**
+       * Tail of list of resolutions of the 'ip' of the name server that
+       * are still pending.
+       */
+      struct Gns2DnsPending *gp_tail;
+
+      /**
+       * Handle to perform DNS lookups with this authority (in GNS2DNS 
handling).
+       */
+      struct GNUNET_DNSSTUB_Context *dns_handle;
+
+      /**
+       * Did we succeed in getting an IP address for *any* of the DNS servers 
listed?
+       * Once we do, we can start with DNS queries.
+       */
+      int found;
+
+      /**
+       * Did we start the recursive resolution via DNS?
        */
-      struct sockaddr_storage dns_ip;
+      int launched;
 
     } dns_authority;
 
@@ -218,34 +290,6 @@ struct VpnContext
 
 
 /**
- * Information we keep during the resolution of an
- * IP address for a DNS server while handling a
- * GNS2DNS record.
- */
-struct Gns2DnsContext
-{
-
-  /**
-   * DNS domain in which the resolution will continue
-   * (first part of the GNS2DNS record).
-   */
-  char *ns;
-
-  /**
-   * Handle for the resolution of the IP part of the
-   * GNS2DNS record.  Will return to us the addresses
-   * of the DNS resolver to use.
-   */
-  struct GNS_ResolverHandle *rh;
-
-  /**
-   * Handle for DNS resolution of the DNS nameserver.
-   */
-  struct GNUNET_RESOLVER_RequestHandle *dns_rh;
-};
-
-
-/**
  * Handle to a currenty pending resolution.  On result (positive or
  * negative) the #GNS_ResultProcessor is called.
  */
@@ -278,12 +322,6 @@ struct GNS_ResolverHandle
   void* proc_cls;
 
   /**
-   * Handle used during GNS2DNS resolution for looking up the
-   * IP address of the DNS server.
-   */
-  struct Gns2DnsContext *g2dc;
-
-  /**
    * Handle for DHT lookups. should be NULL if no lookups are in progress
    */
   struct GNUNET_DHT_GetHandle *get_handle;
@@ -431,11 +469,6 @@ static struct GNUNET_VPN_Handle *vpn_handle;
 static struct GNUNET_DHT_Handle *dht_handle;
 
 /**
- * Handle to perform DNS lookups.
- */
-static struct GNUNET_DNSSTUB_Context *dns_handle;
-
-/**
  * Heap for limiting parallel DHT lookups
  */
 static struct GNUNET_CONTAINER_Heap *dht_lookup_heap;
@@ -543,21 +576,53 @@ translate_dot_plus (struct GNS_ResolverHandle *rh,
 
 
 /**
- * Task scheduled to asynchronously fail a resolution.
+ * Wrapper around #GNS_resolver_lookup_cancel() as a task.
+ * Used for delayed cleanup so we can unwind the stack first.
  *
- * @param cls the 'struct GNS_ResolverHandle' of the resolution to fail
+ * @param cls the `struct GNS_ResolverHandle`
  */
 static void
-fail_resolution (void *cls)
+GNS_resolver_lookup_cancel_ (void *cls)
 {
   struct GNS_ResolverHandle *rh = cls;
 
   rh->task_id = NULL;
-  rh->proc (rh->proc_cls, 0, NULL);
   GNS_resolver_lookup_cancel (rh);
 }
 
 
+/**
+ * Function called to asynchronously fail a resolution.
+ *
+ * @param rh the resolution to fail
+ */
+static void
+fail_resolution (struct GNS_ResolverHandle *rh)
+{
+  rh->proc (rh->proc_cls,
+            0,
+            NULL);
+  GNUNET_assert (NULL == rh->task_id);
+  rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
+                                          rh);
+}
+
+
+/**
+ * Function called when a resolution times out.
+ *
+ * @param cls the `struct GNS_ResolverHandle`
+ */
+static void
+timeout_resolution (void *cls)
+{
+  struct GNS_ResolverHandle *rh = cls;
+
+  rh->task_id = NULL;
+  fail_resolution (rh);
+}
+
+
 #if (defined WINDOWS) || (defined DARWIN)
 /* Don't have this on W32, here's a naive implementation
  * Was somehow removed on OS X ...  */
@@ -831,13 +896,11 @@ start_resolver_lookup (void *cls);
  *
  * @param cls the request handle of the resolution that
  *        we were attempting to make
- * @param rs socket that received the response
  * @param dns dns response, never NULL
  * @param dns_len number of bytes in @a dns
  */
 static void
 dns_result_parser (void *cls,
-                  struct GNUNET_DNSSTUB_RequestSocket *rs,
                   const struct GNUNET_TUN_DnsHeader *dns,
                   size_t dns_len)
 {
@@ -845,18 +908,13 @@ dns_result_parser (void *cls,
   struct GNUNET_DNSPARSER_Packet *p;
   const struct GNUNET_DNSPARSER_Record *rec;
   unsigned int rd_count;
-  unsigned int i;
 
-  (void) rs;
   if (NULL == dns)
   {
     rh->dns_request = NULL;
     GNUNET_SCHEDULER_cancel (rh->task_id);
     rh->task_id = NULL;
-    rh->proc (rh->proc_cls,
-              0,
-              NULL);
-    GNS_resolver_lookup_cancel (rh);
+    fail_resolution (rh);
     return;
   }
   if (rh->original_dns_id != dns->id)
@@ -872,6 +930,8 @@ dns_result_parser (void *cls,
                _("Failed to parse DNS response\n"));
     return;
   }
+
+  /* We got a result from DNS */
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received DNS response for `%s' with %u answers\n",
              rh->ac_tail->label,
@@ -920,8 +980,10 @@ dns_result_parser (void *cls,
 
     buf_off = 0;
     skip = 0;
-    memset (rd, 0, sizeof (rd));
-    for (i=0;i<rd_count;i++)
+    memset (rd,
+            0,
+            sizeof (rd));
+    for (unsigned int i=0;i<rd_count;i++)
     {
       if (i < p->num_answers)
        rec = &p->answers[i];
@@ -1043,9 +1105,12 @@ dns_result_parser (void *cls,
     rh->proc (rh->proc_cls,
               rd_count - skip,
               rd);
-    GNS_resolver_lookup_cancel (rh);
   }
   GNUNET_DNSPARSER_free_packet (p);
+  if (NULL != rh->task_id)
+    GNUNET_SCHEDULER_cancel (rh->task_id); /* should be timeout task */
+  rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
+                                          rh);
 }
 
 
@@ -1061,7 +1126,6 @@ static void
 recursive_dns_resolution (struct GNS_ResolverHandle *rh)
 {
   struct AuthorityChain *ac;
-  socklen_t sa_len;
   struct GNUNET_DNSPARSER_Query *query;
   struct GNUNET_DNSPARSER_Packet *p;
   char *dns_request;
@@ -1074,20 +1138,6 @@ recursive_dns_resolution (struct GNS_ResolverHandle *rh)
              "Starting DNS lookup for `%s'\n",
              ac->label);
   GNUNET_assert (GNUNET_NO == ac->gns_authority);
-  switch (((const struct sockaddr *) 
&ac->authority_info.dns_authority.dns_ip)->sa_family)
-  {
-  case AF_INET:
-    sa_len = sizeof (struct sockaddr_in);
-    break;
-  case AF_INET6:
-    sa_len = sizeof (struct sockaddr_in6);
-    break;
-  default:
-    GNUNET_break (0);
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
-    return;
-  }
   query = GNUNET_new (struct GNUNET_DNSPARSER_Query);
   query->name = GNUNET_strdup (ac->label);
   query->type = rh->record_type;
@@ -1109,20 +1159,22 @@ recursive_dns_resolution (struct GNS_ResolverHandle *rh)
     rh->proc (rh->proc_cls,
              0,
              NULL);
-    GNS_resolver_lookup_cancel (rh);
+    GNUNET_assert (NULL == rh->task_id);
+    rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
+                                            rh);
   }
   else
   {
     rh->original_dns_id = p->id;
-    rh->dns_request = GNUNET_DNSSTUB_resolve (dns_handle,
-                                             (const struct sockaddr *) 
&ac->authority_info.dns_authority.dns_ip,
-                                             sa_len,
+    GNUNET_assert (NULL != ac->authority_info.dns_authority.dns_handle);
+    GNUNET_assert (NULL == rh->dns_request);
+    rh->dns_request = GNUNET_DNSSTUB_resolve 
(ac->authority_info.dns_authority.dns_handle,
                                              dns_request,
                                              dns_request_length,
                                              &dns_result_parser,
                                              rh);
     rh->task_id = GNUNET_SCHEDULER_add_delayed (DNS_LOOKUP_TIMEOUT,
-                                               &fail_resolution,
+                                               &timeout_resolution,
                                                rh);
   }
   if (GNUNET_SYSERR != ret)
@@ -1293,15 +1345,47 @@ vpn_allocation_cb (void *cls,
 
 
 /**
+ * We have resolved one or more of the nameservers for a
+ * GNS2DNS lookup.  Once we have some of them, begin using
+ * the DNSSTUB resolver.
+ *
+ * @param ac context for GNS2DNS resolution
+ */
+static void
+continue_with_gns2dns (struct AuthorityChain *ac)
+{
+  struct GNS_ResolverHandle *rh = ac->rh;
+
+  if ( (NULL != ac->authority_info.dns_authority.gp_head) &&
+       (GNUNET_NO == ac->authority_info.dns_authority.found) )
+    return; /* more pending and none found yet */
+  if (GNUNET_NO == ac->authority_info.dns_authority.found)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Failed to resolve DNS server for `%s' in GNS2DNS 
resolution\n",
+                ac->authority_info.dns_authority.name);
+    fail_resolution (rh);
+    return;
+  }
+  if (GNUNET_NO != ac->authority_info.dns_authority.launched)
+    return; /* already running, do not launch again! */
+  /* recurse */
+  ac->authority_info.dns_authority.launched = GNUNET_YES;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Will continue resolution using DNS to resolve `%s'\n",
+              ac->label);
+  GNUNET_assert (NULL == rh->task_id);
+  rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
+                                          rh);
+
+}
+
+
+/**
  * We've resolved the IP address for the DNS resolver to use
  * after encountering a GNS2DNS record.
  *
- * TODO: Right now we only foward the request to ONE DNS resolver,
- * even if we get multiple IP addresses back; a correct implementation
- * should try all DNS resolvers.
- *
- * @param cls the `struct GNS_ResolverHandle` where we encountered
- *            the GNS2DNS record
+ * @param cls the `struct Gns2DnsPending` used for this request
  * @param rd_count number of records in @a rd
  * @param rd addresses for the DNS resolver  (presumably)
  */
@@ -1310,136 +1394,96 @@ handle_gns2dns_result (void *cls,
                        unsigned int rd_count,
                        const struct GNUNET_GNSRECORD_Data *rd)
 {
-  struct GNS_ResolverHandle *rh = cls;
-  struct AuthorityChain *ac;
-  struct sockaddr *sa;
-  struct sockaddr_in v4;
-  struct sockaddr_in6 v6;
-  size_t sa_len;
-
-  /* find suitable A/AAAA record */
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received %u results for IP address of DNS server for GNS2DNS 
transition\n",
-              rd_count);
-  /* enable cleanup of 'rh' handle that comes next... */
-  if (NULL != rh->g2dc->rh)
+  struct Gns2DnsPending *gp = cls;
+  struct AuthorityChain *ac = gp->ac;
+
+  GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head,
+                               ac->authority_info.dns_authority.gp_tail,
+                               gp);
+  /* enable cleanup of 'rh' handle that automatically comes after we return,
+     and which expects 'rh' to be in the #rlh_head DLL. */
+  if (NULL != gp->rh)
   {
     GNUNET_CONTAINER_DLL_insert (rlh_head,
                                  rlh_tail,
-                                 rh->g2dc->rh);
-    rh->g2dc->rh = NULL;
+                                 gp->rh);
+    gp->rh = NULL;
   }
-  sa = NULL;
-  sa_len = 0;
+  GNUNET_free (gp);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received %u results for IP address of DNS server for GNS2DNS 
transition\n",
+              rd_count);
+  /* find suitable A/AAAA record */
   for (unsigned int j=0;j<rd_count;j++)
   {
     switch (rd[j].record_type)
     {
     case GNUNET_DNSPARSER_TYPE_A:
-      if (sizeof (struct in_addr) != rd[j].data_size)
       {
-        GNUNET_break_op (0);
-        rh->proc (rh->proc_cls, 0, NULL);
-        GNS_resolver_lookup_cancel (rh);
-        return;
-      }
-      /* FIXME: might want to check if we support IPv4 here,
-         and otherwise skip this one and hope we find another */
-      memset (&v4, 0, sizeof (v4));
-      sa_len = sizeof (v4);
-      v4.sin_family = AF_INET;
-      v4.sin_port = htons (53);
+        struct sockaddr_in v4;
+
+        if (sizeof (struct in_addr) != rd[j].data_size)
+        {
+          GNUNET_break_op (0);
+          continue;
+        }
+        memset (&v4,
+                0,
+                sizeof (v4));
+        v4.sin_family = AF_INET;
+        v4.sin_port = htons (53);
 #if HAVE_SOCKADDR_IN_SIN_LEN
-      v4.sin_len = (u_char) sa_len;
+        v4.sin_len = (u_char) sizeof (v4);
 #endif
-      GNUNET_memcpy (&v4.sin_addr,
-              rd[j].data,
-              sizeof (struct in_addr));
-      sa = (struct sockaddr *) &v4;
-      break;
+        GNUNET_memcpy (&v4.sin_addr,
+                       rd[j].data,
+                       sizeof (struct in_addr));
+        if (GNUNET_OK ==
+            GNUNET_DNSSTUB_add_dns_sa 
(ac->authority_info.dns_authority.dns_handle,
+                                       (const struct sockaddr *) &v4))
+          ac->authority_info.dns_authority.found = GNUNET_YES;
+        break;
+      }
     case GNUNET_DNSPARSER_TYPE_AAAA:
-      if (sizeof (struct in6_addr) != rd[j].data_size)
       {
-        GNUNET_break_op (0);
-        rh->proc (rh->proc_cls, 0, NULL);
-        GNS_resolver_lookup_cancel (rh);
-        return;
-      }
-      /* FIXME: might want to check if we support IPv6 here,
-         and otherwise skip this one and hope we find another */
-      memset (&v6, 0, sizeof (v6));
-      sa_len = sizeof (v6);
-      v6.sin6_family = AF_INET6;
-      v6.sin6_port = htons (53);
+        struct sockaddr_in6 v6;
+
+        if (sizeof (struct in6_addr) != rd[j].data_size)
+        {
+          GNUNET_break_op (0);
+          continue;
+        }
+        /* FIXME: might want to check if we support IPv6 here,
+           and otherwise skip this one and hope we find another */
+        memset (&v6,
+                0,
+                sizeof (v6));
+        v6.sin6_family = AF_INET6;
+        v6.sin6_port = htons (53);
 #if HAVE_SOCKADDR_IN_SIN_LEN
-      v6.sin6_len = (u_char) sa_len;
+        v6.sin6_len = (u_char) sizeof (v6);
 #endif
-      GNUNET_memcpy (&v6.sin6_addr,
-              rd[j].data,
-              sizeof (struct in6_addr));
-      sa = (struct sockaddr *) &v6;
-      break;
+        GNUNET_memcpy (&v6.sin6_addr,
+                       rd[j].data,
+                       sizeof (struct in6_addr));
+        if (GNUNET_OK ==
+            GNUNET_DNSSTUB_add_dns_sa 
(ac->authority_info.dns_authority.dns_handle,
+                                       (const struct sockaddr *) &v6))
+          ac->authority_info.dns_authority.found = GNUNET_YES;
+        break;
+      }
     default:
       break;
     }
-    if (NULL != sa)
-      break;
-  }
-  if (NULL == sa)
-  {
-    /* we cannot continue; NS without A/AAAA */
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
-    return;
-  }
-  /* expand authority chain */
-  ac = GNUNET_new (struct AuthorityChain);
-  ac->rh = rh;
-  GNUNET_assert (strlen (rh->g2dc->ns) <= GNUNET_DNSPARSER_MAX_NAME_LENGTH);
-  strcpy (ac->authority_info.dns_authority.name,
-          rh->g2dc->ns);
-  GNUNET_memcpy (&ac->authority_info.dns_authority.dns_ip,
-          sa,
-          sa_len);
-  /* for DNS recursion, the label is the full DNS name,
-     created from the remainder of the GNS name and the
-     name in the NS record */
-  GNUNET_asprintf (&ac->label,
-                   "%.*s%s%s",
-                   (int) rh->name_resolution_pos,
-                   rh->name,
-                   (0 != rh->name_resolution_pos) ? "." : "",
-                   rh->g2dc->ns);
-  GNUNET_free (rh->g2dc->ns);
-  GNUNET_free (rh->g2dc);
-  rh->g2dc = NULL;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Will continue resolution using DNS server `%s' to resolve 
`%s'\n",
-              GNUNET_a2s (sa,
-                          sa_len),
-              ac->label);
-  GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
-                                    rh->ac_tail,
-                                    ac);
-  if (strlen (ac->label) > GNUNET_DNSPARSER_MAX_NAME_LENGTH)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                _("GNS lookup resulted in DNS name that is too long (`%s')\n"),
-                ac->label);
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
-    return;
   }
-  /* recurse */
-  rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
-                                          rh);
+  continue_with_gns2dns (ac);
 }
 
 
 /**
  * Function called by the resolver for each address obtained from DNS.
  *
- * @param cls closure, a `struct Gns2DnsContext *`
+ * @param cls closure, a `struct Gns2DnsPending *`
  * @param addr one of the addresses of the host, NULL for the last address
  * @param addrlen length of @a addr
  */
@@ -1448,57 +1492,270 @@ handle_gns2dns_ip (void *cls,
                    const struct sockaddr *addr,
                    socklen_t addrlen)
 {
-  struct Gns2DnsContext *g2dc = cls;
-  struct GNUNET_GNSRECORD_Data rd;
-
+  struct Gns2DnsPending *gp = cls;
+  struct AuthorityChain *ac = gp->ac;
+
+  GNUNET_RESOLVER_request_cancel (gp->dns_rh);
+  GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head,
+                               ac->authority_info.dns_authority.gp_tail,
+                               gp);
+  GNUNET_free (gp);
   if (NULL == addr)
   {
     /* DNS resolution failed */
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Failed to use DNS to resolve name of DNS resolver\n");
-    g2dc->rh->g2dc = NULL;
-    fail_resolution (g2dc->rh);
-    GNUNET_free (g2dc);
   }
-  switch (addr->sa_family)
+  else
   {
-  case AF_INET:
+    if (GNUNET_OK ==
+        GNUNET_DNSSTUB_add_dns_sa (ac->authority_info.dns_authority.dns_handle,
+                                   addr))
+      ac->authority_info.dns_authority.found = GNUNET_YES;
+  }
+  continue_with_gns2dns (ac);
+}
+
+
+/**
+ * We found a CNAME record, perform recursive resolution on it.
+ *
+ * @param rh resolution handle
+ * @param rd record with CNAME to resolve recursively
+ */
+static void
+recursive_cname_resolution (struct GNS_ResolverHandle *rh,
+                            const struct GNUNET_GNSRECORD_Data *rd)
+{
+  char *cname;
+  size_t off;
+
+  off = 0;
+  cname = GNUNET_DNSPARSER_parse_name (rd->data,
+                                       rd->data_size,
+                                       &off);
+  if ( (NULL == cname) ||
+       (off != rd->data_size) )
+  {
+    GNUNET_break_op (0); /* record not well-formed */
+    GNUNET_free_non_null (cname);
+    fail_resolution (rh);
+    return;
+  }
+  handle_gns_cname_result (rh,
+                           cname);
+  GNUNET_free (cname);
+}
+
+
+/**
+ * We found a PKEY record, perform recursive resolution on it.
+ *
+ * @param rh resolution handle
+ * @param rd record with PKEY to resolve recursively
+ */
+static void
+recursive_pkey_resolution (struct GNS_ResolverHandle *rh,
+                           const struct GNUNET_GNSRECORD_Data *rd)
+{
+  struct AuthorityChain *ac;
+
+  /* delegation to another zone */
+  if (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) !=
+      rd->data_size)
+  {
+    GNUNET_break_op (0);
+    fail_resolution (rh);
+    return;
+  }
+  /* expand authority chain */
+  ac = GNUNET_new (struct AuthorityChain);
+  ac->rh = rh;
+  ac->gns_authority = GNUNET_YES;
+  ac->suggested_shortening_label = NULL;
+  ac->shortening_started = GNUNET_NO;
+  GNUNET_memcpy (&ac->authority_info.gns_authority,
+                 rd->data,
+                 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+  ac->label = resolver_lookup_get_next_label (rh);
+  /* add AC to tail */
+  GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
+                                    rh->ac_tail,
+                                    ac);
+  /* recurse */
+  rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
+                                          rh);
+}
+
+
+/**
+ * We found one or more GNS2DNS records, perform recursive resolution on it.
+ * (to be precise, one or more records in @a rd is GNS2DNS, there may be 
others,
+ * so this function still needs to check which ones are GNS2DNS).
+ *
+ * @param rh resolution handle
+ * @param rd_count length of the @a rd array
+ * @param rd record with PKEY to resolve recursively
+ * @return #GNUNET_OK if this worked, #GNUNET_SYSERR if no GNS2DNS records 
were in @a rd
+ */
+static int
+recursive_gns2dns_resolution (struct GNS_ResolverHandle *rh,
+                              unsigned int rd_count,
+                              const struct GNUNET_GNSRECORD_Data *rd)
+{
+  struct AuthorityChain *ac;
+  const char *tld;
+  char *ns;
+
+  ns = NULL;
+  /* expand authority chain */
+  ac = GNUNET_new (struct AuthorityChain);
+  ac->rh = rh;
+  ac->authority_info.dns_authority.dns_handle = GNUNET_DNSSTUB_start (4);
+
+  for (unsigned int i=0;i<rd_count;i++)
+  {
+    char *ip;
+    char *n;
+    size_t off;
+    struct Gns2DnsPending *gp;
+    struct GNUNET_CRYPTO_EcdsaPublicKey zone;
+
+    if (GNUNET_GNSRECORD_TYPE_GNS2DNS != rd[i].record_type)
+      continue;
+    off = 0;
+    n = GNUNET_DNSPARSER_parse_name (rd[i].data,
+                                     rd[i].data_size,
+                                     &off);
+    ip = GNUNET_DNSPARSER_parse_name (rd[i].data,
+                                      rd[i].data_size,
+                                      &off);
+    if ( (NULL == n) ||
+         (NULL == ip) ||
+         (off != rd[i].data_size) )
     {
-      const struct sockaddr_in *v4 = (const struct sockaddr_in *) addr;
-
-      GNUNET_assert (sizeof (*v4) == addrlen);
-      rd.data = v4;
-      rd.data_size = sizeof (*v4);
-      rd.expiration_time = UINT64_MAX;
-      rd.record_type = GNUNET_DNSPARSER_TYPE_A;
-      rd.flags = 0;
-      break;
+      GNUNET_break_op (0);
+      GNUNET_free_non_null (n);
+      GNUNET_free_non_null (ip);
+      continue;
     }
-  case AF_INET6:
+    /* resolve 'ip' to determine the IP(s) of the DNS
+       resolver to use for lookup of 'ns' */
+    if (NULL != ns)
     {
-      const struct sockaddr_in6 *v6 = (const struct sockaddr_in6 *) addr;
-
-      GNUNET_assert (sizeof (*v6) == addrlen);
-      rd.data = v6;
-      rd.data_size = sizeof (v6);
-      rd.expiration_time = UINT64_MAX;
-      rd.record_type = GNUNET_DNSPARSER_TYPE_AAAA;
-      rd.flags = 0;
-      break;
+      if (0 != strcasecmp (ns,
+                           n))
+      {
+        /* NS values must all be the same for all GNS2DNS records,
+           anything else leads to insanity */
+        GNUNET_break_op (0);
+        GNUNET_free (n);
+        GNUNET_free (ip);
+        continue;
+      }
+      GNUNET_free (n);
+    }
+    else
+    {
+      ns = n;
     }
-  default:
-    return;
-  }
-  GNUNET_RESOLVER_request_cancel (g2dc->dns_rh);
-  g2dc->dns_rh = NULL;
-  handle_gns2dns_result (g2dc->rh,
-                         1,
-                         &rd);
 
+    /* check if 'ip' is already an IPv4/IPv6 address */
+    if (GNUNET_OK ==
+        GNUNET_DNSSTUB_add_dns_ip (ac->authority_info.dns_authority.dns_handle,
+                                   ip))
+    {
+      ac->authority_info.dns_authority.found = GNUNET_YES;
+      GNUNET_free (ip);
+      continue;
+    }
+    tld = GNS_get_tld (ip);
+    if (0 != strcmp (tld,
+                     "+"))
+    {
+      /* 'ip' is a DNS name */
+      gp = GNUNET_new (struct Gns2DnsPending);
+      gp->ac = ac;
+      GNUNET_CONTAINER_DLL_insert (ac->authority_info.dns_authority.gp_head,
+                                   ac->authority_info.dns_authority.gp_tail,
+                                   gp);
+      gp->dns_rh = GNUNET_RESOLVER_ip_get (ip,
+                                           AF_UNSPEC,
+                                           GNUNET_TIME_UNIT_FOREVER_REL,
+                                           &handle_gns2dns_ip,
+                                           gp);
+      GNUNET_free (ip);
+      continue;
+    }
+    /* 'ip' should be a GNS name */
+    gp = GNUNET_new (struct Gns2DnsPending);
+    gp->ac = ac;
+    GNUNET_CONTAINER_DLL_insert (ac->authority_info.dns_authority.gp_head,
+                                 ac->authority_info.dns_authority.gp_tail,
+                                 gp);
+    gp->rh = GNUNET_new (struct GNS_ResolverHandle);
+    ip = translate_dot_plus (rh,
+                             ip);
+    tld = GNS_get_tld (ip);
+    if (GNUNET_OK !=
+        GNUNET_GNSRECORD_zkey_to_pkey (tld,
+                                       &zone))
+    {
+      GNUNET_break_op (0);
+      GNUNET_free (ip);
+      continue;
+    }
+    gp->rh->authority_zone = zone;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Resolving `%s' to determine IP address of DNS server for 
GNS2DNS transition for `%s'\n",
+                ip,
+                ns);
+    gp->rh->name = ip;
+    gp->rh->name_resolution_pos = strlen (ip) - strlen (tld) - 1;
+    gp->rh->proc = &handle_gns2dns_result;
+    gp->rh->proc_cls = gp;
+    gp->rh->record_type = GNUNET_GNSRECORD_TYPE_ANY;
+    gp->rh->options = GNUNET_GNS_LO_DEFAULT;
+    gp->rh->loop_limiter = rh->loop_limiter + 1;
+    gp->rh->task_id
+      = GNUNET_SCHEDULER_add_now (&start_resolver_lookup,
+                                  gp->rh);
+  } /* end 'for all records' */
+
+  if (NULL == ns)
+  {
+    /* not a single GNS2DNS record found */
+    GNUNET_free (ac);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_assert (strlen (ns) <= GNUNET_DNSPARSER_MAX_NAME_LENGTH);
+  strcpy (ac->authority_info.dns_authority.name,
+          ns);
+  /* for DNS recursion, the label is the full DNS name,
+     created from the remainder of the GNS name and the
+     name in the NS record */
+  GNUNET_asprintf (&ac->label,
+                   "%.*s%s%s",
+                   (int) rh->name_resolution_pos,
+                   rh->name,
+                   (0 != rh->name_resolution_pos) ? "." : "",
+                   ns);
+  GNUNET_free (ns);
+  GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
+                                    rh->ac_tail,
+                                    ac);
+  if (strlen (ac->label) > GNUNET_DNSPARSER_MAX_NAME_LENGTH)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("GNS lookup resulted in DNS name that is too long (`%s')\n"),
+                ac->label);
+    return GNUNET_SYSERR;
+  }
+  continue_with_gns2dns (ac);
+  return GNUNET_OK;
 }
 
 
-
 /**
  * Process a records that were decrypted from a block.
  *
@@ -1512,7 +1769,6 @@ handle_gns_resolution_result (void *cls,
                              const struct GNUNET_GNSRECORD_Data *rd)
 {
   struct GNS_ResolverHandle *rh = cls;
-  struct AuthorityChain *ac;
   struct AuthorityChain *shorten_ac;
   char *cname;
   struct VpnContext *vpn_ctx;
@@ -1532,6 +1788,14 @@ handle_gns_resolution_result (void *cls,
              rh->ac_tail->label,
              GNUNET_GNSRECORD_z2s (&rh->ac_tail->authority_info.gns_authority),
              rd_count);
+  if (0 == rd_count)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("GNS lookup failed (zero records found)\n"));
+    fail_resolution (rh);
+    return;
+  }
+
   if (0 == rh->name_resolution_pos)
   {
     /* top-level match, are we done yet? */
@@ -1547,9 +1811,8 @@ handle_gns_resolution_result (void *cls,
           (off != rd[0].data_size) )
       {
        GNUNET_break_op (0);
-       rh->proc (rh->proc_cls, 0, NULL);
-       GNS_resolver_lookup_cancel (rh);
         GNUNET_free_non_null (cname);
+        fail_resolution (rh);
        return;
       }
       handle_gns_cname_result (rh,
@@ -1573,8 +1836,7 @@ handle_gns_resolution_result (void *cls,
                rd[i].data_size)
            {
              GNUNET_break_op (0);
-             rh->proc (rh->proc_cls, 0, NULL);
-             GNS_resolver_lookup_cancel (rh);
+              fail_resolution (rh);
              return;
            }
            vpn = (const struct GNUNET_TUN_GnsVpnRecord *) rd[i].data;
@@ -1582,8 +1844,7 @@ handle_gns_resolution_result (void *cls,
            if ('\0' != vname[rd[i].data_size - 1 - sizeof (struct 
GNUNET_TUN_GnsVpnRecord)])
            {
              GNUNET_break_op (0);
-             rh->proc (rh->proc_cls, 0, NULL);
-             GNS_resolver_lookup_cancel (rh);
+              fail_resolution (rh);
              return;
            }
            GNUNET_TUN_service_name_to_hash (vname,
@@ -1621,7 +1882,13 @@ handle_gns_resolution_result (void *cls,
            /* delegation to DNS */
             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                         "Found GNS2DNS record, delegating to DNS!\n");
-           goto do_recurse;
+            if (GNUNET_OK ==
+                recursive_gns2dns_resolution (rh,
+                                              rd_count,
+                                              rd))
+              return;
+            else
+              goto fail;
          }
        default:
          break;
@@ -1823,7 +2090,9 @@ handle_gns_resolution_result (void *cls,
            GNUNET_break_op (0);
            break;
          }
-         GNUNET_memcpy (&pub, rd[i].data, rd[i].data_size);
+         GNUNET_memcpy (&pub,
+                         rd[i].data,
+                         rd[i].data_size);
           rd_off++;
           if (GNUNET_GNSRECORD_TYPE_PKEY != rh->record_type)
           {
@@ -1856,7 +2125,13 @@ handle_gns_resolution_result (void *cls,
           }
           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                       "Found GNS2DNS record, delegating to DNS!\n");
-          goto do_recurse;
+          if (GNUNET_OK ==
+              recursive_gns2dns_resolution (rh,
+                                            rd_count,
+                                            rd))
+            return;
+          else
+            goto fail;
         }
       case GNUNET_GNSRECORD_TYPE_BOX:
         {
@@ -1901,200 +2176,35 @@ handle_gns_resolution_result (void *cls,
     rh->proc (rh->proc_cls,
               rd_off,
               rd_new);
-    GNS_resolver_lookup_cancel (rh);
+    rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
+                                            rh);
     return;
   }
- do_recurse:
-  /* need to recurse, check if we can */
-  for (unsigned int i=0;i<rd_count;i++)
+
+  switch (rd[0].record_type)
   {
-    switch (rd[i].record_type)
-    {
-    case GNUNET_GNSRECORD_TYPE_PKEY:
-      /* delegation to another zone */
-      if (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) !=
-         rd[i].data_size)
-      {
-       GNUNET_break_op (0);
-       rh->proc (rh->proc_cls, 0, NULL);
-       GNS_resolver_lookup_cancel (rh);
-       return;
-      }
-      /* expand authority chain */
-      ac = GNUNET_new (struct AuthorityChain);
-      ac->rh = rh;
-      ac->gns_authority = GNUNET_YES;
-      ac->suggested_shortening_label = NULL;
-      ac->shortening_started = GNUNET_NO;
-      GNUNET_memcpy (&ac->authority_info.gns_authority,
-             rd[i].data,
-             sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
-      ac->label = resolver_lookup_get_next_label (rh);
-      /* add AC to tail */
-      GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
-                                       rh->ac_tail,
-                                       ac);
-      /* recurse */
-      rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
-                                             rh);
+  case GNUNET_DNSPARSER_TYPE_CNAME:
+    GNUNET_break_op (1 == rd_count); /* CNAME should be unique */
+    recursive_cname_resolution (rh,
+                                &rd[0]);
+    return;
+  case GNUNET_GNSRECORD_TYPE_PKEY:
+    GNUNET_break_op (1 == rd_count); /* PKEY should be unique */
+    recursive_pkey_resolution (rh,
+                               &rd[0]);
+    return;
+  default:
+    if (GNUNET_OK ==
+        recursive_gns2dns_resolution (rh,
+                                      rd_count,
+                                      rd))
       return;
-    case GNUNET_GNSRECORD_TYPE_GNS2DNS:
-      {
-        /* TODO: Right now we only foward the request to ONE DNS resolver,
-           even if we get multiple IP addresses back; a correct implementation
-           should try all DNS resolvers. */
-       /* resolution continues within DNS */
-        struct Gns2DnsContext *g2dc;
-        char *ip;
-        char *ns;
-        const char *tld;
-        struct GNUNET_CRYPTO_EcdsaPublicKey zone;
-        struct in_addr v4;
-        struct in6_addr v6;
-
-        off = 0;
-        ns = GNUNET_DNSPARSER_parse_name (rd[i].data,
-                                          rd[i].data_size,
-                                          &off);
-        ip = GNUNET_DNSPARSER_parse_name (rd[i].data,
-                                          rd[i].data_size,
-                                          &off);
-        if ( (NULL == ns) ||
-             (NULL == ip) ||
-             (off != rd[i].data_size) )
-        {
-          GNUNET_break_op (0);
-          GNUNET_free_non_null (ns);
-          GNUNET_free_non_null (ip);
-          fail_resolution (rh);
-          return;
-        }
-        /* resolve 'ip' to determine the IP(s) of the DNS
-           resolver to use for lookup of 'ns' */
-        g2dc = GNUNET_new (struct Gns2DnsContext);
-        g2dc->ns = ns;
-        rh->g2dc = g2dc;
-
-        /* check if 'ip' is already an IPv4/IPv6 address */
-        if (1 == inet_pton (AF_INET,
-                            ip,
-                            &v4))
-        {
-          /* name is IPv4 address, pretend it's an A record */
-          struct GNUNET_GNSRECORD_Data rd;
-
-          GNUNET_free (ip);
-          rd.data = &v4;
-          rd.data_size = sizeof (v4);
-          rd.expiration_time = UINT64_MAX;
-          rd.record_type = GNUNET_DNSPARSER_TYPE_A;
-          rd.flags = 0;
-          handle_gns2dns_result (rh,
-                                 1,
-                                 &rd);
-          return;
-        }
-        if (1 == inet_pton (AF_INET6,
-                            ip,
-                            &v6))
-        {
-          /* name is IPv6 address, pretend it's an AAAA record */
-          struct GNUNET_GNSRECORD_Data rd;
-
-          GNUNET_free (ip);
-          rd.data = &v6;
-          rd.data_size = sizeof (v6);
-          rd.expiration_time = UINT64_MAX;
-          rd.record_type = GNUNET_DNSPARSER_TYPE_AAAA;
-          rd.flags = 0;
-          handle_gns2dns_result (rh,
-                                 1,
-                                 &rd);
-          return;
-        }
-
-        tld = GNS_get_tld (ip);
-        if (0 != strcmp (tld,
-                         "+"))
-        {
-          /* 'ip' is a DNS name */
-          g2dc->dns_rh = GNUNET_RESOLVER_ip_get (ip,
-                                                 AF_UNSPEC,
-                                                 GNUNET_TIME_UNIT_FOREVER_REL,
-                                                 &handle_gns2dns_ip,
-                                                 g2dc);
-          GNUNET_free (ip);
-          return;
-        }
-
-        /* 'ip' should be a GNS name */
-        g2dc->rh = GNUNET_new (struct GNS_ResolverHandle);
-
-        ip = translate_dot_plus (rh,
-                                 ip);
-        tld = GNS_get_tld (ip);
-        if (GNUNET_OK !=
-            GNUNET_GNSRECORD_zkey_to_pkey (tld,
-                                           &zone))
-        {
-          GNUNET_break_op (0);
-          GNUNET_free_non_null (ns);
-          GNUNET_free_non_null (ip);
-          GNUNET_free (g2dc);
-          fail_resolution (rh);
-          return;
-        }
-        g2dc->rh->authority_zone = zone;
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                    "Resolving `%s' to determine IP address of DNS server for 
GNS2DNS transition for `%s'\n",
-                    ip,
-                    ns);
-        g2dc->rh->name = ip;
-        g2dc->rh->name_resolution_pos = strlen (ip) - strlen (tld) - 1;
-        g2dc->rh->proc = &handle_gns2dns_result;
-        g2dc->rh->proc_cls = rh;
-        g2dc->rh->record_type = GNUNET_GNSRECORD_TYPE_ANY;
-        g2dc->rh->options = GNUNET_GNS_LO_DEFAULT;
-        g2dc->rh->loop_limiter = rh->loop_limiter + 1;
-        g2dc->rh->task_id
-         = GNUNET_SCHEDULER_add_now (&start_resolver_lookup,
-                                     g2dc->rh);
-       return;
-      }
-    case GNUNET_DNSPARSER_TYPE_CNAME:
-      {
-       char *cname;
-
-       off = 0;
-       cname = GNUNET_DNSPARSER_parse_name (rd[i].data,
-                                            rd[i].data_size,
-                                            &off);
-       if ( (NULL == cname) ||
-            (off != rd[i].data_size) )
-       {
-         GNUNET_break_op (0); /* record not well-formed */
-         rh->proc (rh->proc_cls, 0, NULL);
-         GNS_resolver_lookup_cancel (rh);
-         GNUNET_free_non_null (cname);
-         return;
-       }
-       handle_gns_cname_result (rh,
-                                cname);
-       GNUNET_free (cname);
-       return;
-      }
-      /* FIXME: handle DNAME */
-    default:
-      /* skip */
-      break;
-    }
+    break;
   }
+ fail:
   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
              _("GNS lookup recursion failed (no delegation record found)\n"));
-  rh->proc (rh->proc_cls,
-            0,
-            NULL);
-  GNS_resolver_lookup_cancel (rh);
+  fail_resolution (rh);
 }
 
 
@@ -2176,8 +2286,7 @@ handle_dht_response (void *cls,
   {
     /* how did this pass DHT block validation!? */
     GNUNET_break (0);
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
+    fail_resolution (rh);
     return;
   }
   block = data;
@@ -2188,8 +2297,7 @@ handle_dht_response (void *cls,
   {
     /* how did this pass DHT block validation!? */
     GNUNET_break (0);
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
+    fail_resolution (rh);
     return;
   }
   if (GNUNET_OK !=
@@ -2200,8 +2308,7 @@ handle_dht_response (void *cls,
                                      rh))
   {
     GNUNET_break_op (0); /* block was ill-formed */
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
+    fail_resolution (rh);
     return;
   }
   if (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh 
(block->expiration_time)).rel_value_us)
@@ -2252,8 +2359,7 @@ start_dht_request (struct GNS_ResolverHandle *rh,
     /* fail longest-standing DHT request */
     rx = GNUNET_CONTAINER_heap_peek (dht_lookup_heap);
     GNUNET_assert (NULL != rx);
-    rx->proc (rx->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rx);
+    fail_resolution (rx);
   }
 }
 
@@ -2324,8 +2430,7 @@ handle_namecache_block_response (void *cls,
                "Resolution failed for `%s' in zone %s (DHT lookup not 
permitted by configuration)\n",
                ac->label,
                GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority));
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
+    fail_resolution (rh);
     return;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2408,8 +2513,7 @@ handle_revocation_result (void *cls,
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 _("Zone %s was revoked, resolution fails\n"),
                 GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority));
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
+    fail_resolution (rh);
     return;
   }
   recursive_gns_resolution_namecache (rh);
@@ -2453,8 +2557,7 @@ recursive_resolution (void *cls)
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Encountered unbounded recursion resolving `%s'\n",
                rh->name);
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
+    fail_resolution (rh);
     return;
   }
   if (GNUNET_YES == rh->ac_tail->gns_authority)
@@ -2491,8 +2594,12 @@ start_resolver_lookup (void *cls)
     rd.expiration_time = UINT64_MAX;
     rd.record_type = GNUNET_DNSPARSER_TYPE_A;
     rd.flags = 0;
-    rh->proc (rh->proc_cls, 1, &rd);
-    GNS_resolver_lookup_cancel (rh);
+    rh->proc (rh->proc_cls,
+              1,
+              &rd);
+    GNUNET_assert (NULL == rh->task_id);
+    rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
+                                            rh);
     return;
   }
   if (1 == inet_pton (AF_INET6,
@@ -2507,8 +2614,12 @@ start_resolver_lookup (void *cls)
     rd.expiration_time = UINT64_MAX;
     rd.record_type = GNUNET_DNSPARSER_TYPE_AAAA;
     rd.flags = 0;
-    rh->proc (rh->proc_cls, 1, &rd);
-    GNS_resolver_lookup_cancel (rh);
+    rh->proc (rh->proc_cls,
+              1,
+              &rd);
+    GNUNET_assert (NULL == rh->task_id);
+    rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
+                                            rh);
     return;
   }
 
@@ -2587,37 +2698,51 @@ GNS_resolver_lookup_cancel (struct GNS_ResolverHandle 
*rh)
   GNUNET_CONTAINER_DLL_remove (rlh_head,
                               rlh_tail,
                               rh);
+  if (NULL != rh->dns_request)
+  {
+    GNUNET_DNSSTUB_resolve_cancel (rh->dns_request);
+    rh->dns_request = NULL;
+  }
   while (NULL != (ac = rh->ac_head))
   {
     GNUNET_CONTAINER_DLL_remove (rh->ac_head,
                                 rh->ac_tail,
                                 ac);
+    if (GNUNET_NO == ac->gns_authority)
+    {
+      struct Gns2DnsPending *gp;
+
+      while (NULL != (gp = ac->authority_info.dns_authority.gp_head))
+      {
+        GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head,
+                                     ac->authority_info.dns_authority.gp_tail,
+                                     gp);
+        if (NULL != gp->rh)
+        {
+          /* rh->g2dc->rh is NOT in the DLL yet, so to enable us
+             using GNS_resolver_lookup_cancel here, we need to
+             add it first... */
+          GNUNET_CONTAINER_DLL_insert (rlh_head,
+                                       rlh_tail,
+                                       gp->rh);
+          GNUNET_assert (NULL == gp->rh->task_id);
+          gp->rh->task_id = GNUNET_SCHEDULER_add_now 
(&GNS_resolver_lookup_cancel_,
+                                                      gp->rh);
+          gp->rh = NULL;
+        }
+        if (NULL != gp->dns_rh)
+        {
+          GNUNET_RESOLVER_request_cancel (gp->dns_rh);
+          gp->dns_rh = NULL;
+        }
+        GNUNET_free (gp);
+      }
+      GNUNET_DNSSTUB_stop (ac->authority_info.dns_authority.dns_handle);
+    }
     GNUNET_free (ac->label);
     GNUNET_free_non_null (ac->suggested_shortening_label);
     GNUNET_free (ac);
   }
-  if (NULL != rh->g2dc)
-  {
-    /* rh->g2dc->rh is NOT in the DLL yet, so to enable us
-       using GNS_resolver_lookup_cancel here, we need to
-       add it first... */
-    if (NULL != rh->g2dc->rh)
-    {
-      GNUNET_CONTAINER_DLL_insert (rlh_head,
-                                   rlh_tail,
-                                   rh->g2dc->rh);
-      GNS_resolver_lookup_cancel (rh->g2dc->rh);
-      rh->g2dc->rh = NULL;
-    }
-    if (NULL != rh->g2dc->dns_rh)
-    {
-      GNUNET_RESOLVER_request_cancel (rh->g2dc->dns_rh);
-      rh->g2dc->rh = NULL;
-    }
-    GNUNET_free (rh->g2dc->ns);
-    GNUNET_free (rh->g2dc);
-    rh->g2dc = NULL;
-  }
   if (NULL != rh->task_id)
   {
     GNUNET_SCHEDULER_cancel (rh->task_id);
@@ -2639,11 +2764,6 @@ GNS_resolver_lookup_cancel (struct GNS_ResolverHandle 
*rh)
     GNUNET_free (vpn_ctx->rd_data);
     GNUNET_free (vpn_ctx);
   }
-  if (NULL != rh->dns_request)
-  {
-    GNUNET_DNSSTUB_resolve_cancel (rh->dns_request);
-    rh->dns_request = NULL;
-  }
   if (NULL != rh->namecache_qe)
   {
     GNUNET_NAMECACHE_cancel (rh->namecache_qe);
@@ -2690,8 +2810,6 @@ GNS_resolver_init (struct GNUNET_NAMECACHE_Handle *nc,
                   const struct GNUNET_CONFIGURATION_Handle *c,
                   unsigned long long max_bg_queries)
 {
-  char *dns_ip;
-
   cfg = c;
   namecache_handle = nc;
   dht_handle = dht;
@@ -2706,18 +2824,6 @@ GNS_resolver_init (struct GNUNET_NAMECACHE_Handle *nc,
   if (GNUNET_NO == use_cache)
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Namecache disabled\n");
-
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (c,
-                                            "gns",
-                                            "DNS_RESOLVER",
-                                            &dns_ip))
-  {
-    /* user did not specify DNS resolver, use 8.8.8.8 */
-    dns_ip = GNUNET_strdup ("8.8.8.8");
-  }
-  dns_handle = GNUNET_DNSSTUB_start (dns_ip);
-  GNUNET_free (dns_ip);
   vpn_handle = GNUNET_VPN_connect (cfg);
 }
 
@@ -2734,7 +2840,9 @@ GNS_resolver_done ()
   /* abort active resolutions */
   while (NULL != (rh = rlh_head))
   {
-    rh->proc (rh->proc_cls, 0, NULL);
+    rh->proc (rh->proc_cls,
+              0,
+              NULL);
     GNS_resolver_lookup_cancel (rh);
   }
   while (NULL != (co = co_head))
@@ -2747,8 +2855,6 @@ GNS_resolver_done ()
   }
   GNUNET_CONTAINER_heap_destroy (dht_lookup_heap);
   dht_lookup_heap = NULL;
-  GNUNET_DNSSTUB_stop (dns_handle);
-  dns_handle = NULL;
   GNUNET_VPN_disconnect (vpn_handle);
   vpn_handle = NULL;
   dht_handle = NULL;
diff --git a/src/gns/test_gns_lookup.conf b/src/gns/test_gns_lookup.conf
index ea8c7c3fc..a9a2345c7 100644
--- a/src/gns/test_gns_lookup.conf
+++ b/src/gns/test_gns_lookup.conf
@@ -10,7 +10,7 @@ AUTOSTART = YES
 PLUGINS =
 
 [gns]
-#PREFIX = valgrind --leak-check=full --track-origins=yes
+# PREFIX = valgrind --leak-check=full --track-origins=yes
 AUTOSTART = YES
 AUTO_IMPORT_PKEY = YES
 MAX_PARALLEL_BACKGROUND_QUERIES = 10
diff --git a/src/include/gnunet_dnsstub_lib.h b/src/include/gnunet_dnsstub_lib.h
index 1c3305f7b..41e30d044 100644
--- a/src/include/gnunet_dnsstub_lib.h
+++ b/src/include/gnunet_dnsstub_lib.h
@@ -1,6 +1,6 @@
 /*
       This file is part of GNUnet
-      Copyright (C) 2012 GNUnet e.V.
+      Copyright (C) 2012, 2018 GNUnet e.V.
 
       GNUnet is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published
@@ -48,14 +48,52 @@ struct GNUNET_DNSSTUB_RequestSocket;
 /**
  * Start a DNS stub resolver.
  *
- * @param dns_ip target IP address to use
+ * @param num_sockets how many sockets should we open
+ *        in parallel for DNS queries for this stub?
  * @return NULL on error
  */
 struct GNUNET_DNSSTUB_Context *
-GNUNET_DNSSTUB_start (const char *dns_ip);
+GNUNET_DNSSTUB_start (unsigned int num_sockets);
 
 
 /**
+ * Add nameserver for use by the DNSSTUB.  We will use
+ * all provided nameservers for resolution (round-robin).
+ *
+ * @param ctx resolver context to modify
+ * @param dns_ip target IP address to use (as string)
+ * @return #GNUNET_OK on success
+ */
+int
+GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
+                           const char *dns_ip);
+
+
+/**
+ * Add nameserver for use by the DNSSTUB.  We will use
+ * all provided nameservers for resolution (round-robin).
+ *
+ * @param ctx resolver context to modify
+ * @param sa socket address of DNS resolver to use
+ * @return #GNUNET_OK on success
+ */
+int
+GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
+                           const struct sockaddr *sa);
+
+
+/**
+ * How long should we try requests before timing out?
+ * Only effective for requests issued after this call.
+ *
+ * @param ctx resolver context to modify
+ * @param retry_frequ how long to wait between retries
+ */
+void
+GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
+                          struct GNUNET_TIME_Relative retry_freq);
+
+/**
  * Cleanup DNSSTUB resolver.
  *
  * @param ctx stub resolver to clean up
@@ -66,57 +104,36 @@ GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx);
 
 /**
  * Function called with the result of a DNS resolution.
+ * Once this function is called, the resolution request
+ * is automatically cancelled / cleaned up.  In particular,
+ * the function will only be called once.
  *
  * @param cls closure
- * @param rs socket that received the response
- * @param dns dns response, never NULL
+ * @param dns dns response, NULL on hard error (i.e. timeout)
  * @param dns_len number of bytes in @a dns
  */
 typedef void
 (*GNUNET_DNSSTUB_ResultCallback)(void *cls,
-                                 struct GNUNET_DNSSTUB_RequestSocket *rs,
                                  const struct GNUNET_TUN_DnsHeader *dns,
                                  size_t dns_len);
 
 
 /**
- * Perform DNS resolution using given address.
- *
- * @param ctx stub resolver to use
- * @param sa the socket address
- * @param sa_len the socket length
- * @param request DNS request to transmit
- * @param request_len number of bytes in msg
- * @param rc function to call with result
- * @param rc_cls closure for @a rc
- * @return socket used for the request, NULL on error
- */
-struct GNUNET_DNSSTUB_RequestSocket *
-GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
-                       const struct sockaddr *sa,
-                       socklen_t sa_len,
-                       const void *request,
-                       size_t request_len,
-                       GNUNET_DNSSTUB_ResultCallback rc,
-                       void *rc_cls);
-
-
-/**
  * Perform DNS resolution using our default IP from init.
  *
  * @param ctx stub resolver to use
  * @param request DNS request to transmit
  * @param request_len number of bytes in msg
- * @param rc function to call with result
+ * @param rc function to call with result (once)
  * @param rc_cls closure for @a rc
  * @return socket used for the request, NULL on error
  */
 struct GNUNET_DNSSTUB_RequestSocket *
-GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
-                        const void *request,
-                        size_t request_len,
-                        GNUNET_DNSSTUB_ResultCallback rc,
-                        void *rc_cls);
+GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
+                        const void *request,
+                        size_t request_len,
+                        GNUNET_DNSSTUB_ResultCallback rc,
+                        void *rc_cls);
 
 
 /**
diff --git a/src/namestore/gnunet-zoneimport.c 
b/src/namestore/gnunet-zoneimport.c
index 4f4151c94..279bfddea 100644
--- a/src/namestore/gnunet-zoneimport.c
+++ b/src/namestore/gnunet-zoneimport.c
@@ -818,13 +818,11 @@ store_completed_cb (void *cls,
  * Function called with the result of a DNS resolution.
  *
  * @param cls closure with the `struct Request`
- * @param rs socket that received the response
  * @param dns dns response, never NULL
  * @param dns_len number of bytes in @a dns
  */
 static void
 process_result (void *cls,
-                struct GNUNET_DNSSTUB_RequestSocket *rs,
                 const struct GNUNET_TUN_DnsHeader *dns,
                 size_t dns_len)
 {
@@ -833,7 +831,6 @@ process_result (void *cls,
   struct GNUNET_DNSPARSER_Packet *p;
   unsigned int rd_count;
 
-  (void) rs;
   GNUNET_assert (NULL == req->hn);
   if (NULL == dns)
   {
@@ -970,11 +967,11 @@ submit_req (struct Request *req)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Requesting resolution for `%s'\n",
              req->hostname);
-  req->rs = GNUNET_DNSSTUB_resolve2 (ctx,
-                                     req->raw,
-                                     req->raw_len,
-                                     &process_result,
-                                     req);
+  req->rs = GNUNET_DNSSTUB_resolve (ctx,
+                                    req->raw,
+                                    req->raw_len,
+                                    &process_result,
+                                    req);
   GNUNET_assert (NULL != req->rs);
   req->issue_num++;
   last_request = now;
@@ -1396,13 +1393,33 @@ run (void *cls,
   (void) args;
   (void) cfgfile;
   req_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
-  ctx = GNUNET_DNSSTUB_start (dns_server);
+  ctx = GNUNET_DNSSTUB_start (256);
   if (NULL == ctx)
   {
     fprintf (stderr,
              "Failed to initialize GNUnet DNS STUB\n");
     return;
   }
+  if (NULL == args[1])
+  {
+    fprintf (stderr,
+             "You must provide a list of DNS resolvers on the command line\n");
+    return;
+  }
+  for (unsigned int i=1;NULL != args[i];i++)
+  {
+    if (GNUNET_OK !=
+        GNUNET_DNSSTUB_add_dns_ip (ctx,
+                                   args[1]))
+    {
+      fprintf (stderr,
+               "Failed to use `%s' for DNS resolver\n",
+               args[i]);
+      return;
+    }
+  }
+
+
   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
                                  NULL);
   ns = GNUNET_NAMESTORE_connect (cfg);

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]