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