additional stylistic changes to gnunet-helper-nat-client anticipating next round...
[oweals/gnunet.git] / src / nat / gnunet-helper-nat-client.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file src/nat/gnunet-helper-nat-client.c
23  * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do)
24  *        This code will work under GNU/Linux only.
25  * @author Christian Grothoff
26  *
27  * This program will send ONE ICMP message using RAW sockets
28  * to the IP address specified as the second argument.  Since
29  * it uses RAW sockets, it must be installed SUID or run as 'root'.
30  * In order to keep the security risk of the resulting SUID binary
31  * minimal, the program ONLY opens the RAW socket with root
32  * privileges, then drops them and only then starts to process
33  * command line arguments.  The code also does not link against
34  * any shared libraries (except libc) and is strictly minimal
35  * (except for checking for errors).  The following list of people
36  * have reviewed this code and considered it safe since the last
37  * modification (if you reviewed it, please have your name added
38  * to the list):
39  *
40  * - Christian Grothoff
41  * - Nathan Evans
42  * - Benjamin Kuperman (22 Aug 2010)
43  */
44 #if HAVE_CONFIG_H
45 /* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
46 #include "gnunet_config.h"
47 #else
48 #define _GNU_SOURCE
49 #endif
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <arpa/inet.h>
53 #include <sys/types.h>
54 #include <unistd.h>
55 #include <stdio.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <stdlib.h>
59 #include <stdint.h>
60 #include <netinet/ip.h>
61 #include <netinet/ip_icmp.h>
62 #include <netinet/in.h>
63
64 /**
65  * Must match IP given in the server.
66  */
67 #define DUMMY_IP "192.0.2.86"
68
69 #define NAT_TRAV_PORT 22225
70
71 /**
72  * Must match packet ID used by gnunet-helper-nat-server.c
73  */
74 #define PACKET_ID 256
75
76 /**
77  * IPv4 header.
78  */
79 struct ip_header
80 {
81
82   /**
83    * Version (4 bits) + Internet header length (4 bits)
84    */
85   uint8_t vers_ihl;
86
87   /**
88    * Type of service
89    */
90   uint8_t tos;
91
92   /**
93    * Total length
94    */
95   uint16_t pkt_len;
96
97   /**
98    * Identification
99    */
100   uint16_t id;
101
102   /**
103    * Flags (3 bits) + Fragment offset (13 bits)
104    */
105   uint16_t flags_frag_offset;
106
107   /**
108    * Time to live
109    */
110   uint8_t ttl;
111
112   /**
113    * Protocol
114    */
115   uint8_t proto;
116
117   /**
118    * Header checksum
119    */
120   uint16_t checksum;
121
122   /**
123    * Source address
124    */
125   uint32_t src_ip;
126
127   /**
128    * Destination address
129    */
130   uint32_t dst_ip;
131 };
132
133 /**
134  * Format of ICMP packet.
135  */
136 struct icmp_ttl_exceeded_header
137 {
138   uint8_t type;
139
140   uint8_t code;
141
142   uint16_t checksum;
143
144   uint32_t unused;
145
146   /* followed by original payload */
147 };
148
149 struct icmp_echo_header
150 {
151   uint8_t type;
152
153   uint8_t code;
154
155   uint16_t checksum;
156
157   uint32_t reserved;
158 };
159
160 /**
161  * Beginning of UDP packet.
162  */
163 struct udp_header
164 {
165   uint16_t src_port;
166
167   uint16_t dst_port;
168
169   uint16_t length;
170
171   uint16_t crc;
172 };
173
174 /**
175  * Socket we use to send our fake ICMP replies.
176  */
177 static int rawsock;
178
179 /**
180  * Target "dummy" address of the packet we pretend to respond to.
181  */
182 static struct in_addr dummy;
183
184 /**
185  * Our "source" port.
186  */
187 static uint16_t port;
188
189
190 /**
191  * CRC-16 for IP/ICMP headers.
192  *
193  * @param data what to calculate the CRC over
194  * @param bytes number of bytes in data (must be multiple of 2)
195  * @return the CRC 16.
196  */
197 static uint16_t
198 calc_checksum (const uint16_t * data, unsigned int bytes)
199 {
200   uint32_t sum;
201   unsigned int i;
202
203   sum = 0;
204   for (i = 0; i < bytes / 2; i++)
205     sum += data[i];
206   sum = (sum & 0xffff) + (sum >> 16);
207   sum = htons (0xffff - sum);
208   return sum;
209 }
210
211
212 /**
213  * Send an ICMP message to the target.
214  *
215  * @param my_ip source address
216  * @param other target address
217  */
218 static void
219 send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
220 {
221   char packet[sizeof (struct ip_header) * 2 +
222               sizeof (struct icmp_ttl_exceeded_header) +
223               sizeof (struct udp_header)];
224   struct ip_header ip_pkt;
225   struct icmp_ttl_exceeded_header icmp_pkt;
226   struct udp_header udp_pkt;
227   struct sockaddr_in dst;
228   size_t off;
229   int err;
230
231   /* ip header: send to (known) ip address */
232   off = 0;
233   ip_pkt.vers_ihl = 0x45;
234   ip_pkt.tos = 0;
235   ip_pkt.pkt_len = htons (sizeof (packet));
236   ip_pkt.id = htons (PACKET_ID);
237   ip_pkt.flags_frag_offset = 0;
238   ip_pkt.ttl = 128;
239   ip_pkt.proto = IPPROTO_ICMP;
240   ip_pkt.checksum = 0;
241   ip_pkt.src_ip = my_ip->s_addr;
242   ip_pkt.dst_ip = other->s_addr;
243   ip_pkt.checksum =
244       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
245   memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
246   off += sizeof (struct ip_header);
247
248   icmp_pkt.type = ICMP_TIME_EXCEEDED;
249   icmp_pkt.code = 0;
250   icmp_pkt.checksum = 0;
251   icmp_pkt.unused = 0;
252   memcpy (&packet[off], &icmp_pkt, sizeof (struct icmp_ttl_exceeded_header));
253   off += sizeof (struct icmp_ttl_exceeded_header);
254
255   /* ip header of the presumably 'lost' udp packet */
256   ip_pkt.vers_ihl = 0x45;
257   ip_pkt.tos = 0;
258   ip_pkt.pkt_len =
259       htons (sizeof (struct ip_header) + sizeof (struct udp_header));
260   ip_pkt.id = htons (0);
261   ip_pkt.flags_frag_offset = 0;
262   ip_pkt.ttl = 128;
263   ip_pkt.proto = IPPROTO_UDP;
264   ip_pkt.checksum = 0;
265   ip_pkt.src_ip = other->s_addr;
266   ip_pkt.dst_ip = dummy.s_addr;
267   ip_pkt.checksum =
268       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
269   memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
270   off += sizeof (struct ip_header);
271
272   /* build UDP header */
273   udp_pkt.src_port = htons (NAT_TRAV_PORT);
274   udp_pkt.dst_port = htons (NAT_TRAV_PORT);
275   udp_pkt.length = htons (port);
276   udp_pkt.crc = 0;
277   memcpy (&packet[off], &udp_pkt, sizeof (struct udp_header));
278   off += sizeof (struct udp_header);
279
280   /* set ICMP checksum */
281   icmp_pkt.checksum =
282       htons (calc_checksum
283              ((uint16_t *) & packet[sizeof (struct ip_header)],
284               sizeof (struct icmp_ttl_exceeded_header) +
285               sizeof (struct ip_header) + sizeof (struct udp_header)));
286   memcpy (&packet[sizeof (struct ip_header)], &icmp_pkt,
287           sizeof (struct icmp_ttl_exceeded_header));
288
289   memset (&dst, 0, sizeof (dst));
290   dst.sin_family = AF_INET;
291 #if HAVE_SOCKADDR_IN_SIN_LEN
292   dst.sin_len = sizeof (struct sockaddr_in);
293 #endif
294   dst.sin_addr = *other;
295   err =
296       sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
297               sizeof (dst));
298   if (err < 0)
299   {
300     fprintf (stderr, "sendto failed: %s\n", strerror (errno));
301   }
302   else if (sizeof (packet) != (size_t) err)
303   {
304     fprintf (stderr, "Error: partial send of ICMP message\n");
305   }
306 }
307
308
309 /**
310  * Send an ICMP message to the target.
311  *
312  * @param my_ip source address
313  * @param other target address
314  */
315 static void
316 send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
317 {
318   struct ip_header ip_pkt;
319   struct icmp_ttl_exceeded_header icmp_ttl;
320   struct icmp_echo_header icmp_echo;
321   struct sockaddr_in dst;
322   char packet[sizeof (struct ip_header) * 2 +
323               sizeof (struct icmp_ttl_exceeded_header) +
324               sizeof (struct icmp_echo_header)];
325   size_t off;
326   int err;
327
328   /* ip header: send to (known) ip address */
329   off = 0;
330   ip_pkt.vers_ihl = 0x45;
331   ip_pkt.tos = 0;
332   ip_pkt.pkt_len = htons (sizeof (packet));
333   ip_pkt.id = htons (PACKET_ID);
334   ip_pkt.flags_frag_offset = 0;
335   ip_pkt.ttl = IPDEFTTL;
336   ip_pkt.proto = IPPROTO_ICMP;
337   ip_pkt.checksum = 0;
338   ip_pkt.src_ip = my_ip->s_addr;
339   ip_pkt.dst_ip = other->s_addr;
340   ip_pkt.checksum =
341       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
342   memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
343   off = sizeof (ip_pkt);
344
345   /* icmp reply: time exceeded */
346   icmp_ttl.type = ICMP_TIME_EXCEEDED;
347   icmp_ttl.code = 0;
348   icmp_ttl.checksum = 0;
349   icmp_ttl.unused = 0;
350   memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
351   off += sizeof (struct icmp_ttl_exceeded_header);
352
353   /* ip header of the presumably 'lost' udp packet */
354   ip_pkt.vers_ihl = 0x45;
355   ip_pkt.tos = 0;
356   ip_pkt.pkt_len =
357       htons (sizeof (struct ip_header) + sizeof (struct icmp_echo_header));
358   ip_pkt.id = htons (PACKET_ID);
359   ip_pkt.flags_frag_offset = 0;
360   ip_pkt.ttl = 1;               /* real TTL would be 1 on a time exceeded packet */
361   ip_pkt.proto = IPPROTO_ICMP;
362   ip_pkt.src_ip = other->s_addr;
363   ip_pkt.dst_ip = dummy.s_addr;
364   ip_pkt.checksum = 0;
365   ip_pkt.checksum =
366       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
367   memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
368   off += sizeof (struct ip_header);
369
370   icmp_echo.type = ICMP_ECHO;
371   icmp_echo.code = 0;
372   icmp_echo.reserved = htonl (port);
373   icmp_echo.checksum = 0;
374   icmp_echo.checksum =
375       htons (calc_checksum
376              ((uint16_t *) & icmp_echo, sizeof (struct icmp_echo_header)));
377   memcpy (&packet[off], &icmp_echo, sizeof (struct icmp_echo_header));
378
379   /* no go back to calculate ICMP packet checksum */
380   off = sizeof (struct ip_header);
381   icmp_ttl.checksum =
382       htons (calc_checksum
383              ((uint16_t *) & packet[off],
384               sizeof (struct icmp_ttl_exceeded_header) +
385               sizeof (struct ip_header) + sizeof (struct icmp_echo_header)));
386   memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
387
388   /* prepare for transmission */
389   memset (&dst, 0, sizeof (dst));
390   dst.sin_family = AF_INET;
391 #if HAVE_SOCKADDR_IN_SIN_LEN
392   dst.sin_len = sizeof (struct sockaddr_in);
393 #endif
394   dst.sin_addr = *other;
395   err =
396       sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
397               sizeof (dst));
398   if (err < 0)
399   {
400     fprintf (stderr, "sendto failed: %s\n", strerror (errno));
401   }
402   else if (sizeof (packet) != (size_t) err)
403   {
404     fprintf (stderr, "Error: partial send of ICMP message\n");
405   }
406 }
407
408
409 /**
410  * Create an ICMP raw socket for writing.
411  *
412  * @return -1 on error
413  */
414 static int
415 make_raw_socket ()
416 {
417   const int one = 1;
418   int ret;
419
420   ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
421   if (-1 == ret)
422   {
423     fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
424     return -1;
425   }
426   if (0 !=
427       setsockopt (ret, SOL_SOCKET, SO_BROADCAST, (char *) &one, sizeof (one)))
428   {
429     fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
430     close (ret);
431     return -1;
432   }
433   if (0 !=
434       setsockopt (ret, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof (one)))
435   {
436     fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
437     close (ret);
438     return -1;
439   }
440   return ret;
441 }
442
443
444 int
445 main (int argc, char *const *argv)
446 {
447   struct in_addr external;
448   struct in_addr target;
449   uid_t uid;
450   unsigned int p;
451
452   if (4 != argc)
453   {
454     fprintf (stderr,
455              "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
456     return 1;
457   }
458   if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
459       (1 != inet_pton (AF_INET, argv[2], &target)))
460   {
461     fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno));
462     return 1;
463   }
464   if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
465   {
466     fprintf (stderr, "Error parsing port value `%s'\n", argv[3]);
467     return 1;
468   }
469   port = (uint16_t) p;
470   if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
471   {
472     fprintf (stderr, "Internal error converting dummy IP to binary.\n");
473     return 2;
474   }
475   if (-1 == (rawsock = make_raw_socket ()))
476     return 2;
477   uid = getuid ();
478 #ifdef HAVE_SETRESUID
479   if (0 != setresuid (uid, uid, uid))
480   {
481     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
482     return 3;
483   }
484 #else
485   if (0 != (setuid (uid) | seteuid (uid)))
486   {
487     fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
488     return 6;
489   }
490 #endif
491   send_icmp (&external, &target);
492   send_icmp_udp (&external, &target);
493   close (rawsock);
494   return 0;
495 }
496
497 /* end of gnunet-helper-nat-client.c */