cleaning up set handlers, eliminating 2nd level demultiplexing and improving use...
[oweals/gnunet.git] / src / exit / gnunet-helper-exit.c
index 9a7445d936afe1a5241a41a8f95dd66fe32b27e2..e14c6ca43fab517e4fa7ef831c05a5f994a16cc6 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2010, 2011, 2012 Christian Grothoff
+     Copyright (C) 2010, 2011, 2012 Christian Grothoff
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-     Boston, MA 02111-1307, USA.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 
 /**
- * @file exit/gnunet-helper-exit.c 
+ * @file exit/gnunet-helper-exit.c
  *
  * @brief the helper for exit nodes. Opens a virtual
  * network-interface, sends data received on the if to stdout, sends
@@ -47,6 +47,7 @@
 /**
  * Need 'struct GNUNET_MessageHeader'.
  */
+#include "gnunet_crypto_lib.h"
 #include "gnunet_common.h"
 
 /**
@@ -83,22 +84,48 @@ static const char *sbin_iptables;
 struct in6_ifreq
 {
   struct in6_addr ifr6_addr;
-  uint32_t ifr6_prefixlen;
-  unsigned int ifr6_ifindex;
+  uint32_t ifr6_prefixlen; /* __u32 in the original */
+  int ifr6_ifindex;
 };
 #endif
 
 
+/**
+ * Open '/dev/null' and make the result the given
+ * file descriptor.
+ *
+ * @param target_fd desired FD to point to /dev/null
+ * @param flags open flags (O_RDONLY, O_WRONLY)
+ */
+static void
+open_dev_null (int target_fd,
+              int flags)
+{
+  int fd;
+
+  fd = open ("/dev/null", flags);
+  if (-1 == fd)
+    abort ();
+  if (fd == target_fd)
+    return;
+  if (-1 == dup2 (fd, target_fd))
+  {
+    (void) close (fd);
+    abort ();
+  }
+  (void) close (fd);
+}
+
 
 /**
  * Run the given command and wait for it to complete.
- * 
+ *
  * @param file name of the binary to run
  * @param cmd command line arguments (as given to 'execv')
  * @return 0 on success, 1 on any error
  */
 static int
