check
[oweals/gnunet.git] / src / vpn / gnunet-service-dns.c
index 0dd58027a1fce86b0e328b81ecfc49c423f961c7..a735788b1076332ce978810fd2ed57b2e38f6ba3 100644 (file)
@@ -29,6 +29,7 @@
 #include "gnunet_os_lib.h"
 #include "gnunet-service-dns-p.h"
 #include "gnunet_protocols.h"
+#include "gnunet_applications.h"
 #include "gnunet-vpn-packet.h"
 #include "gnunet_container_lib.h"
 #include "gnunet-dns-parser.h"
 #include "gnunet_block_lib.h"
 #include "block_dns.h"
 #include "gnunet_crypto_lib.h"
+#include "gnunet_mesh_service.h"
 #include "gnunet_signatures.h"
 
+struct GNUNET_MESH_Handle *mesh_handle;
+
 /**
  * The UDP-Socket through which DNS-Resolves will be sent if they are not to be
  * sent through gnunet. The port of this socket will not be hijacked.
@@ -190,8 +194,178 @@ send_answer(void* cls, size_t size, void* buf) {
     return len;
 }
 
+struct tunnel_cls {
+    struct GNUNET_MESH_Tunnel *tunnel;
+    struct GNUNET_MessageHeader hdr;
+    struct dns_pkt dns;
+};
+
+struct tunnel_cls *remote_pending[UINT16_MAX];
+
+
+static size_t
+mesh_send (void *cls, size_t size, void *buf)
+{
+  struct tunnel_cls *cls_ = (struct tunnel_cls *) cls;
+
+  GNUNET_assert(cls_->hdr.size <= size);
+
+  size = cls_->hdr.size;
+  cls_->hdr.size = htons(cls_->hdr.size);
+
+  memcpy(buf, &cls_->hdr, size);
+  return size;
+}
+
+
+void mesh_connect (void* cls, const struct GNUNET_PeerIdentity* peer, const struct GNUNET_TRANSPORT_ATS_Information *atsi) {
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Connected to peer %x\n", *((unsigned long*)peer));
+  struct tunnel_cls *cls_ = (struct tunnel_cls*)cls;
+
+  GNUNET_MESH_notify_transmit_ready(cls_->tunnel,
+                                    GNUNET_YES,
+                                    42,
+                                    GNUNET_TIME_UNIT_MINUTES,
+                                    NULL,
+                                    cls_->hdr.size,
+                                    mesh_send,
+                                    cls);
+}
+
+
+static void
+send_mesh_query (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    return;
+
+  struct tunnel_cls *cls_ = (struct tunnel_cls*)cls;
+
+  cls_->tunnel = GNUNET_MESH_peer_request_connect_by_type(mesh_handle,
+                                           GNUNET_TIME_UNIT_HOURS,
+                                           GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER,
+                                           mesh_connect,
+                                           NULL,
+                                           cls_);
+
+  remote_pending[cls_->dns.s.id] = cls_;
+}
+
+static int
+receive_mesh_query (void *cls,
+                    struct GNUNET_MESH_Tunnel *tunnel,
+                    void **ctx,
+                    const struct GNUNET_PeerIdentity *sender,
+                    const struct GNUNET_MessageHeader *message,
+                    const struct GNUNET_TRANSPORT_ATS_Information *atsi)
+{
+  return GNUNET_SYSERR;
+}
+
+static int
+receive_mesh_answer (void *cls,
+                     struct GNUNET_MESH_Tunnel *tunnel,
+                     void **ctx,
+                     const struct GNUNET_PeerIdentity *sender,
+                     const struct GNUNET_MessageHeader *message,
+                     const struct GNUNET_TRANSPORT_ATS_Information *atsi)
+{
+  /* TODo: size check */
+  struct dns_pkt *dns = (struct dns_pkt *) (message + 1);
+
+  /* They sent us a packet we were not waiting for */
+  if (remote_pending[dns->s.id] == NULL
+      || remote_pending[dns->s.id]->tunnel != tunnel)
+    return GNUNET_SYSERR;
+  if (query_states[dns->s.id].valid != GNUNET_YES)
+    return GNUNET_SYSERR;
+  query_states[dns->s.id].valid = GNUNET_NO;
+
+  size_t len = sizeof (struct answer_packet) - 1 + sizeof (struct dns_static) + query_states[dns->s.id].namelen + sizeof (struct dns_query_line) + 2    /* To hold the pointer (as defined in RFC1035) to the name */
+    + sizeof (struct dns_record_line) - 1 + 16; /* To hold the IPv6-Address */
+
+  struct answer_packet_list *answer =
+    GNUNET_malloc (len + 2 * sizeof (struct answer_packet_list *));
+  memset (answer, 0, len + 2 * sizeof (struct answer_packet_list *));
+
+  answer->pkt.hdr.type = htons (GNUNET_MESSAGE_TYPE_LOCAL_RESPONSE_DNS);
+  answer->pkt.hdr.size = htons (len);
+  answer->pkt.subtype = GNUNET_DNS_ANSWER_TYPE_REMOTE;
+
+  struct dns_pkt_parsed* pdns = parse_dns_packet(dns);
+
+  if (ntohs(pdns->s.ancount) < 1)
+    {
+      free_parsed_dns_packet(pdns);
+      return GNUNET_OK;
+    }
+
+  answer->pkt.addrsize = pdns->answers[0]->data_len;
+  memcpy(answer->pkt.addr, pdns->answers[0]->data, ntohs(pdns->answers[0]->data_len));
+
+  answer->pkt.from = query_states[dns->s.id].remote_ip;
+
+  answer->pkt.to = query_states[dns->s.id].local_ip;
+  answer->pkt.dst_port = query_states[dns->s.id].local_port;
+
+  struct dns_pkt *dpkt = (struct dns_pkt *) answer->pkt.data;
+
+  dpkt->s.id = dns->s.id;
+  dpkt->s.aa = 1;
+  dpkt->s.qr = 1;
+  dpkt->s.ra = 1;
+  dpkt->s.qdcount = htons (1);
+  dpkt->s.ancount = htons (1);
+
+  memcpy (dpkt->data, query_states[dns->s.id].name,
+          query_states[dns->s.id].namelen);
+  GNUNET_free (query_states[dns->s.id].name);
+
+  struct dns_query_line *dque =
+    (struct dns_query_line *) (dpkt->data +
+                               (query_states[dns->s.id].namelen));
+  dque->type = htons (28);      /* AAAA */
+  dque->class = htons (1);      /* IN */
+
+  char *anname =
+    (char *) (dpkt->data + (query_states[dns->s.id].namelen) +
+              sizeof (struct dns_query_line));
+  memcpy (anname, "\xc0\x0c", 2);
+
+  struct dns_record_line *drec_data =
+    (struct dns_record_line *) (dpkt->data +
+                                (query_states[dns->s.id].namelen) +
+                                sizeof (struct dns_query_line) + 2);
+  drec_data->type = htons (28); /* AAAA */
+  drec_data->class = htons (1); /* IN */
+
+  drec_data->ttl = pdns->answers[0]->ttl;
+  drec_data->data_len = htons (16);
+
+  /* Calculate at which offset in the packet the IPv6-Address belongs, it is
+   * filled in by the daemon-vpn */
+  answer->pkt.addroffset =
+    htons ((unsigned short) ((unsigned long) (&drec_data->data) -
+                             (unsigned long) (&answer->pkt)));
+
+  GNUNET_CONTAINER_DLL_insert_after (head, tail, tail, answer);
+
+  GNUNET_SERVER_notify_transmit_ready (query_states[dns->s.id].client,
+                                       len,
+                                       GNUNET_TIME_UNIT_FOREVER_REL,
+                                       &send_answer,
+                                       query_states[dns->s.id].client);
+
+  free_parsed_dns_packet(pdns);
+  return GNUNET_OK;
+}
+
+
 static void
 send_rev_query(void * cls, const struct GNUNET_SCHEDULER_TaskContext *tc) {
+    if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+      return;
+
     struct dns_pkt_parsed* pdns = (struct dns_pkt_parsed*) cls;
 
     unsigned short id = pdns->s.id;
@@ -439,7 +613,8 @@ receive_query(void *cls,
       }
 
     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Query for '%s'; namelen=%d\n", pdns->queries[0]->name, pdns->queries[0]->namelen);
-    /* The query is for a PTR of a previosly resolved virtual IP */
+
+    /* This is a PTR-Query. Check if it is for "our" network */
     if (htons(pdns->queries[0]->qtype) == 12 &&
        74 == pdns->queries[0]->namelen)
       {
@@ -490,6 +665,46 @@ receive_query(void *cls,
           }
       }
 
+    char* virt_dns;
+    int virt_dns_bytes;
+    if (GNUNET_SYSERR ==
+        GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "VIRTDNS",
+                                               &virt_dns))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "No entry 'VIRTDNS' in configuration!\n");
+        exit (1);
+      }
+
+    if (1 != inet_pton (AF_INET, virt_dns, &virt_dns_bytes))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Error parsing 'VIRTDNS': %s; %m!\n", virt_dns);
+        exit(1);
+      }
+
+    GNUNET_free(virt_dns);
+
+    if (virt_dns_bytes == pkt->orig_to)
+      {
+        /* This is a packet that was sent directly to the virtual dns-server
+         *
+         * This means we have to send this query over gnunet
+         */
+
+        size_t size = sizeof(struct GNUNET_MESH_Tunnel*) + sizeof(struct GNUNET_MessageHeader) + (ntohs(message->size) - sizeof(struct query_packet) + 1);
+        struct tunnel_cls *cls_ =  GNUNET_malloc(size);
+        cls_->hdr.size = size - sizeof(struct GNUNET_MESH_Tunnel*);
+
+        cls_->hdr.type = ntohs(GNUNET_MESSAGE_TYPE_REMOTE_QUERY_DNS);
+
+        memcpy(&cls_->dns, dns, cls_->hdr.size);
+        GNUNET_SCHEDULER_add_now(send_mesh_query, cls_);
+
+        goto out;
+      }
+
+
     /* The query should be sent to the network */
 
     struct sockaddr_in dest;
