glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / nat / gnunet-helper-nat-server-windows.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-windows.c
18  * @brief Windows tool to help bypass NATs using ICMP method
19  *        This code will work under W32 only
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 run as an administrative user.
25  * In order to keep the security risk of the resulting binary
26  * minimal, the program ONLY opens the two RAW sockets with administrative
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  * - Nathan Evans
36  * - Christian Grothoff
37  */
38 #define _GNU_SOURCE
39 /* Instead of including gnunet_common.h */
40 #define GNUNET_memcpy(dst,src,n) do { if (0 != n) { (void) memcpy (dst,src,n); } } while (0)
41
42 #define FD_SETSIZE 1024
43 #include <winsock2.h>
44 #include <ws2tcpip.h>
45 #include <sys/time.h>
46 #include <sys/types.h>
47 #include <unistd.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <errno.h>
51 #include <stdlib.h>
52 #include <stdint.h>
53 #include <time.h>
54
55 /**
56  * Should we print some debug output?
57  */
58 #define VERBOSE 0
59
60 /**
61  * Must match IP given in the client.
62  */
63 #define DUMMY_IP "192.0.2.86"
64
65 /**
66  * Default Port
67  */
68 #define NAT_TRAV_PORT 22225
69
70 /**
71  * Must match packet ID used by gnunet-helper-nat-client.c
72  */
73 #define PACKET_ID 256
74
75 /**
76  * TTL to use for our outgoing messages.
77  */
78 #define IPDEFTTL 64
79
80 #define ICMP_ECHO 8
81
82 #define ICMP_TIME_EXCEEDED 11
83
84 /**
85  * How often do we send our ICMP messages to receive replies?
86  */
87 #define ICMP_SEND_FREQUENCY_MS 500
88
89 /**
90  * IPv4 header.
91  */
92 struct ip_header
93 {
94
95   /**
96    * Version (4 bits) + Internet header length (4 bits)
97    */
98   uint8_t vers_ihl;
99
100   /**
101    * Type of service
102    */
103   uint8_t tos;
104
105   /**
106    * Total length
107    */
108   uint16_t pkt_len;
109
110   /**
111    * Identification
112    */
113   uint16_t id;
114
115   /**
116    * Flags (3 bits) + Fragment offset (13 bits)
117    */
118   uint16_t flags_frag_offset;
119
120   /**
121    * Time to live
122    */
123   uint8_t ttl;
124
125   /**
126    * Protocol
127    */
128   uint8_t proto;
129
130   /**
131    * Header checksum
132    */
133   uint16_t checksum;
134
135   /**
136    * Source address
137    */
138   uint32_t src_ip;
139
140   /**
141    * Destination address
142    */
143   uint32_t dst_ip;
144 };
145
146 /**
147  * Format of ICMP packet.
148  */
149 struct icmp_ttl_exceeded_header
150 {
151   uint8_t type;
152
153   uint8_t code;
154
155   uint16_t checksum;
156
157   uint32_t unused;
158
159   /* followed by original payload */
160 };
161
162 struct icmp_echo_header
163 {
164   uint8_t type;
165
166   uint8_t code;
167
168   uint16_t checksum;
169
170   uint32_t reserved;
171 };
172
173 /**
174  * Beginning of UDP packet.
175  */
176 struct udp_header
177 {
178   uint16_t src_port;
179
180   uint16_t dst_port;
181
182   uint16_t length;
183
184   uint16_t crc;
185 };
186
187 /**
188  * Will this binary be run in permissions testing mode?
189  */
190 static boolean privilege_testing = FALSE;
191
192 /**
193  * Socket we use to receive "fake" ICMP replies.
194  */
195 static SOCKET icmpsock;
196
197 /**
198  * Socket we use to send our ICMP requests.
199  */
200 static SOCKET rawsock;
201
202 /**
203  * Socket we use to send our UDP requests.
204  */
205 static SOCKET 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  * Convert IPv4 address from text to binary form.
237  *
238  * @param af address family
239  * @param cp the address to print
240  * @param buf where to write the address result
241  * @return 1 on success
242  */
243 static int
244 inet_pton (int af, const char *cp, struct in_addr *buf)
245 {
246   buf->s_addr = inet_addr (cp);
247   if (buf->s_addr == INADDR_NONE)
248   {
249     fprintf (stderr, "Error %d handling address %s", WSAGetLastError (), cp);
250     return 0;
251   }
252   return 1;
253 }
254
255
256 /**
257  * Send an ICMP message to the dummy IP.
258  *
259  * @param my_ip source address (our ip address)
260  */
261 static void
262 send_icmp_echo (const struct in_addr *my_ip)
263 {
264   char packet[sizeof (struct ip_header) + sizeof (struct icmp_echo_header)];
265   struct icmp_echo_header icmp_echo;
266   struct ip_header ip_pkt;
267   struct sockaddr_in dst;
268   size_t off;
269   int err;
270
271   off = 0;
272   ip_pkt.vers_ihl = 0x45;
273   ip_pkt.tos = 0;
274   ip_pkt.pkt_len = htons (sizeof (packet));
275   ip_pkt.id = htons (PACKET_ID);
276   ip_pkt.flags_frag_offset = 0;
277   ip_pkt.ttl = IPDEFTTL;
278   ip_pkt.proto = IPPROTO_ICMP;
279   ip_pkt.checksum = 0;
280   ip_pkt.src_ip = my_ip->s_addr;
281   ip_pkt.dst_ip = dummy.s_addr;
282   ip_pkt.checksum =
283       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
284   GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
285   off += sizeof (struct ip_header);
286
287   icmp_echo.type = ICMP_ECHO;
288   icmp_echo.code = 0;
289   icmp_echo.reserved = 0;
290   icmp_echo.checksum = 0;
291   icmp_echo.checksum =
292       htons (calc_checksum
293              ((uint16_t *) & icmp_echo, sizeof (struct icmp_echo_header)));
294   GNUNET_memcpy (&packet[off], &icmp_echo, sizeof (struct icmp_echo_header));
295   off += sizeof (struct icmp_echo_header);
296
297   memset (&dst, 0, sizeof (dst));
298   dst.sin_family = AF_INET;
299   dst.sin_addr = dummy;
300   err =
301       sendto (rawsock, packet, off, 0, (struct sockaddr *) &dst, sizeof (dst));
302   if (err < 0)
303   {
304 #if VERBOSE
305     fprintf (stderr, "sendto failed: %s\n", strerror (errno));
306 #endif
307   }
308   else if (err != off)
309   {
310     fprintf (stderr, "Error: partial send of ICMP message\n");
311   }
312 }
313
314
315 /**
316  * Send a UDP message to the dummy IP.
317  */
318 static void
319 send_udp ()
320 {
321   struct sockaddr_in dst;
322   ssize_t err;
323
324   memset (&dst, 0, sizeof (dst));
325   dst.sin_family = AF_INET;
326   dst.sin_addr = dummy;
327   dst.sin_port = htons (NAT_TRAV_PORT);
328   err = sendto (udpsock, NULL, 0, 0, (struct sockaddr *) &dst, sizeof (dst));
329   if (err < 0)
330   {
331 #if VERBOSE
332     fprintf (stderr, "sendto failed: %s\n", strerror (errno));
333 #endif
334   }
335   else if (0 != err)
336   {
337     fprintf (stderr, "Error: partial send of ICMP message\n");
338   }
339 }
340
341
342 /**
343  * We've received an ICMP response.  Process it.
344  */
345 static void
346 process_icmp_response ()
347 {
348   char buf[65536];
349   ssize_t have;
350   struct in_addr source_ip;
351   struct ip_header ip_pkt;
352   struct icmp_ttl_exceeded_header icmp_ttl;
353   struct icmp_echo_header icmp_echo;
354   struct udp_header udp_pkt;
355   size_t off;
356   uint16_t port;
357   DWORD ssize;
358
359   have = read (icmpsock, buf, sizeof (buf));
360   if (have == -1)
361   {
362     fprintf (stderr, "Error reading raw socket: %s\n", strerror (errno));
363     return;
364   }
365 #if VERBOSE
366   fprintf (stderr, "Received message of %u bytes\n", (unsigned int) have);
367 #endif
368   if (have <
369       (ssize_t) (sizeof (struct ip_header) +
370                  sizeof (struct icmp_ttl_exceeded_header) +
371                  sizeof (struct ip_header)))
372   {
373     /* malformed */
374     return;
375   }
376   off = 0;
377   GNUNET_memcpy (&ip_pkt, &buf[off], sizeof (struct ip_header));
378   off += sizeof (struct ip_header);
379   GNUNET_memcpy (&source_ip, &ip_pkt.src_ip, sizeof (source_ip));
380   GNUNET_memcpy (&icmp_ttl, &buf[off], sizeof (struct icmp_ttl_exceeded_header));
381   off += sizeof (struct icmp_ttl_exceeded_header);
382   if ((ICMP_TIME_EXCEEDED != icmp_ttl.type) || (0 != icmp_ttl.code))
383   {
384     /* different type than what we want */
385     return;
386   }
387   /* skip 2nd IP header */
388   GNUNET_memcpy (&ip_pkt, &buf[off], sizeof (struct ip_header));
389   off += sizeof (struct ip_header);
390
391   switch (ip_pkt.proto)
392   {
393   case IPPROTO_ICMP:
394     if (have !=
395         (sizeof (struct ip_header) * 2 +
396          sizeof (struct icmp_ttl_exceeded_header) +
397          sizeof (struct icmp_echo_header)))
398     {
399       /* malformed */
400       return;
401     }
402     /* grab ICMP ECHO content */
403     GNUNET_memcpy (&icmp_echo, &buf[off], sizeof (struct icmp_echo_header));
404     port = (uint16_t) ntohl (icmp_echo.reserved);
405     break;
406   case IPPROTO_UDP:
407     if (have !=
408         (sizeof (struct ip_header) * 2 +
409          sizeof (struct icmp_ttl_exceeded_header) + sizeof (struct udp_header)))
410     {
411       /* malformed */
412       return;
413     }
414     /* grab UDP content */
415     GNUNET_memcpy (&udp_pkt, &buf[off], sizeof (struct udp_header));
416     port = ntohs (udp_pkt.length);
417     break;
418   default:
419     /* different type than what we want */
420     return;
421   }
422
423   ssize = sizeof (buf);
424   WSAAddressToString ((LPSOCKADDR) & source_ip, sizeof (source_ip), NULL, buf,
425                       &ssize);
426   if (port == 0)
427     fprintf (stdout, "%s\n", buf);
428   else
429     fprintf (stdout, "%s:%u\n", buf, (unsigned int) port);
430   fflush (stdout);
431 }
432
433
434 /**
435  * Create an ICMP raw socket for reading.
436  *
437  * @return INVALID_SOCKET on error
438  */
439 static SOCKET
440 make_icmp_socket ()
441 {
442   SOCKET ret;
443
444   ret = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
445   if (INVALID_SOCKET == ret)
446   {
447     fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
448     return INVALID_SOCKET;
449   }
450   return ret;
451 }
452
453
454 /**
455  * Create an ICMP raw socket for writing.
456  *
457  * @return INVALID_SOCKET on error
458  */
459 static SOCKET
460 make_raw_socket ()
461 {
462   DWORD bOptVal = TRUE;
463   int bOptLen = sizeof (bOptVal);
464
465   rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
466   if (INVALID_SOCKET == rawsock)
467   {
468     fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
469     return INVALID_SOCKET;
470   }
471
472   if (0 !=
473       setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &bOptVal,
474                   bOptLen))
475   {
476     fprintf (stderr, "Error setting SO_BROADCAST to ON: %s\n",
477              strerror (errno));
478     closesocket (rawsock);
479     return INVALID_SOCKET;
480   }
481   if (0 !=
482       setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &bOptVal, bOptLen))
483   {
484     fprintf (stderr, "Error setting IP_HDRINCL to ON: %s\n", strerror (errno));
485     closesocket (rawsock);
486     return INVALID_SOCKET;
487   }
488   return rawsock;
489 }
490
491
492 /**
493  * Create a UDP socket for writing.
494  *
495  * @param my_ip source address (our ip address)
496  * @return INVALID_SOCKET on error
497  */
498 static SOCKET
499 make_udp_socket (const struct in_addr *my_ip)
500 {
501   SOCKET ret;
502   struct sockaddr_in addr;
503
504   ret = socket (AF_INET, SOCK_DGRAM, 0);
505   if (INVALID_SOCKET == ret)
506   {
507     fprintf (stderr, "Error opening UDP socket: %s\n", strerror (errno));
508     return INVALID_SOCKET;
509   }
510   memset (&addr, 0, sizeof (addr));
511   addr.sin_family = AF_INET;
512   addr.sin_addr = *my_ip;
513   addr.sin_port = htons (NAT_TRAV_PORT);
514   if (0 != bind (ret, (struct sockaddr *) &addr, sizeof (addr)))
515   {
516     fprintf (stderr, "Error binding UDP socket to port %u: %s\n", NAT_TRAV_PORT,
517              strerror (errno));
518     /* likely problematic, but not certain, try to continue */
519   }
520   return ret;
521 }
522
523
524 int
525 main (int argc, char *const *argv)
526 {
527   struct in_addr external;
528   fd_set rs;
529   struct timeval tv;
530   WSADATA wsaData;
531   unsigned int alt = 0;
532
533   if ( (argc > 1) && (0 != strcmp (argv[1], "-d")))
534   {
535     privilege_testing = TRUE;
536     fprintf (stderr,
537              "%s",
538              "DEBUG: Running binary in privilege testing mode.");
539     argv++;
540     argc--;
541   }
542
543   if (2 != argc)
544   {
545     fprintf (stderr,
546              "This program must be started with our (internal NAT) IP as the only argument.\n");
547     return 1;
548   }
549   if (1 != inet_pton (AF_INET, argv[1], &external))
550   {
551     fprintf (stderr, "Error parsing IPv4 address: %s, error %s\n", argv[1],
552              strerror (errno));
553     return 1;
554   }
555   if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
556   {
557     fprintf (stderr, "Internal error converting dummy IP to binary.\n");
558     return 2;
559   }
560   if (WSAStartup (MAKEWORD (2, 1), &wsaData) != 0)
561   {
562     fprintf (stderr, "Failed to find Winsock 2.1 or better.\n");
563     return 2;
564   }
565   if (INVALID_SOCKET == (icmpsock = make_icmp_socket ()))
566   {
567     return 3;
568   }
569   if (INVALID_SOCKET == (make_raw_socket ()))
570   {
571     closesocket (icmpsock);
572     return 3;
573   }
574   if (INVALID_SOCKET == (udpsock = make_udp_socket (&external)))
575   {
576     closesocket (icmpsock);
577     closesocket (rawsock);
578     return 3;
579   }
580
581   while ( ! privilege_testing)
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 (FD_ISSET (icmpsock, &rs))
595       process_icmp_response ();
596     if (0 == (++alt % 2))
597       send_icmp_echo (&external);
598     else
599       send_udp ();
600   }
601   /* select failed (internal error or OS out of resources) */
602   closesocket (icmpsock);
603   closesocket (rawsock);
604   closesocket (udpsock);
605   WSACleanup ();
606   if (privilege_testing)
607     return 0;
608   return 4;
609 }
610
611
612 /* end of gnunet-helper-nat-server-windows.c */