fixes relating to intercepting DNS queries over IPv6
authorChristian Grothoff <christian@grothoff.org>
Tue, 27 Sep 2016 22:25:05 +0000 (22:25 +0000)
committerChristian Grothoff <christian@grothoff.org>
Tue, 27 Sep 2016 22:25:05 +0000 (22:25 +0000)
src/dns/Makefile.am
src/dns/dnsstub.c
src/dns/gnunet-helper-dns.c
src/dns/gnunet-service-dns.c
src/dns/test_gnunet_dns.sh
src/include/gnunet_tun_lib.h
src/tun/tun.c

index 45b86c71b81f963d132384d1858d0a5015f47df7..f5fcf3782e8883e2d4d2f6735328a9fd95309c3d 100644 (file)
@@ -89,6 +89,7 @@ libgnunetdnsparser_la_LDFLAGS = \
 libgnunetdnsstub_la_SOURCES = \
  dnsstub.c
 libgnunetdnsstub_la_LIBADD = \
+  $(top_builddir)/src/tun/libgnunettun.la \
  $(top_builddir)/src/util/libgnunetutil.la $(XLIB)
 libgnunetdnsstub_la_LDFLAGS = \
   $(GN_LIB_LDFLAGS) \
index b3cd2817eaf9f7e1163728638c0533b31061ba5a..68cd55275085ed85a70f8824e7f88b4d6af79798 100644 (file)
@@ -24,6 +24,7 @@
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
+#include "gnunet_tun_lib.h"
 #include "gnunet_dnsstub_lib.h"
 
 /**
@@ -381,9 +382,7 @@ GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
                _("Failed to send DNS request to %s\n"),
                GNUNET_a2s (sa, salen));
   rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
-
   return rs;
-
 }
 
 
@@ -441,9 +440,10 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
     }
     dns = (struct GNUNET_TUN_DnsHeader *) buf;
     if ( (addrlen != rs->addrlen) ||
-        (0 != memcmp (&rs->addr,
-                      &addr,
-                      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) )
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
index 2f723d09da83d0f3fa7e7a2a291fe5758305e9d1..1d411379f5fb9af2435e8e0805f9e206c4a6633f 100644 (file)
@@ -100,6 +100,11 @@ struct in6_ifreq
  */
 static const char *sbin_iptables;
 
+/**
+ * Name and full path of IPTABLES binary.
+ */
+static const char *sbin_ip6tables;
+
 /**
  * Name and full path of sysctl binary
  */
@@ -757,7 +762,7 @@ main (int argc, char *const*argv)
     return 254;
   }
 #endif
-  if (0 == strncmp(argv[6], "1", 2))
+  if (0 == strncmp (argv[6], "1", 2))
     nortsetup = 1;
 
   if (0 == nortsetup)
@@ -774,6 +779,17 @@ main (int argc, char *const*argv)
               strerror (errno));
       return 3;
     }
+    if (0 == access ("/sbin/ip6tables", X_OK))
+      sbin_ip6tables = "/sbin/ip6tables";
+    else if (0 == access ("/usr/sbin/ip6tables", X_OK))
+      sbin_ip6tables = "/usr/sbin/ip6tables";
+    else
+    {
+      fprintf (stderr,
+              "Fatal: executable ip6tables not found in approved directories: %s\n",
+              strerror (errno));
+      return 3;
+    }
     if (0 == access ("/sbin/ip", X_OK))
       sbin_ip = "/sbin/ip";
     else if (0 == access ("/usr/sbin/ip", X_OK))
@@ -942,6 +958,16 @@ main (int argc, char *const*argv)
       if (0 != fork_and_exec (sbin_iptables, mangle_args))
         goto cleanup_rest;
     }
+    {
+      char *const mangle_args[] =
+        {
+        "ip6tables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
+        "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
+        "ACCEPT", NULL
+        };
+      if (0 != fork_and_exec (sbin_ip6tables, mangle_args))
+        goto cleanup_rest;
+    }
     /* Mark all of the other DNS traffic using our mark DNS_MARK */
     {
       char *const mark_args[] =
@@ -953,6 +979,16 @@ main (int argc, char *const*argv)
       if (0 != fork_and_exec (sbin_iptables, mark_args))
         goto cleanup_mangle_1;
     }
+    {
+      char *const mark_args[] =
+        {
+        "ip6tables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
+        "udp", "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK,
+        NULL
+        };
+      if (0 != fork_and_exec (sbin_ip6tables, mark_args))
+        goto cleanup_mangle_1;
+    }
     /* Forward all marked DNS traffic to our DNS_TABLE */
     {
       char *const forward_args[] =
@@ -962,6 +998,14 @@ main (int argc, char *const*argv)
       if (0 != fork_and_exec (sbin_ip, forward_args))
         goto cleanup_mark_2;
     }
