porting xdht to new service API, major code de-duplication effort
[oweals/gnunet.git] / src / nat / gnunet-helper-nat-client-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-client-windows.c
23  * @brief Tool to help bypass NATs using ICMP method; must run as
24  *        administrator on W32
25  *        This code is forx W32.
26  * @author Nathan Evans
27  *
28  * This program will send ONE ICMP message using RAW sockets
29  * to the IP address specified as the second argument.  Since
30  * it uses RAW sockets, it must be installed SUID or run as 'root'.
31  * In order to keep the security risk of the resulting SUID binary
32  * minimal, the program ONLY opens the RAW socket with root
33  * privileges, then drops them and only then starts to process
34  * command line arguments.  The code also does not link against
35  * any shared libraries (except libc) and is strictly minimal
36  * (except for checking for errors).  The following list of people
37  * have reviewed this code and considered it safe since the last
38  * modification (if you reviewed it, please have your name added
39  * to the list):
40  *
41  * - Christian Grothoff
42  * - Nathan Evans
43  */
44 #define _GNU_SOURCE
45
46 #define FD_SETSIZE 1024
47 #include <winsock2.h>
48 #include <ws2tcpip.h>
49 #include <sys/time.h>
50 #include <sys/types.h>
51 #include <unistd.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <errno.h>
55 #include <stdlib.h>
56 #include <stdint.h>
57 #include <time.h>
58
59
60 #define ICMP_ECHO 8
61 #define IPDEFTTL 64
62 #define ICMP_TIME_EXCEEDED 11
63
64 /**
65  * Must match IP given in the server.
66  */
67 #define DUMMY_IP "192.0.2.86"
68
69 #define NAT_TRAV_PORT 22225
70
71 /**
72  * IPv4 header.
73  */
74 struct ip_header
75 {
76
77   /**
78    * Version (4 bits) + Internet header length (4 bits)
79    */
80   uint8_t vers_ihl;
81
82   /**
83    * Type of service
84    */
85   uint8_t tos;
86
87   /**
88    * Total length
89    */
90   uint16_t pkt_len;
91
92   /**
93    * Identification
94    */
95   uint16_t id;
96
97   /**
98    * Flags (3 bits) + Fragment offset (13 bits)
99    */
100   uint16_t flags_frag_offset;
101
102   /**
103    * Time to live
104    */
105   uint8_t ttl;
106
107   /**
108    * Protocol
109    */
110   uint8_t proto;
111
112   /**
113    * Header checksum
114    */
115   uint16_t checksum;
116
117   /**
118    * Source address
119    */
120   uint32_t src_ip;
121
122   /**
123    * Destination address
124    */
125   uint32_t dst_ip;
126 };
127
128
129 /**
130  * Format of ICMP packet.
131  */
132 struct icmp_ttl_exceeded_header
133 {
134   uint8_t type;
135
136   uint8_t code;
137
138   uint16_t checksum;
139
140   uint32_t unused;
141
142   /* followed by original payload */
143 };
144
145 struct icmp_echo_header
146 {
147   uint8_t type;
148
149   uint8_t code;
150
151   uint16_t checksum;
152
153   uint32_t reserved;
154 };
155
156 /**
157  * Beginning of UDP packet.
158  */
159 struct udp_header
160 {
161   uint16_t src_port;
162
163   uint16_t dst_port;
164
165   uint16_t length;
166
167   uint16_t crc;
168 };
169
170 /**
171  * Will this binary be run in permissions testing mode?
172  */
173 static boolean privilege_testing = FALSE;
174
175 /**
176  * Socket we use to send our ICMP packets.
177  */
178 static SOCKET rawsock;
179
180 /**
181  * Target "dummy" address.
182  */
183 static struct in_addr dummy;
184
185 /**
186  * Port we are listening on (communicated to the server).
187  */
188 static uint16_t port;
189
190
191
192 /**
193  * Convert IPv4 address from text to binary form.
194  *
195  * @param af address family
196  * @param cp the address to print
197  * @param buf where to write the address result
198  * @return 1 on success
199  */
200 static int
201 inet_pton (int af, const char *cp, struct in_addr *buf)
202 {
203   buf->s_addr = inet_addr (cp);
204   if (buf->s_addr == INADDR_NONE)
205   {
206     fprintf (stderr, "Error %d handling address %s", WSAGetLastError (), cp);
207     return 0;
208   }
209   return 1;
210 }
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  * Send an ICMP message to the target.
237  *
238  * @param my_ip source address
239  * @param other target address
240  */
241 static void
242 send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
243 {
244   char packet[sizeof (struct ip_header) * 2 +
245               sizeof (struct icmp_ttl_exceeded_header) +
246               sizeof (struct udp_header)];
247   struct ip_header ip_pkt;
248   struct icmp_ttl_exceeded_header icmp_pkt;
249   struct udp_header udp_pkt;
250   struct sockaddr_in dst;
251   size_t off;
252   int err;
253
254   /* ip header: send to (known) ip address */
255   off = 0;
256   ip_pkt.vers_ihl = 0x45;
257   ip_pkt.tos = 0;
258   ip_pkt.pkt_len = htons (sizeof (packet));
259   ip_pkt.id = htons (256);
260   ip_pkt.flags_frag_offset = 0;
261   ip_pkt.ttl = 128;
262   ip_pkt.proto = IPPROTO_ICMP;
263   ip_pkt.checksum = 0;
264   ip_pkt.src_ip = my_ip->s_addr;
265   ip_pkt.dst_ip = other->s_addr;
266   ip_pkt.checksum =
267       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
268   GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
269   off += sizeof (struct ip_header);
270
271   icmp_pkt.type = ICMP_TIME_EXCEEDED;
272   icmp_pkt.code = 0;
273   icmp_pkt.checksum = 0;
274   icmp_pkt.unused = 0;
275   GNUNET_memcpy (&packet[off], &icmp_pkt, sizeof (struct icmp_ttl_exceeded_header));
276   off += sizeof (struct icmp_ttl_exceeded_header);
277
278   /* ip header of the presumably 'lost' udp packet */
279   ip_pkt.vers_ihl = 0x45;
280   ip_pkt.tos = 0;
281   ip_pkt.pkt_len =
282       htons (sizeof (struct ip_header) + sizeof (struct udp_header));
283   ip_pkt.id = htons (0);
284   ip_pkt.flags_frag_offset = 0;
285   ip_pkt.ttl = 128;
286   ip_pkt.proto = IPPROTO_UDP;
287   ip_pkt.checksum = 0;
288   ip_pkt.src_ip = other->s_addr;
289   ip_pkt.dst_ip = dummy.s_addr;
290   ip_pkt.checksum =
291       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
292   GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
293   off += sizeof (struct ip_header);
294
295   /* build UDP header */
296   udp_pkt.src_port = htons (NAT_TRAV_PORT);
297   udp_pkt.dst_port = htons (NAT_TRAV_PORT);
298   udp_pkt.length = htons (port);
299   udp_pkt.crc = 0;
300   GNUNET_memcpy (&packet[off], &udp_pkt, sizeof (struct udp_header));
301   off += sizeof (struct udp_header);
302
303   /* no go back to calculate ICMP packet checksum */
304   icmp_pkt.checksum =
305       htons (calc_checksum
306              ((uint16_t *) & packet[off],
307               sizeof (struct icmp_ttl_exceeded_header) +
308               sizeof (struct ip_header) + sizeof (struct udp_header)));
309   GNUNET_memcpy (&packet[sizeof (struct ip_header)], &icmp_pkt,
310           sizeof (struct icmp_ttl_exceeded_header));
311
312   memset (&dst, 0, sizeof (dst));
313   dst.sin_family = AF_INET;
314   dst.sin_addr = *other;
315   err =
316       sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
317               sizeof (dst));
318   if (err < 0)
319   {
320     fprintf (stderr, "sendto failed: %s\n", strerror (errno));
321   }
322   else if (sizeof (packet) != (size_t) err)
323   {
324     fprintf (stderr, "Error: partial send of ICMP message\n");
325   }
326 }
327
328
329 /**
330  * Send an ICMP message to the target.
331  *
332  * @param my_ip source address
333  * @param other target address
334  */
335 static void
336 send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
337 {
338   struct ip_header ip_pkt;
339   struct icmp_ttl_exceeded_header icmp_ttl;
340   struct icmp_echo_header icmp_echo;
341   struct sockaddr_in dst;
342   char packet[sizeof (struct ip_header) * 2 +
343               sizeof (struct icmp_ttl_exceeded_header) +
344               sizeof (struct icmp_echo_header)];
345   size_t off;
346   int err;
347
348   /* ip header: send to (known) ip address */
349   off = 0;
350   ip_pkt.vers_ihl = 0x45;
351   ip_pkt.tos = 0;
352   ip_pkt.pkt_len = htons (sizeof (packet));
353   ip_pkt.id = htons (256);
354   ip_pkt.flags_frag_offset = 0;
355   ip_pkt.ttl = IPDEFTTL;
356   ip_pkt.proto = IPPROTO_ICMP;
357   ip_pkt.checksum = 0;
358   ip_pkt.src_ip = my_ip->s_addr;
359   ip_pkt.dst_ip = other->s_addr;
360   ip_pkt.checksum =
361       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
362   GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
363   off += sizeof (ip_pkt);
364
365   /* icmp reply: time exceeded */
366   icmp_ttl.type = ICMP_TIME_EXCEEDED;
367   icmp_ttl.code = 0;
368   icmp_ttl.checksum = 0;
369   icmp_ttl.unused = 0;
370   GNUNET_memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
371   off += sizeof (struct icmp_ttl_exceeded_header);
372
373   /* ip header of the presumably 'lost' udp packet */
374   ip_pkt.vers_ihl = 0x45;
375   ip_pkt.tos = 0;
376   ip_pkt.pkt_len =
377       htons (sizeof (struct ip_header) + sizeof (struct icmp_echo_header));
378   ip_pkt.id = htons (256);
379   ip_pkt.flags_frag_offset = 0;
380   ip_pkt.ttl = 1;               /* real TTL would be 1 on a time exceeded packet */
381   ip_pkt.proto = IPPROTO_ICMP;
382   ip_pkt.src_ip = other->s_addr;
383   ip_pkt.dst_ip = dummy.s_addr;
384   ip_pkt.checksum = 0;
385   ip_pkt.checksum =
386       htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
387   GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
388   off += sizeof (struct ip_header);
389
390   icmp_echo.type = ICMP_ECHO;
391   icmp_echo.code = 0;
392   icmp_echo.reserved = htonl (port);
393   icmp_echo.checksum = 0;
394   icmp_echo.checksum =
395       htons (calc_checksum
396              ((uint16_t *) & icmp_echo, sizeof (struct icmp_echo_header)));
397   GNUNET_memcpy (&packet[off], &icmp_echo, sizeof (struct icmp_echo_header));
398
399   /* no go back to calculate ICMP packet checksum */
400   off = sizeof (struct ip_header);
401   icmp_ttl.checksum =
402       htons (calc_checksum
403              ((uint16_t *) & packet[off],
404               sizeof (struct icmp_ttl_exceeded_header) +
405               sizeof (struct ip_header) + sizeof (struct icmp_echo_header)));
406   GNUNET_memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
407
408   memset (&dst, 0, sizeof (dst));
409   dst.sin_family = AF_INET;
410   dst.sin_addr = *other;
411
412   err =
413       sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
414               sizeof (dst));
415
416   if (err < 0)
417   {
418     fprintf (stderr, "sendto failed: %s\n", strerror (errno));
419   }
420   else if (sizeof (packet) != (size_t) err)
421   {
422     fprintf (stderr, "Error: partial send of ICMP message\n");
423   }
424 }
425
426
427 /**
428  * Create an ICMP raw socket.
429  *
430  * @return INVALID_SOCKET on error
431  */
432 static SOCKET
433 make_raw_socket ()
434 {
435   DWORD bOptVal = TRUE;
436   int bOptLen = sizeof (bOptVal);
437   SOCKET ret;
438
439   ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
440   if (INVALID_SOCKET == ret)
441   {
442     fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
443     return INVALID_SOCKET;
444   }
445   if (0 !=
446       setsockopt (ret, SOL_SOCKET, SO_BROADCAST, (char *) &bOptVal, bOptLen))
447   {
448     fprintf (stderr, "Error setting SO_BROADCAST to ON: %s\n",
449              strerror (errno));
450     closesocket (rawsock);
451     return INVALID_SOCKET;
452   }
453
454   if (0 != setsockopt (ret, IPPROTO_IP, IP_HDRINCL, (char *) &bOptVal, bOptLen))
455   {
456     fprintf (stderr, "Error setting IP_HDRINCL to ON: %s\n", strerror (errno));
457     closesocket (rawsock);
458     return INVALID_SOCKET;
459   }
460   return ret;
461 }
462
463
464 int
465 main (int argc, char *const *argv)
466 {
467   struct in_addr external;
468   struct in_addr target;
469   WSADATA wsaData;
470   unsigned int p;
471
472   if (argc > 1 && 0 != strcmp (argv[1], "-d")){
473       privilege_testing = TRUE;
474       fprintf (stderr,
475                "%s",
476                "DEBUG: Running binary in privilege testing mode.");
477       argv++;
478       argc--;
479     }
480
481   if (argc != 4)
482   {
483     fprintf (stderr,
484              "%s",
485              "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
486     return 1;
487   }
488   if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
489       (1 != inet_pton (AF_INET, argv[2], &target)))
490   {
491     fprintf (stderr,
492              "Error parsing IPv4 address: %s\n",
493              strerror (errno));
494     return 1;
495   }
496   if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
497   {
498     fprintf (stderr,
499              "Error parsing port value `%s'\n",
500              argv[3]);
501     return 1;
502   }
503   port = (uint16_t) p;
504
505   if (0 != WSAStartup (MAKEWORD (2, 1), &wsaData))
506   {
507     fprintf (stderr,
508              "%s",
509              "Failed to find Winsock 2.1 or better.\n");
510     return 2;
511   }
512   if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
513   {
514     fprintf (stderr,
515              "%s",
516              "Internal error converting dummy IP to binary.\n");
517     return 2;
518   }
519   if (-1 == (rawsock = make_raw_socket ()))
520     return 3;
521   if (!privilege_testing){
522     send_icmp (&external, &target);
523     send_icmp_udp (&external, &target);
524   }
525   closesocket (rawsock);
526   WSACleanup ();
527   return 0;
528 }
529
530 /* end of gnunet-helper-nat-client-windows.c */