-remove trailing whitespace
[oweals/gnunet.git] / src / dns / gnunet-service-dns.c
index 570f742d8cc24fdc7f96364dd7475761bd15d0bb..53c19d01cdacbb2e7c638ade8c7f3314c973224b 100644 (file)
@@ -34,7 +34,7 @@
  * each request will be given one of 128 random source ports, and the
  * 128 random source ports will also change "often" (less often if the
  * system is very busy, each time if we are mostly idle).  At the same
- * time, the system will never use more than 256 UDP sockets.  
+ * time, the system will never use more than 256 UDP sockets.
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
 #include "dns.h"
 #include "gnunet_dns_service.h"
 #include "gnunet_dnsparser_lib.h"
-#include "gnunet_mesh_service.h"
+#include "gnunet_dnsstub_lib.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_tun_lib.h"
 
-
 /**
- * Timeout for an external (Internet-DNS) DNS resolution
+ * Port number for DNS
  */
-#define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
+#define DNS_PORT 53
+
 
 /**
- * How many DNS sockets do we open at most at the same time?
- * (technical socket maximum is this number x2 for IPv4+IPv6)
+ * Generic logging shorthand
  */
-#define DNS_SOCKET_MAX 128
+#define LOG(kind, ...)                          \
+  GNUNET_log_from (kind, "dns", __VA_ARGS__);
+
 
 /**
  * Phases each request goes through.
@@ -87,7 +88,7 @@ enum RequestPhase
    * Global Internet query is now pending.
    */
   RP_INTERNET_DNS,
