- implementation for mantis 0002485
[oweals/gnunet.git] / src / dns / gnunet-service-dns.c
index 463176fbc5232dda7199d80e80a08b7963b6602a..6337538d10a9a8f83886772df29a606c38e7634c 100644 (file)
@@ -44,6 +44,7 @@
 #include "gnunet_signatures.h"
 #include "dns.h"
 #include "gnunet_dns_service.h"
+#include "gnunet_dnsparser_lib.h"
 #include "gnunet_mesh_service.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_tun_lib.h"
@@ -288,6 +289,11 @@ struct TunnelState
 };
 
 
+/**
+ * Global return value from 'main'.
+ */
+static int global_ret;
+
 /**
  * The configuration to use
  */
@@ -429,6 +435,11 @@ cleanup_task (void *cls GNUNET_UNUSED,
     GNUNET_free (dns_exit);
     dns_exit = NULL;
   }
+  if (NULL != mesh)
+  {
+    GNUNET_MESH_disconnect(mesh);
+    mesh = NULL;
+  }
 }
 
 
@@ -462,6 +473,7 @@ open_socket (int af)
     break;
   default:
     GNUNET_break (0);
+    GNUNET_NETWORK_socket_close (ret);
     return NULL;
   }
   sa->sa_family = af;
@@ -528,7 +540,7 @@ request_done (struct RequestRecord *rr)
     return;    
   }
   {
-    char buf[reply_len];
+    char buf[reply_len] GNUNET_ALIGN;
     size_t off;
     struct GNUNET_TUN_IPv4Header ip4;
     struct GNUNET_TUN_IPv6Header ip6;
@@ -619,10 +631,10 @@ request_done (struct RequestRecord *rr)
     }
     /* final checks & sending */
     GNUNET_assert (off == reply_len);
-    GNUNET_HELPER_send (hijacker,
-                       hdr,
-                       GNUNET_YES,
-                       NULL, NULL);
+    (void) GNUNET_HELPER_send (hijacker,
+                              hdr,
+                              GNUNET_YES,
+                              NULL, NULL);
     GNUNET_STATISTICS_update (stats,
                              gettext_noop ("# DNS requests answered via TUN interface"),
                              1, GNUNET_NO);
@@ -643,7 +655,7 @@ static void
 send_request_to_client (struct RequestRecord *rr,
                        struct GNUNET_SERVER_Client *client)
 {
-  char buf[sizeof (struct GNUNET_DNS_Request) + rr->payload_length];
+  char buf[sizeof (struct GNUNET_DNS_Request) + rr->payload_length] GNUNET_ALIGN;
   struct GNUNET_DNS_Request *req;
 
   if (sizeof (buf) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
@@ -722,7 +734,6 @@ get_request_socket (int af)
   if (NULL != rs->dnsout6)
     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
   rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                              GNUNET_SCHEDULER_NO_TASK,
                                               REQUEST_TIMEOUT,
                                               rset,
                                               NULL,
@@ -975,7 +986,7 @@ do_dns_read (struct GNUNET_NETWORK_Handle *dnsout)
 #endif
 
   {
-    unsigned char buf[len];
+    unsigned char buf[len] GNUNET_ALIGN;
 
     addrlen = sizeof (addr);
     memset (&addr, 0, sizeof (addr));  
@@ -1088,7 +1099,6 @@ read_response (void *cls,
   if (NULL != rs->dnsout6)
     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
   rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                              GNUNET_SCHEDULER_NO_TASK,
                                               GNUNET_TIME_absolute_get_remaining (rs->timeout),
                                               rset,
                                               NULL,
@@ -1186,10 +1196,8 @@ handle_client_response (void *cls GNUNET_UNUSED,
        return;
       }
       GNUNET_free_non_null (rr->payload);
-#if DEBUG_DNS
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 _("Changing DNS reply according to client specifications\n"));
-#endif
+                 "Changing DNS reply according to client specifications\n");
       rr->payload = GNUNET_malloc (msize);
       rr->payload_length = msize;
       memcpy (rr->payload, &resp[1], msize);
@@ -1200,6 +1208,16 @@ handle_client_response (void *cls GNUNET_UNUSED,
                           rr->client_wait_list_length,
                           0);
       }
+      /* 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)
+      {
+       rr->phase = RP_INTERNET_DNS;
+       GNUNET_array_grow (rr->client_wait_list,
+                          rr->client_wait_list_length,
+                          0);
+      }
       break;
     }
     next_phase (rr); 
@@ -1221,7 +1239,7 @@ handle_client_response (void *cls GNUNET_UNUSED,
  * @param client identification of the client
  * @param message the actual message, a DNS request we should handle
  */
-static void
+static int
 process_helper_messages (void *cls GNUNET_UNUSED, void *client,
                         const struct GNUNET_MessageHeader *message)
 {
@@ -1242,7 +1260,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
   {
     /* non-IP packet received on TUN!? */
     GNUNET_break (0);
-    return;
+    return GNUNET_OK;
   }
   msize -= sizeof (struct GNUNET_MessageHeader);
   tun = (const struct GNUNET_TUN_Layer2PacketHeader *) &message[1];
@@ -1251,6 +1269,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
   {
   case ETH_P_IPV4:
     ip4 = (const struct GNUNET_TUN_IPv4Header *) &tun[1];
+    ip6 = NULL; /* make compiler happy */
     if ( (msize < sizeof (struct GNUNET_TUN_IPv4Header)) ||
         (ip4->version != 4) ||
         (ip4->header_length != sizeof (struct GNUNET_TUN_IPv4Header) / 4) ||
@@ -1260,12 +1279,13 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
       /* non-IP/UDP packet received on TUN (or with options) */
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  _("Received malformed IPv4-UDP packet on TUN interface.\n"));
-      return;
+      return GNUNET_OK;
     }
     udp = (const struct GNUNET_TUN_UdpHeader*) &ip4[1];
     msize -= sizeof (struct GNUNET_TUN_IPv4Header);
     break;
   case ETH_P_IPV6:
+    ip4 = NULL; /* make compiler happy */
     ip6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1];
     if ( (msize < sizeof (struct GNUNET_TUN_IPv6Header)) ||
         (ip6->version != 6) ||
@@ -1275,7 +1295,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
       /* non-IP/UDP packet received on TUN (or with extensions) */
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  _("Received malformed IPv6-UDP packet on TUN interface.\n"));
-      return;
+      return GNUNET_OK;
     }
     udp = (const struct GNUNET_TUN_UdpHeader*) &ip6[1];
     msize -= sizeof (struct GNUNET_TUN_IPv6Header);
