(no commit message)
[oweals/gnunet.git] / src / transport / gnunet-nat-client.c
index 42af5ca77fd6adc325d66ccec950d5d6843f4c3b..14b7675574e6023f7ddade6a908ce6ae2df70489 100644 (file)
@@ -29,7 +29,7 @@
  * 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 RAW socket with root
- * priviledges, then drops them and only then starts to process
+ * 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
@@ -57,7 +57,9 @@
 /**
  * Must match IP given in the server.
  */
-#define DUMMY_IP "1.2.3.4"
+#define DUMMY_IP "192.0.2.86"
+
+#define NAT_TRAV_PORT 22225
 
 struct ip_packet 
 {
@@ -79,14 +81,32 @@ struct icmp_packet
   uint8_t code;
   uint16_t checksum;
   uint32_t reserved;
+
 };
+
+struct icmp_echo_packet
+{
+  uint8_t type;
+  uint8_t code;
+  uint16_t checksum;
+  uint32_t reserved;
+  uint32_t data;
+};
+
+struct udp_packet
+{
+  uint16_t src_port;
+
+  uint16_t dst_port;
+
+  uint32_t length;
+};
+
 static int rawsock;
 
 static struct in_addr dummy;
  
-static struct in_addr target;
-
+static uint32_t port;
 
 static uint16_t 
 calc_checksum(const uint16_t *data, 
@@ -106,15 +126,118 @@ calc_checksum(const uint16_t *data,
 
 static void
 make_echo (const struct in_addr *src_ip,
-          struct icmp_packet *echo)
+          struct icmp_echo_packet *echo, uint32_t num)
 {
-  memset(echo, 0, sizeof(struct icmp_packet));
+  memset(echo, 0, sizeof(struct icmp_echo_packet));
   echo->type = ICMP_ECHO;
   echo->code = 0;
   echo->reserved = 0;
   echo->checksum = 0;
+  echo->data = htons(num);
   echo->checksum = htons(calc_checksum((uint16_t*)echo, 
-                                      sizeof (struct icmp_packet)));
+                                      sizeof (struct icmp_echo_packet)));
+}
+
+
+/**
+ * Send an ICMP message to the target.
+ *
+ * @param my_ip source address
+ * @param other target address
+ */
+static void
+send_icmp_udp (const struct in_addr *my_ip,
+               const struct in_addr *other)
+{
+  struct ip_packet ip_pkt;
+  struct icmp_packet icmp_pkt;
+  struct udp_packet udp_pkt;
+
+  struct sockaddr_in dst;
+  char packet[sizeof(ip_pkt) * 2 + sizeof(icmp_pkt) * 2 + sizeof(uint32_t)];
+
+  size_t off;
+  int err;
+
+  /* ip header: send to (known) ip address */
+  off = 0;
+  memset(&ip_pkt, 0, sizeof(ip_pkt));
+  ip_pkt.vers_ihl = 0x45;
+  ip_pkt.tos = 0;
+  ip_pkt.pkt_len = htons(sizeof (packet));
+  ip_pkt.id = htons(256);
+  ip_pkt.flags_frag_offset = 0;
+  ip_pkt.ttl = 128;
+  ip_pkt.proto = IPPROTO_ICMP;
+  ip_pkt.checksum = 0;
+  ip_pkt.src_ip = my_ip->s_addr;
+  ip_pkt.dst_ip = other->s_addr;
+  ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (ip_pkt)));
+  memcpy(&packet[off], &ip_pkt, sizeof(ip_pkt));
+  off += sizeof(ip_pkt);
+
+  /* ip header of the presumably 'lost' udp packet */
+  ip_pkt.vers_ihl = 0x45;
+  ip_pkt.tos = 0;
+  ip_pkt.pkt_len = (sizeof (struct ip_packet) + sizeof (struct icmp_echo_packet));
+
+  icmp_pkt.type = 11; /* TTL exceeded */
+  icmp_pkt.code = 0;
+  icmp_pkt.checksum = 0;
+  icmp_pkt.reserved = 0;
+  memcpy(&packet[off], &icmp_pkt, sizeof(icmp_pkt));
+  off += sizeof(icmp_pkt);
+
+  /* build inner IP header */
+  memset(&ip_pkt, 0, sizeof(ip_pkt));
+  ip_pkt.vers_ihl = 0x45;
+  ip_pkt.tos = 0;
+  ip_pkt.pkt_len = htons(sizeof (ip_pkt) + sizeof(udp_pkt));
+  ip_pkt.id = htons(0);
+  ip_pkt.flags_frag_offset = 0;
+  ip_pkt.ttl = 128;
+  ip_pkt.proto = IPPROTO_UDP;
+  ip_pkt.checksum = 0;
+  ip_pkt.src_ip = other->s_addr;
+  ip_pkt.dst_ip = dummy.s_addr;
+  ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (ip_pkt)));
+  memcpy(&packet[off], &ip_pkt, sizeof(ip_pkt));
+  off += sizeof(ip_pkt);
+
+  /* build UDP header */
+  udp_pkt.src_port = htons(NAT_TRAV_PORT); /* FIXME: does this port matter? */
+  udp_pkt.dst_port = htons(NAT_TRAV_PORT);
+
+  memset(&udp_pkt.length, 0, sizeof(uint32_t));
+  udp_pkt.length = htonl(port);
+  memcpy(&packet[off], &udp_pkt, sizeof(udp_pkt));
+  off += sizeof(udp_pkt);
+
+  /* set ICMP checksum */
+  icmp_pkt.checksum = htons(calc_checksum((uint16_t*)&packet[sizeof(ip_pkt)],
+                            sizeof (icmp_pkt) + sizeof(ip_pkt) + sizeof(udp_pkt)));
+  memcpy (&packet[sizeof(ip_pkt)], &icmp_pkt, sizeof (icmp_pkt));
+
+
+  memset (&dst, 0, sizeof (dst));
+  dst.sin_family = AF_INET;
+  dst.sin_addr = *other;
+  err = sendto(rawsock,
+               packet,
+               off, 0,
+               (struct sockaddr*)&dst,
+               sizeof(dst));
+
+  if (err < 0)
+    {
+      fprintf(stderr,
+              "sendto failed: %s\n", strerror(errno));
+    }
+  else if (err != off)
+    {
+      fprintf(stderr,
+              "Error: partial send of ICMP message\n");
+    }
 }
 
 
