X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fnat%2Fgnunet-helper-nat-server.c;h=95b46fd6c8f23199feb1e3f6de34f24aafa46da0;hb=e2f70a2f9fa099e3ce0e7451028bea8c373522a6;hp=636ae6003cd82dfba73a5add19cf586bf4b25b4c;hpb=502af2167f7c218366666ca4944bd7cc54b5b19a;p=oweals%2Fgnunet.git diff --git a/src/nat/gnunet-helper-nat-server.c b/src/nat/gnunet-helper-nat-server.c index 636ae6003..95b46fd6c 100644 --- a/src/nat/gnunet-helper-nat-server.c +++ b/src/nat/gnunet-helper-nat-server.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2010 Christian Grothoff (and other contributing authors) + Copyright (C) 2010 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -14,8 +14,8 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** @@ -40,6 +40,7 @@ * - Christian Grothoff * - Nathan Evans * - Benjamin Kuperman (22 Aug 2010) + * - Jacob Appelbaum (19 Dec 2011) */ #if HAVE_CONFIG_H /* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */ @@ -64,11 +65,21 @@ #include #include +/* The following constant is missing from FreeBSD 9.2 */ +#ifndef ICMP_TIME_EXCEEDED +#define ICMP_TIME_EXCEEDED 11 +#endif + /** * Should we print some debug output? */ #define VERBOSE 0 +/** + * Must match packet ID used by gnunet-helper-nat-client.c + */ +#define PACKET_ID 256 + /** * Must match IP given in the client. */ @@ -245,15 +256,15 @@ send_icmp_echo (const struct in_addr *my_ip) ip_pkt.vers_ihl = 0x45; ip_pkt.tos = 0; ip_pkt.pkt_len = htons (sizeof (packet)); - ip_pkt.id = htons (256); + ip_pkt.id = htons (PACKET_ID); 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 = dummy.s_addr; - ip_pkt.checksum = htons (calc_checksum ((uint16_t *) & ip_pkt, - sizeof (struct ip_header))); + 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); @@ -261,8 +272,9 @@ send_icmp_echo (const struct in_addr *my_ip) icmp_echo.code = 0; icmp_echo.checksum = 0; icmp_echo.reserved = 0; - icmp_echo.checksum = htons (calc_checksum ((uint16_t *) & icmp_echo, - sizeof (struct icmp_echo_header))); + 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); @@ -272,8 +284,8 @@ send_icmp_echo (const struct in_addr *my_ip) dst.sin_len = sizeof (struct sockaddr_in); #endif dst.sin_addr = dummy; - err = sendto (rawsock, - packet, off, 0, (struct sockaddr *) &dst, sizeof (dst)); + err = + sendto (rawsock, packet, off, 0, (struct sockaddr *) &dst, sizeof (dst)); if (err < 0) { #if VERBOSE @@ -353,7 +365,6 @@ process_icmp_response () off = 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)) @@ -361,6 +372,9 @@ process_icmp_response () /* different type than what we want */ return; } + /* grab source IP of 1st IP header */ + source_ip.s_addr = ip_pkt.src_ip; + /* skip 2nd IP header */ memcpy (&ip_pkt, &buf[off], sizeof (struct ip_header)); off += sizeof (struct ip_header); @@ -368,9 +382,10 @@ process_icmp_response () switch (ip_pkt.proto) { case IPPROTO_ICMP: - if (have != (sizeof (struct ip_header) * 2 + - sizeof (struct icmp_ttl_exceeded_header) + - sizeof (struct icmp_echo_header))) + if (have != + (sizeof (struct ip_header) * 2 + + sizeof (struct icmp_ttl_exceeded_header) + + sizeof (struct icmp_echo_header))) { /* malformed */ return; @@ -380,9 +395,9 @@ process_icmp_response () 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))) + if (have != + (sizeof (struct ip_header) * 2 + + sizeof (struct icmp_ttl_exceeded_header) + sizeof (struct udp_header))) { /* malformed */ return; @@ -397,82 +412,44 @@ process_icmp_response () } if (port == 0) - fprintf (stdout, - "%s\n", inet_ntop (AF_INET, &source_ip, buf, sizeof (buf))); + fprintf (stdout, "%s\n", + inet_ntop (AF_INET, &source_ip, buf, sizeof (buf))); else - fprintf (stdout, - "%s:%u\n", - inet_ntop (AF_INET, - &source_ip, buf, sizeof (buf)), (unsigned int) port); + fprintf (stdout, "%s:%u\n", + inet_ntop (AF_INET, &source_ip, buf, sizeof (buf)), + (unsigned int) port); fflush (stdout); } /** - * Create an ICMP raw socket for reading. - * - * @return -1 on error - */ -static int -make_icmp_socket () -{ - int ret; - - ret = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); - if (-1 == ret) - { - fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno)); - return -1; - } - if (ret >= FD_SETSIZE) - { - fprintf (stderr, - "Socket number too large (%d > %u)\n", - ret, (unsigned int) FD_SETSIZE); - close (ret); - return -1; - } - return ret; -} - - -/** - * Create an ICMP raw socket for writing. + * Fully initialize the raw socket. * - * @return -1 on error + * @return -1 on error, 0 on success */ static int -make_raw_socket () +setup_raw_socket () { const int one = 1; - int ret; - ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); - if (-1 == ret) - { - fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno)); - return -1; - } - if (-1 == setsockopt (ret, - SOL_SOCKET, SO_BROADCAST, (char *) &one, sizeof (one))) + if (-1 == + setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &one, sizeof (one))) { fprintf (stderr, "setsockopt failed: %s\n", strerror (errno)); - close (ret); return -1; } - if (-1 == setsockopt (ret, - IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof (one))) + if (-1 == + setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof (one))) { fprintf (stderr, "setsockopt failed: %s\n", strerror (errno)); - close (ret); return -1; } - return ret; + return 0; } /** - * Create a UDP socket for writinging. + * Create a UDP socket for writing. * * @param my_ip source address (our ip address) * @return -1 on error @@ -486,7 +463,9 @@ make_udp_socket (const struct in_addr *my_ip) ret = socket (AF_INET, SOCK_DGRAM, 0); if (-1 == ret) { - fprintf (stderr, "Error opening UDP socket: %s\n", strerror (errno)); + fprintf (stderr, + "Error opening UDP socket: %s\n", + strerror (errno)); return -1; } memset (&addr, 0, sizeof (addr)); @@ -497,12 +476,16 @@ make_udp_socket (const struct in_addr *my_ip) addr.sin_addr = *my_ip; addr.sin_port = htons (NAT_TRAV_PORT); - if (0 != bind (ret, &addr, sizeof (addr))) + 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 */ + NAT_TRAV_PORT, + strerror (errno)); + (void) close (ret); + return -1; } return ret; } @@ -516,44 +499,97 @@ main (int argc, char *const *argv) struct timeval tv; uid_t uid; unsigned int alt; + int icmp_eno; + int raw_eno; + int global_ret; + + /* Create an ICMP raw socket for reading (we'll check errors later) */ + icmpsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); + icmp_eno = errno; + + /* Create an (ICMP) raw socket for writing (we'll check errors later) */ + rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); + raw_eno = errno; + udpsock = -1; + + /* drop root rights */ + uid = getuid (); +#ifdef HAVE_SETRESUID + if (0 != setresuid (uid, uid, uid)) + { + fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno)); + global_ret = 1; + goto error_exit; + } +#else + if (0 != (setuid (uid) | seteuid (uid))) + { + fprintf (stderr, "Failed to setuid: %s\n", strerror (errno)); + global_ret = 2; + goto error_exit; + } +#endif + /* Now that we run without root rights, we can do error checking... */ if (2 != argc) { fprintf (stderr, "This program must be started with our (internal NAT) IP as the only argument.\n"); - return 1; + global_ret = 3; + goto error_exit; } if (1 != inet_pton (AF_INET, argv[1], &external)) { fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno)); - return 1; + global_ret = 4; + goto error_exit; } if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy)) { fprintf (stderr, "Internal error converting dummy IP to binary.\n"); - return 2; + global_ret = 5; + goto error_exit; } - if (-1 == (icmpsock = make_icmp_socket ())) + + /* error checking icmpsock */ + if (-1 == icmpsock) { - return 3; + fprintf (stderr, "Error opening RAW socket: %s\n", strerror (icmp_eno)); + global_ret = 6; + goto error_exit; } - if (-1 == (rawsock = make_raw_socket ())) + if (icmpsock >= FD_SETSIZE) { - close (icmpsock); - return 3; + /* this could happen if we were started with a large number of already-open + file descriptors... */ + fprintf (stderr, "Socket number too large (%d > %u)\n", icmpsock, + (unsigned int) FD_SETSIZE); + global_ret = 7; + goto error_exit; } - uid = getuid (); - if (0 != setresuid (uid, uid, uid)) + + /* error checking rawsock */ + if (-1 == rawsock) { - fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno)); - /* not critical, continue anyway */ + fprintf (stderr, "Error opening RAW socket: %s\n", strerror (raw_eno)); + global_ret = 8; + goto error_exit; + } + /* no need to check 'rawsock' against FD_SETSIZE as it is never used + with 'select' */ + + if (0 != setup_raw_socket ()) + { + global_ret = 9; + goto error_exit; } + if (-1 == (udpsock = make_udp_socket (&external))) { - close (icmpsock); - close (rawsock); - return 3; + global_ret = 10; + goto error_exit; } + alt = 0; while (1) { @@ -577,11 +613,17 @@ main (int argc, char *const *argv) else send_udp (); } + /* select failed (internal error or OS out of resources) */ - close (icmpsock); - close (rawsock); - close (udpsock); - return 4; + global_ret = 11; +error_exit: + if (-1 != icmpsock) + (void) close (icmpsock); + if (-1 != rawsock) + (void) close (rawsock); + if (-1 != udpsock) + (void) close (udpsock); + return global_ret; }