@@ -868,29 +1083,50 @@ run (void *cls,
      const struct GNUNET_CONFIGURATION_Handle *cfg_)
 {
   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
-      /* callback, cls, type, size */
-       {&receive_query, NULL, GNUNET_MESSAGE_TYPE_LOCAL_QUERY_DNS, 0},
-       {&rehijack, NULL, GNUNET_MESSAGE_TYPE_REHIJACK, sizeof(struct GNUNET_MessageHeader)},
-       {NULL, NULL, 0, 0}
+    /* callback, cls, type, size */
+    {&receive_query, NULL, GNUNET_MESSAGE_TYPE_LOCAL_QUERY_DNS, 0},
+    {&rehijack, NULL, GNUNET_MESSAGE_TYPE_REHIJACK,
+     sizeof (struct GNUNET_MessageHeader)},
+    {NULL, NULL, 0, 0}
   };
 
+  static struct GNUNET_MESH_MessageHandler *mesh_handlers;
+
+  if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno(cfg_, "dns", "PROVIDE_EXIT"))
+    mesh_handlers = (struct GNUNET_MESH_MessageHandler[]) {
+          {receive_mesh_query, GNUNET_MESSAGE_TYPE_REMOTE_QUERY_DNS, 0},
+          {NULL, 0, 0}
+    };
+  else
+    mesh_handlers = (struct GNUNET_MESH_MessageHandler[]) {
+          {receive_mesh_answer, GNUNET_MESSAGE_TYPE_REMOTE_ANSWER_DNS, 0},
+          {NULL, 0, 0}
+    };
+
+  const static GNUNET_MESH_ApplicationType apptypes[] =
+    { GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER,
+    GNUNET_APPLICATION_TYPE_END
+  };
+
+  mesh_handle = GNUNET_MESH_connect (cfg_, NULL, NULL, mesh_handlers, apptypes);
+
   cfg = cfg_;
 
   unsigned int i;
-  for (i = 0; i < 65536; i++) {
+  for (i = 0; i < 65536; i++)
+    {
       query_states[i].valid = GNUNET_NO;
-  }
+    }
 
-  dht = GNUNET_DHT_connect(cfg, 1024);
+  dht = GNUNET_DHT_connect (cfg, 1024);
 
-  open_port();
+  open_port ();
 
   GNUNET_SCHEDULER_add_now (publish_names, NULL);
 
   GNUNET_SERVER_add_handlers (server, handlers);
   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
-                               &cleanup_task,
-                               cls);
+                                &cleanup_task, cls);
 }
 
 /**