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