+    {
+      char *const forward_args[] =
+        {
+          "ip", "-6", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
+        };
+      if (0 != fork_and_exec (sbin_ip, forward_args))
+        goto cleanup_mark_2;
+    }
     /* Finally, add rule in our forwarding table to pass to our virtual interface */
     {
       char *const route_args[] =
@@ -972,6 +1016,15 @@ main (int argc, char *const*argv)
       if (0 != fork_and_exec (sbin_ip, route_args))
         goto cleanup_forward_3;
     }
+    {
+      char *const route_args[] =
+        {
+          "ip", "-6", "route", "add", "default", "dev", dev,
+          "table", DNS_TABLE, NULL
+        };
+      if (0 != fork_and_exec (sbin_ip, route_args))
+        goto cleanup_forward_3;
+    }
   }
 
   /* drop privs *except* for the saved UID; this is not perfect, but better
@@ -1028,6 +1081,16 @@ main (int argc, char *const*argv)
     if (0 != fork_and_exec (sbin_ip, route_clean_args))
       r += 1;
   }
+  if (0 == nortsetup)
+  {
+    char *const route_clean_args[] =
+      {
+       "ip", "-6", "route", "del", "default", "dev", dev,
+       "table", DNS_TABLE, NULL
+      };
+    if (0 != fork_and_exec (sbin_ip, route_clean_args))
+      r += 1;
+  }
  cleanup_forward_3:
   if (0 == nortsetup)
   {
@@ -1038,6 +1101,15 @@ main (int argc, char *const*argv)
     if (0 != fork_and_exec (sbin_ip, forward_clean_args))
       r += 2;
   }
+  if (0 == nortsetup)
+  {
+    char *const forward_clean_args[] =
+      {
+       "ip", "-6", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
+      };
+    if (0 != fork_and_exec (sbin_ip, forward_clean_args))
+      r += 2;
+  }
  cleanup_mark_2:
   if (0 == nortsetup)
   {
@@ -1049,6 +1121,16 @@ main (int argc, char *const*argv)
     if (0 != fork_and_exec (sbin_iptables, mark_clean_args))
       r += 4;
   }
+  if (0 == nortsetup)
+  {
+    char *const mark_clean_args[] =
+      {
+       "ip6tables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
+       "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
+      };
+    if (0 != fork_and_exec (sbin_ip6tables, mark_clean_args))
+      r += 4;
+  }
  cleanup_mangle_1:
   if (0 == nortsetup)
   {
@@ -1061,6 +1143,17 @@ main (int argc, char *const*argv)
     if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
       r += 8;
   }
+  if (0 == nortsetup)
+  {
+    char *const mangle_clean_args[] =
+      {
+       "ip6tables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
+        "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
+       NULL
+      };
+    if (0 != fork_and_exec (sbin_ip6tables, mangle_clean_args))
+      r += 8;
+  }
 
  cleanup_rest:
   /* close virtual interface */
index 50aa730e7731205a1b7f09a067ea254605b33c55..52f924cdf76645c00778e65dc33c3110b4e563db 100644 (file)
@@ -410,7 +410,7 @@ request_done (struct RequestRecord *rr)
        destination_port = src->sin6_port;
        GNUNET_TUN_initialize_ipv6_header (&ip6,
                                           IPPROTO_UDP,
-                                          reply_len - sizeof (struct GNUNET_TUN_IPv6Header),
+                                          reply_len - off - sizeof (struct GNUNET_TUN_IPv6Header),
                                           &dst->sin6_addr,
                                           &src->sin6_addr);
        GNUNET_memcpy (&buf[off], &ip6, sizeof (ip6));
@@ -916,7 +916,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
     ip6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1];
     if ( (msize < sizeof (struct GNUNET_TUN_IPv6Header)) ||
         (ip6->version != 6) ||
-        (ntohs (ip6->payload_length) != msize) ||
+        (ntohs (ip6->payload_length) != msize - sizeof (struct GNUNET_TUN_IPv6Header)) ||
         (ip6->next_header != IPPROTO_UDP) )
     {
       /* non-IP/UDP packet received on TUN (or with extensions) */
@@ -924,7 +924,7 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
                  _("Received malformed IPv6-UDP packet on TUN interface.\n"));
       return GNUNET_OK;
     }
-    udp = (const struct GNUNET_TUN_UdpHeader*) &ip6[1];
+    udp = (const struct GNUNET_TUN_UdpHeader *) &ip6[1];
     msize -= sizeof (struct GNUNET_TUN_IPv6Header);
     break;
   default:
