123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2010 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file src/nat/gnunet-helper-nat-client-windows.c
- * @brief Tool to help bypass NATs using ICMP method; must run as
- * administrator on W32
- * This code is forx W32.
- * @author Nathan Evans
- *
- * This program will send ONE ICMP message using RAW sockets
- * to the IP address specified as the second argument. Since
- * it uses RAW sockets, it must be installed SUID or run as 'root'.
- * In order to keep the security risk of the resulting SUID binary
- * minimal, the program ONLY opens the RAW socket with root
- * privileges, then drops them and only then starts to process
- * command line arguments. The code also does not link against
- * any shared libraries (except libc) and is strictly minimal
- * (except for checking for errors). The following list of people
- * have reviewed this code and considered it safe since the last
- * modification (if you reviewed it, please have your name added
- * to the list):
- *
- * - Christian Grothoff
- * - Nathan Evans
- */
- #define _GNU_SOURCE
- /* Instead of including gnunet_common.h */
- #define GNUNET_memcpy(dst,src,n) do { if (0 != n) { (void) memcpy (dst,src,n); } } while (0)
- #define FD_SETSIZE 1024
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <time.h>
- #define ICMP_ECHO 8
- #define IPDEFTTL 64
- #define ICMP_TIME_EXCEEDED 11
- /**
- * Must match IP given in the server.
- */
- #define DUMMY_IP "192.0.2.86"
- #define NAT_TRAV_PORT 22225
- /**
- * IPv4 header.
- */
- struct ip_header
- {
- /**
- * Version (4 bits) + Internet header length (4 bits)
- */
- uint8_t vers_ihl;
- /**
- * Type of service
- */
- uint8_t tos;
- /**
- * Total length
- */
- uint16_t pkt_len;
- /**
- * Identification
- */
- uint16_t id;
- /**
- * Flags (3 bits) + Fragment offset (13 bits)
- */
- uint16_t flags_frag_offset;
- /**
- * Time to live
- */
- uint8_t ttl;
- /**
- * Protocol
- */
- uint8_t proto;
- /**
- * Header checksum
- */
- uint16_t checksum;
- /**
- * Source address
- */
- uint32_t src_ip;
- /**
- * Destination address
- */
- uint32_t dst_ip;
- };
- /**
- * Format of ICMP packet.
- */
- struct icmp_ttl_exceeded_header
- {
- uint8_t type;
- uint8_t code;
- uint16_t checksum;
- uint32_t unused;
- /* followed by original payload */
- };
- struct icmp_echo_header
- {
- uint8_t type;
- uint8_t code;
- uint16_t checksum;
- uint32_t reserved;
- };
- /**
- * Beginning of UDP packet.
- */
- struct udp_header
- {
- uint16_t src_port;
- uint16_t dst_port;
- uint16_t length;
- uint16_t crc;
- };
- /**
- * Will this binary be run in permissions testing mode?
- */
- static boolean privilege_testing = FALSE;
- /**
- * Socket we use to send our ICMP packets.
- */
- static SOCKET rawsock;
- /**
- * Target "dummy" address.
- */
- static struct in_addr dummy;
- /**
- * Port we are listening on (communicated to the server).
- */
- static uint16_t port;
- /**
- * Convert IPv4 address from text to binary form.
- *
- * @param af address family
- * @param cp the address to print
- * @param buf where to write the address result
- * @return 1 on success
- */
- static int
- inet_pton (int af, const char *cp, struct in_addr *buf)
- {
- buf->s_addr = inet_addr (cp);
- if (buf->s_addr == INADDR_NONE)
- {
- fprintf (stderr, "Error %d handling address %s", WSAGetLastError (), cp);
- return 0;
- }
- return 1;
- }
- /**
- * CRC-16 for IP/ICMP headers.
- *
- * @param data what to calculate the CRC over
- * @param bytes number of bytes in data (must be multiple of 2)
- * @return the CRC 16.
- */
- static uint16_t
- calc_checksum (const uint16_t * data, unsigned int bytes)
- {
- uint32_t sum;
- unsigned int i;
- sum = 0;
- for (i = 0; i < bytes / 2; i++)
- sum += data[i];
- sum = (sum & 0xffff) + (sum >> 16);
- sum = htons (0xffff - sum);
- return sum;
- }
- /**
- * Send an ICMP message to the target.
- *
- * @param my_ip source address
- * @param other target address
- */
- static void
- send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
- {
- char packet[sizeof (struct ip_header) * 2 +
- sizeof (struct icmp_ttl_exceeded_header) +
- sizeof (struct udp_header)];
- struct ip_header ip_pkt;
- struct icmp_ttl_exceeded_header icmp_pkt;
- struct udp_header udp_pkt;
- struct sockaddr_in dst;
- size_t off;
- int err;
- /* ip header: send to (known) ip address */
- off = 0;
- ip_pkt.vers_ihl = 0x45;
- ip_pkt.tos = 0;
- ip_pkt.pkt_len = htons (sizeof (packet));
- ip_pkt.id = htons (256);
- ip_pkt.flags_frag_offset = 0;
- ip_pkt.ttl = 128;
- ip_pkt.proto = IPPROTO_ICMP;
- ip_pkt.checksum = 0;
- ip_pkt.src_ip = my_ip->s_addr;
- ip_pkt.dst_ip = other->s_addr;
- ip_pkt.checksum =
- htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
- GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
- off += sizeof (struct ip_header);
- icmp_pkt.type = ICMP_TIME_EXCEEDED;
- icmp_pkt.code = 0;
- icmp_pkt.checksum = 0;
- icmp_pkt.unused = 0;
- GNUNET_memcpy (&packet[off], &icmp_pkt, sizeof (struct icmp_ttl_exceeded_header));
- off += sizeof (struct icmp_ttl_exceeded_header);
- /* ip header of the presumably 'lost' udp packet */
- ip_pkt.vers_ihl = 0x45;
- ip_pkt.tos = 0;
- ip_pkt.pkt_len =
- htons (sizeof (struct ip_header) + sizeof (struct udp_header));
- ip_pkt.id = htons (0);
- ip_pkt.flags_frag_offset = 0;
- ip_pkt.ttl = 128;
- ip_pkt.proto = IPPROTO_UDP;
- ip_pkt.checksum = 0;
- ip_pkt.src_ip = other->s_addr;
- ip_pkt.dst_ip = dummy.s_addr;
- ip_pkt.checksum =
- htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
- GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
- off += sizeof (struct ip_header);
- /* build UDP header */
- udp_pkt.src_port = htons (NAT_TRAV_PORT);
- udp_pkt.dst_port = htons (NAT_TRAV_PORT);
- udp_pkt.length = htons (port);
- udp_pkt.crc = 0;
- GNUNET_memcpy (&packet[off], &udp_pkt, sizeof (struct udp_header));
- off += sizeof (struct udp_header);
- /* no go back to calculate ICMP packet checksum */
- icmp_pkt.checksum =
- htons (calc_checksum
- ((uint16_t *) & packet[off],
- sizeof (struct icmp_ttl_exceeded_header) +
- sizeof (struct ip_header) + sizeof (struct udp_header)));
- GNUNET_memcpy (&packet[sizeof (struct ip_header)], &icmp_pkt,
- sizeof (struct icmp_ttl_exceeded_header));
- memset (&dst, 0, sizeof (dst));
- dst.sin_family = AF_INET;
- dst.sin_addr = *other;
- err =
- sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
- sizeof (dst));
- if (err < 0)
- {
- fprintf (stderr, "sendto failed: %s\n", strerror (errno));
- }
- else if (sizeof (packet) != (size_t) err)
- {
- fprintf (stderr, "Error: partial send of ICMP message\n");
- }
- }
- /**
- * Send an ICMP message to the target.
- *
- * @param my_ip source address
- * @param other target address
- */
- static void
- send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
- {
- struct ip_header ip_pkt;
- struct icmp_ttl_exceeded_header icmp_ttl;
- struct icmp_echo_header icmp_echo;
- struct sockaddr_in dst;
- char packet[sizeof (struct ip_header) * 2 +
- sizeof (struct icmp_ttl_exceeded_header) +
- sizeof (struct icmp_echo_header)];
- size_t off;
- int err;
- /* ip header: send to (known) ip address */
- off = 0;
- ip_pkt.vers_ihl = 0x45;
- ip_pkt.tos = 0;
- ip_pkt.pkt_len = htons (sizeof (packet));
- ip_pkt.id = htons (256);
- ip_pkt.flags_frag_offset = 0;
- ip_pkt.ttl = IPDEFTTL;
- ip_pkt.proto = IPPROTO_ICMP;
- ip_pkt.checksum = 0;
- ip_pkt.src_ip = my_ip->s_addr;
- ip_pkt.dst_ip = other->s_addr;
- ip_pkt.checksum =
- htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
- GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
- off += sizeof (ip_pkt);
- /* icmp reply: time exceeded */
- icmp_ttl.type = ICMP_TIME_EXCEEDED;
- icmp_ttl.code = 0;
- icmp_ttl.checksum = 0;
- icmp_ttl.unused = 0;
- GNUNET_memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
- off += sizeof (struct icmp_ttl_exceeded_header);
- /* ip header of the presumably 'lost' udp packet */
- ip_pkt.vers_ihl = 0x45;
- ip_pkt.tos = 0;
- ip_pkt.pkt_len =
- htons (sizeof (struct ip_header) + sizeof (struct icmp_echo_header));
- ip_pkt.id = htons (256);
- ip_pkt.flags_frag_offset = 0;
- ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
- ip_pkt.proto = IPPROTO_ICMP;
- ip_pkt.src_ip = other->s_addr;
- ip_pkt.dst_ip = dummy.s_addr;
- ip_pkt.checksum = 0;
- ip_pkt.checksum =
- htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
- GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
- off += sizeof (struct ip_header);
- icmp_echo.type = ICMP_ECHO;
- icmp_echo.code = 0;
- icmp_echo.reserved = htonl (port);
- icmp_echo.checksum = 0;
- icmp_echo.checksum =
- htons (calc_checksum
- ((uint16_t *) & icmp_echo, sizeof (struct icmp_echo_header)));
- GNUNET_memcpy (&packet[off], &icmp_echo, sizeof (struct icmp_echo_header));
- /* no go back to calculate ICMP packet checksum */
- off = sizeof (struct ip_header);
- icmp_ttl.checksum =
- htons (calc_checksum
- ((uint16_t *) & packet[off],
- sizeof (struct icmp_ttl_exceeded_header) +
- sizeof (struct ip_header) + sizeof (struct icmp_echo_header)));
- GNUNET_memcpy (&packet[off], &icmp_ttl, sizeof (struct icmp_ttl_exceeded_header));
- memset (&dst, 0, sizeof (dst));
- dst.sin_family = AF_INET;
- dst.sin_addr = *other;
- err =
- sendto (rawsock, packet, sizeof (packet), 0, (struct sockaddr *) &dst,
- sizeof (dst));
- if (err < 0)
- {
- fprintf (stderr, "sendto failed: %s\n", strerror (errno));
- }
- else if (sizeof (packet) != (size_t) err)
- {
- fprintf (stderr, "Error: partial send of ICMP message\n");
- }
- }
- /**
- * Create an ICMP raw socket.
- *
- * @return INVALID_SOCKET on error
- */
- static SOCKET
- make_raw_socket ()
- {
- DWORD bOptVal = TRUE;
- int bOptLen = sizeof (bOptVal);
- SOCKET ret;
- ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
- if (INVALID_SOCKET == ret)
- {
- fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
- return INVALID_SOCKET;
- }
- if (0 !=
- setsockopt (ret, SOL_SOCKET, SO_BROADCAST, (char *) &bOptVal, bOptLen))
- {
- fprintf (stderr, "Error setting SO_BROADCAST to ON: %s\n",
- strerror (errno));
- closesocket (rawsock);
- return INVALID_SOCKET;
- }
- if (0 != setsockopt (ret, IPPROTO_IP, IP_HDRINCL, (char *) &bOptVal, bOptLen))
- {
- fprintf (stderr, "Error setting IP_HDRINCL to ON: %s\n", strerror (errno));
- closesocket (rawsock);
- return INVALID_SOCKET;
- }
- return ret;
- }
- int
- main (int argc, char *const *argv)
- {
- struct in_addr external;
- struct in_addr target;
- WSADATA wsaData;
- unsigned int p;
- if (argc > 1 && 0 != strcmp (argv[1], "-d")){
- privilege_testing = TRUE;
- fprintf (stderr,
- "%s",
- "DEBUG: Running binary in privilege testing mode.");
- argv++;
- argc--;
- }
- if (argc != 4)
- {
- fprintf (stderr,
- "%s",
- "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
- return 1;
- }
- if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
- (1 != inet_pton (AF_INET, argv[2], &target)))
- {
- fprintf (stderr,
- "Error parsing IPv4 address: %s\n",
- strerror (errno));
- return 1;
- }
- if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
- {
- fprintf (stderr,
- "Error parsing port value `%s'\n",
- argv[3]);
- return 1;
- }
- port = (uint16_t) p;
- if (0 != WSAStartup (MAKEWORD (2, 1), &wsaData))
- {
- fprintf (stderr,
- "%s",
- "Failed to find Winsock 2.1 or better.\n");
- return 2;
- }
- if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
- {
- fprintf (stderr,
- "%s",
- "Internal error converting dummy IP to binary.\n");
- return 2;
- }
- if (-1 == (rawsock = make_raw_socket ()))
- return 3;
- if (!privilege_testing){
- send_icmp (&external, &target);
- send_icmp_udp (&external, &target);
- }
- closesocket (rawsock);
- WSACleanup ();
- return 0;
- }
- /* end of gnunet-helper-nat-client-windows.c */
|