2 This file is part of GNUnet.
3 (C) 2010 Christian Grothoff (and other contributing authors)
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.
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.
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.
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
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
41 * - Christian Grothoff
49 #include <sys/types.h>
61 #define ICMP_TIME_EXCEEDED 11
64 * Must match IP given in the server.
66 #define DUMMY_IP "192.0.2.86"
68 #define NAT_TRAV_PORT 22225
77 * Version (4 bits) + Internet header length (4 bits)
97 * Flags (3 bits) + Fragment offset (13 bits)
99 uint16_t flags_frag_offset;
122 * Destination address
129 * Format of ICMP packet.
131 struct icmp_ttl_exceeded_header
141 /* followed by original payload */
144 struct icmp_echo_header
156 * Beginning of UDP packet.
171 * Socket we use to send our ICMP packets.
173 static SOCKET rawsock;
176 * Target "dummy" address.
178 static struct in_addr dummy;
181 * Port we are listening on (communicated to the server).
183 static uint16_t port;
188 * Convert IPv4 address from text to binary form.
190 * @param af address family
191 * @param cp the address to print
192 * @param buf where to write the address result
193 * @return 1 on success
196 inet_pton (int af, const char *cp, struct in_addr *buf)
198 buf->s_addr = inet_addr (cp);
199 if (buf->s_addr == INADDR_NONE)
201 fprintf (stderr, "Error %d handling address %s", WSAGetLastError (), cp);
209 * CRC-16 for IP/ICMP headers.
211 * @param data what to calculate the CRC over
212 * @param bytes number of bytes in data (must be multiple of 2)
213 * @return the CRC 16.
216 calc_checksum (const uint16_t * data, unsigned int bytes)
222 for (i = 0; i < bytes / 2; i++)
224 sum = (sum & 0xffff) + (sum >> 16);
225 sum = htons (0xffff - sum);
231 * Send an ICMP message to the target.
233 * @param my_ip source address
234 * @param other target address
237 send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
239 char packet[sizeof (struct ip_header) * 2 +
240 sizeof (struct icmp_ttl_exceeded_header) +
241 sizeof (struct udp_header)];
242 struct ip_header ip_pkt;
243 struct icmp_ttl_exceeded_header icmp_pkt;
244 struct udp_header udp_pkt;
245 struct sockaddr_in dst;
249 /* ip header: send to (known) ip address */
251 ip_pkt.vers_ihl = 0x45;
253 ip_pkt.pkt_len = htons (sizeof (packet));
254 ip_pkt.id = htons (256);
255 ip_pkt.flags_frag_offset = 0;
257 ip_pkt.proto = IPPROTO_ICMP;
259 ip_pkt.src_ip = my_ip->s_addr;
260 ip_pkt.dst_ip = other->s_addr;
262 htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
263 memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
264 off += sizeof (struct ip_header);
266 icmp_pkt.type = ICMP_TIME_EXCEEDED;
268 icmp_pkt.checksum = 0;
270 memcpy (&packet[off], &icmp_pkt, sizeof (struct icmp_ttl_exceeded_header));
271 off += sizeof (struct icmp_ttl_exceeded_header);
273 /* ip header of the presumably 'lost' udp packet */
274 ip_pkt.vers_ihl = 0x45;
277 htons (sizeof (struct ip_header) + sizeof (struct udp_header));
278 ip_pkt.id = htons (0);
279 ip_pkt.flags_frag_offset = 0;
281 ip_pkt.proto = IPPROTO_UDP;
283 ip_pkt.src_ip = other->s_addr;
284 ip_pkt.dst_ip = dummy.s_addr;
286 htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
287 memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
288 off += sizeof (struct ip_header);
290 /* build UDP header */
291 udp_pkt.src_port = htons (NAT_TRAV_PORT);
292 udp_pkt.dst_port = htons (NAT_TRAV_PORT);
293 udp_pkt.length = htons (port);
295 memcpy (&packet[off], &udp_pkt, sizeof (struct udp_header));
296 off += sizeof (struct udp_header);
298 /* no go back to calculate ICMP packet checksum */
301 ((uint16_t *) & packet[off],
302 sizeof (struct icmp_ttl_exceeded_header) +
303 sizeof (struct ip_header) + sizeof (struct udp_header)));
304 memcpy (&packet[sizeof (struct ip_header)], &icmp_pkt,
305 sizeof (struct icmp_ttl_exceeded_header));
307 memset (&dst, 0, sizeof (dst));
308 dst.sin_family = AF_INET;
309 dst.sin_addr = *other;
311 sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
315 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
317 else if (sizeof (packet) != (size_t) err)
319 fprintf (stderr, "Error: partial send of ICMP message\n");
325 * Send an ICMP message to the target.
327 * @param my_ip source address
328 * @param other target address
331 send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
333 struct ip_header ip_pkt;
334 struct icmp_ttl_exceeded_header icmp_ttl;
335 struct icmp_echo_header icmp_echo;
336 struct sockaddr_in dst;
337 char packet[sizeof (struct ip_header) * 2 +
338 sizeof (struct icmp_ttl_exceeded_header) +
339 sizeof (struct icmp_echo_header)];
343 /* ip header: send to (known) ip address */
345 ip_pkt.vers_ihl = 0x45;
347 ip_pkt.pkt_len = htons (sizeof (packet));
348 ip_pkt.id = htons (256);
349 ip_pkt.flags_frag_offset = 0;
350 ip_pkt.ttl = IPDEFTTL;
351 ip_pkt.proto = IPPROTO_ICMP;
353 ip_pkt.src_ip = my_ip->s_addr;
354 ip_pkt.dst_ip = other->s_addr;
356 htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
357 memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
358 off += sizeof (ip_pkt);
360 /* icmp reply: time exceeded */
361 icmp_ttl.type = ICMP_TIME_EXCEEDED;
363 icmp_ttl.checksum = 0;
365 memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
366 off += sizeof (struct icmp_ttl_exceeded_header);
368 /* ip header of the presumably 'lost' udp packet */
369 ip_pkt.vers_ihl = 0x45;
372 htons (sizeof (struct ip_header) + sizeof (struct icmp_echo_header));
373 ip_pkt.id = htons (256);
374 ip_pkt.flags_frag_offset = 0;
375 ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
376 ip_pkt.proto = IPPROTO_ICMP;
377 ip_pkt.src_ip = other->s_addr;
378 ip_pkt.dst_ip = dummy.s_addr;
381 htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
382 memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
383 off += sizeof (struct ip_header);
385 icmp_echo.type = ICMP_ECHO;
387 icmp_echo.reserved = htonl (port);
388 icmp_echo.checksum = 0;
391 ((uint16_t *) & icmp_echo, sizeof (struct icmp_echo_header)));
392 memcpy (&packet[off], &icmp_echo, sizeof (struct icmp_echo_header));
394 /* no go back to calculate ICMP packet checksum */
395 off = sizeof (struct ip_header);
398 ((uint16_t *) & packet[off],
399 sizeof (struct icmp_ttl_exceeded_header) +
400 sizeof (struct ip_header) + sizeof (struct icmp_echo_header)));
401 memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
403 memset (&dst, 0, sizeof (dst));
404 dst.sin_family = AF_INET;
405 dst.sin_addr = *other;
408 sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
413 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
415 else if (sizeof (packet) != (size_t) err)
417 fprintf (stderr, "Error: partial send of ICMP message\n");
423 * Create an ICMP raw socket.
425 * @return INVALID_SOCKET on error
430 DWORD bOptVal = TRUE;
431 int bOptLen = sizeof (bOptVal);
434 ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
435 if (INVALID_SOCKET == ret)
437 fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
438 return INVALID_SOCKET;
441 setsockopt (ret, SOL_SOCKET, SO_BROADCAST, (char *) &bOptVal, bOptLen))
443 fprintf (stderr, "Error setting SO_BROADCAST to ON: %s\n",
445 closesocket (rawsock);
446 return INVALID_SOCKET;
449 if (0 != setsockopt (ret, IPPROTO_IP, IP_HDRINCL, (char *) &bOptVal, bOptLen))
451 fprintf (stderr, "Error setting IP_HDRINCL to ON: %s\n", strerror (errno));
452 closesocket (rawsock);
453 return INVALID_SOCKET;
460 main (int argc, char *const *argv)
462 struct in_addr external;
463 struct in_addr target;
471 "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
474 if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
475 (1 != inet_pton (AF_INET, argv[2], &target)))
477 fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno));
480 if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
482 fprintf (stderr, "Error parsing port value `%s'\n", argv[3]);
487 if (0 != WSAStartup (MAKEWORD (2, 1), &wsaData))
489 fprintf (stderr, "Failed to find Winsock 2.1 or better.\n");
492 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
494 fprintf (stderr, "Internal error converting dummy IP to binary.\n");
497 if (-1 == (rawsock = make_raw_socket ()))
499 send_icmp (&external, &target);
500 send_icmp_udp (&external, &target);
501 closesocket (rawsock);
506 /* end of gnunet-helper-nat-client-windows.c */