-changing exit helper code to automatically do the network configuration for an exit...
[oweals/gnunet.git] / src / nat / gnunet-helper-nat-server.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-server.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 (or maybe BSDs, but never W32)
25  * @author Christian Grothoff
26  *
27  * This program will send ONE ICMP message every 500 ms RAW sockets
28  * to a DUMMY IP address and also listens for ICMP replies.  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 two RAW sockets 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  * - Jacob Appelbaum (19 Dec 2011)
44  */
45 #if HAVE_CONFIG_H
46 /* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
47 #include "gnunet_config.h"
48 #else
49 #define _GNU_SOURCE
50 #endif
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <arpa/inet.h>
54 #include <sys/select.h>
55 #include <sys/time.h>
56 #include <sys/types.h>
57 #include <unistd.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <errno.h>
61 #include <stdlib.h>
62 #include <stdint.h>
63 #include <time.h>
64 #include <netinet/ip.h>
65 #include <netinet/ip_icmp.h>
66 #include <netinet/in.h>
67
68 /**
69  * Should we print some debug output?
70  */
71 #define VERBOSE 0
72
73 /**
74  * Must match packet ID used by gnunet-helper-nat-client.c
75  */
76 #define PACKET_ID 256
77
78 /**
79  * Must match IP given in the client.
80  */
81 #define DUMMY_IP "192.0.2.86"
82
83 /**
84  * Port for UDP
85  */
86 #define NAT_TRAV_PORT 22225
87
88 /**
89  * How often do we send our ICMP messages to receive replies?
90  */
91 #define ICMP_SEND_FREQUENCY_MS 500
92
93 /**
94  * IPv4 header.
95  */
96 struct ip_header
97 {
98
99   /**
100    * Version (4 bits) + Internet header length (4 bits)
101    */
102   uint8_t vers_ihl;
103
104   /**
105    * Type of service
106    */
107   uint8_t tos;
108
109   /**
110    * Total length
111    */
112   uint16_t pkt_len;
113
114   /**
115    * Identification
116    */
117   uint16_t id;
118
119   /**
120    * Flags (3 bits) + Fragment offset (13 bits)
121    */
122   uint16_t flags_frag_offset;
123
124   /**
125    * Time to live
126    */
127   uint8_t ttl;
128
129   /**
130    * Protocol
131    */
132   uint8_t proto;
133
134   /**
135    * Header checksum
136    */
137   uint16_t checksum;
138
139   /**
140    * Source address
141    */
142   uint32_t src_ip;
143
144   /**
145    * Destination address
146    */
147   uint32_t dst_ip;
148 };
149
150 /**
151  * Format of ICMP packet.
152  */
153 struct icmp_ttl_exceeded_header
154 {
155   uint8_t type;
156
157   uint8_t code;
158
159   uint16_t checksum;
160
161   uint32_t unused;
162
163   /* followed by original payload */
164 };
165
166 struct icmp_echo_header
167 {
168   uint8_t type;
169
170   uint8_t code;
171
172   uint16_t checksum;
173
174   uint32_t reserved;
175 };
176
177
178 /**
179  * Beginning of UDP packet.
180  */
181 struct udp_header
182 {
183   uint16_t src_port;
184
185   uint16_t dst_port;
186
187   uint16_t length;
188
189   uint16_t crc;
190 };
191
192 /**
193  * Socket we use to receive "fake" ICMP replies.
194  */
195 static int icmpsock;
196
197 /**
198  * Socket we use to send our ICMP requests.
199  */
200 static int rawsock;
201
202 /**
203  * Socket we use to send our UDP requests.
204  */
205 static int udpsock;
206
207 /**
208  * Target "dummy" address.
209  */
210 static struct in_addr dummy;
211
212
213 /**
214  * CRC-16 for IP/ICMP headers.
215  *
216  * @param data what to calculate the CRC over
217  * @param bytes number of bytes in data (must be multiple of 2)
218  * @return the CRC 16.
219  */
220 static uint16_t
221 calc_checksum (const uint16_t * data, unsigned int bytes)
222 {
223   uint32_t sum;
224   unsigned int i;
225
226   sum = 0;
227   for (i = 0; i < bytes / 2; i++)
228     sum += data[i];
229   sum = (sum & 0xffff) + (sum >> 16);
230   sum = htons (0xffff - sum);
231   return sum;
232 }
233
234
235 /**
236  * Send an ICMP message to the dummy IP.
237  *
238  * @param my_ip source address (our ip address)
239  */
240 static void
241 send_icmp_echo (const struct in_addr *my_ip)
242 {
243   char packet[sizeof (struct ip_header) + sizeof (struct icmp_echo_header)];
244   struct icmp_echo_header icmp_echo;
245   struct ip_header ip_pkt;
246   struct sockaddr_in dst;
247   size_t off;
248   int err;
249
250   off = 0;
251   ip_pkt.vers_ihl = 0x45;
252   ip_pkt.tos = 0;
253   ip_pkt.pkt_len = htons (sizeof (packet));
254   ip_pkt.id = htons (PACKET_ID);
255   ip_pkt.flags_frag_offset = 0;
256   ip_pkt.ttl = IPDEFTTL;
257   ip_pkt.proto = IPPROTO_ICMP;
258   ip_pkt.checksum = 0;
259   ip_pkt.src_ip = my_ip->s_addr;
260   ip_pkt.dst_ip = dummy.s_addr;
261   ip_pkt.checksum =
262       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
263   memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
264   off += sizeof (struct ip_header);
265
266   icmp_echo.type = ICMP_ECHO;
267   icmp_echo.code = 0;
268   icmp_echo.checksum = 0;
269   icmp_echo.reserved = 0;
270   icmp_echo.checksum =
271       htons (calc_checksum
272              ((uint16_t *) & icmp_echo, sizeof (struct icmp_echo_header)));
273   memcpy (&packet[off], &icmp_echo, sizeof (struct icmp_echo_header));
274   off += sizeof (struct icmp_echo_header);
275
276   memset (&dst, 0, sizeof (dst));
277   dst.sin_family = AF_INET;
278 #if HAVE_SOCKADDR_IN_SIN_LEN
279   dst.sin_len = sizeof (struct sockaddr_in);
280 #endif
281   dst.sin_addr = dummy;
282   err =
283       sendto (rawsock, packet, off, 0, (struct sockaddr *) &dst, sizeof (dst));
284   if (err < 0)
285   {
286 #if VERBOSE
287     fprintf (stderr, "sendto failed: %s\n", strerror (errno));
288 #endif
289   }
290   else if (sizeof (packet) != err)
291   {
292     fprintf (stderr, "Error: partial send of ICMP message\n");
293   }
294 }
295
296
297 /**
298  * Send a UDP message to the dummy IP.
299  */
300 static void
301 send_udp ()
302 {
303   struct sockaddr_in dst;
304   ssize_t err;
305
306   memset (&dst, 0, sizeof (dst));
307   dst.sin_family = AF_INET;
308 #if HAVE_SOCKADDR_IN_SIN_LEN
309   dst.sin_len = sizeof (struct sockaddr_in);
310 #endif
311   dst.sin_addr = dummy;
312   dst.sin_port = htons (NAT_TRAV_PORT);
313   err = sendto (udpsock, NULL, 0, 0, (struct sockaddr *) &dst, sizeof (dst));
314   if (err < 0)
315   {
316 #if VERBOSE
317     fprintf (stderr, "sendto failed: %s\n", strerror (errno));
318 #endif
319   }
320   else if (0 != err)
321   {
322     fprintf (stderr, "Error: partial send of ICMP message\n");
323   }
324 }
325
326
327 /**
328  * We've received an ICMP response.  Process it.
329  */
330 static void
331 process_icmp_response ()
332 {
333   char buf[65536];
334   ssize_t have;
335   struct in_addr source_ip;
336   struct ip_header ip_pkt;
337   struct icmp_ttl_exceeded_header icmp_ttl;
338   struct icmp_echo_header icmp_echo;
339   struct udp_header udp_pkt;
340   size_t off;
341   uint16_t port;
342
343   have = read (icmpsock, buf, sizeof (buf));
344   if (-1 == have)
345   {
346     fprintf (stderr, "Error reading raw socket: %s\n", strerror (errno));
347     return;
348   }
349 #if VERBOSE
350   fprintf (stderr, "Received message of %u bytes\n", (unsigned int) have);
351 #endif
352   if (have <
353       (ssize_t) (sizeof (struct ip_header) +
354                  sizeof (struct icmp_ttl_exceeded_header) +
355                  sizeof (struct ip_header)))
356   {
357     /* malformed */
358     return;
359   }
360   off = 0;
361   memcpy (&ip_pkt, &buf[off], sizeof (struct ip_header));
362   off += sizeof (struct ip_header);
363   memcpy (&icmp_ttl, &buf[off], sizeof (struct icmp_ttl_exceeded_header));
364   off += sizeof (struct icmp_ttl_exceeded_header);
365   if ((ICMP_TIME_EXCEEDED != icmp_ttl.type) || (0 != icmp_ttl.code))
366   {
367     /* different type than what we want */
368     return;
369   }
370   /* skip 2nd IP header */
371   memcpy (&ip_pkt, &buf[off], sizeof (struct ip_header));
372   off += sizeof (struct ip_header);
373
374   switch (ip_pkt.proto)
375   {
376   case IPPROTO_ICMP:
377     if (have !=
378         (sizeof (struct ip_header) * 2 +
379          sizeof (struct icmp_ttl_exceeded_header) +
380          sizeof (struct icmp_echo_header)))
381     {
382       /* malformed */
383       return;
384     }
385     /* grab ICMP ECHO content */
386     memcpy (&icmp_echo, &buf[off], sizeof (struct icmp_echo_header));
387     port = (uint16_t) ntohl (icmp_echo.reserved);
388     break;
389   case IPPROTO_UDP:
390     if (have !=
391         (sizeof (struct ip_header) * 2 +
392          sizeof (struct icmp_ttl_exceeded_header) + sizeof (struct udp_header)))
393     {
394       /* malformed */
395       return;
396     }
397     /* grab UDP content */
398     memcpy (&udp_pkt, &buf[off], sizeof (struct udp_header));
399     port = ntohs (udp_pkt.length);
400     break;
401   default:
402     /* different type than what we want */
403     return;
404   }
405
406   source_ip.s_addr = ip_pkt.src_ip;
407   if (port == 0)
408     fprintf (stdout, "%s\n",
409              inet_ntop (AF_INET, &source_ip, buf, sizeof (buf)));
410   else
411     fprintf (stdout, "%s:%u\n",
412              inet_ntop (AF_INET, &source_ip, buf, sizeof (buf)),
413              (unsigned int) port);
414   fflush (stdout);
415 }
416
417
418 /**
419  * Fully initialize the raw socket.
420  *
421  * @return -1 on error, 0 on success
422  */
423 static int
424 setup_raw_socket ()
425 {
426   const int one = 1;
427
428   if (-1 ==
429       setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &one, sizeof (one)))
430   {
431     fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
432     return -1;
433   }
434   if (-1 ==
435       setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof (one)))
436   {
437     fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
438     return -1;
439   }
440   return 0;
441 }
442
443
444 /**
445  * Create a UDP socket for writing.
446  *
447  * @param my_ip source address (our ip address)
448  * @return -1 on error
449  */
450 static int
451 make_udp_socket (const struct in_addr *my_ip)
452 {
453   int ret;
454   struct sockaddr_in addr;
455
456   ret = socket (AF_INET, SOCK_DGRAM, 0);
457   if (-1 == ret)
458   {
459     fprintf (stderr, "Error opening UDP socket: %s\n", strerror (errno));
460     return -1;
461   }
462   memset (&addr, 0, sizeof (addr));
463   addr.sin_family = AF_INET;
464 #if HAVE_SOCKADDR_IN_SIN_LEN
465   addr.sin_len = sizeof (struct sockaddr_in);
466 #endif
467   addr.sin_addr = *my_ip;
468   addr.sin_port = htons (NAT_TRAV_PORT);
469
470   if (0 != bind (ret, &addr, sizeof (addr)))
471   {
472     fprintf (stderr, "Error binding UDP socket to port %u: %s\n", NAT_TRAV_PORT,
473              strerror (errno));
474     (void) close (ret);
475     return -1;
476   }
477   return ret;
478 }
479
480
481 int
482 main (int argc, char *const *argv)
483 {
484   struct in_addr external;
485   fd_set rs;
486   struct timeval tv;
487   uid_t uid;
488   unsigned int alt;
489   int icmp_eno;
490   int raw_eno;
491   int global_ret;
492
493   /* Create an ICMP raw socket for reading (we'll check errors later) */
494   icmpsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
495   icmp_eno = errno;
496
497   /* Create an (ICMP) raw socket for writing (we'll check errors later) */
498   rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
499   raw_eno = errno;
500   udpsock = -1;
501
502   /* drop root rights */
503   uid = getuid ();
504 #ifdef HAVE_SETRESUID
505   if (0 != setresuid (uid, uid, uid))
506   {
507     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
508     global_ret = 1;
509     goto error_exit;
510   }
511 #else
512   if (0 != (setuid (uid) | seteuid (uid)))
513   {
514     fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
515     global_ret = 2;
516     goto error_exit;
517   }
518 #endif
519
520   /* Now that we run without root rights, we can do error checking... */
521   if (2 != argc)
522   {
523     fprintf (stderr,
524              "This program must be started with our (internal NAT) IP as the only argument.\n");
525     global_ret = 3;
526     goto error_exit;
527   }
528   if (1 != inet_pton (AF_INET, argv[1], &external))
529   {
530     fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno));
531     global_ret = 4;
532     goto error_exit;
533   }
534   if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
535   {
536     fprintf (stderr, "Internal error converting dummy IP to binary.\n");
537     global_ret = 5;
538     goto error_exit;
539   }
540
541   /* error checking icmpsock */
542   if (-1 == icmpsock)
543   {
544     fprintf (stderr, "Error opening RAW socket: %s\n", strerror (icmp_eno));
545     global_ret = 6;
546     goto error_exit;
547   }
548   if (icmpsock >= FD_SETSIZE)
549   {
550     /* this could happen if we were started with a large number of already-open
551        file descriptors... */
552     fprintf (stderr, "Socket number too large (%d > %u)\n", icmpsock,
553              (unsigned int) FD_SETSIZE);
554     global_ret = 7;
555     goto error_exit;
556   }
557
558   /* error checking rawsock */
559   if (-1 == rawsock)
560   {
561     fprintf (stderr, "Error opening RAW socket: %s\n", strerror (raw_eno));
562     global_ret = 8;
563     goto error_exit;
564   }
565   /* no need to check 'rawsock' against FD_SETSIZE as it is never used
566      with 'select' */
567
568   if (0 != setup_raw_socket ())
569   {
570     global_ret = 9;
571     goto error_exit;
572   }
573
574   if (-1 == (udpsock = make_udp_socket (&external)))
575   {
576     global_ret = 10;
577     goto error_exit;
578   }
579
580   alt = 0;
581   while (1)
582   {
583     FD_ZERO (&rs);
584     FD_SET (icmpsock, &rs);
585     tv.tv_sec = 0;
586     tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
587     if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv))
588     {
589       if (errno == EINTR)
590         continue;
591       fprintf (stderr, "select failed: %s\n", strerror (errno));
592       break;
593     }
594     if (1 == getppid ())        /* Check the parent process id, if 1 the parent has died, so we should die too */
595       break;
596     if (FD_ISSET (icmpsock, &rs))
597       process_icmp_response ();
598     if (0 == (++alt % 2))
599       send_icmp_echo (&external);
600     else
601       send_udp ();
602   }
603
604   /* select failed (internal error or OS out of resources) */
605   global_ret = 11; 
606 error_exit:
607   if (-1 != icmpsock)
608     (void) close (icmpsock);
609   if (-1 != rawsock)
610     (void) close (rawsock);
611   if (-1 != udpsock)
612     (void) close (udpsock);
613   return global_ret;
614 }
615
616
617 /* end of gnunet-helper-nat-server.c */