2 This file is part of GNUnet.
3 Copyright (C) 2010 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @file src/nat/gnunet-helper-nat-client-windows.c
21 * @brief Tool to help bypass NATs using ICMP method; must run as
22 * administrator on W32
23 * This code is forx W32.
24 * @author Nathan Evans
26 * This program will send ONE ICMP message using RAW sockets
27 * to the IP address specified as the second argument. Since
28 * it uses RAW sockets, it must be installed SUID or run as 'root'.
29 * In order to keep the security risk of the resulting SUID binary
30 * minimal, the program ONLY opens the RAW socket with root
31 * privileges, then drops them and only then starts to process
32 * command line arguments. The code also does not link against
33 * any shared libraries (except libc) and is strictly minimal
34 * (except for checking for errors). The following list of people
35 * have reviewed this code and considered it safe since the last
36 * modification (if you reviewed it, please have your name added
39 * - Christian Grothoff
43 /* Instead of including gnunet_common.h */
44 #define GNUNET_memcpy(dst,src,n) do { if (0 != n) { (void) memcpy (dst,src,n); } } while (0)
46 #define FD_SETSIZE 1024
50 #include <sys/types.h>
62 #define ICMP_TIME_EXCEEDED 11
65 * Must match IP given in the server.
67 #define DUMMY_IP "192.0.2.86"
69 #define NAT_TRAV_PORT 22225
78 * Version (4 bits) + Internet header length (4 bits)
98 * Flags (3 bits) + Fragment offset (13 bits)
100 uint16_t flags_frag_offset;
123 * Destination address
130 * Format of ICMP packet.
132 struct icmp_ttl_exceeded_header
142 /* followed by original payload */
145 struct icmp_echo_header
157 * Beginning of UDP packet.
171 * Will this binary be run in permissions testing mode?
173 static boolean privilege_testing = FALSE;
176 * Socket we use to send our ICMP packets.
178 static SOCKET rawsock;
181 * Target "dummy" address.
183 static struct in_addr dummy;
186 * Port we are listening on (communicated to the server).
188 static uint16_t port;
193 * Convert IPv4 address from text to binary form.
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
201 inet_pton (int af, const char *cp, struct in_addr *buf)
203 buf->s_addr = inet_addr (cp);
204 if (buf->s_addr == INADDR_NONE)
206 fprintf (stderr, "Error %d handling address %s", WSAGetLastError (), cp);
214 * CRC-16 for IP/ICMP headers.
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.
221 calc_checksum (const uint16_t * data, unsigned int bytes)
227 for (i = 0; i < bytes / 2; i++)
229 sum = (sum & 0xffff) + (sum >> 16);
230 sum = htons (0xffff - sum);
236 * Send an ICMP message to the target.
238 * @param my_ip source address
239 * @param other target address
242 send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
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;
254 /* ip header: send to (known) ip address */
256 ip_pkt.vers_ihl = 0x45;
258 ip_pkt.pkt_len = htons (sizeof (packet));
259 ip_pkt.id = htons (256);
260 ip_pkt.flags_frag_offset = 0;
262 ip_pkt.proto = IPPROTO_ICMP;
264 ip_pkt.src_ip = my_ip->s_addr;
265 ip_pkt.dst_ip = other->s_addr;
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);
271 icmp_pkt.type = ICMP_TIME_EXCEEDED;
273 icmp_pkt.checksum = 0;
275 GNUNET_memcpy (&packet[off], &icmp_pkt, sizeof (struct icmp_ttl_exceeded_header));
276 off += sizeof (struct icmp_ttl_exceeded_header);
278 /* ip header of the presumably 'lost' udp packet */
279 ip_pkt.vers_ihl = 0x45;
282 htons (sizeof (struct ip_header) + sizeof (struct udp_header));
283 ip_pkt.id = htons (0);
284 ip_pkt.flags_frag_offset = 0;
286 ip_pkt.proto = IPPROTO_UDP;
288 ip_pkt.src_ip = other->s_addr;
289 ip_pkt.dst_ip = dummy.s_addr;
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);
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);
300 GNUNET_memcpy (&packet[off], &udp_pkt, sizeof (struct udp_header));
301 off += sizeof (struct udp_header);
303 /* no go back to calculate ICMP packet 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));
312 memset (&dst, 0, sizeof (dst));
313 dst.sin_family = AF_INET;
314 dst.sin_addr = *other;
316 sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
320 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
322 else if (sizeof (packet) != (size_t) err)
324 fprintf (stderr, "Error: partial send of ICMP message\n");
330 * Send an ICMP message to the target.
332 * @param my_ip source address
333 * @param other target address
336 send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
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)];
348 /* ip header: send to (known) ip address */
350 ip_pkt.vers_ihl = 0x45;
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;
358 ip_pkt.src_ip = my_ip->s_addr;
359 ip_pkt.dst_ip = other->s_addr;
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);
365 /* icmp reply: time exceeded */
366 icmp_ttl.type = ICMP_TIME_EXCEEDED;
368 icmp_ttl.checksum = 0;
370 GNUNET_memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
371 off += sizeof (struct icmp_ttl_exceeded_header);
373 /* ip header of the presumably 'lost' udp packet */
374 ip_pkt.vers_ihl = 0x45;
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;
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);
390 icmp_echo.type = ICMP_ECHO;
392 icmp_echo.reserved = htonl (port);
393 icmp_echo.checksum = 0;
396 ((uint16_t *) & icmp_echo, sizeof (struct icmp_echo_header)));
397 GNUNET_memcpy (&packet[off], &icmp_echo, sizeof (struct icmp_echo_header));
399 /* no go back to calculate ICMP packet checksum */
400 off = sizeof (struct ip_header);
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));
408 memset (&dst, 0, sizeof (dst));
409 dst.sin_family = AF_INET;
410 dst.sin_addr = *other;
413 sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
418 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
420 else if (sizeof (packet) != (size_t) err)
422 fprintf (stderr, "Error: partial send of ICMP message\n");
428 * Create an ICMP raw socket.
430 * @return INVALID_SOCKET on error
435 DWORD bOptVal = TRUE;
436 int bOptLen = sizeof (bOptVal);
439 ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
440 if (INVALID_SOCKET == ret)
442 fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
443 return INVALID_SOCKET;
446 setsockopt (ret, SOL_SOCKET, SO_BROADCAST, (char *) &bOptVal, bOptLen))
448 fprintf (stderr, "Error setting SO_BROADCAST to ON: %s\n",
450 closesocket (rawsock);
451 return INVALID_SOCKET;
454 if (0 != setsockopt (ret, IPPROTO_IP, IP_HDRINCL, (char *) &bOptVal, bOptLen))
456 fprintf (stderr, "Error setting IP_HDRINCL to ON: %s\n", strerror (errno));
457 closesocket (rawsock);
458 return INVALID_SOCKET;
465 main (int argc, char *const *argv)
467 struct in_addr external;
468 struct in_addr target;
472 if (argc > 1 && 0 != strcmp (argv[1], "-d")){
473 privilege_testing = TRUE;
476 "DEBUG: Running binary in privilege testing mode.");
485 "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
488 if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
489 (1 != inet_pton (AF_INET, argv[2], &target)))
492 "Error parsing IPv4 address: %s\n",
496 if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
499 "Error parsing port value `%s'\n",
505 if (0 != WSAStartup (MAKEWORD (2, 1), &wsaData))
509 "Failed to find Winsock 2.1 or better.\n");
512 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
516 "Internal error converting dummy IP to binary.\n");
519 if (-1 == (rawsock = make_raw_socket ()))
521 if (!privilege_testing){
522 send_icmp (&external, &target);
523 send_icmp_udp (&external, &target);
525 closesocket (rawsock);
530 /* end of gnunet-helper-nat-client-windows.c */