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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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
45 /* Instead of including gnunet_common.h */
46 #define GNUNET_memcpy(dst,src,n) do { if (0 != n) { (void) memcpy (dst,src,n); } } while (0)
48 #define FD_SETSIZE 1024
52 #include <sys/types.h>
64 #define ICMP_TIME_EXCEEDED 11
67 * Must match IP given in the server.
69 #define DUMMY_IP "192.0.2.86"
71 #define NAT_TRAV_PORT 22225
80 * Version (4 bits) + Internet header length (4 bits)
100 * Flags (3 bits) + Fragment offset (13 bits)
102 uint16_t flags_frag_offset;
125 * Destination address
132 * Format of ICMP packet.
134 struct icmp_ttl_exceeded_header
144 /* followed by original payload */
147 struct icmp_echo_header
159 * Beginning of UDP packet.
173 * Will this binary be run in permissions testing mode?
175 static boolean privilege_testing = FALSE;
178 * Socket we use to send our ICMP packets.
180 static SOCKET rawsock;
183 * Target "dummy" address.
185 static struct in_addr dummy;
188 * Port we are listening on (communicated to the server).
190 static uint16_t port;
195 * Convert IPv4 address from text to binary form.
197 * @param af address family
198 * @param cp the address to print
199 * @param buf where to write the address result
200 * @return 1 on success
203 inet_pton (int af, const char *cp, struct in_addr *buf)
205 buf->s_addr = inet_addr (cp);
206 if (buf->s_addr == INADDR_NONE)
208 fprintf (stderr, "Error %d handling address %s", WSAGetLastError (), cp);
216 * CRC-16 for IP/ICMP headers.
218 * @param data what to calculate the CRC over
219 * @param bytes number of bytes in data (must be multiple of 2)
220 * @return the CRC 16.
223 calc_checksum (const uint16_t * data, unsigned int bytes)
229 for (i = 0; i < bytes / 2; i++)
231 sum = (sum & 0xffff) + (sum >> 16);
232 sum = htons (0xffff - sum);
238 * Send an ICMP message to the target.
240 * @param my_ip source address
241 * @param other target address
244 send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
246 char packet[sizeof (struct ip_header) * 2 +
247 sizeof (struct icmp_ttl_exceeded_header) +
248 sizeof (struct udp_header)];
249 struct ip_header ip_pkt;
250 struct icmp_ttl_exceeded_header icmp_pkt;
251 struct udp_header udp_pkt;
252 struct sockaddr_in dst;
256 /* ip header: send to (known) ip address */
258 ip_pkt.vers_ihl = 0x45;
260 ip_pkt.pkt_len = htons (sizeof (packet));
261 ip_pkt.id = htons (256);
262 ip_pkt.flags_frag_offset = 0;
264 ip_pkt.proto = IPPROTO_ICMP;
266 ip_pkt.src_ip = my_ip->s_addr;
267 ip_pkt.dst_ip = other->s_addr;
269 htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
270 GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
271 off += sizeof (struct ip_header);
273 icmp_pkt.type = ICMP_TIME_EXCEEDED;
275 icmp_pkt.checksum = 0;
277 GNUNET_memcpy (&packet[off], &icmp_pkt, sizeof (struct icmp_ttl_exceeded_header));
278 off += sizeof (struct icmp_ttl_exceeded_header);
280 /* ip header of the presumably 'lost' udp packet */
281 ip_pkt.vers_ihl = 0x45;
284 htons (sizeof (struct ip_header) + sizeof (struct udp_header));
285 ip_pkt.id = htons (0);
286 ip_pkt.flags_frag_offset = 0;
288 ip_pkt.proto = IPPROTO_UDP;
290 ip_pkt.src_ip = other->s_addr;
291 ip_pkt.dst_ip = dummy.s_addr;
293 htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
294 GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
295 off += sizeof (struct ip_header);
297 /* build UDP header */
298 udp_pkt.src_port = htons (NAT_TRAV_PORT);
299 udp_pkt.dst_port = htons (NAT_TRAV_PORT);
300 udp_pkt.length = htons (port);
302 GNUNET_memcpy (&packet[off], &udp_pkt, sizeof (struct udp_header));
303 off += sizeof (struct udp_header);
305 /* no go back to calculate ICMP packet checksum */
308 ((uint16_t *) & packet[off],
309 sizeof (struct icmp_ttl_exceeded_header) +
310 sizeof (struct ip_header) + sizeof (struct udp_header)));
311 GNUNET_memcpy (&packet[sizeof (struct ip_header)], &icmp_pkt,
312 sizeof (struct icmp_ttl_exceeded_header));
314 memset (&dst, 0, sizeof (dst));
315 dst.sin_family = AF_INET;
316 dst.sin_addr = *other;
318 sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
322 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
324 else if (sizeof (packet) != (size_t) err)
326 fprintf (stderr, "Error: partial send of ICMP message\n");
332 * Send an ICMP message to the target.
334 * @param my_ip source address
335 * @param other target address
338 send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
340 struct ip_header ip_pkt;
341 struct icmp_ttl_exceeded_header icmp_ttl;
342 struct icmp_echo_header icmp_echo;
343 struct sockaddr_in dst;
344 char packet[sizeof (struct ip_header) * 2 +
345 sizeof (struct icmp_ttl_exceeded_header) +
346 sizeof (struct icmp_echo_header)];
350 /* ip header: send to (known) ip address */
352 ip_pkt.vers_ihl = 0x45;
354 ip_pkt.pkt_len = htons (sizeof (packet));
355 ip_pkt.id = htons (256);
356 ip_pkt.flags_frag_offset = 0;
357 ip_pkt.ttl = IPDEFTTL;
358 ip_pkt.proto = IPPROTO_ICMP;
360 ip_pkt.src_ip = my_ip->s_addr;
361 ip_pkt.dst_ip = other->s_addr;
363 htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
364 GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
365 off += sizeof (ip_pkt);
367 /* icmp reply: time exceeded */
368 icmp_ttl.type = ICMP_TIME_EXCEEDED;
370 icmp_ttl.checksum = 0;
372 GNUNET_memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
373 off += sizeof (struct icmp_ttl_exceeded_header);
375 /* ip header of the presumably 'lost' udp packet */
376 ip_pkt.vers_ihl = 0x45;
379 htons (sizeof (struct ip_header) + sizeof (struct icmp_echo_header));
380 ip_pkt.id = htons (256);
381 ip_pkt.flags_frag_offset = 0;
382 ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
383 ip_pkt.proto = IPPROTO_ICMP;
384 ip_pkt.src_ip = other->s_addr;
385 ip_pkt.dst_ip = dummy.s_addr;
388 htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
389 GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
390 off += sizeof (struct ip_header);
392 icmp_echo.type = ICMP_ECHO;
394 icmp_echo.reserved = htonl (port);
395 icmp_echo.checksum = 0;
398 ((uint16_t *) & icmp_echo, sizeof (struct icmp_echo_header)));
399 GNUNET_memcpy (&packet[off], &icmp_echo, sizeof (struct icmp_echo_header));
401 /* no go back to calculate ICMP packet checksum */
402 off = sizeof (struct ip_header);
405 ((uint16_t *) & packet[off],
406 sizeof (struct icmp_ttl_exceeded_header) +
407 sizeof (struct ip_header) + sizeof (struct icmp_echo_header)));
408 GNUNET_memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
410 memset (&dst, 0, sizeof (dst));
411 dst.sin_family = AF_INET;
412 dst.sin_addr = *other;
415 sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
420 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
422 else if (sizeof (packet) != (size_t) err)
424 fprintf (stderr, "Error: partial send of ICMP message\n");
430 * Create an ICMP raw socket.
432 * @return INVALID_SOCKET on error
437 DWORD bOptVal = TRUE;
438 int bOptLen = sizeof (bOptVal);
441 ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
442 if (INVALID_SOCKET == ret)
444 fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
445 return INVALID_SOCKET;
448 setsockopt (ret, SOL_SOCKET, SO_BROADCAST, (char *) &bOptVal, bOptLen))
450 fprintf (stderr, "Error setting SO_BROADCAST to ON: %s\n",
452 closesocket (rawsock);
453 return INVALID_SOCKET;
456 if (0 != setsockopt (ret, IPPROTO_IP, IP_HDRINCL, (char *) &bOptVal, bOptLen))
458 fprintf (stderr, "Error setting IP_HDRINCL to ON: %s\n", strerror (errno));
459 closesocket (rawsock);
460 return INVALID_SOCKET;
467 main (int argc, char *const *argv)
469 struct in_addr external;
470 struct in_addr target;
474 if (argc > 1 && 0 != strcmp (argv[1], "-d")){
475 privilege_testing = TRUE;
478 "DEBUG: Running binary in privilege testing mode.");
487 "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
490 if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
491 (1 != inet_pton (AF_INET, argv[2], &target)))
494 "Error parsing IPv4 address: %s\n",
498 if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
501 "Error parsing port value `%s'\n",
507 if (0 != WSAStartup (MAKEWORD (2, 1), &wsaData))
511 "Failed to find Winsock 2.1 or better.\n");
514 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
518 "Internal error converting dummy IP to binary.\n");
521 if (-1 == (rawsock = make_raw_socket ()))
523 if (!privilege_testing){
524 send_icmp (&external, &target);
525 send_icmp_udp (&external, &target);
527 closesocket (rawsock);
532 /* end of gnunet-helper-nat-client-windows.c */