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