more code cleanup
[oweals/gnunet.git] / src / transport / gnunet-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/transport/gnunet-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  */
43 #define _GNU_SOURCE
44 #if HAVE_CONFIG_H
45 /* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
46 #include "gnunet_config.h"
47 #endif
48 #include <sys/types.h> 
49 #include <sys/socket.h>
50 #include <arpa/inet.h>
51 #include <sys/select.h>
52 #include <sys/time.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 <time.h>
61 #include <netinet/ip.h>
62 #include <netinet/ip_icmp.h>
63 #include <netinet/in.h> 
64
65 /**
66  * Should we print some debug output?
67  */
68 #define VERBOSE 0
69
70 /**
71  * Must match IP given in the client.
72  */
73 #define DUMMY_IP "192.0.2.86"
74
75 /**
76  * How often do we send our ICMP messages to receive replies?
77  */
78 #define ICMP_SEND_FREQUENCY_MS 500
79
80 /**
81  * IPv4 header.
82  */
83 struct ip_packet 
84 {
85
86   /**
87    * Version (4 bits) + Internet header length (4 bits) 
88    */
89   uint8_t vers_ihl;
90
91   /**
92    * Type of service
93    */
94   uint8_t tos;
95
96   /**
97    * Total length
98    */
99   uint16_t pkt_len;
100
101   /**
102    * Identification
103    */
104   uint16_t id;
105
106   /**
107    * Flags (3 bits) + Fragment offset (13 bits)
108    */
109   uint16_t flags_frag_offset;
110
111   /**
112    * Time to live
113    */
114   uint8_t ttl;
115
116   /**
117    * Protocol       
118    */
119   uint8_t proto;
120   
121   /**
122    * Header checksum
123    */
124   uint16_t checksum;
125
126   /**
127    * Source address
128    */
129   uint32_t src_ip;
130
131   /**
132    * Destination address 
133    */
134   uint32_t dst_ip;
135 };
136
137 /**
138  * Format of ICMP packet.
139  */
140 struct icmp_packet 
141 {
142   uint8_t type;
143
144   uint8_t code;
145
146   uint16_t checksum;
147
148   uint32_t reserved;
149 };
150
151 /**
152  * Beginning of UDP packet.
153  */
154 struct udp_packet
155 {
156   uint16_t src_port;
157
158   uint16_t dst_port;
159
160   uint32_t length;
161 };
162
163 /**
164  * Socket we use to receive "fake" ICMP replies.
165  */
166 static int icmpsock;
167
168 /**
169  * Socket we use to send our ICMP requests.
170  */
171 static int rawsock;
172
173 /**
174  * Target "dummy" address.
175  */
176 static struct in_addr dummy;
177
178
179 /**
180  * CRC-16 for IP/ICMP headers.
181  *
182  * @param data what to calculate the CRC over
183  * @param bytes number of bytes in data (must be multiple of 2)
184  * @return the CRC 16.
185  */
186 static uint16_t 
187 calc_checksum(const uint16_t *data, 
188               unsigned int bytes)
189 {
190   uint32_t sum;
191   unsigned int i;
192
193   sum = 0;
194   for (i=0;i<bytes/2;i++) 
195     sum += data[i];        
196   sum = (sum & 0xffff) + (sum >> 16);
197   sum = htons(0xffff - sum);
198   return sum;
199 }
200
201
202 /**
203  * Send an ICMP message to the dummy IP.
204  *
205  * @param my_ip source address (our ip address)
206  */
207 static void
208 send_icmp_echo (const struct in_addr *my_ip)
209 {
210   struct icmp_packet icmp_echo;
211   struct sockaddr_in dst;
212   size_t off;
213   int err;
214   struct ip_packet ip_pkt;
215   struct icmp_packet icmp_pkt;
216   char packet[sizeof (ip_pkt) + sizeof (icmp_pkt)];
217
218   off = 0;
219   memset(&ip_pkt, 0, sizeof(ip_pkt));
220   ip_pkt.vers_ihl = 0x45;
221   ip_pkt.tos = 0;
222   ip_pkt.pkt_len = sizeof (packet);
223   ip_pkt.id = 1;
224   ip_pkt.flags_frag_offset = 0;
225   ip_pkt.ttl = IPDEFTTL;
226   ip_pkt.proto = IPPROTO_ICMP;
227   ip_pkt.checksum = 0; 
228   ip_pkt.src_ip = my_ip->s_addr;
229   ip_pkt.dst_ip = dummy.s_addr;
230   ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (ip_pkt)));
231   memcpy (packet, &ip_pkt, sizeof (ip_pkt));
232   off += sizeof (ip_pkt);
233
234   icmp_echo.type = ICMP_ECHO;
235   icmp_echo.code = 0;
236   icmp_echo.reserved = 0;
237   icmp_echo.checksum = 0;
238   icmp_echo.checksum = htons(calc_checksum((uint16_t*)&icmp_echo, 
239                                            sizeof (struct icmp_packet)));
240
241   make_echo (my_ip, &icmp_echo);
242
243   memcpy (&packet[off], &icmp_echo, sizeof (icmp_echo));
244   off += sizeof (icmp_echo);
245  
246   memset (&dst, 0, sizeof (dst));
247   dst.sin_family = AF_INET;
248 #if HAVE_SOCKADDR_IN_SIN_LEN
249   dst.sin_len = sizeof (struct sockaddr_in);
250 #endif
251   dst.sin_addr = dummy;
252   err = sendto(rawsock, 
253                packet, off, 0, 
254                (struct sockaddr*)&dst, 
255                sizeof(dst));
256   if (err < 0) 
257     {
258 #if VERBOSE
259       fprintf(stderr,
260               "sendto failed: %s\n", strerror(errno));
261 #endif
262     }
263   else if (err != off) 
264     {
265       fprintf(stderr,
266               "Error: partial send of ICMP message\n");
267     }
268 }
269
270
271 /**
272  * We've received an ICMP response.  Process it.
273  */
274 static void
275 process_icmp_response ()
276 {
277   char buf[65536];
278   ssize_t have;
279   struct in_addr sip;
280   struct ip_packet ip_pkt;
281   struct icmp_packet icmp_pkt;
282   struct udp_packet udp_pkt;
283   size_t off;
284   int have_port;
285   uint32_t port;
286   
287   have = read (icmpsock, buf, sizeof (buf));
288   if (have == -1)
289     {
290       fprintf (stderr,
291                "Error reading raw socket: %s\n",
292                strerror (errno));
293       return; 
294     }
295   have_port = 0;
296 #if VERBOSE
297   fprintf (stderr,
298            "Received message of %u bytes\n",
299            (unsigned int) have);
300 #endif
301   if (have == sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2 + sizeof(uint32_t))
302     {
303       have_port = 1;
304     }
305   else if (have != sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2)
306     {
307 #if VERBOSE
308       fprintf (stderr,
309                "Received ICMP message of unexpected size: %u bytes\n",
310                (unsigned int) have);
311 #endif
312       return;
313     }
314   off = 0;
315   memcpy (&ip_pkt, &buf[off], sizeof (ip_pkt));
316   off += sizeof (ip_pkt);
317   memcpy (&icmp_pkt, &buf[off], sizeof (icmp_pkt));
318   off += sizeof (icmp_pkt);
319   if ( ((ip_pkt.proto != IPPROTO_ICMP) && (ip_pkt.proto != IPPROTO_UDP)) ||
320        (icmp_pkt.type != ICMP_TIME_EXCEEDED) || 
321        (icmp_pkt.code != 0) )
322     {
323       /* maybe we got an actual reply back... */
324       return;    
325     }
326   memcpy(&sip, 
327          &ip_pkt.src_ip, 
328          sizeof (sip));
329   memcpy (&ip_pkt, &buf[off], sizeof (ip_pkt));
330   off += sizeof (ip_pkt);
331
332   if (have_port)
333     {
334       memcpy(&port, 
335              &buf[sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2],
336              sizeof(uint32_t));
337       port = ntohs(port);
338       fprintf (stdout,
339                "%s:%d\n",
340                inet_ntop (AF_INET,
341                           &sip,
342                           buf,
343                           sizeof (buf)), 
344                port);
345     }
346   else if (ip_pkt.proto == IPPROTO_UDP)
347     {
348       memcpy(&udp_pkt, 
349              &buf[off], 
350              sizeof(udp_pkt));
351       fprintf (stdout,
352                "%s:%d\n",
353                inet_ntop (AF_INET,
354                           &sip,
355                           buf,
356                           sizeof (buf)), 
357                ntohs((uint16_t) udp_pkt.length));
358     }
359   else
360     {
361       fprintf (stdout,
362                "%s\n",
363                inet_ntop (AF_INET,
364                           &sip,
365                           buf,
366                           sizeof (buf)));
367     }
368   fflush (stdout);
369 }
370
371
372 /**
373  * Create an ICMP raw socket for reading.
374  *
375  * @return -1 on error
376  */
377 static int
378 make_icmp_socket ()
379 {
380   int ret;
381
382   ret = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
383   if (-1 == ret)
384     {
385       fprintf (stderr,
386                "Error opening RAW socket: %s\n",
387                strerror (errno));
388       return -1;
389     }  
390   if (ret >= FD_SETSIZE) 
391     {
392       fprintf (stderr,
393                "Socket number too large (%d > %u)\n",
394                ret,
395                (unsigned int) FD_SETSIZE);
396       close (ret);
397       return -1;
398     }
399   return ret;
400 }
401
402
403 /**
404  * Create an ICMP raw socket for writing.
405  *
406  * @return -1 on error
407  */
408 static int
409 make_raw_socket ()
410 {
411   const int one = 1;
412   int ret;
413
414   ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
415   if (-1 == ret)
416     {
417       fprintf (stderr,
418                "Error opening RAW socket: %s\n",
419                strerror (errno));
420       return -1;
421     }  
422   if (setsockopt(ret, 
423                  SOL_SOCKET, 
424                  SO_BROADCAST,
425                  (char *)&one, 
426                  sizeof(one)) == -1)
427     {
428       fprintf(stderr,
429               "setsockopt failed: %s\n",
430               strerror (errno));
431       close (ret);
432       return -1;
433     }
434   if (setsockopt(ret, 
435                  IPPROTO_IP, 
436                  IP_HDRINCL,
437                  (char *)&one, sizeof(one)) == -1)
438     {
439       fprintf(stderr,
440               "setsockopt failed: %s\n",
441               strerror (errno));
442       close (ret);
443       return -1;
444     }
445   return ret;
446 }
447
448
449 int
450 main (int argc, 
451       char *const *argv)
452 {
453   struct in_addr external;
454   fd_set rs;
455   struct timeval tv;
456   uid_t uid;
457
458   if (argc != 2)
459     {
460       fprintf (stderr,
461                "This program must be started with our (internal NAT) IP as the only argument.\n");
462       return 1;
463     }
464   if (1 != inet_pton (AF_INET, argv[1], &external))
465     {
466       fprintf (stderr,
467                "Error parsing IPv4 address: %s\n",
468                strerror (errno));
469       return 1;
470     }
471   if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy)) 
472     {
473       fprintf (stderr,
474                "Internal error converting dummy IP to binary.\n");
475       return 2;
476     }
477   if (-1 == (icmpsock = make_icmp_socket()))
478     {
479       return 3; 
480     }
481   if (-1 == (rawsock = make_raw_socket()))
482     {
483       close (icmpsock);
484       return 3; 
485     }
486   uid = getuid ();
487   if (0 != setresuid (uid, uid, uid))
488     {
489       fprintf (stderr,
490                "Failed to setresuid: %s\n",
491                strerror (errno));    
492       /* not critical, continue anyway */
493     }
494   while (1)
495     {
496       FD_ZERO (&rs);
497       FD_SET (icmpsock, &rs);
498       tv.tv_sec = 0;
499       tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000; 
500       if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv))
501         {
502           if (errno == EINTR)
503             continue;
504           fprintf (stderr,
505                    "select failed: %s\n",
506                    strerror (errno));
507           break;
508         }
509       if (FD_ISSET (icmpsock, &rs))
510         process_icmp_response ();
511       send_icmp_echo (&external);
512     }  
513   /* select failed (internal error or OS out of resources) */
514   close (icmpsock);
515   close (rawsock);
516   return 4;
517 }
518
519
520 /* end of gnunet-nat-server.c */