@@ -1286,7 +1306,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
                _("Got non-IP packet with %u bytes and protocol %u from TUN\n"),
                (unsigned int) msize,
                ntohs (tun->proto));
-    return;
+    return GNUNET_OK;
   }
   if (msize <= sizeof (struct GNUNET_TUN_UdpHeader) + sizeof (struct GNUNET_TUN_DnsHeader))
   {    
@@ -1294,7 +1314,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
     GNUNET_STATISTICS_update (stats,
                              gettext_noop ("# Non-DNS UDP packet received via TUN interface"),
                              1, GNUNET_NO);
-    return;
+    return GNUNET_OK;
   }
   msize -= sizeof (struct GNUNET_TUN_UdpHeader);
   dns = (const struct GNUNET_TUN_DnsHeader*) &udp[1];
@@ -1324,7 +1344,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
       srca4->sin_port = udp->source_port;
       dsta4->sin_port = udp->destination_port;
 #if HAVE_SOCKADDR_IN_SIN_LEN
-      srca4->sin_len = sizeof (struct sockaddr_in))
+      srca4->sin_len = sizeof (struct sockaddr_in);
       dsta4->sin_len = sizeof (struct sockaddr_in);
 #endif
     }
@@ -1361,6 +1381,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
                            1, GNUNET_NO);
   /* start request processing state machine */
   next_phase (rr);
+  return GNUNET_OK;
 }
 
 
@@ -1387,13 +1408,13 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
   const struct GNUNET_TUN_DnsHeader *dns;
   size_t mlen = ntohs (message->size);
   size_t dlen = mlen - sizeof (struct GNUNET_MessageHeader);
-  char buf[dlen];
+  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);
@@ -1409,7 +1430,6 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
   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))
@@ -1422,8 +1442,8 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
 #endif
     so = (struct sockaddr *) &v4;
     ts->dnsout = get_request_socket (AF_INET);
-  } 
-  if (1 == inet_pton (AF_INET6, dns_exit, &v6.sin6_addr))
+  }
+  else if (1 == inet_pton (AF_INET6, dns_exit, &v6.sin6_addr))
   {
     salen = sizeof (v6);
     v6.sin6_family = AF_INET6;
@@ -1434,6 +1454,11 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
     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,
@@ -1529,6 +1554,16 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
   struct in6_addr dns_exit6;
 
   cfg = cfg_;
+  if (GNUNET_YES !=
+      GNUNET_OS_check_helper_binary ("gnunet-helper-dns"))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               _("`%s' must be installed SUID, refusing to run\n"),
+               "gnunet-helper-dns");
+    global_ret = 1;
+    return;
+  }
+
   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,
@@ -1541,8 +1576,12 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
                                                 &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_free_non_null (dns_exit);
+    dns_exit = NULL;
+  }
 
   helper_argv[0] = GNUNET_strdup ("gnunet-dns");
   if (GNUNET_SYSERR ==
@@ -1635,7 +1674,7 @@ main (int argc, char *const *argv)
 {
   return (GNUNET_OK ==
           GNUNET_SERVICE_run (argc, argv, "dns", GNUNET_SERVICE_OPTION_NONE,
-                              &run, NULL)) ? 0 : 1;
+                              &run, NULL)) ? global_ret : 1;
 }