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