@@ -130,9 +253,10 @@ send_icmp (const struct in_addr *my_ip,
 {
   struct ip_packet ip_pkt;
   struct icmp_packet *icmp_pkt;
-  struct icmp_packet icmp_echo;
+  struct icmp_echo_packet icmp_echo;
   struct sockaddr_in dst;
-  char packet[sizeof (struct ip_packet)*2 + sizeof (struct icmp_packet)*2];
+  char packet[sizeof (struct ip_packet)*2 + sizeof (struct icmp_packet) + sizeof(struct icmp_echo_packet)];
+
   size_t off;
   int err;
 
@@ -159,12 +283,14 @@ send_icmp (const struct in_addr *my_ip,
   icmp_pkt->code = 0; 
   icmp_pkt->reserved = 0;
   icmp_pkt->checksum = 0;
+
   off += sizeof (struct icmp_packet);
 
   /* ip header of the presumably 'lost' udp packet */
   ip_pkt.vers_ihl = 0x45;
   ip_pkt.tos = 0;
-  ip_pkt.pkt_len = (sizeof (struct ip_packet) + sizeof (struct icmp_packet));
+  ip_pkt.pkt_len = (sizeof (struct ip_packet) + sizeof (struct icmp_echo_packet));
+
   ip_pkt.id = 1; 
   ip_pkt.flags_frag_offset = 0;
   ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
@@ -175,11 +301,13 @@ send_icmp (const struct in_addr *my_ip,
   ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (struct ip_packet)));  
   memcpy (&packet[off], &ip_pkt, sizeof (struct ip_packet));
   off += sizeof (struct ip_packet);
-  make_echo (other, &icmp_echo);
-  memcpy (&packet[off], &icmp_echo, sizeof(struct icmp_packet));
-  off += sizeof (struct icmp_packet);
-  icmp_pkt->checksum = htons(calc_checksum((uint16_t*)icmp_pkt, 
-                                          sizeof (struct icmp_packet)*2 + sizeof(struct ip_packet)));
+
+  make_echo (other, &icmp_echo, port);
+  memcpy (&packet[off], &icmp_echo, sizeof(struct icmp_echo_packet));
+  off += sizeof (struct icmp_echo_packet);
+
+  icmp_pkt->checksum = htons(calc_checksum((uint16_t*)icmp_pkt,
+                                             sizeof (struct icmp_packet) + sizeof(struct ip_packet) + sizeof(struct icmp_echo_packet)));
 
   memset (&dst, 0, sizeof (dst));
   dst.sin_family = AF_INET;
@@ -234,6 +362,7 @@ int
 main (int argc, char *const *argv)
 {
   struct in_addr external;
+  struct in_addr target;
   uid_t uid;
 
   if (-1 == (rawsock = make_raw_socket()))
@@ -243,12 +372,15 @@ main (int argc, char *const *argv)
     fprintf (stderr,
             "Failed to setresuid: %s\n",
             strerror (errno));
-  if (argc != 3)
+
+  if (argc != 4)
     {
       fprintf (stderr,
-              "This program must be started with our IP and the targets external IP as arguments.\n");
+              "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
       return 1;
     }
+  port = atoi(argv[3]);
+
   if ( (1 != inet_pton (AF_INET, argv[1], &external)) ||
        (1 != inet_pton (AF_INET, argv[2], &target)) )
     {
@@ -257,9 +389,11 @@ 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 ();
   send_icmp (&external,
             &target);
+  send_icmp_udp (&external,
+             &target);
   close (rawsock);
   return 0;
 }