-  
+
   /**
    * Client (or global DNS request) has resulted in a response.
    * Forward to all POST-RESOLUTION clients.  If client list is empty,
@@ -110,22 +111,22 @@ enum RequestPhase
 
 /**
  * Entry we keep for each client.
- */ 
+ */
 struct ClientRecord
 {
   /**
    * Kept in doubly-linked list.
-   */ 
+   */
   struct ClientRecord *next;
 
   /**
    * Kept in doubly-linked list.
-   */ 
+   */
   struct ClientRecord *prev;
 
   /**
    * Handle to the client.
-   */ 
+   */
   struct GNUNET_SERVER_Client *client;
 
   /**
@@ -136,37 +137,9 @@ struct ClientRecord
 };
 
 
-/**
- * UDP socket we are using for sending DNS requests to the Internet.
- */
-struct RequestSocket
-{
-  
-  /**
-   * UDP socket we use for this request for IPv4
-   */
-  struct GNUNET_NETWORK_Handle *dnsout4;
-
-  /**
-   * UDP socket we use for this request for IPv6
-   */
-  struct GNUNET_NETWORK_Handle *dnsout6;
-
-  /**
-   * Task for reading from dnsout4 and dnsout6.
-   */
-  GNUNET_SCHEDULER_TaskIdentifier read_task;
-
-  /**
-   * When should this socket be closed?
-   */
-  struct GNUNET_TIME_Absolute timeout;
-};
-
-
 /**
  * Entry we keep for each active request.
- */ 
+ */
 struct RequestRecord
 {
 
@@ -184,10 +157,9 @@ struct RequestRecord
 
   /**
    * Socket we are using to transmit this request (must match if we receive
-   * a response).  Must NOT be freed as part of this request record (as it
-   * might be shared with other requests).
+   * a response).
    */
-  struct GNUNET_NETWORK_Handle *dnsout;
+  struct GNUNET_DNSSTUB_RequestSocket *rs;
 
   /**
    * Source address of the original request (for sending response).
@@ -199,11 +171,6 @@ struct RequestRecord
    */
   struct sockaddr_storage dst_addr;
 
-  /**
-   * When should this request time out?
-   */
-  struct GNUNET_TIME_Absolute timeout;
-
   /**
    * ID of this request, also basis for hashing.  Lowest 16 bit will
    * be our message ID when doing a global DNS request and our index
@@ -213,7 +180,7 @@ struct RequestRecord
 
   /**
    * Number of bytes in payload.
-   */ 
+   */
   size_t payload_length;
 
   /**
@@ -229,66 +196,6 @@ struct RequestRecord
 };
 
 
-/**
- * State we keep for each DNS tunnel that terminates at this node.
- */
-struct TunnelState
-{
-
-  /**
-   * Associated MESH tunnel.
-   */
-  struct GNUNET_MESH_Tunnel *tunnel;
-
-  /**
-   * Active request for sending a reply.
-   */
-  struct GNUNET_MESH_TransmitHandle *th;
-
-  /**
-   * DNS reply ready for transmission.
-   */
-  char *reply;
-
-  /**
-   * Socket we are using to transmit this request (must match if we receive
-   * a response).  Must NOT be freed as part of this request record (as it
-   * might be shared with other requests).
-   */
-  struct GNUNET_NETWORK_Handle *dnsout;
-
-  /**
-   * Address we sent the DNS request to.
-   */
-  struct sockaddr_storage addr;
-
-  /**
-   * When should this request time out?
-   */
-  struct GNUNET_TIME_Absolute timeout;
-
-  /**
-   * Number of bytes in 'addr'.
-   */
-  socklen_t addrlen;
-
-  /**
-   * Number of bytes in 'reply'.
-   */
-  size_t reply_length;
-
-  /**
-   * Original DNS request ID as used by the client.
-   */
-  uint16_t original_id;
-
-  /**
-   * DNS request ID that we used for forwarding.
-   */
-  uint16_t my_id;
-};
-
-
 /**
  * Global return value from 'main'.
  */
@@ -334,58 +241,15 @@ static struct GNUNET_SERVER_NotificationContext *nc;
  */
 static struct RequestRecord requests[UINT16_MAX + 1];
 
-/**
- * Array of all open requests from tunnels.
- */
-static struct TunnelState *tunnels[UINT16_MAX + 1];
-
-/**
- * Array of all open sockets for DNS requests. 
- */
-static struct RequestSocket sockets[DNS_SOCKET_MAX];
-
 /**
  * Generator for unique request IDs.
  */
 static uint64_t request_id_gen;
 
 /**
- * IP address to use for the DNS server if we are a DNS exit service
- * (for VPN via mesh); otherwise NULL.
+ * Handle to the DNS Stub resolver.
  */
-static char *dns_exit;
-
-/**
- * Handle to the MESH service (for receiving DNS queries), or NULL 
- * if we are not a DNS exit.
- */
-static struct GNUNET_MESH_Handle *mesh;
-
-
-/**
- * We're done with a RequestSocket, close it for now.
- *
- * @param rs request socket to clean up
- */
-static void
-cleanup_rs (struct RequestSocket *rs)
-{
-  if (NULL != rs->dnsout4)
-  {
-    GNUNET_NETWORK_socket_close (rs->dnsout4);
-    rs->dnsout4 = NULL;
-  }
-  if (NULL != rs->dnsout6)
-  {
-    GNUNET_NETWORK_socket_close (rs->dnsout6);
-    rs->dnsout6 = NULL;
-  }
-  if (GNUNET_SCHEDULER_NO_TASK != rs->read_task)
-  {
-    GNUNET_SCHEDULER_cancel (rs->read_task);
-    rs->read_task = GNUNET_SCHEDULER_NO_TASK;
-  }
-}
+static struct GNUNET_DNSSTUB_Context *dnsstub;
 
 
 /**
@@ -417,7 +281,7 @@ cleanup_task (void *cls GNUNET_UNUSED,
 {
   unsigned int i;
 
-  GNUNET_HELPER_stop (hijacker);
+  GNUNET_HELPER_stop (hijacker, GNUNET_NO);
   hijacker = NULL;
   for (i=0;i<7;i++)
     GNUNET_free_non_null (helper_argv[i]);
@@ -430,64 +294,11 @@ cleanup_task (void *cls GNUNET_UNUSED,
     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
     stats = NULL;
   }
-  if (NULL != dns_exit)
-  {
-    GNUNET_free (dns_exit);
-    dns_exit = NULL;
-  }
-  if (NULL != mesh)
-  {
-    GNUNET_MESH_disconnect(mesh);
-    mesh = NULL;
-  }
-}
-
-
-/**
- * Open source port for sending DNS requests
- *
- * @param af AF_INET or AF_INET6
- * @return GNUNET_OK on success
- */ 
-static struct GNUNET_NETWORK_Handle *
-open_socket (int af)
-{
-  struct sockaddr_in a4;
-  struct sockaddr_in6 a6;
-  struct sockaddr *sa;
-  socklen_t alen;
-  struct GNUNET_NETWORK_Handle *ret;
-
-  ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
-  if (NULL == ret)
-    return NULL;
-  switch (af)
-  {
-  case AF_INET:
-    memset (&a4, 0, alen = sizeof (struct sockaddr_in));
-    sa = (struct sockaddr *) &a4;
-    break;
-  case AF_INET6:
-    memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
-    sa = (struct sockaddr *) &a6;
-    break;
-  default:
-    GNUNET_break (0);
-    GNUNET_NETWORK_socket_close (ret);
-    return NULL;
-  }
-  sa->sa_family = af;
-  if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
-                                              sa, 
-                                              alen))
+  if (NULL != dnsstub)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
-               _("Could not bind to any port: %s\n"),
-               STRERROR (errno));
-    GNUNET_NETWORK_socket_close (ret);
-    return NULL;
+    GNUNET_DNSSTUB_stop (dnsstub);
+    dnsstub = NULL;
   }
