Answer questions for .gnunet with an ipv6-address
[oweals/gnunet.git] / src / vpn / gnunet-daemon-vpn.c
index cc408dc62686b366562b5016aef1e644baf23184..a02af54a20a16a2213371761b7053e7580f8801b 100644 (file)
@@ -36,6 +36,7 @@
 #include "gnunet-service-dns-p.h"
 #include "gnunet_client_lib.h"
 #include "gnunet_container_lib.h"
+#include "block_dns.h"
 
 /**
  * Final status code.
@@ -53,29 +54,43 @@ struct vpn_cls {
        struct GNUNET_SCHEDULER_Handle *sched;
 
        struct GNUNET_CLIENT_Connection *dns_connection;
+       unsigned char restart_hijack;
 
        pid_t helper_pid;
 
+       const struct GNUNET_CONFIGURATION_Handle *cfg;
+
        struct query_packet_list *head;
        struct query_packet_list *tail;
 
-       struct answer_packet_list *answer_head;
-       struct answer_packet_list *answer_tail;
+       struct answer_packet_list *answer_proc_head;
+       struct answer_packet_list *answer_proc_tail;
 };
 
 static struct vpn_cls mycls;
 
+size_t send_query(void* cls, size_t size, void* buf);
+
 static void cleanup(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tskctx) {
-       if (tskctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) {
-               PLIBC_KILL(mycls.helper_pid, SIGTERM);
-               GNUNET_OS_process_wait(mycls.helper_pid);
-       }
+  GNUNET_assert (0 != (tskctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN));
+  PLIBC_KILL(mycls.helper_pid, SIGTERM);
+  GNUNET_OS_process_wait(mycls.helper_pid);
+  if (mycls.dns_connection != NULL)
+    {
+      GNUNET_CLIENT_disconnect (mycls.dns_connection, GNUNET_NO);
+      mycls.dns_connection = NULL;
+    }
 }
 
 static void helper_read(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx);
 
-static void start_helper_and_schedule() {
-       mycls.helper_in = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO);;
+static void start_helper_and_schedule(void *cls,
+                                     const struct GNUNET_SCHEDULER_TaskContext *tc) {
+
+       if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+         return;
+
+       mycls.helper_in = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO);
        mycls.helper_out = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_NO, GNUNET_YES);
 
        if (mycls.helper_in == NULL || mycls.helper_out == NULL) return;
@@ -93,19 +108,21 @@ static void start_helper_and_schedule() {
 
 
 static void restart_helper(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tskctx) {
-       // FIXME: Ratelimit this!
-
        // Kill the helper
        PLIBC_KILL(mycls.helper_pid, SIGKILL);
        GNUNET_OS_process_wait(mycls.helper_pid);
 
-       // FIXME: send msg to service-dns -- the hijacker has to be started again, too, the routing table is flushed if it depends on one interface
+       /* Tell the dns-service to rehijack the dns-port
+        * The routing-table gets flushed if an interface disappears.
+        */
+       mycls.restart_hijack = 1;
+       GNUNET_CLIENT_notify_transmit_ready(mycls.dns_connection, sizeof(struct GNUNET_MessageHeader), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL);
 
        GNUNET_DISK_pipe_close(mycls.helper_in);
        GNUNET_DISK_pipe_close(mycls.helper_out);
 
        // Restart the helper
-       start_helper_and_schedule(mycls);
+       GNUNET_SCHEDULER_add_delayed (mycls.sched, GNUNET_TIME_UNIT_SECONDS, start_helper_and_schedule, NULL);
 
 }
 