-fork_and_exec (const char *file, 
+fork_and_exec (const char *file,
               char *const cmd[])
 {
   int status;
@@ -108,8 +135,8 @@ fork_and_exec (const char *file,
   pid = fork ();
   if (-1 == pid)
   {
-    fprintf (stderr, 
-            "fork failed: %s\n", 
+    fprintf (stderr,
+            "fork failed: %s\n",
             strerror (errno));
     return 1;
   }
@@ -118,23 +145,25 @@ fork_and_exec (const char *file,
     /* we are the child process */
     /* close stdin/stdout to not cause interference
        with the helper's main protocol! */
-    (void) close (0); 
-    (void) close (1); 
+    (void) close (0);
+    open_dev_null (0, O_RDONLY);
+    (void) close (1);
+    open_dev_null (1, O_WRONLY);
     (void) execv (file, cmd);
     /* can only get here on error */
-    fprintf (stderr, 
-            "exec `%s' failed: %s\n", 
+    fprintf (stderr,
+            "exec `%s' failed: %s\n",
             file,
             strerror (errno));
     _exit (1);
   }
   /* keep running waitpid as long as the only error we get is 'EINTR' */
   while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
-         (errno == EINTR) ); 
+         (errno == EINTR) );
   if (-1 == ret)
   {
-    fprintf (stderr, 
-            "waitpid failed: %s\n", 
+    fprintf (stderr,
+            "waitpid failed: %s\n",
             strerror (errno));
     return 1;
   }
@@ -186,7 +215,7 @@ init_tun (char *dev)
 
   if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
   {
-    fprintf (stderr, 
+    fprintf (stderr,
             "Error with ioctl on `%s': %s\n", "/dev/net/tun",
              strerror (errno));
     (void) close (fd);
@@ -208,16 +237,16 @@ static void
 set_address6 (const char *dev, const char *address, unsigned long prefix_len)
 {
   struct ifreq ifr;
-  struct in6_ifreq ifr6;
   struct sockaddr_in6 sa6;
   int fd;
+  struct in6_ifreq ifr6;
 
   /*
    * parse the new address
    */
   memset (&sa6, 0, sizeof (struct sockaddr_in6));
   sa6.sin6_family = AF_INET6;
-  if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
+  if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr))
   {
     fprintf (stderr, "Failed to parse address `%s': %s\n", address,
              strerror (errno));
@@ -422,7 +451,7 @@ run (int fd_tun)
   /* write refers to reading from stdin, writing to fd_tun */
   int write_open = 1;
 
-  while ((1 == read_open) || (1 == write_open))
+  while ((1 == read_open) && (1 == write_open))
   {
     FD_ZERO (&fds_w);
     FD_ZERO (&fds_r);
@@ -474,7 +503,9 @@ run (int fd_tun)
                   MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
         if (-1 == buftun_size)
         {
-          fprintf (stderr, "read-error: %s\n", strerror (errno));
+          fprintf (stderr,
+                   "read-error: %s\n",
+                   strerror (errno));
           shutdown (fd_tun, SHUT_RD);
           shutdown (1, SHUT_WR);
           read_open = 0;
@@ -509,7 +540,9 @@ run (int fd_tun)
 #if !DEBUG
          if (errno != EPIPE)
 #endif
-           fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
+           fprintf (stderr,
+                     "write-error to stdout: %s\n",
+                     strerror (errno));
           shutdown (fd_tun, SHUT_RD);
           shutdown (1, SHUT_WR);
           read_open = 0;
@@ -610,7 +643,8 @@ PROCESS_BUFFER:
  * @param argc must be 6
  * @param argv 0: binary name ("gnunet-helper-exit")
  *             1: tunnel interface name ("gnunet-exit")
- *             2: IPv4 "physical" interface name ("eth0"), or "%" to not do IPv4 NAT
+ *             2: "physical" interface name ("eth0"), or "-" to not setup NAT
+ *                and routing
  *             3: IPv6 address ("::1"), or "-" to skip IPv6
  *             4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"]
  *             5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4
@@ -634,27 +668,30 @@ main (int argc, char **argv)
     fprintf (stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n");
     return 1;
   }
-  if (0 == access ("/sbin/iptables", X_OK))
-    sbin_iptables = "/sbin/iptables";
-  else if (0 == access ("/usr/sbin/iptables", X_OK))
-    sbin_iptables = "/usr/sbin/iptables";
-  else
+  if (0 != strcmp (argv[2], "-"))
   {
-    fprintf (stderr, 
-            "Fatal: executable iptables not found in approved directories: %s\n",
-            strerror (errno));
-    return 1;
-  }
-  if (0 == access ("/sbin/sysctl", X_OK))
-    sbin_sysctl = "/sbin/sysctl";
-  else if (0 == access ("/usr/sbin/sysctl", X_OK))
-    sbin_sysctl = "/usr/sbin/sysctl";
-  else
-  {
-    fprintf (stderr,
-            "Fatal: executable sysctl not found in approved directories: %s\n",
-            strerror (errno));
-    return 1;
+    if (0 == access ("/sbin/iptables", X_OK))
+      sbin_iptables = "/sbin/iptables";
+    else if (0 == access ("/usr/sbin/iptables", X_OK))
+      sbin_iptables = "/usr/sbin/iptables";
+    else
+    {
+      fprintf (stderr,
+              "Fatal: executable iptables not found in approved directories: %s\n",
+              strerror (errno));
+      return 1;
+    }
+    if (0 == access ("/sbin/sysctl", X_OK))
+      sbin_sysctl = "/sbin/sysctl";
+    else if (0 == access ("/usr/sbin/sysctl", X_OK))
+      sbin_sysctl = "/usr/sbin/sysctl";
+    else
+    {
+      fprintf (stderr,
+              "Fatal: executable sysctl not found in approved directories: %s\n",
+              strerror (errno));
+      return 1;
+    }
   }
 
   strncpy (dev, argv[1], IFNAMSIZ);
@@ -662,7 +699,13 @@ main (int argc, char **argv)
 
   if (-1 == (fd_tun = init_tun (dev)))
   {
-    fprintf (stderr, "Fatal: could not initialize tun-interface\n");
+    fprintf (stderr,
+            "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n",
+            dev,
+            argv[3],
+            argv[4],
+            argv[5],
+            argv[6]);
     return 1;
   }
 
@@ -671,14 +714,15 @@ main (int argc, char **argv)
     {
       const char *address = argv[3];
       long prefix_len = atol (argv[4]);
-      
+
       if ((prefix_len < 1) || (prefix_len > 127))
       {
        fprintf (stderr, "Fatal: prefix_len out of range\n");
        return 1;
-      }      
-      set_address6 (dev, address, prefix_len);    
+      }
+      set_address6 (dev, address, prefix_len);
     }
+    if (0 != strcmp (argv[2], "-"))
     {
       char *const sysctl_args[] =
        {
@@ -689,7 +733,7 @@ main (int argc, char **argv)
       {
        fprintf (stderr,
                 "Failed to enable IPv6 forwarding.  Will continue anyway.\n");
-      }    
+      }
     }
   }
 
@@ -698,36 +742,38 @@ main (int argc, char **argv)
     {
       const char *address = argv[5];
       const char *mask = argv[6];
-      
+
       set_address4 (dev, address, mask);
     }
+    if (0 != strcmp (argv[2], "-"))
     {
-      char *const sysctl_args[] =
-       {
-         "sysctl", "-w", "net.ipv4.ip_forward=1", NULL
-       };
-      if (0 != fork_and_exec (sbin_sysctl,
-                             sysctl_args))
       {
-       fprintf (stderr,
-                "Failed to enable IPv4 forwarding.  Will continue anyway.\n");
-      }    
-    }
-    if (0 != strcmp (argv[2], "%"))
-    {
-      char *const iptables_args[] =
-       {
-         "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", "MASQUERADE", NULL
-       };
-      if (0 != fork_and_exec (sbin_iptables,
-                             iptables_args))
+        char *const sysctl_args[] =
+         {
+           "sysctl", "-w", "net.ipv4.ip_forward=1", NULL
+         };
+        if (0 != fork_and_exec (sbin_sysctl,
+                               sysctl_args))
+        {
+         fprintf (stderr,
+                  "Failed to enable IPv4 forwarding.  Will continue anyway.\n");
+        }
+      }
       {
-       fprintf (stderr,
-                "Failed to enable IPv4 masquerading (NAT).  Will continue anyway.\n");
-      }    
+        char *const iptables_args[] =
+         {
+           "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", "MASQUERADE", NULL
+         };
+        if (0 != fork_and_exec (sbin_iptables,
+                               iptables_args))
+        {
+         fprintf (stderr,
+                  "Failed to enable IPv4 masquerading (NAT).  Will continue anyway.\n");
+        }
+      }
     }
   }
-  
+
   uid_t uid = getuid ();
 #ifdef HAVE_SETRESUID
   if (0 != setresuid (uid, uid, uid))
@@ -754,7 +800,7 @@ main (int argc, char **argv)
   run (fd_tun);
   global_ret = 0;
  cleanup:
-  close (fd_tun);
+  (void) close (fd_tun);
   return global_ret;
 }