-  return ret;
 }
 
 
@@ -506,14 +317,20 @@ request_done (struct RequestRecord *rr)
 
   GNUNET_array_grow (rr->client_wait_list,
                     rr->client_wait_list_length,
-                    0); 
+                    0);
   if (RP_RESPONSE_MONITOR != rr->phase)
   {
     /* no response, drop */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "Got no response for request %llu, dropping\n",
+        (unsigned long long) rr->request_id);
     cleanup_rr (rr);
     return;
   }
-  
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Transmitting response for request %llu\n",
+       (unsigned long long) rr->request_id);
   /* send response via hijacker */
   reply_len = sizeof (struct GNUNET_MessageHeader);
   reply_len += sizeof (struct GNUNET_TUN_Layer2PacketHeader);
@@ -528,7 +345,7 @@ request_done (struct RequestRecord *rr)
   default:
     GNUNET_break (0);
     cleanup_rr (rr);
-    return;   
+    return;
   }
   reply_len += sizeof (struct GNUNET_TUN_UdpHeader);
   reply_len += rr->payload_length;
@@ -537,7 +354,7 @@ request_done (struct RequestRecord *rr)
     /* response too big, drop */
     GNUNET_break (0); /* how can this be? */
     cleanup_rr(rr);
-    return;    
+    return;
   }
   {
     char buf[reply_len] GNUNET_ALIGN;
@@ -557,7 +374,7 @@ request_done (struct RequestRecord *rr)
 
       tun.flags = htons (0);
       if (rr->src_addr.ss_family == AF_INET)
-       tun.proto = htons (ETH_P_IPV4); 
+       tun.proto = htons (ETH_P_IPV4);
       else
        tun.proto = htons (ETH_P_IPV6);
       memcpy (&buf[off], &tun, sizeof (struct GNUNET_TUN_Layer2PacketHeader));
@@ -664,83 +481,37 @@ send_request_to_client (struct RequestRecord *rr,
     cleanup_rr (rr);
     return;
   }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Sending information about request %llu to local client\n",
+       (unsigned long long) rr->request_id);
   req = (struct GNUNET_DNS_Request*) buf;
   req->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST);
   req->header.size = htons (sizeof (buf));
   req->reserved = htonl (0);
   req->request_id = rr->request_id;
   memcpy (&req[1], rr->payload, rr->payload_length);
-  GNUNET_SERVER_notification_context_unicast (nc, 
+  GNUNET_SERVER_notification_context_unicast (nc,
                                              client,
                                              &req->header,
                                              GNUNET_NO);
 }
 
 
-/**
- * Read a DNS response from the (unhindered) UDP-Socket
- *
- * @param cls socket to read from
- * @param tc scheduler context (must be shutdown or read ready)
- */
-static void
-read_response (void *cls,
-              const struct GNUNET_SCHEDULER_TaskContext *tc);
-
 
 /**
- * Get a socket of the specified address family to send out a
- * UDP DNS request to the Internet.  
+ * Callback called from DNSSTUB resolver when a resolution
+ * succeeded.
  *
- * @param af desired address family
- * @return NULL on error (given AF not "supported")
+ * @param cls NULL
+ * @param rs the socket that received the response
+ * @param dns the response itself
+ * @param r number of bytes in dns
  */
