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