@@ -142,14 +159,17 @@ static uint16_t calculate_ip_checksum(uint16_t* hdr, short len) {
 static void helper_write(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx) {
        if (tsdkctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)
                return;
-       struct answer_packet_list* ans = mycls.answer_head;
+       struct answer_packet_list* ans = mycls.answer_proc_head;
        size_t len = ntohs(ans->pkt.hdr.size);
 
+       GNUNET_assert(ans->pkt.subtype == GNUNET_DNS_ANSWER_TYPE_IP);
+
        size_t data_len = len - sizeof(struct answer_packet) + 1;
        size_t net_len = sizeof(struct ip_hdr) + sizeof(struct udp_dns) + data_len;
        size_t pkt_len = sizeof(struct GNUNET_MessageHeader) + sizeof(struct pkt_tun) + net_len;
 
        struct ip_udp_dns* pkt = alloca(pkt_len);
+       memset(pkt, 0, pkt_len);
 
        pkt->shdr.size = htons(pkt_len);
        pkt->shdr.type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER);
@@ -179,19 +199,31 @@ static void helper_write(void* cls, const struct GNUNET_SCHEDULER_TaskContext* t
 
        memcpy(&pkt->udp_dns.data, ans->pkt.data, data_len);
        
-       GNUNET_CONTAINER_DLL_remove (mycls.answer_head, mycls.answer_tail, ans);
+       GNUNET_CONTAINER_DLL_remove (mycls.answer_proc_head, mycls.answer_proc_tail, ans);
        GNUNET_free(ans);
 
        /* FIXME */ GNUNET_DISK_file_write(mycls.fh_to_helper, pkt, pkt_len);
 
-       if (mycls.answer_head != NULL)
+       if (mycls.answer_proc_head != NULL)
                GNUNET_SCHEDULER_add_write_file (mycls.sched, GNUNET_TIME_UNIT_FOREVER_REL, mycls.fh_to_helper, &helper_write, NULL);
 }
 
 size_t send_query(void* cls, size_t size, void* buf)
 {
+  size_t len;
+  if (mycls.restart_hijack == 1)
+    {
+      mycls.restart_hijack = 0;
+      GNUNET_assert(sizeof(struct GNUNET_MessageHeader) >= size);
+      struct GNUNET_MessageHeader* hdr = buf;
+      len = sizeof(struct GNUNET_MessageHeader);
+      hdr->size = htons(len);
+      hdr->type = htons(GNUNET_MESSAGE_TYPE_REHIJACK);
+    }
+  else
+    {
        struct query_packet_list* query = mycls.head;
-       size_t len = ntohs(query->pkt.hdr.size);
+       len = ntohs(query->pkt.hdr.size);
 
        GNUNET_assert(len <= size);
 
@@ -200,8 +232,9 @@ size_t send_query(void* cls, size_t size, void* buf)
        GNUNET_CONTAINER_DLL_remove (mycls.head, mycls.tail, query);
 
        GNUNET_free(query);
+    }
 
-       if (mycls.head != NULL) {
+       if (mycls.head != NULL || mycls.restart_hijack == 1) {
                GNUNET_CLIENT_notify_transmit_ready(mycls.dns_connection, ntohs(mycls.head->pkt.hdr.size), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL);
        }
 
@@ -248,27 +281,93 @@ static void message_token(void *cls, void *client, const struct GNUNET_MessageHe
 
                        GNUNET_CONTAINER_DLL_insert_after(mycls.head, mycls.tail, mycls.tail, query);
 
-                       /* struct GNUNET_CLIENT_TransmitHandle* th = */ GNUNET_CLIENT_notify_transmit_ready(mycls.dns_connection, len, GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL);
+                       if (mycls.dns_connection != NULL)
+                         /* struct GNUNET_CLIENT_TransmitHandle* th = */ GNUNET_CLIENT_notify_transmit_ready(mycls.dns_connection, len, GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL);
                }
        }
 
 }
 
-void dns_answer_handler(void* cls, const struct GNUNET_MessageHeader *msg) {
-       if (msg == NULL) return;
+static void 
+dns_answer_handler(void* cls, const struct GNUNET_MessageHeader *msg);
+
+static void 
+reconnect_to_service_dns (void *cls,
+                         const struct GNUNET_SCHEDULER_TaskContext *tc) {
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    return;
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Connecting\n");
+  GNUNET_assert (mycls.dns_connection == NULL);
+  mycls.dns_connection = GNUNET_CLIENT_connect (mycls.sched, "dns", mycls.cfg); 
+  GNUNET_CLIENT_receive(mycls.dns_connection, &dns_answer_handler, NULL, GNUNET_TIME_UNIT_FOREVER_REL);
+  if (mycls.head != NULL)
+    /* struct GNUNET_CLIENT_TransmitHandle* th = */ GNUNET_CLIENT_notify_transmit_ready(mycls.dns_connection, ntohs(mycls.head->pkt.hdr.size), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL);
+}
+
+static void
+process_answer(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tc) {
+    struct answer_packet* pkt = cls;
+
+    if (pkt->subtype == GNUNET_DNS_ANSWER_TYPE_SERVICE)
+      {
+      pkt->subtype = GNUNET_DNS_ANSWER_TYPE_IP;
+       unsigned char ip6addr[16];
+       memcpy(ip6addr, (int[]){htons(0x1234)}, 2);
+       memcpy(ip6addr+2, &pkt->peer, 7);
+       memcpy(ip6addr+9, &pkt->service_descriptor, 7);
+
+       memcpy(((char*)pkt)+ntohs(pkt->addroffset), ip6addr, 16);
 
-       if (msg->type != htons(GNUNET_MESSAGE_TYPE_LOCAL_RESPONSE_DNS)) goto out;
+         /*FIXME:
+          * -save DNS_Record into hashmap, pointed to by ip
+          * -regularily walk through hashmap, deleting old entries
+          *  when is an entry old?
+          *  have a last-used field
+          *  don't remove if last-used "recent", ask dht again if record expired
+          */
+      }
 
-       struct answer_packet_list* pkt = GNUNET_malloc(ntohs(msg->size) + 2*sizeof(struct answer_packet_list*));
+       struct answer_packet_list* list = GNUNET_malloc(htons(pkt->hdr.size) + 2*sizeof(struct answer_packet_list*));
 
-       memcpy(&pkt->pkt, msg, ntohs(msg->size));
+       memcpy(&list->pkt, pkt, htons(pkt->hdr.size));
 
-       GNUNET_CONTAINER_DLL_insert_after(mycls.answer_head, mycls.answer_tail, mycls.answer_tail, pkt);
+       GNUNET_CONTAINER_DLL_insert_after(mycls.answer_proc_head, mycls.answer_proc_tail, mycls.answer_proc_tail, list);
 
        GNUNET_SCHEDULER_add_write_file (mycls.sched, GNUNET_TIME_UNIT_FOREVER_REL, mycls.fh_to_helper, &helper_write, NULL);
 
-out:
-       GNUNET_CLIENT_receive(mycls.dns_connection, &dns_answer_handler, NULL, GNUNET_TIME_UNIT_FOREVER_REL);
+       return;
+}
+
+static void
+dns_answer_handler(void* cls, const struct GNUNET_MessageHeader *msg)
+{
+  if (msg == NULL)
+    {
+      GNUNET_CLIENT_disconnect(mycls.dns_connection, GNUNET_NO);
+      mycls.dns_connection = NULL;
+      GNUNET_SCHEDULER_add_delayed (mycls.sched,
+                                   GNUNET_TIME_UNIT_SECONDS,
+                                   &reconnect_to_service_dns,
+                                   NULL);
+      return;
+    }
+
+  if (msg->type != htons(GNUNET_MESSAGE_TYPE_LOCAL_RESPONSE_DNS)) 
+    {
+      GNUNET_break (0);
+      GNUNET_CLIENT_disconnect(mycls.dns_connection, GNUNET_NO);
+      mycls.dns_connection = NULL;
+      GNUNET_SCHEDULER_add_now (mycls.sched,
+                               &reconnect_to_service_dns,
+                               NULL);
+      return;
+    }
+  void *pkt = GNUNET_malloc(ntohs(msg->size));
+
+  memcpy(pkt, msg, ntohs(msg->size));
+
+  GNUNET_SCHEDULER_add_now(mycls.sched, process_answer, pkt);
+  GNUNET_CLIENT_receive(mycls.dns_connection, &dns_answer_handler, NULL, GNUNET_TIME_UNIT_FOREVER_REL);
 }
 
 /**
@@ -289,13 +388,11 @@ run (void *cls,
 {
   mycls.sched = sched;
   mycls.mst = GNUNET_SERVER_mst_create(&message_token, NULL);
-
-  mycls.dns_connection = GNUNET_CLIENT_connect (sched, "dns", cfg);
-
-  GNUNET_CLIENT_receive(mycls.dns_connection, &dns_answer_handler, NULL, GNUNET_TIME_UNIT_FOREVER_REL);
-
+  mycls.cfg = cfg;
+  mycls.restart_hijack = 0;
+  GNUNET_SCHEDULER_add_now (sched, &reconnect_to_service_dns, NULL);
   GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls); 
-  start_helper_and_schedule(mycls);
+  GNUNET_SCHEDULER_add_now (sched, start_helper_and_schedule, NULL);
 }