-static struct GNUNET_NETWORK_Handle *
-get_request_socket (int af)
-{
-  struct RequestSocket *rs;
-  struct GNUNET_NETWORK_FDSet *rset;
-  static struct GNUNET_NETWORK_Handle *ret;
-
-  rs = &sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, 
-                                         DNS_SOCKET_MAX)];
-  rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
-  switch (af)
-  {
-  case AF_INET:
-    if (NULL == rs->dnsout4)
-      rs->dnsout4 = open_socket (AF_INET);
-    ret = rs->dnsout4;
-    break;
-  case AF_INET6:
-    if (NULL == rs->dnsout6)
-      rs->dnsout6 = open_socket (AF_INET6);
-    ret = rs->dnsout6;
-    break;
-  default:
-    return NULL;
-  }  
-  if (GNUNET_SCHEDULER_NO_TASK != rs->read_task)
-  {
-    GNUNET_SCHEDULER_cancel (rs->read_task);
-    rs->read_task = GNUNET_SCHEDULER_NO_TASK;
-  }
-  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 ret;
-}
+static void
+process_dns_result (void *cls,
+                   struct GNUNET_DNSSTUB_RequestSocket *rs,
+                   const struct GNUNET_TUN_DnsHeader *dns,
+                   size_t r);
 
 
 /**
@@ -770,13 +541,17 @@ next_phase (struct RequestRecord *rr)
       nz = (int) j;
       break;
     }
-  }  
-  if (-1 != nz) 
+  }
+  if (-1 != nz)
   {
     send_request_to_client (rr, rr->client_wait_list[nz]->client);
     return;
   }
   /* done with current phase, advance! */
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Request %llu now in phase %d\n",
+       rr->request_id,
+       rr->phase);
   switch (rr->phase)
   {
   case RP_INIT:
@@ -815,8 +590,14 @@ next_phase (struct RequestRecord *rr)
     }
 
     rr->phase = RP_INTERNET_DNS;
-    rr->dnsout = get_request_socket (rr->dst_addr.ss_family);
-    if (NULL == rr->dnsout)
+    rr->rs = GNUNET_DNSSTUB_resolve (dnsstub,
+                                    (struct sockaddr*) &rr->dst_addr,
+                                    salen,
+                                    rr->payload,
+                                    rr->payload_length,
+                                    &process_dns_result,
+                                    NULL);
+    if (NULL == rr->rs)
     {
       GNUNET_STATISTICS_update (stats,
                                gettext_noop ("# DNS exit failed (failed to open socket)"),
@@ -824,12 +605,6 @@ next_phase (struct RequestRecord *rr)
       cleanup_rr (rr);
       return;
     }
-    GNUNET_NETWORK_socket_sendto (rr->dnsout,
-                                 rr->payload,
-                                 rr->payload_length,
-                                 (struct sockaddr*) &rr->dst_addr,
-                                 salen);
-    rr->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
     return;
   case RP_INTERNET_DNS:
     rr->phase = RP_MODIFY;
@@ -872,7 +647,7 @@ next_phase (struct RequestRecord *rr)
  *
  * @param cls unused
  * @param client handle of client that disconnected
- */ 
+ */
 static void
 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
 {
@@ -899,7 +674,7 @@ client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
          if (rr->client_wait_list[j] == cr)
          {
            rr->client_wait_list[j] = NULL;
-           next_phase (rr); 
+           next_phase (rr);
          }
        }
       }
