X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fgnunet-nat-server-windows.c;h=8cfad2ff297db6971d11f3c96d8bd070963232a2;hb=accae6c152eaacdf41b6069b88b3e5744ff06bba;hp=4115c9122a42a8eceae504dd6a7d70d7bf2c2834;hpb=d9bbc4f11667b6eb3c7ad860b52d78f52ddeacbf;p=oweals%2Fgnunet.git diff --git a/src/transport/gnunet-nat-server-windows.c b/src/transport/gnunet-nat-server-windows.c index 4115c9122..8cfad2ff2 100644 --- a/src/transport/gnunet-nat-server-windows.c +++ b/src/transport/gnunet-nat-server-windows.c @@ -38,20 +38,13 @@ * to the list): * * - Nathan Evans + * - Christian Grothoff */ #define _GNU_SOURCE -#ifdef WIN32 + #include -#else -#include -#include -#include -#include -#include -#include -#include -#endif +#include #include #include #include @@ -62,120 +55,200 @@ #include #include - -#ifdef WIN32 -typedef unsigned int uid_t; -typedef SOCKET Socket; -typedef unsigned short ushort; -#define ICMP_ECHO 8 -#define IPDEFTTL 64 /* default ttl, from RFC 1340 */ -#define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */ -#define IP_HDRINCL 3 /* int; Header is included with data. */ -#else -typedef int Socket; -#endif +/** + * Should we print some debug output? + */ +#define VERBOSE 0 /** * Must match IP given in the client. */ #define DUMMY_IP "192.0.2.86" -#define VERBOSE 0 +/** + * Default Port + */ +#define NAT_TRAV_PORT 22225 + +/** + * TTL to use for our outgoing messages. + */ +#define IPDEFTTL 64 + +#define ICMP_ECHO 8 + +#define ICMP_TIME_EXCEEDED 11 /** * How often do we send our ICMP messages to receive replies? */ #define ICMP_SEND_FREQUENCY_MS 500 -struct ip_packet +/** + * 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; - uint8_t ttl; - uint8_t proto; + + /** + * 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 src_ip; - uint32_t dst_ip; + + uint32_t unused; + + /* followed by original payload */ }; -struct icmp_packet +struct icmp_echo_header { uint8_t type; + uint8_t code; + uint16_t checksum; + uint32_t reserved; }; -struct udp_packet +/** + * Beginning of UDP packet. + */ +struct udp_header { uint16_t src_port; uint16_t dst_port; - uint32_t length; + uint16_t length; + + uint16_t crc; }; -static Socket icmpsock; +/** + * Socket we use to receive "fake" ICMP replies. + */ +static SOCKET icmpsock; -static Socket rawsock; +/** + * Socket we use to send our ICMP requests. + */ +static SOCKET rawsock; +/** + * Socket we use to send our UDP requests. + */ +static SOCKET udpsock; + +/** + * Target "dummy" address. + */ static struct in_addr dummy; -static uint16_t -calc_checksum(const uint16_t *data, + +/** + * 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> 16); sum = htons(0xffff - sum); return sum; } -#if WIN32 + /** + * 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, char *cp, void *buf) +static int +inet_pton (int af, + const char *cp, + struct in_addr *buf) { - int ret; - int ssize; - - ssize = sizeof(struct in_addr); - fprintf (stderr, "in_addr size %d", ssize); - fprintf (stderr, "buf size %d", sizeof(buf)); - ret = WSAStringToAddress (cp, af, NULL, (LPSOCKADDR)buf, &ssize); - if (ret != 0) + buf->s_addr = inet_addr(cp); + if (buf->s_addr == INADDR_NONE) { - - fprintf(stderr, "Error %d handling address %s", WSAGetLastError(), cp); + fprintf(stderr, + "Error %d handling address %s", + WSAGetLastError(), + cp); + return 0; } - if (ret == 0) - return 1; - else - return 0; -} -#endif - -static void -make_echo (const struct in_addr *src_ip, - struct icmp_packet *echo) -{ - memset(echo, 0, sizeof(struct icmp_packet)); - echo->type = ICMP_ECHO; - echo->code = 0; - echo->reserved = 0; - echo->checksum = 0; - echo->checksum = htons(calc_checksum((uint16_t*)echo, sizeof (struct icmp_packet))); + return 1; } @@ -187,48 +260,89 @@ make_echo (const struct in_addr *src_ip, static void send_icmp_echo (const struct in_addr *my_ip) { - struct icmp_packet icmp_echo; + char packet[sizeof (struct ip_header) + sizeof (struct icmp_echo_header)]; + struct icmp_echo_header icmp_echo; + struct ip_header ip_pkt; struct sockaddr_in dst; size_t off; int err; - struct ip_packet ip_pkt; - struct icmp_packet icmp_pkt; - char packet[sizeof (ip_pkt) + sizeof (icmp_pkt)]; off = 0; - memset(&ip_pkt, 0, sizeof(ip_pkt)); ip_pkt.vers_ihl = 0x45; ip_pkt.tos = 0; - ip_pkt.pkt_len = sizeof (packet); - ip_pkt.id = 1; + 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.checksum = 0; ip_pkt.src_ip = my_ip->s_addr; ip_pkt.dst_ip = dummy.s_addr; - ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (ip_pkt))); - memcpy (packet, &ip_pkt, sizeof (ip_pkt)); - off += sizeof (ip_pkt); - make_echo (my_ip, &icmp_echo); - memcpy (&packet[off], &icmp_echo, sizeof (icmp_echo)); - off += sizeof (icmp_echo); - + ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, + sizeof (struct ip_header))); + 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 = 0; + icmp_echo.checksum = 0; + icmp_echo.checksum = htons(calc_checksum((uint16_t*) &icmp_echo, + sizeof (struct icmp_echo_header))); + memcpy (&packet[off], + &icmp_echo, + sizeof (struct icmp_echo_header)); + off += sizeof (struct icmp_echo_header); + memset (&dst, 0, sizeof (dst)); dst.sin_family = AF_INET; dst.sin_addr = dummy; - err = sendto(rawsock, - packet, off, 0, - (struct sockaddr*)&dst, + err = sendto(rawsock, + packet, off, 0, + (struct sockaddr*)&dst, + sizeof(dst)); + if (err < 0) + { +#if VERBOSE + fprintf(stderr, + "sendto failed: %s\n", strerror(errno)); +#endif + } + else if (err != off) + { + fprintf(stderr, + "Error: partial send of ICMP message\n"); + } +} + + +/** + * Send a UDP message to the dummy IP. + */ +static void +send_udp () +{ + struct sockaddr_in dst; + ssize_t err; + + memset (&dst, 0, sizeof (dst)); + dst.sin_family = AF_INET; + dst.sin_addr = dummy; + dst.sin_port = htons (NAT_TRAV_PORT); + err = sendto(udpsock, + NULL, 0, 0, + (struct sockaddr*)&dst, sizeof(dst)); - if (err < 0) + if (err < 0) { #if VERBOSE fprintf(stderr, "sendto failed: %s\n", strerror(errno)); #endif } - else if (err != off) + else if (0 != err) { fprintf(stderr, "Error: partial send of ICMP message\n"); @@ -236,251 +350,303 @@ send_icmp_echo (const struct in_addr *my_ip) } +/** + * We've received an ICMP response. Process it. + */ static void process_icmp_response () { char buf[65536]; ssize_t have; - struct in_addr sip; - struct ip_packet ip_pkt; - struct icmp_packet icmp_pkt; - struct udp_packet udp_pkt; + struct in_addr source_ip; + struct ip_header ip_pkt; + struct icmp_ttl_exceeded_header icmp_ttl; + struct icmp_echo_header icmp_echo; + struct udp_header udp_pkt; size_t off; - int have_port; - int have_udp; - uint32_t port; + uint16_t port; + DWORD ssize; + have = read (icmpsock, buf, sizeof (buf)); if (have == -1) { fprintf (stderr, "Error reading raw socket: %s\n", strerror (errno)); - return; - } - have_port = 0; - - if (have == sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2 + sizeof(uint32_t)) - { - have_port = 1; + return; } - else if (have != sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2) - { #if VERBOSE - fprintf (stderr, - "Received ICMP message of unexpected size: %u bytes\n", - (unsigned int) have); + fprintf (stderr, + "Received message of %u bytes\n", + (unsigned int) have); #endif + if (have < (ssize_t) (sizeof (struct ip_header) + sizeof (struct icmp_ttl_exceeded_header) + sizeof (struct ip_header))) + { + /* malformed */ return; } off = 0; - memcpy (&ip_pkt, &buf[off], sizeof (ip_pkt)); - off += sizeof (ip_pkt); - memcpy (&icmp_pkt, &buf[off], sizeof (icmp_pkt)); - off += sizeof (icmp_pkt); - if ( ((ip_pkt.proto != IPPROTO_ICMP) && (ip_pkt.proto != IPPROTO_UDP)) || - (icmp_pkt.type != ICMP_TIME_EXCEEDED) || - (icmp_pkt.code != 0) ) + memcpy (&ip_pkt, + &buf[off], + sizeof (struct ip_header)); + off += sizeof (struct ip_header); + memcpy(&source_ip, + &ip_pkt.src_ip, + sizeof (source_ip)); + memcpy (&icmp_ttl, + &buf[off], + sizeof (struct icmp_ttl_exceeded_header)); + off += sizeof (struct icmp_ttl_exceeded_header); + if ( (ICMP_TIME_EXCEEDED != icmp_ttl.type) || + (0 != icmp_ttl.code) ) { - /* maybe we got an actual reply back... */ - return; + /* different type than what we want */ + return; } - memcpy(&sip, - &ip_pkt.src_ip, - sizeof (sip)); - - memcpy (&ip_pkt, &buf[off], sizeof (ip_pkt)); - off += sizeof (ip_pkt); + /* skip 2nd IP header */ + memcpy (&ip_pkt, + &buf[off], + sizeof (struct ip_header)); + off += sizeof (struct ip_header); - have_udp = 0; - if (ip_pkt.proto == IPPROTO_UDP) + switch (ip_pkt.proto) { - have_udp = 1; + case IPPROTO_ICMP: + if (have != (sizeof (struct ip_header) * 2 + + sizeof (struct icmp_ttl_exceeded_header) + + sizeof (struct icmp_echo_header)) ) + { + /* malformed */ + return; + } + /* grab ICMP ECHO content */ + memcpy (&icmp_echo, + &buf[off], + sizeof (struct icmp_echo_header)); + port = (uint16_t) ntohl (icmp_echo.reserved); + break; + case IPPROTO_UDP: + if (have != (sizeof (struct ip_header) * 2 + + sizeof (struct icmp_ttl_exceeded_header) + + sizeof (struct udp_header)) ) + { + /* malformed */ + return; + } + /* grab UDP content */ + memcpy (&udp_pkt, + &buf[off], + sizeof (struct udp_header)); + port = ntohs (udp_pkt.length); + break; + default: + /* different type than what we want */ + return; } - if (have_port) - { - memcpy(&port, &buf[sizeof (struct ip_packet) *2 + sizeof (struct icmp_packet) * 2], sizeof(uint32_t)); - port = ntohs(port); -#ifdef WIN32 - DWORD ssize = sizeof(buf); - WSAAddressToString((LPSOCKADDR)&sip, sizeof(sip), NULL, buf, &ssize); - fprintf (stdout, "%s:%d\n", buf, port); -#else - fprintf (stdout, - "%s:%d\n", - inet_ntop (AF_INET, - &sip, - buf, - sizeof (buf)), port); -#endif - } - else if (have_udp) - { - memcpy(&udp_pkt, &buf[off], sizeof(udp_pkt)); -#ifdef WIN32 - DWORD ssize = sizeof(buf); - WSAAddressToString((LPSOCKADDR)&sip, sizeof(sip), NULL, buf, &ssize); - fprintf (stdout, "%s:%d\n", buf, ntohs((int)udp_pkt.length)); -#else - fprintf (stdout, - "%s:%d\n", - inet_ntop (AF_INET, - &sip, - buf, - sizeof (buf)), ntohl(udp_pkt.length)); -#endif - } + ssize = sizeof(buf); + WSAAddressToString((LPSOCKADDR)&source_ip, + sizeof(source_ip), + NULL, + buf, + &ssize); + if (port == 0) + fprintf (stdout, + "%s\n", + buf); else - { -#ifdef WIN32 - DWORD ssize = sizeof(buf); - WSAAddressToString((LPSOCKADDR)&sip, sizeof(sip), NULL, buf, &ssize); - fprintf (stdout, "%s\n", buf); -#else - fprintf (stdout, - "%s\n", - inet_ntop (AF_INET, - &sip, - buf, - sizeof (buf))); -#endif - } + fprintf (stdout, + "%s:%u\n", + buf, + (unsigned int) port); fflush (stdout); } -static Socket +/** + * Create an ICMP raw socket for reading. + * + * @return INVALID_SOCKET on error + */ +static SOCKET make_icmp_socket () { - Socket ret; + SOCKET ret; ret = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); - if (-1 == ret) + if (INVALID_SOCKET == ret) { fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno)); - return -1; - } -#if WIN32 - if (ret == INVALID_SOCKET) - { - fprintf (stderr, "Invalid socket %d!\n", ret); - closesocket (ret); - } -#else - if (ret >= FD_SETSIZE) - { - fprintf (stderr, - "Socket number too large (%d > %u)\n", - ret, - (unsigned int) FD_SETSIZE); - close (ret); - return -1; + return INVALID_SOCKET; } -#endif return ret; } -static Socket +/** + * Create an ICMP raw socket for writing. + * + * @return INVALID_SOCKET on error + */ +static SOCKET make_raw_socket () { - const int one = 1; - Socket ret; + DWORD bOptVal = TRUE; + int bOptLen = sizeof(bOptVal); - ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); - if (-1 == ret) + rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (INVALID_SOCKET == rawsock) { fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno)); - return -1; - } - if (setsockopt(ret, SOL_SOCKET, SO_BROADCAST, - (char *)&one, sizeof(one)) == -1) - fprintf(stderr, - "setsockopt failed: %s\n", - strerror (errno)); - if (setsockopt(ret, IPPROTO_IP, IP_HDRINCL, - (char *)&one, sizeof(one)) == -1) - fprintf(stderr, - "setsockopt failed: %s\n", - strerror (errno)); + return INVALID_SOCKET; + } + + if (0 != setsockopt(rawsock, + 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(rawsock, + 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 rawsock; +} + + +/** + * Create a UDP socket for writing. + * + * @param my_ip source address (our ip address) + * @return INVALID_SOCKET on error + */ +static SOCKET +make_udp_socket (const struct in_addr *my_ip) +{ + SOCKET ret; + struct sockaddr_in addr; + + ret = socket (AF_INET, SOCK_DGRAM, 0); + if (INVALID_SOCKET == ret) + { + fprintf (stderr, + "Error opening UDP socket: %s\n", + strerror (errno)); + return INVALID_SOCKET; + } + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_addr = *my_ip; + addr.sin_port = htons (NAT_TRAV_PORT); + if (0 != bind (ret, + (struct sockaddr *)&addr, + sizeof(addr))) + { + fprintf (stderr, + "Error binding UDP socket to port %u: %s\n", + NAT_TRAV_PORT, + strerror (errno)); + /* likely problematic, but not certain, try to continue */ + } return ret; } int -main (int argc, char *const *argv) +main (int argc, + char *const *argv) { - struct sockaddr_storage external; + struct in_addr external; fd_set rs; struct timeval tv; -#ifndef WIN32 - uid_t uid; -#else - struct sockaddr_storage saddr; -#endif - -#ifdef WIN32 - // WSA startup WSADATA wsaData; - if (WSAStartup (MAKEWORD (2, 1), &wsaData) != 0) - { - fprintf (stderr, "Failed to find Winsock 2.1 or better.\n"); - return 4; // ERROR - } -#endif + unsigned int alt; - if (-1 == (icmpsock = make_icmp_socket())) - return 1; - if (-1 == (rawsock = make_raw_socket())) - { - close (icmpsock); - return 1; - } -#ifndef WIN32 - uid = getuid (); - if (0 != setresuid (uid, uid, uid)) - fprintf (stderr, - "Failed to setresuid: %s\n", - strerror (errno)); -#endif - if (argc != 2) + alt = 0; + if (2 != argc) { fprintf (stderr, "This program must be started with our (internal NAT) IP as the only argument.\n"); return 1; } - - if (1 != inet_pton (AF_INET, argv[1], &saddr)) + if (1 != inet_pton (AF_INET, argv[1], &external)) { fprintf (stderr, "Error parsing IPv4 address: %s, error %s\n", argv[1], strerror (errno)); return 1; } - - if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy)) abort (); + if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy)) + { + fprintf (stderr, + "Internal error converting dummy IP to binary.\n"); + return 2; + } + if (WSAStartup (MAKEWORD (2, 1), &wsaData) != 0) + { + fprintf (stderr, "Failed to find Winsock 2.1 or better.\n"); + return 2; + } + if (INVALID_SOCKET == (icmpsock = make_icmp_socket())) + { + return 3; + } + if (INVALID_SOCKET == (make_raw_socket())) + { + closesocket (icmpsock); + return 3; + } + if (INVALID_SOCKET == (udpsock = make_udp_socket(&external))) + { + closesocket (icmpsock); + closesocket (rawsock); + return 3; + } while (1) { FD_ZERO (&rs); FD_SET (icmpsock, &rs); tv.tv_sec = 0; - tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000; - select (icmpsock + 1, &rs, NULL, NULL, &tv); + tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000; + if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv)) + { + if (errno == EINTR) + continue; + fprintf (stderr, + "select failed: %s\n", + strerror (errno)); + break; + } if (FD_ISSET (icmpsock, &rs)) - process_icmp_response (); - send_icmp_echo (&external); - } - -#ifdef WIN32 + process_icmp_response (); + if (0 == (++alt % 2)) + send_icmp_echo (&external); + else + send_udp (); + } + /* select failed (internal error or OS out of resources) */ closesocket(icmpsock); closesocket(rawsock); + closesocket(udpsock); WSACleanup (); -#endif - return 0; + return 4; }