@@ -939,6 +939,8 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client,
        (DNS_PORT != ntohs (udp->destination_port)) )
   {
     /* non-DNS packet received on TUN, ignore */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("DNS interceptor got non-DNS packet (dropped)\n"));
     GNUNET_STATISTICS_update (stats,
                              gettext_noop ("# Non-DNS UDP packet received via TUN interface"),
                              1, GNUNET_NO);
index ab8cca4b1569b0b7495505f0a61e2b85af43984b..84a57f87eca258a119eaef517eff50878d15bd9d 100755 (executable)
@@ -6,7 +6,7 @@ then
   echo "This test only works if run as root.  Skipping."
   exit 77
 fi
-if ! which sudo > /dev/null 
+if ! which sudo > /dev/null
 then
   echo "This test requires sudo.  Skipping."
   exit 77
@@ -17,7 +17,7 @@ then
   exit 77
 fi
 if ! which nslookup > /dev/null
-then 
+then
   echo "This test requires nslookup.  Skipping."
   exit 77
 fi
@@ -38,7 +38,7 @@ gnunet-dns-redirector -c dns.conf -4 127.0.0.1 &
 sleep 1
 # need to run 'nslookup' as 'nobody', as gnunet-service-dns runs as root
 # and thus 'root' is excepted from DNS interception!
-LO=`sudo -u nobody nslookup gnunet.org | grep Address | tail -n1`
+LO=`sudo -u nobody nslookup -type=A gnunet.org | grep Address | tail -n1`
 if [ "$LO" != "Address: 127.0.0.1" ]
 then
  echo "Fail: got address $LO, wanted 127.0.0.1"
index 822a661cfb2fddde79fbcde3d3953954ef95fc4b..7bef0a6bebad64f4e15254013f493b1446cf5737 100644 (file)
@@ -917,6 +917,20 @@ GNUNET_TUN_service_name_to_hash (const char *service_name,
                                  struct GNUNET_HashCode *hc);
 
 
+/**
+ * Check if two sockaddrs are equal.
+ *
+ * @param sa one address
+ * @param sb another address
+ * @param include_port also check ports
+ * @return #GNUNET_YES if they are equal
+ */
+int
+GNUNET_TUN_sockaddr_cmp (const struct sockaddr *sa,
+                         const struct sockaddr *sb,
+                         int include_port);
+
+
 /**
  * Compute the CADET port given a service descriptor
  * (returned from #GNUNET_TUN_service_name_to_hash) and
index f39b1898b719cf4de4b9fc5303c5916e63c43664..b55de1ea9487f6c15e2a9a02df3dd414972cc3f7 100644 (file)
@@ -70,7 +70,7 @@ GNUNET_TUN_initialize_ipv4_header (struct GNUNET_TUN_IPv4Header *ip,
  *
  * @param ip header to initialize
  * @param protocol protocol to use (i.e. IPPROTO_UDP), technically "next_header" for IPv6
- * @param payload_length number of bytes of payload that follow (excluding IPv4 header)
+ * @param payload_length number of bytes of payload that follow (excluding IPv6 header)
  * @param src source IP address to use
  * @param dst destination IP address to use
  */
@@ -268,4 +268,44 @@ GNUNET_TUN_calculate_icmp_checksum (struct GNUNET_TUN_IcmpHeader *icmp,
 }
 
 
+/**
+ * Check if two sockaddrs are equal.
+ *
+ * @param sa one address
+ * @param sb another address
+ * @param include_port also check ports
+ * @return #GNUNET_YES if they are equal
+ */
+int
+GNUNET_TUN_sockaddr_cmp (const struct sockaddr *sa,
+                         const struct sockaddr *sb,
+                         int include_port)
+{
+  if (sa->sa_family != sb->sa_family)
+    return GNUNET_NO;
+
+  switch (sa->sa_family)
+  {
+  case AF_INET:
+    {
+      const struct sockaddr_in *sa4 = (const struct sockaddr_in *) sa;
+      const struct sockaddr_in *sb4 = (const struct sockaddr_in *) sb;
+      return (sa4->sin_addr.s_addr == sb4->sin_addr.s_addr);
+    }
+  case AF_INET6:
+    {
+      const struct sockaddr_in6 *sa6 = (const struct sockaddr_in6 *) sa;
+      const struct sockaddr_in6 *sb6 = (const struct sockaddr_in6 *) sb;
+
+      return (0 == memcmp(&sa6->sin6_addr,
+                          &sb6->sin6_addr,
+                          sizeof (struct in6_addr)));
+    }
+  default:
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
 /* end of tun.c */