@@ -911,199 +686,45 @@ client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
 
 
 /**
- * We got a reply from DNS for a request of a MESH tunnel.  Send it
- * via the tunnel (after changing the request ID back).
- *
- * @param cls the 'struct TunnelState'
- * @param size number of bytes available in buf
- * @param buf where to copy the reply
- * @return number of bytes written to buf
- */
-static size_t
-transmit_reply_to_mesh (void *cls,
-                       size_t size,
-                       void *buf)
-{
-  struct TunnelState *ts = cls;
-  size_t off;
-  size_t ret;
-  char *cbuf = buf;
-  struct GNUNET_MessageHeader hdr;
-  struct GNUNET_TUN_DnsHeader dns;
-
-  ts->th = NULL;
-  GNUNET_assert (ts->reply != NULL);
-  if (size == 0)
-    return 0;
-  ret = sizeof (struct GNUNET_MessageHeader) + ts->reply_length; 
-  GNUNET_assert (ret <= size);
-  hdr.size = htons (ret);
-  hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET);
-  memcpy (&dns, ts->reply, sizeof (dns));
-  dns.id = ts->original_id;
-  off = 0;
-  memcpy (&cbuf[off], &hdr, sizeof (hdr));
-  off += sizeof (hdr);
-  memcpy (&cbuf[off], &dns, sizeof (dns));
-  off += sizeof (dns);
-  memcpy (&cbuf[off], &ts->reply[sizeof (dns)], ts->reply_length - sizeof (dns));
-  off += ts->reply_length - sizeof (dns);
-  GNUNET_free (ts->reply);
-  ts->reply = NULL;
-  ts->reply_length = 0;  
-  GNUNET_assert (ret == off);
-  return ret;
-}
-
-
-/**
- * Actually do the reading of a DNS packet from our UDP socket and see
- * if we have a valid, matching, pending request.
+ * Callback called from DNSSTUB resolver when a resolution
+ * succeeded.
  *
- * @param dnsout socket to read from
- * @return GNUNET_OK on success, GNUNET_NO on drop, GNUNET_SYSERR on IO-errors (closed socket)
- */
-static int
-do_dns_read (struct GNUNET_NETWORK_Handle *dnsout)
-{
-  struct sockaddr_storage addr;
-  socklen_t addrlen;
-  struct GNUNET_TUN_DnsHeader *dns;
-  struct RequestRecord *rr;
-  struct TunnelState *ts;
-  ssize_t r;
-  int len;
-
-#ifndef MINGW
-  if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
-  {
-    /* conservative choice: */
-    len = UINT16_MAX;
-  }
-#else
-  /* port the code above? */
-  len = UINT16_MAX;
-#endif
-
-  {
-    unsigned char buf[len] GNUNET_ALIGN;
-
-    addrlen = sizeof (addr);
-    memset (&addr, 0, sizeof (addr));  
-    r = GNUNET_NETWORK_socket_recvfrom (dnsout, 
-                                       buf, sizeof (buf),
-                                       (struct sockaddr*) &addr, &addrlen);
-    if (-1 == r)
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
-      GNUNET_NETWORK_socket_close (dnsout);
-      return GNUNET_SYSERR;
-    }
-    if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
-                 _("Received DNS response that is too small (%u bytes)"),
-                 r);
-      return GNUNET_NO;
-    }
-    dns = (struct GNUNET_TUN_DnsHeader *) buf;
-    /* Handle case that this is a reply to a request from a MESH DNS tunnel */
-    ts = tunnels[dns->id];
-    if ( (NULL == ts) ||
-        (ts->dnsout != dnsout) ||
-        (addrlen != ts->addrlen) ||
-        (0 != memcmp (&ts->addr,
-                      &addr,
-                      addrlen)) ||      
-        (0 == GNUNET_TIME_absolute_get_remaining (ts->timeout).rel_value) )
-      ts = NULL; /* DNS responder address missmatch */
-    if (NULL != ts)
-    {
-      tunnels[dns->id] = NULL;
-      GNUNET_free_non_null (ts->reply);
-      ts->reply = GNUNET_malloc (r);
-      ts->reply_length = r;
-      memcpy (ts->reply, dns, r);
-      if (ts->th != NULL)
-       GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
-      ts->th = GNUNET_MESH_notify_transmit_ready (ts->tunnel,
-                                                 GNUNET_NO, 0,
-                                                 GNUNET_TIME_UNIT_FOREVER_REL,
-                                                 NULL,
-                                                 sizeof (struct GNUNET_MessageHeader) + r,
-                                                 &transmit_reply_to_mesh,
-                                                 ts);
-    }
-    /* Handle case that this is a reply to a local request (intercepted from TUN interface) */
-    rr = &requests[dns->id];
-    if ( (rr->phase != RP_INTERNET_DNS) ||
-        (rr->dnsout != dnsout) ||
-        (0 != memcmp (&rr->dst_addr,
-                      &addr,
-                      addrlen)) ||
-        (0 == GNUNET_TIME_absolute_get_remaining (rr->timeout).rel_value) )
-    {
-      if (NULL == ts)
-      {
-       /* unexpected / bogus reply */
-       GNUNET_STATISTICS_update (stats,
-                                 gettext_noop ("# External DNS response discarded (no matching request)"),
-                                 1, GNUNET_NO);
-      }
-      return GNUNET_NO; 
-    }
-    GNUNET_free_non_null (rr->payload);
-    rr->payload = GNUNET_malloc (r);
-    memcpy (rr->payload, buf, r);
-    rr->payload_length = r;
-    next_phase (rr);
-  }  
-  return GNUNET_OK;
-}
-
-
-/**
- * Read a DNS response from the (unhindered) UDP-Socket
- *
- * @param cls socket to read from
- * @param tc scheduler context (must be shutdown or read ready)
+ * @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
-read_response (void *cls,
-              const struct GNUNET_SCHEDULER_TaskContext *tc)
+process_dns_result (void *cls,
+                   struct GNUNET_DNSSTUB_RequestSocket *rs,
+                   const struct GNUNET_TUN_DnsHeader *dns,
+                   size_t r)
 {
-  struct RequestSocket *rs = cls;
-  struct GNUNET_NETWORK_FDSet *rset;
+  struct RequestRecord *rr;
 
-  rs->read_task = GNUNET_SCHEDULER_NO_TASK;
-  if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Processing DNS result from stub resolver\n");
+  GNUNET_assert (NULL == cls);
+  rr = &requests[dns->id];
+  if ( (rr->phase != RP_INTERNET_DNS) ||
+       (rr->rs != rs) )
   {
-    /* timeout or shutdown */
-    cleanup_rs (rs);
+    /* unexpected / bogus reply */
+    GNUNET_STATISTICS_update (stats,
+                             gettext_noop ("# External DNS response discarded (no matching request)"),
+                             1, GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Received DNS reply that does not match any pending request.  Dropping.\n");
     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->dnsout4)))
