(no commit message)
[oweals/gnunet.git] / src / transport / gnunet-nat-server.c
index 225c0af5fdb46b59c13e30d4d31a7af74853a51a..13a54058b71f46358fc6d9977119e5ff28db232f 100644 (file)
  * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do)
  *        This code will work under GNU/Linux only (or maybe BSDs, but never W32)
  * @author Christian Grothoff
+ *
+ * This program will send ONE ICMP message every 500 ms RAW sockets
+ * to a DUMMY IP address and also listens for ICMP replies.  Since
+ * it uses RAW sockets, it must be installed SUID or run as 'root'.
+ * In order to keep the security risk of the resulting SUID binary
+ * minimal, the program ONLY opens the two RAW sockets with root
+ * privileges, then drops them and only then starts to process
+ * command line arguments.  The code also does not link against
+ * any shared libraries (except libc) and is strictly minimal
+ * (except for checking for errors).  The following list of people
+ * have reviewed this code and considered it safe since the last
+ * modification (if you reviewed it, please have your name added
+ * to the list):
+ *
+ * - Christian Grothoff
  */
 #define _GNU_SOURCE
 #include <sys/types.h> 
@@ -45,7 +60,9 @@
 /**
  * Must match IP given in the client.
  */
-#define DUMMY_IP "1.2.3.4"
+#define DUMMY_IP "192.0.2.86"
+
+#define VERBOSE 0
 
 /**
  * How often do we send our ICMP messages to receive replies?
@@ -74,6 +91,14 @@ struct icmp_packet
   uint32_t reserved;
 };
 
+struct udp_packet
+{
+  uint16_t src_port;
+
+  uint16_t dst_port;
+
+  uint32_t length;
+};
 
 static int icmpsock;
 
@@ -154,8 +179,10 @@ send_icmp_echo (const struct in_addr *my_ip)
               sizeof(dst));
   if (err < 0) 
     {
+#if VERBOSE
       fprintf(stderr,
              "sendto failed: %s\n", strerror(errno));
+#endif
     }
   else if (err != off) 
     {
@@ -173,7 +200,11 @@ process_icmp_response ()
   struct in_addr sip;
   struct ip_packet ip_pkt;
   struct icmp_packet icmp_pkt;
+  struct udp_packet udp_pkt;
   size_t off;
+  int have_port;
+  int have_udp;
+  uint32_t port;
   
   have = read (icmpsock, buf, sizeof (buf));
   if (have == -1)
@@ -183,11 +214,19 @@ process_icmp_response ()
               strerror (errno));
       return; 
     }
-  if (have != sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2)
+  have_port = 0;
+
+  if (have == sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2 + sizeof(uint32_t))
+    {
+      have_port = 1;
+    }
+  else if (have != sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2)
     {
+#if VERBOSE
       fprintf (stderr,
               "Received ICMP message of unexpected size: %u bytes\n",
               (unsigned int) have);
+#endif
       return;
     }
   off = 0;
@@ -195,7 +234,7 @@ process_icmp_response ()
   off += sizeof (ip_pkt);
   memcpy (&icmp_pkt, &buf[off], sizeof (icmp_pkt));
   off += sizeof (icmp_pkt);
-  if ( (ip_pkt.proto != IPPROTO_ICMP) ||
+  if ( ((ip_pkt.proto != IPPROTO_ICMP) && (ip_pkt.proto != IPPROTO_UDP)) ||
        (icmp_pkt.type != ICMP_TIME_EXCEEDED) || 
        (icmp_pkt.code != 0) )
     {
@@ -205,12 +244,47 @@ process_icmp_response ()
   memcpy(&sip, 
         &ip_pkt.src_ip, 
         sizeof (sip));
-  fprintf (stdout,
-          "%s\n",
-          inet_ntop (AF_INET,
-                     &sip,
-                     buf,
-                     sizeof (buf)));
+
+  memcpy (&ip_pkt, &buf[off], sizeof (ip_pkt));
+  off += sizeof (ip_pkt);
+
+  have_udp = 0;
+  if (ip_pkt.proto == IPPROTO_UDP)
+    {
+      have_udp = 1;
+    }
+
+  if (have_port)
+    {
+      memcpy(&port, &buf[sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2], sizeof(uint32_t));
+      port = ntohs(port);
+      fprintf (stdout,
+              "%s:%d\n",
+              inet_ntop (AF_INET,
+                         &sip,
+                         buf,
+                         sizeof (buf)), port);
+    }
+  else if (have_udp)
+    {
+      memcpy(&udp_pkt, &buf[off], sizeof(udp_pkt));
+      fprintf (stdout,
+               "%s:%d\n",
+               inet_ntop (AF_INET,
+                          &sip,
+                          buf,
+                          sizeof (buf)), ntohl(udp_pkt.length));
+    }
+  else
+    {
+      fprintf (stdout,
+              "%s\n",
+              inet_ntop (AF_INET,
+                         &sip,
+                         buf,
+                         sizeof (buf)));
+    }
+  fflush (stdout);
 }
 
 
@@ -291,7 +365,7 @@ main (int argc, char *const *argv)
   if (argc != 2)
     {
       fprintf (stderr,
-              "This program must be started with our external IP as the only argument.\n");
+              "This program must be started with our (internal NAT) IP as the only argument.\n");
       return 1;
     }
   if (1 != inet_pton (AF_INET, argv[1], &external))
@@ -301,7 +375,7 @@ main (int argc, char *const *argv)
               strerror (errno));
       return 1;
     }
-  inet_pton (AF_INET, DUMMY_IP, &dummy);
+  if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy)) abort ();
   while (1)
     {
       FD_ZERO (&rs);