-    rs->dnsout4 = NULL;
-  if ((NULL != rs->dnsout6) &&
-      (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
-      (GNUNET_SYSERR == do_dns_read (rs->dnsout6)))
-    rs->dnsout6 = NULL;
-
-  /* re-schedule read task */
-  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,
-                                              GNUNET_TIME_absolute_get_remaining (rs->timeout),
-                                              rset,
-                                              NULL,
-                                              &read_response, rs);
-  GNUNET_NETWORK_fdset_destroy (rset);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Got a response from the stub resolver for DNS request %llu intercepted locally!\n",
+       (unsigned long long) rr->request_id);
+  GNUNET_free_non_null (rr->payload);
+  rr->payload = GNUNET_malloc (r);
+  memcpy (rr->payload, dns, r);
+  rr->payload_length = r;
+  next_phase (rr);
 }
 
 
@@ -1115,7 +736,7 @@ read_response (void *cls,
  * @param message the init message (unused)
  */
 static void
-handle_client_init (void *cls GNUNET_UNUSED, 
+handle_client_init (void *cls GNUNET_UNUSED,
                    struct GNUNET_SERVER_Client *client,
                    const struct GNUNET_MessageHeader *message)
 {
@@ -1124,7 +745,7 @@ handle_client_init (void *cls GNUNET_UNUSED,
 
   cr = GNUNET_malloc (sizeof (struct ClientRecord));
   cr->client = client;
-  cr->flags = (enum GNUNET_DNS_Flags) ntohl (reg->flags);  
+  cr->flags = (enum GNUNET_DNS_Flags) ntohl (reg->flags);
   GNUNET_SERVER_client_keep (client);
   GNUNET_CONTAINER_DLL_insert (clients_head,
                               clients_tail,
@@ -1142,7 +763,7 @@ handle_client_init (void *cls GNUNET_UNUSED,
  * @param message the response
  */
 static void
-handle_client_response (void *cls GNUNET_UNUSED, 
+handle_client_response (void *cls GNUNET_UNUSED,
                        struct GNUNET_SERVER_Client *client,
                        const struct GNUNET_MessageHeader *message)
 {
@@ -1162,6 +783,9 @@ handle_client_response (void *cls GNUNET_UNUSED,
   resp = (const struct GNUNET_DNS_Response*) message;
   off = (uint16_t) resp->request_id;
   rr = &requests[off];
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received DNS response with ID %llu from local client!\n",
+       (unsigned long long) resp->request_id);
   if (rr->request_id != resp->request_id)
   {
     GNUNET_STATISTICS_update (stats,
@@ -1192,7 +816,7 @@ handle_client_response (void *cls GNUNET_UNUSED,
       {
        GNUNET_break (0);
        GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-       next_phase (rr); 
+       next_phase (rr);
        return;
       }
       GNUNET_free_non_null (rr->payload);
@@ -1211,7 +835,7 @@ handle_client_response (void *cls GNUNET_UNUSED,
       /* if query changed to answer, move past DNS resolution phase... */
       if ( (RP_QUERY == rr->phase) &&
           (rr->payload_length > sizeof (struct GNUNET_TUN_DnsHeader)) &&
-          ((struct GNUNET_DNSPARSER_Flags*)&(((struct GNUNET_TUN_DnsHeader*) rr->payload)->flags))->query_or_response == 1)
+          ((struct GNUNET_TUN_DnsFlags*)&(((struct GNUNET_TUN_DnsHeader*) rr->payload)->flags))->query_or_response == 1)
       {
        rr->phase = RP_INTERNET_DNS;
        GNUNET_array_grow (rr->client_wait_list,
@@ -1220,9 +844,9 @@ handle_client_response (void *cls GNUNET_UNUSED,
       }
       break;
     }
-    next_phase (rr); 
+    next_phase (rr);
     GNUNET_SERVER_receive_done (client, GNUNET_OK);
-    return;      
+    return;
   }
   /* odd, client was not on our list for the request, that ought
      to be an error */
@@ -1255,6 +879,8 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
   struct sockaddr_in *dsta4;
   struct sockaddr_in6 *dsta6;
 
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Intercepted message via DNS hijacker\n");
   msize = ntohs (message->size);
   if (msize < sizeof (struct GNUNET_MessageHeader) + sizeof (struct GNUNET_TUN_Layer2PacketHeader) + sizeof (struct GNUNET_TUN_IPv4Header))
   {
@@ -1308,8 +934,9 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
                ntohs (tun->proto));
     return GNUNET_OK;
   }
-  if (msize <= sizeof (struct GNUNET_TUN_UdpHeader) + sizeof (struct GNUNET_TUN_DnsHeader))
-  {    
+  if ( (msize <= sizeof (struct GNUNET_TUN_UdpHeader) + sizeof (struct GNUNET_TUN_DnsHeader)) ||
+       (DNS_PORT != ntohs (udp->destination_port)) )
+  {
     /* non-DNS packet received on TUN, ignore */
     GNUNET_STATISTICS_update (stats,
                              gettext_noop ("# Non-DNS UDP packet received via TUN interface"),
@@ -1375,7 +1002,9 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
   memcpy (rr->payload, dns, msize);
   rr->request_id = dns->id | (request_id_gen << 16);
   request_id_gen++;
-
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Creating new DNS request %llu\n",
+       (unsigned long long) rr->request_id);
   GNUNET_STATISTICS_update (stats,
                            gettext_noop ("# DNS requests received via TUN interface"),
                            1, GNUNET_NO);
@@ -1385,150 +1014,6 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
 }
 
 
-/**
- * Process a request via mesh to perform a DNS query.
- *
- * @param cls closure, NULL
- * @param tunnel connection to the other end
- * @param tunnel_ctx pointer to our 'struct TunnelState *'
- * @param sender who sent the message
- * @param message the actual message
- * @param atsi performance data for the connection
- * @return GNUNET_OK to keep the connection open,
- *         GNUNET_SYSERR to close it (signal serious error)
- */
-static int
-receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
-                     void **tunnel_ctx,
-                     const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
-                     const struct GNUNET_MessageHeader *message,
-                     const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
-{
-  struct TunnelState *ts = *tunnel_ctx;
-  const struct GNUNET_TUN_DnsHeader *dns;
-  size_t mlen = ntohs (message->size);
-  size_t dlen = mlen - sizeof (struct GNUNET_MessageHeader);
-  char buf[dlen] GNUNET_ALIGN;
-  struct GNUNET_TUN_DnsHeader *dout;
-  struct sockaddr_in v4;
-  struct sockaddr_in6 v6;
-  struct sockaddr *so;
-  socklen_t salen;
-  
-  if (dlen < sizeof (struct GNUNET_TUN_DnsHeader))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  dns = (const struct GNUNET_TUN_DnsHeader *) &message[1];
-  ts->original_id = dns->id;
-  if (tunnels[ts->my_id] == ts)
-    tunnels[ts->my_id] = NULL;
-  ts->my_id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 
-                                                  UINT16_MAX + 1);
-  tunnels[ts->my_id] = ts;
-  memcpy (buf, dns, dlen);
-  dout = (struct GNUNET_TUN_DnsHeader*) buf;
-  dout->id = ts->my_id;
-  memset (&v4, 0, sizeof (v4));
-  memset (&v6, 0, sizeof (v6));
-  if (1 == inet_pton (AF_INET, 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
-    so = (struct sockaddr *) &v4;
-    ts->dnsout = get_request_socket (AF_INET);
-  }
-  else if (1 == inet_pton (AF_INET6, 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
-    so = (struct sockaddr *) &v6;
-    ts->dnsout = get_request_socket (AF_INET6);
-  }  
-  else
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (NULL == ts->dnsout)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               _("Configured DNS exit `%s' is not working / valid.\n"),
-               dns_exit);
-    return GNUNET_SYSERR;
-  }
-  memcpy (&ts->addr,
-         so,
-         salen);
-  ts->addrlen = salen;
-  GNUNET_NETWORK_socket_sendto (ts->dnsout,
-                               buf, dlen, so, salen); 
-  ts->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
-  return GNUNET_OK;
-}
-
-
-/**
- * Callback from GNUNET_MESH for new tunnels.
- *
- * @param cls closure
- * @param tunnel new handle to the tunnel
- * @param initiator peer that started the tunnel
- * @param ats performance information for the tunnel
- * @return initial tunnel context for the tunnel
- */
-static void *
-accept_dns_tunnel (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
-                  const struct GNUNET_PeerIdentity *initiator GNUNET_UNUSED,
-                  const struct GNUNET_ATS_Information *ats GNUNET_UNUSED)
-{
-  struct TunnelState *ts = GNUNET_malloc (sizeof (struct TunnelState));
-
-  GNUNET_STATISTICS_update (stats,
-                           gettext_noop ("# Inbound MESH tunnels created"),
-                           1, GNUNET_NO);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Received inbound tunnel from `%s'\n",
-             GNUNET_i2s (initiator));
-  ts->tunnel = tunnel;
-  return ts;
-}
-
-
-/**
- * Function called by mesh whenever an inbound tunnel is destroyed.
- * Should clean up any associated state.
- *
- * @param cls closure (set from GNUNET_MESH_connect)
- * @param tunnel connection to the other end (henceforth invalid)
- * @param tunnel_ctx place where local state associated
- *                   with the tunnel is stored
- */
-static void
-destroy_dns_tunnel (void *cls GNUNET_UNUSED, 
-                   const struct GNUNET_MESH_Tunnel *tunnel,
-                   void *tunnel_ctx)
-{
-  struct TunnelState *ts = tunnel_ctx;
-
-  if (tunnels[ts->my_id] == ts)
-    tunnels[ts->my_id] = NULL;
-  if (NULL != ts->th)
-    GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
-  GNUNET_free_non_null (ts->reply);
-  GNUNET_free (ts);
-}
-
-
 /**
  * @param cls closure
  * @param server the initialized server
@@ -1540,7 +1025,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
 {
   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
     /* callback, cls, type, size */
-    {&handle_client_init, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT, 
+    {&handle_client_init, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT,
      sizeof (struct GNUNET_DNS_Register)},
     {&handle_client_response, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE, 0},
     {NULL, NULL, 0, 0}
@@ -1552,37 +1037,40 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
   char *ipv6prefix;
   struct in_addr dns_exit4;
   struct in6_addr dns_exit6;
+  char *dns_exit;
+  char *binary;
 
   cfg = cfg_;
+  binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-dns");
   if (GNUNET_YES !=
-      GNUNET_OS_check_helper_binary ("gnunet-helper-dns"))
+      GNUNET_OS_check_helper_binary (binary, GNUNET_YES, NULL)) // TODO: once we have a windows-testcase, add test parameters here
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("`%s' must be installed SUID, refusing to run\n"),
-               "gnunet-helper-dns");
+               binary);
     global_ret = 1;
+    GNUNET_free (binary);
     return;
   }
-
+  GNUNET_free (binary);
   stats = GNUNET_STATISTICS_create ("dns", cfg);
   nc = GNUNET_SERVER_notification_context_create (server, 1);
   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
                                 cls);
-  if ( (GNUNET_YES ==
-       GNUNET_CONFIGURATION_get_value_yesno (cfg_, "dns", "PROVIDE_EXIT")) &&
-       ( (GNUNET_OK !=
-         GNUNET_CONFIGURATION_get_value_string (cfg, "dns", 
+  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)) ) ) )
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               _("Configured to provide DNS exit, but no valid DNS server configured!\n"));
+    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);
   helper_argv[0] = GNUNET_strdup ("gnunet-dns");
   if (GNUNET_SYSERR ==
       GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IFNAME", &ifc_name))
@@ -1635,25 +1123,8 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
   }
   helper_argv[5] = ipv4mask;
   helper_argv[6] = NULL;
-
-  if (NULL != dns_exit)
-  {
-    static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
-      {&receive_dns_request, GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET, 0},
-      {NULL, 0, 0}
-    };
-    static GNUNET_MESH_ApplicationType mesh_types[] = {
-      GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER,
-      GNUNET_APPLICATION_TYPE_END
-    };
-    mesh = GNUNET_MESH_connect (cfg,
-                               1, NULL,
-                               &accept_dns_tunnel, 
-                               &destroy_dns_tunnel,
-                               mesh_handlers,
-                               mesh_types);
-  }
-  hijacker = GNUNET_HELPER_start ("gnunet-helper-dns",
+  hijacker = GNUNET_HELPER_start (GNUNET_NO,
+                                 "gnunet-helper-dns",
                                  helper_argv,
                                  &process_helper_messages,
                                  NULL, NULL);
@@ -1686,7 +1157,7 @@ main (int argc, char *const *argv)
             strerror (errno));
   }
   else if (sgid != rgid)
-  {    
+  {
     if (-1 ==  setregid (sgid, sgid))
       fprintf (stderr, "setregid failed: %s\n", strerror (errno));
   }