commit 42b3caa6ad70106e7bca90bceca378b91045f545 Author: Nick Mathewson nickm@torproject.org Date: Tue Jun 26 20:42:47 2018 -0400
Move network code to libtor-net.
There are some additional changes to come: those points are marked by XXXX. --- .gitignore | 2 + Makefile.am | 2 + src/common/compat.c | 869 +------------------------------------- src/common/compat.h | 186 +------- src/common/include.am | 2 - src/common/sandbox.c | 179 +------- src/common/sandbox.h | 24 -- src/include.am | 1 + src/lib/net/.may_include | 13 + src/{common => lib/net}/address.c | 44 +- src/{common => lib/net}/address.h | 21 +- src/lib/net/include.am | 26 ++ src/lib/net/ipv4.c | 52 +++ src/lib/net/ipv4.h | 17 + src/lib/net/ipv6.c | 221 ++++++++++ src/lib/net/ipv6.h | 86 ++++ src/lib/net/nettypes.h | 39 ++ src/lib/net/resolve.c | 236 +++++++++++ src/lib/net/resolve.h | 50 +++ src/lib/net/socket.c | 649 ++++++++++++++++++++++++++++ src/lib/net/socket.h | 113 +++++ src/rust/build.rs | 1 + src/test/test_util.c | 1 + 23 files changed, 1554 insertions(+), 1280 deletions(-)
diff --git a/.gitignore b/.gitignore index 025d7202d..d6f56f525 100644 --- a/.gitignore +++ b/.gitignore @@ -183,6 +183,8 @@ uptime-*.json /src/lib/libtor-log-testing.a /src/lib/libtor-malloc.a /src/lib/libtor-malloc-testing.a +/src/lib/libtor-net.a +/src/lib/libtor-net-testing.a /src/lib/libtor-string.a /src/lib/libtor-string-testing.a /src/lib/libtor-tls.a diff --git a/Makefile.am b/Makefile.am index 61451ed26..d80f81de1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,6 +40,7 @@ endif # "Common" libraries used to link tor's utility code. TOR_UTIL_LIBS = \ src/common/libor.a \ + src/lib/libtor-net.a \ src/lib/libtor-log.a \ src/lib/libtor-lock.a \ src/lib/libtor-fdio.a \ @@ -55,6 +56,7 @@ TOR_UTIL_LIBS = \ # and tests) TOR_UTIL_TESTING_LIBS = \ src/common/libor-testing.a \ + src/lib/libtor-net-testing.a \ src/lib/libtor-log-testing.a \ src/lib/libtor-lock-testing.a \ src/lib/libtor-fdio-testing.a \ diff --git a/src/common/compat.c b/src/common/compat.c index 8e418f4c1..265e82a48 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -131,11 +131,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt) #include "common/address.h" #include "common/sandbox.h"
-/* When set_max_file_descriptors() is called, update this with the max file - * descriptor value so we can use it to check the limit when opening a new - * socket. Default value is what Debian sets as the default hard limit. */ -static int max_sockets = 1024; - /** As open(path, flags, mode), but return an fd with the close-on-exec mode * set. */ int @@ -707,618 +702,6 @@ tor_lockfile_unlock(tor_lockfile_t *lockfile) tor_free(lockfile); }
-#undef DEBUG_SOCKET_COUNTING -#ifdef DEBUG_SOCKET_COUNTING -/** A bitarray of all fds that should be passed to tor_socket_close(). Only - * used if DEBUG_SOCKET_COUNTING is defined. */ -static bitarray_t *open_sockets = NULL; -/** The size of <b>open_sockets</b>, in bits. */ -static int max_socket = -1; -#endif /* defined(DEBUG_SOCKET_COUNTING) */ - -/** Count of number of sockets currently open. (Undercounts sockets opened by - * eventdns and libevent.) */ -static int n_sockets_open = 0; - -/** Mutex to protect open_sockets, max_socket, and n_sockets_open. */ -static tor_mutex_t *socket_accounting_mutex = NULL; - -/** Helper: acquire the socket accounting lock. */ -static inline void -socket_accounting_lock(void) -{ - if (PREDICT_UNLIKELY(!socket_accounting_mutex)) - socket_accounting_mutex = tor_mutex_new(); - tor_mutex_acquire(socket_accounting_mutex); -} - -/** Helper: release the socket accounting lock. */ -static inline void -socket_accounting_unlock(void) -{ - tor_mutex_release(socket_accounting_mutex); -} - -/** As close(), but guaranteed to work for sockets across platforms (including - * Windows, where close()ing a socket doesn't work. Returns 0 on success and - * the socket error code on failure. */ -int -tor_close_socket_simple(tor_socket_t s) -{ - int r = 0; - - /* On Windows, you have to call close() on fds returned by open(), - * and closesocket() on fds returned by socket(). On Unix, everything - * gets close()'d. We abstract this difference by always using - * tor_close_socket to close sockets, and always using close() on - * files. - */ - #if defined(_WIN32) - r = closesocket(s); - #else - r = close(s); - #endif - - if (r != 0) { - int err = tor_socket_errno(-1); - log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err)); - return err; - } - - return r; -} - -/** As tor_close_socket_simple(), but keeps track of the number - * of open sockets. Returns 0 on success, -1 on failure. */ -MOCK_IMPL(int, -tor_close_socket,(tor_socket_t s)) -{ - int r = tor_close_socket_simple(s); - - socket_accounting_lock(); -#ifdef DEBUG_SOCKET_COUNTING - if (s > max_socket || ! bitarray_is_set(open_sockets, s)) { - log_warn(LD_BUG, "Closing a socket (%d) that wasn't returned by tor_open_" - "socket(), or that was already closed or something.", s); - } else { - tor_assert(open_sockets && s <= max_socket); - bitarray_clear(open_sockets, s); - } -#endif /* defined(DEBUG_SOCKET_COUNTING) */ - if (r == 0) { - --n_sockets_open; - } else { -#ifdef _WIN32 - if (r != WSAENOTSOCK) - --n_sockets_open; -#else - if (r != EBADF) - --n_sockets_open; // LCOV_EXCL_LINE -- EIO and EINTR too hard to force. -#endif /* defined(_WIN32) */ - r = -1; - } - - tor_assert_nonfatal(n_sockets_open >= 0); - socket_accounting_unlock(); - return r; -} - -/** @{ */ -#ifdef DEBUG_SOCKET_COUNTING -/** Helper: if DEBUG_SOCKET_COUNTING is enabled, remember that <b>s</b> is - * now an open socket. */ -static inline void -mark_socket_open(tor_socket_t s) -{ - /* XXXX This bitarray business will NOT work on windows: sockets aren't - small ints there. */ - if (s > max_socket) { - if (max_socket == -1) { - open_sockets = bitarray_init_zero(s+128); - max_socket = s+128; - } else { - open_sockets = bitarray_expand(open_sockets, max_socket, s+128); - max_socket = s+128; - } - } - if (bitarray_is_set(open_sockets, s)) { - log_warn(LD_BUG, "I thought that %d was already open, but socket() just " - "gave it to me!", s); - } - bitarray_set(open_sockets, s); -} -#else /* !(defined(DEBUG_SOCKET_COUNTING)) */ -#define mark_socket_open(s) ((void) (s)) -#endif /* defined(DEBUG_SOCKET_COUNTING) */ -/** @} */ - -/** As socket(), but counts the number of open sockets. */ -MOCK_IMPL(tor_socket_t, -tor_open_socket,(int domain, int type, int protocol)) -{ - return tor_open_socket_with_extensions(domain, type, protocol, 1, 0); -} - -/** Mockable wrapper for connect(). */ -MOCK_IMPL(tor_socket_t, -tor_connect_socket,(tor_socket_t sock, const struct sockaddr *address, - socklen_t address_len)) -{ - return connect(sock,address,address_len); -} - -/** As socket(), but creates a nonblocking socket and - * counts the number of open sockets. */ -tor_socket_t -tor_open_socket_nonblocking(int domain, int type, int protocol) -{ - return tor_open_socket_with_extensions(domain, type, protocol, 1, 1); -} - -/** As socket(), but counts the number of open sockets and handles - * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. - * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate - * if the corresponding extension should be used.*/ -tor_socket_t -tor_open_socket_with_extensions(int domain, int type, int protocol, - int cloexec, int nonblock) -{ - tor_socket_t s; - - /* We are about to create a new file descriptor so make sure we have - * enough of them. */ - if (get_n_open_sockets() >= max_sockets - 1) { -#ifdef _WIN32 - WSASetLastError(WSAEMFILE); -#else - errno = EMFILE; -#endif - return TOR_INVALID_SOCKET; - } - -#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) - int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | - (nonblock ? SOCK_NONBLOCK : 0); - s = socket(domain, type|ext_flags, protocol); - if (SOCKET_OK(s)) - goto socket_ok; - /* If we got an error, see if it is EINVAL. EINVAL might indicate that, - * even though we were built on a system with SOCK_CLOEXEC and SOCK_NONBLOCK - * support, we are running on one without. */ - if (errno != EINVAL) - return s; -#endif /* defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) */ - - s = socket(domain, type, protocol); - if (! SOCKET_OK(s)) - return s; - -#if defined(FD_CLOEXEC) - if (cloexec) { - if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { - log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); - tor_close_socket_simple(s); - return TOR_INVALID_SOCKET; - } - } -#else /* !(defined(FD_CLOEXEC)) */ - (void)cloexec; -#endif /* defined(FD_CLOEXEC) */ - - if (nonblock) { - if (set_socket_nonblocking(s) == -1) { - tor_close_socket_simple(s); - return TOR_INVALID_SOCKET; - } - } - - goto socket_ok; /* So that socket_ok will not be unused. */ - - socket_ok: - tor_take_socket_ownership(s); - return s; -} - -/** - * For socket accounting: remember that we are the owner of the socket - * <b>s</b>. This will prevent us from overallocating sockets, and prevent us - * from asserting later when we close the socket <b>s</b>. - */ -void -tor_take_socket_ownership(tor_socket_t s) -{ - socket_accounting_lock(); - ++n_sockets_open; - mark_socket_open(s); - socket_accounting_unlock(); -} - -/** As accept(), but counts the number of open sockets. */ -tor_socket_t -tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) -{ - return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 0); -} - -/** As accept(), but returns a nonblocking socket and - * counts the number of open sockets. */ -tor_socket_t -tor_accept_socket_nonblocking(tor_socket_t sockfd, struct sockaddr *addr, - socklen_t *len) -{ - return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 1); -} - -/** As accept(), but counts the number of open sockets and handles - * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. - * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate - * if the corresponding extension should be used.*/ -tor_socket_t -tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr, - socklen_t *len, int cloexec, int nonblock) -{ - tor_socket_t s; - - /* We are about to create a new file descriptor so make sure we have - * enough of them. */ - if (get_n_open_sockets() >= max_sockets - 1) { -#ifdef _WIN32 - WSASetLastError(WSAEMFILE); -#else - errno = EMFILE; -#endif - return TOR_INVALID_SOCKET; - } - -#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) \ - && defined(SOCK_NONBLOCK) - int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | - (nonblock ? SOCK_NONBLOCK : 0); - s = accept4(sockfd, addr, len, ext_flags); - if (SOCKET_OK(s)) - goto socket_ok; - /* If we got an error, see if it is ENOSYS. ENOSYS indicates that, - * even though we were built on a system with accept4 support, we - * are running on one without. Also, check for EINVAL, which indicates that - * we are missing SOCK_CLOEXEC/SOCK_NONBLOCK support. */ - if (errno != EINVAL && errno != ENOSYS) - return s; -#endif /* defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) ... */ - - s = accept(sockfd, addr, len); - if (!SOCKET_OK(s)) - return s; - -#if defined(FD_CLOEXEC) - if (cloexec) { - if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { - log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno)); - tor_close_socket_simple(s); - return TOR_INVALID_SOCKET; - } - } -#else /* !(defined(FD_CLOEXEC)) */ - (void)cloexec; -#endif /* defined(FD_CLOEXEC) */ - - if (nonblock) { - if (set_socket_nonblocking(s) == -1) { - tor_close_socket_simple(s); - return TOR_INVALID_SOCKET; - } - } - - goto socket_ok; /* So that socket_ok will not be unused. */ - - socket_ok: - tor_take_socket_ownership(s); - return s; -} - -/** Return the number of sockets we currently have opened. */ -int -get_n_open_sockets(void) -{ - int n; - socket_accounting_lock(); - n = n_sockets_open; - socket_accounting_unlock(); - return n; -} - -/** Mockable wrapper for getsockname(). */ -MOCK_IMPL(int, -tor_getsockname,(tor_socket_t sock, struct sockaddr *address, - socklen_t *address_len)) -{ - return getsockname(sock, address, address_len); -} - -/** - * Find the local address associated with the socket <b>sock</b>, and - * place it in *<b>addr_out</b>. Return 0 on success, -1 on failure. - * - * (As tor_getsockname, but instead places the result in a tor_addr_t.) */ -int -tor_addr_from_getsockname(tor_addr_t *addr_out, tor_socket_t sock) -{ - struct sockaddr_storage ss; - socklen_t ss_len = sizeof(ss); - memset(&ss, 0, sizeof(ss)); - - if (tor_getsockname(sock, (struct sockaddr *) &ss, &ss_len) < 0) - return -1; - - return tor_addr_from_sockaddr(addr_out, (struct sockaddr *)&ss, NULL); -} - -/** Turn <b>socket</b> into a nonblocking socket. Return 0 on success, -1 - * on failure. - */ -int -set_socket_nonblocking(tor_socket_t sock) -{ -#if defined(_WIN32) - unsigned long nonblocking = 1; - ioctlsocket(sock, FIONBIO, (unsigned long*) &nonblocking); -#else - int flags; - - flags = fcntl(sock, F_GETFL, 0); - if (flags == -1) { - log_warn(LD_NET, "Couldn't get file status flags: %s", strerror(errno)); - return -1; - } - flags |= O_NONBLOCK; - if (fcntl(sock, F_SETFL, flags) == -1) { - log_warn(LD_NET, "Couldn't set file status flags: %s", strerror(errno)); - return -1; - } -#endif /* defined(_WIN32) */ - - return 0; -} - -/** - * Allocate a pair of connected sockets. (Like socketpair(family, - * type,protocol,fd), but works on systems that don't have - * socketpair.) - * - * Currently, only (AF_UNIX, SOCK_STREAM, 0) sockets are supported. - * - * Note that on systems without socketpair, this call will fail if - * localhost is inaccessible (for example, if the networking - * stack is down). And even if it succeeds, the socket pair will not - * be able to read while localhost is down later (the socket pair may - * even close, depending on OS-specific timeouts). - * - * Returns 0 on success and -errno on failure; do not rely on the value - * of errno or WSAGetLastError(). - **/ -/* It would be nicer just to set errno, but that won't work for windows. */ -int -tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) -{ -//don't use win32 socketpairs (they are always bad) -#if defined(HAVE_SOCKETPAIR) && !defined(_WIN32) - int r; - -#ifdef SOCK_CLOEXEC - r = socketpair(family, type|SOCK_CLOEXEC, protocol, fd); - if (r == 0) - goto sockets_ok; - /* If we got an error, see if it is EINVAL. EINVAL might indicate that, - * even though we were built on a system with SOCK_CLOEXEC support, we - * are running on one without. */ - if (errno != EINVAL) - return -errno; -#endif /* defined(SOCK_CLOEXEC) */ - - r = socketpair(family, type, protocol, fd); - if (r < 0) - return -errno; - -#if defined(FD_CLOEXEC) - if (SOCKET_OK(fd[0])) { - r = fcntl(fd[0], F_SETFD, FD_CLOEXEC); - if (r == -1) { - close(fd[0]); - close(fd[1]); - return -errno; - } - } - if (SOCKET_OK(fd[1])) { - r = fcntl(fd[1], F_SETFD, FD_CLOEXEC); - if (r == -1) { - close(fd[0]); - close(fd[1]); - return -errno; - } - } -#endif /* defined(FD_CLOEXEC) */ - goto sockets_ok; /* So that sockets_ok will not be unused. */ - - sockets_ok: - socket_accounting_lock(); - if (SOCKET_OK(fd[0])) { - ++n_sockets_open; - mark_socket_open(fd[0]); - } - if (SOCKET_OK(fd[1])) { - ++n_sockets_open; - mark_socket_open(fd[1]); - } - socket_accounting_unlock(); - - return 0; -#else /* !(defined(HAVE_SOCKETPAIR) && !defined(_WIN32)) */ - return tor_ersatz_socketpair(family, type, protocol, fd); -#endif /* defined(HAVE_SOCKETPAIR) && !defined(_WIN32) */ -} - -#ifdef NEED_ERSATZ_SOCKETPAIR - -static inline socklen_t -SIZEOF_SOCKADDR(int domain) -{ - switch (domain) { - case AF_INET: - return sizeof(struct sockaddr_in); - case AF_INET6: - return sizeof(struct sockaddr_in6); - default: - return 0; - } -} - -/** - * Helper used to implement socketpair on systems that lack it, by - * making a direct connection to localhost. - */ -STATIC int -tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) -{ - /* This socketpair does not work when localhost is down. So - * it's really not the same thing at all. But it's close enough - * for now, and really, when localhost is down sometimes, we - * have other problems too. - */ - tor_socket_t listener = TOR_INVALID_SOCKET; - tor_socket_t connector = TOR_INVALID_SOCKET; - tor_socket_t acceptor = TOR_INVALID_SOCKET; - tor_addr_t listen_tor_addr; - struct sockaddr_storage connect_addr_ss, listen_addr_ss; - struct sockaddr *listen_addr = (struct sockaddr *) &listen_addr_ss; - uint16_t listen_port = 0; - tor_addr_t connect_tor_addr; - uint16_t connect_port = 0; - struct sockaddr *connect_addr = (struct sockaddr *) &connect_addr_ss; - socklen_t size; - int saved_errno = -1; - int ersatz_domain = AF_INET; - - memset(&connect_tor_addr, 0, sizeof(connect_tor_addr)); - memset(&connect_addr_ss, 0, sizeof(connect_addr_ss)); - memset(&listen_tor_addr, 0, sizeof(listen_tor_addr)); - memset(&listen_addr_ss, 0, sizeof(listen_addr_ss)); - - if (protocol -#ifdef AF_UNIX - || family != AF_UNIX -#endif - ) { -#ifdef _WIN32 - return -WSAEAFNOSUPPORT; -#else - return -EAFNOSUPPORT; -#endif - } - if (!fd) { - return -EINVAL; - } - - listener = tor_open_socket(ersatz_domain, type, 0); - if (!SOCKET_OK(listener)) { - int first_errno = tor_socket_errno(-1); - if (first_errno == SOCK_ERRNO(EPROTONOSUPPORT) - && ersatz_domain == AF_INET) { - /* Assume we're on an IPv6-only system */ - ersatz_domain = AF_INET6; - listener = tor_open_socket(ersatz_domain, type, 0); - if (!SOCKET_OK(listener)) { - /* Keep the previous behaviour, which was to return the IPv4 error. - * (This may be less informative on IPv6-only systems.) - * XX/teor - is there a better way to decide which errno to return? - * (I doubt we care much either way, once there is an error.) - */ - return -first_errno; - } - } - } - /* If there is no 127.0.0.1 or ::1, this will and must fail. Otherwise, we - * risk exposing a socketpair on a routable IP address. (Some BSD jails - * use a routable address for localhost. Fortunately, they have the real - * AF_UNIX socketpair.) */ - if (ersatz_domain == AF_INET) { - tor_addr_from_ipv4h(&listen_tor_addr, INADDR_LOOPBACK); - } else { - tor_addr_parse(&listen_tor_addr, "[::1]"); - } - tor_assert(tor_addr_is_loopback(&listen_tor_addr)); - size = tor_addr_to_sockaddr(&listen_tor_addr, - 0 /* kernel chooses port. */, - listen_addr, - sizeof(listen_addr_ss)); - if (bind(listener, listen_addr, size) == -1) - goto tidy_up_and_fail; - if (listen(listener, 1) == -1) - goto tidy_up_and_fail; - - connector = tor_open_socket(ersatz_domain, type, 0); - if (!SOCKET_OK(connector)) - goto tidy_up_and_fail; - /* We want to find out the port number to connect to. */ - size = sizeof(connect_addr_ss); - if (getsockname(listener, connect_addr, &size) == -1) - goto tidy_up_and_fail; - if (size != SIZEOF_SOCKADDR (connect_addr->sa_family)) - goto abort_tidy_up_and_fail; - if (connect(connector, connect_addr, size) == -1) - goto tidy_up_and_fail; - - size = sizeof(listen_addr_ss); - acceptor = tor_accept_socket(listener, listen_addr, &size); - if (!SOCKET_OK(acceptor)) - goto tidy_up_and_fail; - if (size != SIZEOF_SOCKADDR(listen_addr->sa_family)) - goto abort_tidy_up_and_fail; - /* Now check we are talking to ourself by matching port and host on the - two sockets. */ - if (getsockname(connector, connect_addr, &size) == -1) - goto tidy_up_and_fail; - /* Set *_tor_addr and *_port to the address and port that was used */ - tor_addr_from_sockaddr(&listen_tor_addr, listen_addr, &listen_port); - tor_addr_from_sockaddr(&connect_tor_addr, connect_addr, &connect_port); - if (size != SIZEOF_SOCKADDR (connect_addr->sa_family) - || tor_addr_compare(&listen_tor_addr, &connect_tor_addr, CMP_SEMANTIC) - || listen_port != connect_port) { - goto abort_tidy_up_and_fail; - } - tor_close_socket(listener); - fd[0] = connector; - fd[1] = acceptor; - - return 0; - - abort_tidy_up_and_fail: -#ifdef _WIN32 - saved_errno = WSAECONNABORTED; -#else - saved_errno = ECONNABORTED; /* I hope this is portable and appropriate. */ -#endif - tidy_up_and_fail: - if (saved_errno < 0) - saved_errno = errno; - if (SOCKET_OK(listener)) - tor_close_socket(listener); - if (SOCKET_OK(connector)) - tor_close_socket(connector); - if (SOCKET_OK(acceptor)) - tor_close_socket(acceptor); - return -saved_errno; -} - -#undef SIZEOF_SOCKADDR - -#endif /* defined(NEED_ERSATZ_SOCKETPAIR) */ - -/* Return the maximum number of allowed sockets. */ -int -get_max_sockets(void) -{ - return max_sockets; -} - /** Number of extra file descriptors to keep in reserve beyond those that we * tell Tor it's allowed to use. */ #define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond ConnLimit_ */ @@ -1396,7 +779,8 @@ set_max_file_descriptors(rlim_t limit, int *max_out) } /* Set the current limit value so if the attempt to set the limit to the * max fails at least we'll have a valid value of maximum sockets. */ - *max_out = max_sockets = (int)rlim.rlim_cur - ULIMIT_BUFFER; + *max_out = (int)rlim.rlim_cur - ULIMIT_BUFFER; + set_max_sockets(*max_out); rlim.rlim_cur = rlim.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { @@ -1438,7 +822,9 @@ set_max_file_descriptors(rlim_t limit, int *max_out) if (limit > INT_MAX) limit = INT_MAX; tor_assert(max_out); - *max_out = max_sockets = (int)limit - ULIMIT_BUFFER; + *max_out = (int)limit - ULIMIT_BUFFER; + set_max_sockets(*max_out); + return 0; }
@@ -2124,251 +1510,6 @@ tor_gethostname,(char *name, size_t namelen)) return gethostname(name,namelen); }
-/** Set *addr to the IP address (in dotted-quad notation) stored in *str. - * Return 1 on success, 0 if *str is badly formatted. - * (Like inet_aton(str,addr), but works on Windows and Solaris.) - */ -int -tor_inet_aton(const char *str, struct in_addr* addr) -{ - unsigned a,b,c,d; - char more; - if (tor_sscanf(str, "%3u.%3u.%3u.%3u%c", &a,&b,&c,&d,&more) != 4) - return 0; - if (a > 255) return 0; - if (b > 255) return 0; - if (c > 255) return 0; - if (d > 255) return 0; - addr->s_addr = htonl((a<<24) | (b<<16) | (c<<8) | d); - return 1; -} - -/** Given <b>af</b>==AF_INET and <b>src</b> a struct in_addr, or - * <b>af</b>==AF_INET6 and <b>src</b> a struct in6_addr, try to format the - * address and store it in the <b>len</b>-byte buffer <b>dst</b>. Returns - * <b>dst</b> on success, NULL on failure. - * - * (Like inet_ntop(af,src,dst,len), but works on platforms that don't have it: - * Tor sometimes needs to format ipv6 addresses even on platforms without ipv6 - * support.) */ -const char * -tor_inet_ntop(int af, const void *src, char *dst, size_t len) -{ - if (af == AF_INET) { - if (tor_inet_ntoa(src, dst, len) < 0) - return NULL; - else - return dst; - } else if (af == AF_INET6) { - const struct in6_addr *addr = src; - char buf[64], *cp; - int longestGapLen = 0, longestGapPos = -1, i, - curGapPos = -1, curGapLen = 0; - uint16_t words[8]; - for (i = 0; i < 8; ++i) { - words[i] = (((uint16_t)addr->s6_addr[2*i])<<8) + addr->s6_addr[2*i+1]; - } - if (words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 && - words[4] == 0 && ((words[5] == 0 && words[6] && words[7]) || - (words[5] == 0xffff))) { - /* This is an IPv4 address. */ - if (words[5] == 0) { - tor_snprintf(buf, sizeof(buf), "::%d.%d.%d.%d", - addr->s6_addr[12], addr->s6_addr[13], - addr->s6_addr[14], addr->s6_addr[15]); - } else { - tor_snprintf(buf, sizeof(buf), "::%x:%d.%d.%d.%d", words[5], - addr->s6_addr[12], addr->s6_addr[13], - addr->s6_addr[14], addr->s6_addr[15]); - } - if ((strlen(buf) + 1) > len) /* +1 for \0 */ - return NULL; - strlcpy(dst, buf, len); - return dst; - } - i = 0; - while (i < 8) { - if (words[i] == 0) { - curGapPos = i++; - curGapLen = 1; - while (i<8 && words[i] == 0) { - ++i; ++curGapLen; - } - if (curGapLen > longestGapLen) { - longestGapPos = curGapPos; - longestGapLen = curGapLen; - } - } else { - ++i; - } - } - if (longestGapLen<=1) - longestGapPos = -1; - - cp = buf; - for (i = 0; i < 8; ++i) { - if (words[i] == 0 && longestGapPos == i) { - if (i == 0) - *cp++ = ':'; - *cp++ = ':'; - while (i < 8 && words[i] == 0) - ++i; - --i; /* to compensate for loop increment. */ - } else { - tor_snprintf(cp, sizeof(buf)-(cp-buf), "%x", (unsigned)words[i]); - cp += strlen(cp); - if (i != 7) - *cp++ = ':'; - } - } - *cp = '\0'; - if ((strlen(buf) + 1) > len) /* +1 for \0 */ - return NULL; - strlcpy(dst, buf, len); - return dst; - } else { - return NULL; - } -} - -/** Given <b>af</b>==AF_INET or <b>af</b>==AF_INET6, and a string <b>src</b> - * encoding an IPv4 address or IPv6 address correspondingly, try to parse the - * address and store the result in <b>dst</b> (which must have space for a - * struct in_addr or a struct in6_addr, as appropriate). Return 1 on success, - * 0 on a bad parse, and -1 on a bad <b>af</b>. - * - * (Like inet_pton(af,src,dst) but works on platforms that don't have it: Tor - * sometimes needs to format ipv6 addresses even on platforms without ipv6 - * support.) */ -int -tor_inet_pton(int af, const char *src, void *dst) -{ - if (af == AF_INET) { - return tor_inet_aton(src, dst); - } else if (af == AF_INET6) { - struct in6_addr *out = dst; - uint16_t words[8]; - int gapPos = -1, i, setWords=0; - const char *dot = strchr(src, '.'); - const char *eow; /* end of words. */ - memset(words, 0xf8, sizeof(words)); - if (dot == src) - return 0; - else if (!dot) - eow = src+strlen(src); - else { - unsigned byte1,byte2,byte3,byte4; - char more; - for (eow = dot-1; eow > src && TOR_ISDIGIT(*eow); --eow) - ; - if (*eow != ':') - return 0; - ++eow; - - /* We use "scanf" because some platform inet_aton()s are too lax - * about IPv4 addresses of the form "1.2.3" */ - if (tor_sscanf(eow, "%3u.%3u.%3u.%3u%c", - &byte1,&byte2,&byte3,&byte4,&more) != 4) - return 0; - - if (byte1 > 255 || byte2 > 255 || byte3 > 255 || byte4 > 255) - return 0; - - words[6] = (byte1<<8) | byte2; - words[7] = (byte3<<8) | byte4; - setWords += 2; - } - - i = 0; - while (src < eow) { - if (i > 7) - return 0; - if (TOR_ISXDIGIT(*src)) { - char *next; - ssize_t len; - long r = strtol(src, &next, 16); - if (next == NULL || next == src) { - /* The 'next == src' error case can happen on versions of openbsd - * which treat "0xfoo" as an error, rather than as "0" followed by - * "xfoo". */ - return 0; - } - - len = *next == '\0' ? eow - src : next - src; - if (len > 4) - return 0; - if (len > 1 && !TOR_ISXDIGIT(src[1])) - return 0; /* 0x is not valid */ - - tor_assert(r >= 0); - tor_assert(r < 65536); - words[i++] = (uint16_t)r; - setWords++; - src = next; - if (*src != ':' && src != eow) - return 0; - ++src; - } else if (*src == ':' && i > 0 && gapPos == -1) { - gapPos = i; - ++src; - } else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' && - gapPos == -1) { - gapPos = i; - src += 2; - } else { - return 0; - } - } - - if (setWords > 8 || - (setWords == 8 && gapPos != -1) || - (setWords < 8 && gapPos == -1)) - return 0; - - if (gapPos >= 0) { - int nToMove = setWords - (dot ? 2 : 0) - gapPos; - int gapLen = 8 - setWords; - tor_assert(nToMove >= 0); - memmove(&words[gapPos+gapLen], &words[gapPos], - sizeof(uint16_t)*nToMove); - memset(&words[gapPos], 0, sizeof(uint16_t)*gapLen); - } - for (i = 0; i < 8; ++i) { - out->s6_addr[2*i ] = words[i] >> 8; - out->s6_addr[2*i+1] = words[i] & 0xff; - } - - return 1; - } else { - return -1; - } -} - -/** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set - * *<b>addr</b> to the proper IP address, in host byte order. Returns 0 - * on success, -1 on failure; 1 on transient failure. - * - * (This function exists because standard windows gethostbyname - * doesn't treat raw IP addresses properly.) - */ - -MOCK_IMPL(int, -tor_lookup_hostname,(const char *name, uint32_t *addr)) -{ - tor_addr_t myaddr; - int ret; - - if ((ret = tor_addr_lookup(name, AF_INET, &myaddr))) - return ret; - - if (tor_addr_family(&myaddr) == AF_INET) { - *addr = tor_addr_to_ipv4h(&myaddr); - return ret; - } - - return -1; -} - /** Hold the result of our call to <b>uname</b>. */ static char uname_result[256]; /** True iff uname_result is set. */ diff --git a/src/common/compat.h b/src/common/compat.h index 691824a2e..efd436324 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -50,6 +50,10 @@ #include "lib/string/compat_ctype.h" #include "lib/string/compat_string.h" #include "lib/string/printf.h" +#include "lib/net/socket.h" +#include "lib/net/ipv4.h" +#include "lib/net/ipv6.h" +#include "lib/net/resolve.h"
#include <stdio.h> #include <errno.h> @@ -162,180 +166,7 @@ int64_t tor_get_avail_disk_space(const char *path);
/* ===== Net compatibility */
-#if (SIZEOF_SOCKLEN_T == 0) -typedef int socklen_t; -#endif - -#ifdef _WIN32 -/* XXX Actually, this should arguably be SOCKET; we use intptr_t here so that - * any inadvertent checks for the socket being <= 0 or > 0 will probably - * still work. */ -#define tor_socket_t intptr_t -#define TOR_SOCKET_T_FORMAT INTPTR_T_FORMAT -#define SOCKET_OK(s) ((SOCKET)(s) != INVALID_SOCKET) -#define TOR_INVALID_SOCKET INVALID_SOCKET -#else /* !(defined(_WIN32)) */ -/** Type used for a network socket. */ -#define tor_socket_t int -#define TOR_SOCKET_T_FORMAT "%d" -/** Macro: true iff 's' is a possible value for a valid initialized socket. */ -#define SOCKET_OK(s) ((s) >= 0) -/** Error/uninitialized value for a tor_socket_t. */ -#define TOR_INVALID_SOCKET (-1) -#endif /* defined(_WIN32) */ - -int tor_close_socket_simple(tor_socket_t s); -MOCK_DECL(int, tor_close_socket, (tor_socket_t s)); -void tor_take_socket_ownership(tor_socket_t s); -tor_socket_t tor_open_socket_with_extensions( - int domain, int type, int protocol, - int cloexec, int nonblock); -MOCK_DECL(tor_socket_t, -tor_open_socket,(int domain, int type, int protocol)); -tor_socket_t tor_open_socket_nonblocking(int domain, int type, int protocol); -tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, - socklen_t *len); -tor_socket_t tor_accept_socket_nonblocking(tor_socket_t sockfd, - struct sockaddr *addr, - socklen_t *len); -tor_socket_t tor_accept_socket_with_extensions(tor_socket_t sockfd, - struct sockaddr *addr, - socklen_t *len, - int cloexec, int nonblock); -MOCK_DECL(tor_socket_t, -tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address, - socklen_t address_len)); -int get_n_open_sockets(void); - -MOCK_DECL(int, -tor_getsockname,(tor_socket_t socket, struct sockaddr *address, - socklen_t *address_len)); -struct tor_addr_t; -int tor_addr_from_getsockname(struct tor_addr_t *addr_out, tor_socket_t sock); - -#define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags) -#define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags) - -/** Implementation of struct in6_addr for platforms that do not have it. - * Generally, these platforms are ones without IPv6 support, but we want to - * have a working in6_addr there anyway, so we can use it to parse IPv6 - * addresses. */ -#if !defined(HAVE_STRUCT_IN6_ADDR) -struct in6_addr -{ - union { - uint8_t u6_addr8[16]; - uint16_t u6_addr16[8]; - uint32_t u6_addr32[4]; - } in6_u; -#define s6_addr in6_u.u6_addr8 -#define s6_addr16 in6_u.u6_addr16 -#define s6_addr32 in6_u.u6_addr32 -}; -#endif /* !defined(HAVE_STRUCT_IN6_ADDR) */ - -/** @{ */ -/** Many BSD variants seem not to define these. */ -#if defined(__APPLE__) || defined(__darwin__) || \ - defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) -#ifndef s6_addr16 -#define s6_addr16 __u6_addr.__u6_addr16 -#endif -#ifndef s6_addr32 -#define s6_addr32 __u6_addr.__u6_addr32 -#endif -#endif /* defined(__APPLE__) || defined(__darwin__) || ... */ -/** @} */ - -#ifndef HAVE_SA_FAMILY_T -typedef uint16_t sa_family_t; -#endif - -/** @{ */ -/** Apparently, MS and Solaris don't define s6_addr16 or s6_addr32; these - * macros get you a pointer to s6_addr32 or local equivalent. */ -#ifdef HAVE_STRUCT_IN6_ADDR_S6_ADDR32 -#define S6_ADDR32(x) ((uint32_t*)(x).s6_addr32) -#else -#define S6_ADDR32(x) ((uint32_t*)((char*)&(x).s6_addr)) -#endif -#ifdef HAVE_STRUCT_IN6_ADDR_S6_ADDR16 -#define S6_ADDR16(x) ((uint16_t*)(x).s6_addr16) -#else -#define S6_ADDR16(x) ((uint16_t*)((char*)&(x).s6_addr)) -#endif -/** @} */ - -/** Implementation of struct sockaddr_in6 on platforms that do not have - * it. See notes on struct in6_addr. */ -#if !defined(HAVE_STRUCT_SOCKADDR_IN6) -struct sockaddr_in6 { - sa_family_t sin6_family; - uint16_t sin6_port; - // uint32_t sin6_flowinfo; - struct in6_addr sin6_addr; - // uint32_t sin6_scope_id; -}; -#endif /* !defined(HAVE_STRUCT_SOCKADDR_IN6) */ - MOCK_DECL(int,tor_gethostname,(char *name, size_t namelen)); -int tor_inet_aton(const char *cp, struct in_addr *addr) ATTR_NONNULL((1,2)); -const char *tor_inet_ntop(int af, const void *src, char *dst, size_t len); -int tor_inet_pton(int af, const char *src, void *dst); -MOCK_DECL(int,tor_lookup_hostname,(const char *name, uint32_t *addr)); -int set_socket_nonblocking(tor_socket_t socket); -int tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]); -int network_init(void); - -/* For stupid historical reasons, windows sockets have an independent - * set of errnos, and an independent way to get them. Also, you can't - * always believe WSAEWOULDBLOCK. Use the macros below to compare - * errnos against expected values, and use tor_socket_errno to find - * the actual errno after a socket operation fails. - */ -#if defined(_WIN32) -/** Expands to WSA<b>e</b> on Windows, and to <b>e</b> elsewhere. */ -#define SOCK_ERRNO(e) WSA##e -/** Return true if e is EAGAIN or the local equivalent. */ -#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == WSAEWOULDBLOCK) -/** Return true if e is EINPROGRESS or the local equivalent. */ -#define ERRNO_IS_EINPROGRESS(e) ((e) == WSAEINPROGRESS) -/** Return true if e is EINPROGRESS or the local equivalent as returned by - * a call to connect(). */ -#define ERRNO_IS_CONN_EINPROGRESS(e) \ - ((e) == WSAEINPROGRESS || (e)== WSAEINVAL || (e) == WSAEWOULDBLOCK) -/** Return true if e is EAGAIN or another error indicating that a call to - * accept() has no pending connections to return. */ -#define ERRNO_IS_ACCEPT_EAGAIN(e) ERRNO_IS_EAGAIN(e) -/** Return true if e is EMFILE or another error indicating that a call to - * accept() has failed because we're out of fds or something. */ -#define ERRNO_IS_RESOURCE_LIMIT(e) \ - ((e) == WSAEMFILE || (e) == WSAENOBUFS) -/** Return true if e is EADDRINUSE or the local equivalent. */ -#define ERRNO_IS_EADDRINUSE(e) ((e) == WSAEADDRINUSE) -/** Return true if e is EINTR or the local equivalent */ -#define ERRNO_IS_EINTR(e) ((e) == WSAEINTR || 0) -int tor_socket_errno(tor_socket_t sock); -const char *tor_socket_strerror(int e); -#else /* !(defined(_WIN32)) */ -#define SOCK_ERRNO(e) e -#if EAGAIN == EWOULDBLOCK -/* || 0 is for -Wparentheses-equality (-Wall?) appeasement under clang */ -#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || 0) -#else -#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == EWOULDBLOCK) -#endif /* EAGAIN == EWOULDBLOCK */ -#define ERRNO_IS_EINTR(e) ((e) == EINTR || 0) -#define ERRNO_IS_EINPROGRESS(e) ((e) == EINPROGRESS || 0) -#define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == EINPROGRESS || 0) -#define ERRNO_IS_ACCEPT_EAGAIN(e) \ - (ERRNO_IS_EAGAIN(e) || (e) == ECONNABORTED) -#define ERRNO_IS_RESOURCE_LIMIT(e) \ - ((e) == EMFILE || (e) == ENFILE || (e) == ENOBUFS || (e) == ENOMEM) -#define ERRNO_IS_EADDRINUSE(e) (((e) == EADDRINUSE) || 0) -#define tor_socket_errno(sock) (errno) -#define tor_socket_strerror(e) strerror(e) -#endif /* defined(_WIN32) */
/** Specified SOCKS5 status codes. */ typedef enum { @@ -372,7 +203,6 @@ set_uint8(void *cp, uint8_t v) #if !defined(HAVE_RLIM_T) typedef unsigned long rlim_t; #endif -int get_max_sockets(void); int set_max_file_descriptors(rlim_t limit, int *max); int tor_disable_debugger_attach(void);
@@ -440,14 +270,6 @@ char *format_win32_error(DWORD err);
#endif /* defined(_WIN32) */
-#ifdef COMPAT_PRIVATE -#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS) -#define NEED_ERSATZ_SOCKETPAIR -STATIC int tor_ersatz_socketpair(int family, int type, int protocol, - tor_socket_t fd[2]); -#endif -#endif /* defined(COMPAT_PRIVATE) */ - ssize_t tor_getpass(const char *prompt, char *output, size_t buflen);
/* This needs some of the declarations above so we include it here. */ diff --git a/src/common/include.am b/src/common/include.am index 4927f5742..56666b87f 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -31,7 +31,6 @@ readpassphrase_source= endif
LIBOR_A_SRC = \ - src/common/address.c \ src/common/address_set.c \ src/common/buffers.c \ src/common/compat.c \ @@ -76,7 +75,6 @@ src_common_libor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_common_libor_event_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
COMMONHEADERS = \ - src/common/address.h \ src/common/address_set.h \ src/common/buffers.h \ src/common/compat.h \ diff --git a/src/common/sandbox.c b/src/common/sandbox.c index b41b8f4ba..9842fd983 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -37,6 +37,7 @@ #include "lib/err/torerr.h" #include "lib/log/torlog.h" #include "lib/cc/torint.h" +#include "lib/net/resolve.h" #include "common/util.h" #include "tor_queue.h"
@@ -1458,183 +1459,6 @@ sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file) return 0; }
-/** Cache entry for getaddrinfo results; used when sandboxing is implemented - * so that we can consult the cache when the sandbox prevents us from doing - * getaddrinfo. - * - * We support only a limited range of getaddrinfo calls, where servname is null - * and hints contains only socktype=SOCK_STREAM, family in INET,INET6,UNSPEC. - */ -typedef struct cached_getaddrinfo_item_t { - HT_ENTRY(cached_getaddrinfo_item_t) node; - char *name; - int family; - /** set if no error; otherwise NULL */ - struct addrinfo *res; - /** 0 for no error; otherwise an EAI_* value */ - int err; -} cached_getaddrinfo_item_t; - -static unsigned -cached_getaddrinfo_item_hash(const cached_getaddrinfo_item_t *item) -{ - return (unsigned)siphash24g(item->name, strlen(item->name)) + item->family; -} - -static unsigned -cached_getaddrinfo_items_eq(const cached_getaddrinfo_item_t *a, - const cached_getaddrinfo_item_t *b) -{ - return (a->family == b->family) && 0 == strcmp(a->name, b->name); -} - -#define cached_getaddrinfo_item_free(item) \ - FREE_AND_NULL(cached_getaddrinfo_item_t, \ - cached_getaddrinfo_item_free_, (item)) - -static void -cached_getaddrinfo_item_free_(cached_getaddrinfo_item_t *item) -{ - if (item == NULL) - return; - - tor_free(item->name); - if (item->res) - freeaddrinfo(item->res); - tor_free(item); -} - -static HT_HEAD(getaddrinfo_cache, cached_getaddrinfo_item_t) - getaddrinfo_cache = HT_INITIALIZER(); - -HT_PROTOTYPE(getaddrinfo_cache, cached_getaddrinfo_item_t, node, - cached_getaddrinfo_item_hash, - cached_getaddrinfo_items_eq) -HT_GENERATE2(getaddrinfo_cache, cached_getaddrinfo_item_t, node, - cached_getaddrinfo_item_hash, - cached_getaddrinfo_items_eq, - 0.6, tor_reallocarray_, tor_free_) - -/** If true, don't try to cache getaddrinfo results. */ -static int sandbox_getaddrinfo_cache_disabled = 0; - -/** Tell the sandbox layer not to try to cache getaddrinfo results. Used as in - * tor-resolve, when we have no intention of initializing crypto or of - * installing the sandbox.*/ -void -sandbox_disable_getaddrinfo_cache(void) -{ - sandbox_getaddrinfo_cache_disabled = 1; -} - -void -sandbox_freeaddrinfo(struct addrinfo *ai) -{ - if (sandbox_getaddrinfo_cache_disabled) - freeaddrinfo(ai); -} - -int -sandbox_getaddrinfo(const char *name, const char *servname, - const struct addrinfo *hints, - struct addrinfo **res) -{ - int err; - struct cached_getaddrinfo_item_t search, *item; - - if (sandbox_getaddrinfo_cache_disabled) { - return getaddrinfo(name, NULL, hints, res); - } - - if (servname != NULL) { - log_warn(LD_BUG, "called with non-NULL servname"); - return EAI_NONAME; - } - if (name == NULL) { - log_warn(LD_BUG, "called with NULL name"); - return EAI_NONAME; - } - - *res = NULL; - - memset(&search, 0, sizeof(search)); - search.name = (char *) name; - search.family = hints ? hints->ai_family : AF_UNSPEC; - item = HT_FIND(getaddrinfo_cache, &getaddrinfo_cache, &search); - - if (! sandbox_is_active()) { - /* If the sandbox is not turned on yet, then getaddrinfo and store the - result. */ - - err = getaddrinfo(name, NULL, hints, res); - log_info(LD_NET,"(Sandbox) getaddrinfo %s.", err ? "failed" : "succeeded"); - - if (! item) { - item = tor_malloc_zero(sizeof(*item)); - item->name = tor_strdup(name); - item->family = hints ? hints->ai_family : AF_UNSPEC; - HT_INSERT(getaddrinfo_cache, &getaddrinfo_cache, item); - } - - if (item->res) { - freeaddrinfo(item->res); - item->res = NULL; - } - item->res = *res; - item->err = err; - return err; - } - - /* Otherwise, the sandbox is on. If we have an item, yield its cached - result. */ - if (item) { - *res = item->res; - return item->err; - } - - /* getting here means something went wrong */ - log_err(LD_BUG,"(Sandbox) failed to get address %s!", name); - return EAI_NONAME; -} - -int -sandbox_add_addrinfo(const char *name) -{ - struct addrinfo *res; - struct addrinfo hints; - int i; - static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC }; - - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; - for (i = 0; i < 3; ++i) { - hints.ai_family = families[i]; - - res = NULL; - (void) sandbox_getaddrinfo(name, NULL, &hints, &res); - if (res) - sandbox_freeaddrinfo(res); - } - - return 0; -} - -void -sandbox_free_getaddrinfo_cache(void) -{ - cached_getaddrinfo_item_t **next, **item, *this; - - for (item = HT_START(getaddrinfo_cache, &getaddrinfo_cache); - item; - item = next) { - this = *item; - next = HT_NEXT_RMV(getaddrinfo_cache, &getaddrinfo_cache, item); - cached_getaddrinfo_item_free(this); - } - - HT_CLEAR(getaddrinfo_cache, &getaddrinfo_cache); -} - /** * Function responsible for going through the parameter syscall filters and * call each function pointer in the list. @@ -1727,6 +1551,7 @@ install_syscall_filter(sandbox_cfg_t* cfg)
// marking the sandbox as active sandbox_active = 1; + sandbox_make_getaddrinfo_cache_active();
end: seccomp_release(ctx); diff --git a/src/common/sandbox.h b/src/common/sandbox.h index f7c990e01..60d8e8816 100644 --- a/src/common/sandbox.h +++ b/src/common/sandbox.h @@ -104,27 +104,6 @@ typedef struct { #endif /* defined(USE_LIBSECCOMP) */
#ifdef USE_LIBSECCOMP -/** Pre-calls getaddrinfo in order to pre-record result. */ -int sandbox_add_addrinfo(const char *addr); - -struct addrinfo; -/** Replacement for getaddrinfo(), using pre-recorded results. */ -int sandbox_getaddrinfo(const char *name, const char *servname, - const struct addrinfo *hints, - struct addrinfo **res); -void sandbox_freeaddrinfo(struct addrinfo *addrinfo); -void sandbox_free_getaddrinfo_cache(void); -#else /* !(defined(USE_LIBSECCOMP)) */ -#define sandbox_getaddrinfo(name, servname, hints, res) \ - getaddrinfo((name),(servname), (hints),(res)) -#define sandbox_add_addrinfo(name) \ - ((void)(name)) -#define sandbox_freeaddrinfo(addrinfo) \ - freeaddrinfo((addrinfo)) -#define sandbox_free_getaddrinfo_cache() -#endif /* defined(USE_LIBSECCOMP) */ - -#ifdef USE_LIBSECCOMP /** Returns a registered protected string used with the sandbox, given that * it matches the parameter. */ @@ -168,7 +147,4 @@ int sandbox_init(sandbox_cfg_t* cfg); /** Return true iff the sandbox is turned on. */ int sandbox_is_active(void);
-void sandbox_disable_getaddrinfo_cache(void); - #endif /* !defined(SANDBOX_H_) */ - diff --git a/src/include.am b/src/include.am index 1d2a037e9..5b8aacdd5 100644 --- a/src/include.am +++ b/src/include.am @@ -12,6 +12,7 @@ include src/lib/intmath/include.am include src/lib/lock/include.am include src/lib/log/include.am include src/lib/malloc/include.am +include src/lib/net/include.am include src/lib/string/include.am include src/lib/testsupport/include.am include src/lib/tls/include.am diff --git a/src/lib/net/.may_include b/src/lib/net/.may_include new file mode 100644 index 000000000..479f85293 --- /dev/null +++ b/src/lib/net/.may_include @@ -0,0 +1,13 @@ +orconfig.h +siphash.h +ht.h + +lib/cc/*.h +lib/container/*.h +lib/ctime/*.h +lib/lock/*.h +lib/log/*.h +lib/net/*.h +lib/string/*.h +lib/testsupport/*.h +lib/malloc/*.h \ No newline at end of file diff --git a/src/common/address.c b/src/lib/net/address.c similarity index 98% rename from src/common/address.c rename to src/lib/net/address.c index 42052f85e..70b09b419 100644 --- a/src/common/address.c +++ b/src/lib/net/address.c @@ -35,13 +35,21 @@ #include <iphlpapi.h> #endif /* defined(_WIN32) */
-#include "common/compat.h" -#include "common/util.h" -#include "common/util_format.h" -#include "common/address.h" -#include "lib/log/torlog.h" +#include "lib/net/address.h" +#include "lib/net/socket.h" +#include "lib/net/resolve.h" #include "lib/container/smartlist.h" -#include "common/sandbox.h" +#include "lib/ctime/di_ops.h" +#include "lib/log/torlog.h" +#include "lib/log/escape.h" +#include "lib/malloc/util_malloc.h" +#include "lib/net/ipv4.h" +#include "lib/string/compat_ctype.h" +#include "lib/string/compat_string.h" +#include "lib/string/parse_int.h" +#include "lib/string/printf.h" +#include "lib/string/util_string.h" + #include "siphash.h"
#ifdef HAVE_SYS_TIME_H @@ -53,9 +61,6 @@ #ifdef HAVE_ERRNO_H #include <errno.h> #endif -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif @@ -1474,7 +1479,10 @@ ip_adapter_addresses_to_smartlist(const IP_ADAPTER_ADDRESSES *addresses) STATIC smartlist_t * get_interface_addresses_win32(int severity, sa_family_t family) { - + /* + XXXX We can assume that this function exists now; we can't + XXXX provide backward compatibility to pre-windows-XP. + */ /* Windows XP began to provide GetAdaptersAddresses. Windows 2000 had a "GetAdaptersInfo", but that's deprecated; let's just try GetAdaptersAddresses and fall back to connect+getsockname. @@ -2087,22 +2095,6 @@ parse_port_range(const char *port, uint16_t *port_min_out, return 0; }
-/** Given an IPv4 in_addr struct *<b>in</b> (in network order, as usual), - * write it as a string into the <b>buf_len</b>-byte buffer in - * <b>buf</b>. Returns a non-negative integer on success. - * Returns -1 on failure. - */ -int -tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len) -{ - uint32_t a = ntohl(in->s_addr); - return tor_snprintf(buf, buf_len, "%d.%d.%d.%d", - (int)(uint8_t)((a>>24)&0xff), - (int)(uint8_t)((a>>16)&0xff), - (int)(uint8_t)((a>>8 )&0xff), - (int)(uint8_t)((a )&0xff)); -} - /** Given a host-order <b>addr</b>, call tor_inet_ntop() on it * and return a strdup of the resulting address. */ diff --git a/src/common/address.h b/src/lib/net/address.h similarity index 97% rename from src/common/address.h rename to src/lib/net/address.h index b95ee364a..78b798755 100644 --- a/src/common/address.h +++ b/src/lib/net/address.h @@ -11,11 +11,22 @@ #ifndef TOR_ADDRESS_H #define TOR_ADDRESS_H
-//#include <sys/sockio.h> #include "orconfig.h" #include "lib/cc/torint.h" -#include "common/compat.h" #include "lib/log/util_bug.h" +#include "lib/net/ipv6.h" +#include "lib/net/nettypes.h" + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + +#include <stddef.h> +#include <stdlib.h>
#ifdef ADDRESS_PRIVATE
@@ -73,6 +84,9 @@ typedef struct tor_addr_port_t
#define TOR_ADDR_NULL {AF_UNSPEC, {0}}
+/* XXXX To do: extract all of the functions here that can possibly invoke + * XXXX resolver, and make sure they have distinctive names. */ + static inline const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a); static inline const struct in6_addr *tor_addr_to_in6_assert( const tor_addr_t *a); @@ -321,9 +335,6 @@ int addr_port_lookup(int severity, const char *addrport, char **address, int parse_port_range(const char *port, uint16_t *port_min_out, uint16_t *port_max_out); int addr_mask_get_bits(uint32_t mask); -/** Length of a buffer to allocate to hold the results of tor_inet_ntoa.*/ -#define INET_NTOA_BUF_LEN 16 -int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len); char *tor_dup_ip(uint32_t addr) ATTR_MALLOC; MOCK_DECL(int,get_interface_address,(int severity, uint32_t *addr)); #define interface_address_list_free(lst)\ diff --git a/src/lib/net/include.am b/src/lib/net/include.am new file mode 100644 index 000000000..6bd829165 --- /dev/null +++ b/src/lib/net/include.am @@ -0,0 +1,26 @@ + +noinst_LIBRARIES += src/lib/libtor-net.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += src/lib/libtor-net-testing.a +endif + +src_lib_libtor_net_a_SOURCES = \ + src/lib/net/address.c \ + src/lib/net/ipv4.c \ + src/lib/net/ipv6.c \ + src/lib/net/resolve.c \ + src/lib/net/socket.c + +src_lib_libtor_net_testing_a_SOURCES = \ + $(src_lib_libtor_net_a_SOURCES) +src_lib_libtor_net_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_lib_libtor_net_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +noinst_HEADERS += \ + src/lib/net/address.h \ + src/lib/net/ipv4.h \ + src/lib/net/ipv6.h \ + src/lib/net/nettypes.h \ + src/lib/net/resolve.h \ + src/lib/net/socket.h diff --git a/src/lib/net/ipv4.c b/src/lib/net/ipv4.c new file mode 100644 index 000000000..18e69761e --- /dev/null +++ b/src/lib/net/ipv4.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "lib/cc/torint.h" +#include "lib/net/ipv4.h" +#include "lib/string/printf.h" +#include "lib/string/scanf.h" + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef _WIN32 +#include <winsock2.h> +#endif + +/** Set *addr to the IP address (in dotted-quad notation) stored in *str. + * Return 1 on success, 0 if *str is badly formatted. + * (Like inet_aton(str,addr), but works on Windows and Solaris.) + */ +int +tor_inet_aton(const char *str, struct in_addr* addr) +{ + unsigned a,b,c,d; + char more; + if (tor_sscanf(str, "%3u.%3u.%3u.%3u%c", &a,&b,&c,&d,&more) != 4) + return 0; + if (a > 255) return 0; + if (b > 255) return 0; + if (c > 255) return 0; + if (d > 255) return 0; + addr->s_addr = htonl((a<<24) | (b<<16) | (c<<8) | d); + return 1; +} + +/** Given an IPv4 in_addr struct *<b>in</b> (in network order, as usual), + * write it as a string into the <b>buf_len</b>-byte buffer in + * <b>buf</b>. Returns a non-negative integer on success. + * Returns -1 on failure. + */ +int +tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len) +{ + uint32_t a = ntohl(in->s_addr); + return tor_snprintf(buf, buf_len, "%d.%d.%d.%d", + (int)(uint8_t)((a>>24)&0xff), + (int)(uint8_t)((a>>16)&0xff), + (int)(uint8_t)((a>>8 )&0xff), + (int)(uint8_t)((a )&0xff)); +} diff --git a/src/lib/net/ipv4.h b/src/lib/net/ipv4.h new file mode 100644 index 000000000..1ccc72997 --- /dev/null +++ b/src/lib/net/ipv4.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_IPV4_H +#define TOR_IPV4_H + +#include <stddef.h> + +struct in_addr; +int tor_inet_aton(const char *str, struct in_addr *addr); +/** Length of a buffer to allocate to hold the results of tor_inet_ntoa.*/ +#define INET_NTOA_BUF_LEN 16 +int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len); + +#endif diff --git a/src/lib/net/ipv6.c b/src/lib/net/ipv6.c new file mode 100644 index 000000000..35d7ddb90 --- /dev/null +++ b/src/lib/net/ipv6.c @@ -0,0 +1,221 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "lib/net/ipv6.h" +#include "lib/net/ipv4.h" +#include "lib/string/util_string.h" +#include "lib/string/compat_string.h" +#include "lib/string/compat_ctype.h" +#include "lib/string/printf.h" +#include "lib/string/scanf.h" +#include "lib/log/util_bug.h" + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#include <stdlib.h> +#include <string.h> + +/** Given <b>af</b>==AF_INET and <b>src</b> a struct in_addr, or + * <b>af</b>==AF_INET6 and <b>src</b> a struct in6_addr, try to format the + * address and store it in the <b>len</b>-byte buffer <b>dst</b>. Returns + * <b>dst</b> on success, NULL on failure. + * + * (Like inet_ntop(af,src,dst,len), but works on platforms that don't have it: + * Tor sometimes needs to format ipv6 addresses even on platforms without ipv6 + * support.) */ +const char * +tor_inet_ntop(int af, const void *src, char *dst, size_t len) +{ + if (af == AF_INET) { + if (tor_inet_ntoa(src, dst, len) < 0) + return NULL; + else + return dst; + } else if (af == AF_INET6) { + const struct in6_addr *addr = src; + char buf[64], *cp; + int longestGapLen = 0, longestGapPos = -1, i, + curGapPos = -1, curGapLen = 0; + uint16_t words[8]; + for (i = 0; i < 8; ++i) { + words[i] = (((uint16_t)addr->s6_addr[2*i])<<8) + addr->s6_addr[2*i+1]; + } + if (words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 && + words[4] == 0 && ((words[5] == 0 && words[6] && words[7]) || + (words[5] == 0xffff))) { + /* This is an IPv4 address. */ + if (words[5] == 0) { + tor_snprintf(buf, sizeof(buf), "::%d.%d.%d.%d", + addr->s6_addr[12], addr->s6_addr[13], + addr->s6_addr[14], addr->s6_addr[15]); + } else { + tor_snprintf(buf, sizeof(buf), "::%x:%d.%d.%d.%d", words[5], + addr->s6_addr[12], addr->s6_addr[13], + addr->s6_addr[14], addr->s6_addr[15]); + } + if ((strlen(buf) + 1) > len) /* +1 for \0 */ + return NULL; + strlcpy(dst, buf, len); + return dst; + } + i = 0; + while (i < 8) { + if (words[i] == 0) { + curGapPos = i++; + curGapLen = 1; + while (i<8 && words[i] == 0) { + ++i; ++curGapLen; + } + if (curGapLen > longestGapLen) { + longestGapPos = curGapPos; + longestGapLen = curGapLen; + } + } else { + ++i; + } + } + if (longestGapLen<=1) + longestGapPos = -1; + + cp = buf; + for (i = 0; i < 8; ++i) { + if (words[i] == 0 && longestGapPos == i) { + if (i == 0) + *cp++ = ':'; + *cp++ = ':'; + while (i < 8 && words[i] == 0) + ++i; + --i; /* to compensate for loop increment. */ + } else { + tor_snprintf(cp, sizeof(buf)-(cp-buf), "%x", (unsigned)words[i]); + cp += strlen(cp); + if (i != 7) + *cp++ = ':'; + } + } + *cp = '\0'; + if ((strlen(buf) + 1) > len) /* +1 for \0 */ + return NULL; + strlcpy(dst, buf, len); + return dst; + } else { + return NULL; + } +} + +/** Given <b>af</b>==AF_INET or <b>af</b>==AF_INET6, and a string <b>src</b> + * encoding an IPv4 address or IPv6 address correspondingly, try to parse the + * address and store the result in <b>dst</b> (which must have space for a + * struct in_addr or a struct in6_addr, as appropriate). Return 1 on success, + * 0 on a bad parse, and -1 on a bad <b>af</b>. + * + * (Like inet_pton(af,src,dst) but works on platforms that don't have it: Tor + * sometimes needs to format ipv6 addresses even on platforms without ipv6 + * support.) */ +int +tor_inet_pton(int af, const char *src, void *dst) +{ + if (af == AF_INET) { + return tor_inet_aton(src, dst); + } else if (af == AF_INET6) { + struct in6_addr *out = dst; + uint16_t words[8]; + int gapPos = -1, i, setWords=0; + const char *dot = strchr(src, '.'); + const char *eow; /* end of words. */ + memset(words, 0xf8, sizeof(words)); + if (dot == src) + return 0; + else if (!dot) + eow = src+strlen(src); + else { + unsigned byte1,byte2,byte3,byte4; + char more; + for (eow = dot-1; eow > src && TOR_ISDIGIT(*eow); --eow) + ; + if (*eow != ':') + return 0; + ++eow; + + /* We use "scanf" because some platform inet_aton()s are too lax + * about IPv4 addresses of the form "1.2.3" */ + if (tor_sscanf(eow, "%3u.%3u.%3u.%3u%c", + &byte1,&byte2,&byte3,&byte4,&more) != 4) + return 0; + + if (byte1 > 255 || byte2 > 255 || byte3 > 255 || byte4 > 255) + return 0; + + words[6] = (byte1<<8) | byte2; + words[7] = (byte3<<8) | byte4; + setWords += 2; + } + + i = 0; + while (src < eow) { + if (i > 7) + return 0; + if (TOR_ISXDIGIT(*src)) { + char *next; + ssize_t len; + long r = strtol(src, &next, 16); + if (next == NULL || next == src) { + /* The 'next == src' error case can happen on versions of openbsd + * which treat "0xfoo" as an error, rather than as "0" followed by + * "xfoo". */ + return 0; + } + + len = *next == '\0' ? eow - src : next - src; + if (len > 4) + return 0; + if (len > 1 && !TOR_ISXDIGIT(src[1])) + return 0; /* 0x is not valid */ + + tor_assert(r >= 0); + tor_assert(r < 65536); + words[i++] = (uint16_t)r; + setWords++; + src = next; + if (*src != ':' && src != eow) + return 0; + ++src; + } else if (*src == ':' && i > 0 && gapPos == -1) { + gapPos = i; + ++src; + } else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' && + gapPos == -1) { + gapPos = i; + src += 2; + } else { + return 0; + } + } + + if (setWords > 8 || + (setWords == 8 && gapPos != -1) || + (setWords < 8 && gapPos == -1)) + return 0; + + if (gapPos >= 0) { + int nToMove = setWords - (dot ? 2 : 0) - gapPos; + int gapLen = 8 - setWords; + tor_assert(nToMove >= 0); + memmove(&words[gapPos+gapLen], &words[gapPos], + sizeof(uint16_t)*nToMove); + memset(&words[gapPos], 0, sizeof(uint16_t)*gapLen); + } + for (i = 0; i < 8; ++i) { + out->s6_addr[2*i ] = words[i] >> 8; + out->s6_addr[2*i+1] = words[i] & 0xff; + } + + return 1; + } else { + return -1; + } +} diff --git a/src/lib/net/ipv6.h b/src/lib/net/ipv6.h new file mode 100644 index 000000000..0a12e046a --- /dev/null +++ b/src/lib/net/ipv6.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_IPV6_H +#define TOR_IPV6_H + +#include "orconfig.h" +#include <stddef.h> +#ifdef HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#include <windows.h> +#endif +#include "lib/cc/torint.h" + +/** Implementation of struct in6_addr for platforms that do not have it. + * Generally, these platforms are ones without IPv6 support, but we want to + * have a working in6_addr there anyway, so we can use it to parse IPv6 + * addresses. */ +#if !defined(HAVE_STRUCT_IN6_ADDR) +struct in6_addr +{ + union { + uint8_t u6_addr8[16]; + uint16_t u6_addr16[8]; + uint32_t u6_addr32[4]; + } in6_u; +#define s6_addr in6_u.u6_addr8 +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +}; +#endif /* !defined(HAVE_STRUCT_IN6_ADDR) */ + +/** @{ */ +/** Many BSD variants seem not to define these. */ +#if defined(__APPLE__) || defined(__darwin__) || \ + defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#ifndef s6_addr16 +#define s6_addr16 __u6_addr.__u6_addr16 +#endif +#ifndef s6_addr32 +#define s6_addr32 __u6_addr.__u6_addr32 +#endif +#endif /* defined(__APPLE__) || defined(__darwin__) || ... */ +/** @} */ + +#ifndef HAVE_SA_FAMILY_T +typedef uint16_t sa_family_t; +#endif + +/** @{ */ +/** Apparently, MS and Solaris don't define s6_addr16 or s6_addr32; these + * macros get you a pointer to s6_addr32 or local equivalent. */ +#ifdef HAVE_STRUCT_IN6_ADDR_S6_ADDR32 +#define S6_ADDR32(x) ((uint32_t*)(x).s6_addr32) +#else +#define S6_ADDR32(x) ((uint32_t*)((char*)&(x).s6_addr)) +#endif +#ifdef HAVE_STRUCT_IN6_ADDR_S6_ADDR16 +#define S6_ADDR16(x) ((uint16_t*)(x).s6_addr16) +#else +#define S6_ADDR16(x) ((uint16_t*)((char*)&(x).s6_addr)) +#endif +/** @} */ + +/** Implementation of struct sockaddr_in6 on platforms that do not have + * it. See notes on struct in6_addr. */ +#if !defined(HAVE_STRUCT_SOCKADDR_IN6) +struct sockaddr_in6 { + sa_family_t sin6_family; + uint16_t sin6_port; + // uint32_t sin6_flowinfo; + struct in6_addr sin6_addr; + // uint32_t sin6_scope_id; +}; +#endif /* !defined(HAVE_STRUCT_SOCKADDR_IN6) */ + +const char *tor_inet_ntop(int af, const void *src, char *dst, size_t len); +int tor_inet_pton(int af, const char *src, void *dst); + +#endif diff --git a/src/lib/net/nettypes.h b/src/lib/net/nettypes.h new file mode 100644 index 000000000..9bae70d5f --- /dev/null +++ b/src/lib/net/nettypes.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_NET_TYPES_H +#define TOR_NET_TYPES_H + +#include "orconfig.h" +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#if (SIZEOF_SOCKLEN_T == 0) +typedef int socklen_t; +#endif + +#ifdef _WIN32 +/* XXX Actually, this should arguably be SOCKET; we use intptr_t here so that + * any inadvertent checks for the socket being <= 0 or > 0 will probably + * still work. */ +#define tor_socket_t intptr_t +#define TOR_SOCKET_T_FORMAT INTPTR_T_FORMAT +#define SOCKET_OK(s) ((SOCKET)(s) != INVALID_SOCKET) +#define TOR_INVALID_SOCKET INVALID_SOCKET +#else /* !(defined(_WIN32)) */ +/** Type used for a network socket. */ +#define tor_socket_t int +#define TOR_SOCKET_T_FORMAT "%d" +/** Macro: true iff 's' is a possible value for a valid initialized socket. */ +#define SOCKET_OK(s) ((s) >= 0) +/** Error/uninitialized value for a tor_socket_t. */ +#define TOR_INVALID_SOCKET (-1) +#endif /* defined(_WIN32) */ + +#endif diff --git a/src/lib/net/resolve.c b/src/lib/net/resolve.c new file mode 100644 index 000000000..fefd5cceb --- /dev/null +++ b/src/lib/net/resolve.c @@ -0,0 +1,236 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "lib/net/resolve.h" +#include "lib/net/address.h" +#include "lib/malloc/util_malloc.h" + +#include "siphash.h" +#include "ht.h" + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif + +#include <string.h> + +/** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set + * *<b>addr</b> to the proper IP address, in host byte order. Returns 0 + * on success, -1 on failure; 1 on transient failure. + * + * (This function exists because standard windows gethostbyname + * doesn't treat raw IP addresses properly.) + */ + +MOCK_IMPL(int, +tor_lookup_hostname,(const char *name, uint32_t *addr)) +{ + tor_addr_t myaddr; + int ret; + + if ((ret = tor_addr_lookup(name, AF_INET, &myaddr))) + return ret; + + if (tor_addr_family(&myaddr) == AF_INET) { + *addr = tor_addr_to_ipv4h(&myaddr); + return ret; + } + + return -1; +} + +#ifdef USE_SANDBOX_GETADDRINFO +/** True if we should only return cached values */ +static int sandbox_getaddrinfo_is_active = 0; + +/** Cache entry for getaddrinfo results; used when sandboxing is implemented + * so that we can consult the cache when the sandbox prevents us from doing + * getaddrinfo. + * + * We support only a limited range of getaddrinfo calls, where servname is null + * and hints contains only socktype=SOCK_STREAM, family in INET,INET6,UNSPEC. + */ +typedef struct cached_getaddrinfo_item_t { + HT_ENTRY(cached_getaddrinfo_item_t) node; + char *name; + int family; + /** set if no error; otherwise NULL */ + struct addrinfo *res; + /** 0 for no error; otherwise an EAI_* value */ + int err; +} cached_getaddrinfo_item_t; + +static unsigned +cached_getaddrinfo_item_hash(const cached_getaddrinfo_item_t *item) +{ + return (unsigned)siphash24g(item->name, strlen(item->name)) + item->family; +} + +static unsigned +cached_getaddrinfo_items_eq(const cached_getaddrinfo_item_t *a, + const cached_getaddrinfo_item_t *b) +{ + return (a->family == b->family) && 0 == strcmp(a->name, b->name); +} + +#define cached_getaddrinfo_item_free(item) \ + FREE_AND_NULL(cached_getaddrinfo_item_t, \ + cached_getaddrinfo_item_free_, (item)) + +static void +cached_getaddrinfo_item_free_(cached_getaddrinfo_item_t *item) +{ + if (item == NULL) + return; + + tor_free(item->name); + if (item->res) + freeaddrinfo(item->res); + tor_free(item); +} + +static HT_HEAD(getaddrinfo_cache, cached_getaddrinfo_item_t) + getaddrinfo_cache = HT_INITIALIZER(); + +HT_PROTOTYPE(getaddrinfo_cache, cached_getaddrinfo_item_t, node, + cached_getaddrinfo_item_hash, + cached_getaddrinfo_items_eq) +HT_GENERATE2(getaddrinfo_cache, cached_getaddrinfo_item_t, node, + cached_getaddrinfo_item_hash, + cached_getaddrinfo_items_eq, + 0.6, tor_reallocarray_, tor_free_) + +/** If true, don't try to cache getaddrinfo results. */ +static int sandbox_getaddrinfo_cache_disabled = 0; + +/** Tell the sandbox layer not to try to cache getaddrinfo results. Used as in + * tor-resolve, when we have no intention of initializing crypto or of + * installing the sandbox.*/ +void +sandbox_disable_getaddrinfo_cache(void) +{ + sandbox_getaddrinfo_cache_disabled = 1; +} + +void +sandbox_freeaddrinfo(struct addrinfo *ai) +{ + if (sandbox_getaddrinfo_cache_disabled) + freeaddrinfo(ai); +} + +int +sandbox_getaddrinfo(const char *name, const char *servname, + const struct addrinfo *hints, + struct addrinfo **res) +{ + int err; + struct cached_getaddrinfo_item_t search, *item; + + if (sandbox_getaddrinfo_cache_disabled) { + return getaddrinfo(name, NULL, hints, res); + } + + if (servname != NULL) { + log_warn(LD_BUG, "called with non-NULL servname"); + return EAI_NONAME; + } + if (name == NULL) { + log_warn(LD_BUG, "called with NULL name"); + return EAI_NONAME; + } + + *res = NULL; + + memset(&search, 0, sizeof(search)); + search.name = (char *) name; + search.family = hints ? hints->ai_family : AF_UNSPEC; + item = HT_FIND(getaddrinfo_cache, &getaddrinfo_cache, &search); + + if (! sandbox_getaddrinfo_is_active) { + /* If the sandbox is not turned on yet, then getaddrinfo and store the + result. */ + + err = getaddrinfo(name, NULL, hints, res); + log_info(LD_NET,"(Sandbox) getaddrinfo %s.", err ? "failed" : "succeeded"); + + if (! item) { + item = tor_malloc_zero(sizeof(*item)); + item->name = tor_strdup(name); + item->family = hints ? hints->ai_family : AF_UNSPEC; + HT_INSERT(getaddrinfo_cache, &getaddrinfo_cache, item); + } + + if (item->res) { + freeaddrinfo(item->res); + item->res = NULL; + } + item->res = *res; + item->err = err; + return err; + } + + /* Otherwise, the sandbox is on. If we have an item, yield its cached + result. */ + if (item) { + *res = item->res; + return item->err; + } + + /* getting here means something went wrong */ + log_err(LD_BUG,"(Sandbox) failed to get address %s!", name); + return EAI_NONAME; +} + +int +sandbox_add_addrinfo(const char *name) +{ + struct addrinfo *res; + struct addrinfo hints; + int i; + static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC }; + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + for (i = 0; i < 3; ++i) { + hints.ai_family = families[i]; + + res = NULL; + (void) sandbox_getaddrinfo(name, NULL, &hints, &res); + if (res) + sandbox_freeaddrinfo(res); + } + + return 0; +} + +void +sandbox_free_getaddrinfo_cache(void) +{ + cached_getaddrinfo_item_t **next, **item, *this; + + for (item = HT_START(getaddrinfo_cache, &getaddrinfo_cache); + item; + item = next) { + this = *item; + next = HT_NEXT_RMV(getaddrinfo_cache, &getaddrinfo_cache, item); + cached_getaddrinfo_item_free(this); + } + + HT_CLEAR(getaddrinfo_cache, &getaddrinfo_cache); +} + +void +sandbox_make_getaddrinfo_cache_active(void) +{ + sandbox_getaddrinfo_is_active = 1; +} +#endif diff --git a/src/lib/net/resolve.h b/src/lib/net/resolve.h new file mode 100644 index 000000000..a225be87f --- /dev/null +++ b/src/lib/net/resolve.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_RESOLVE_H +#define TOR_RESOLVE_H + +#include "orconfig.h" +#include "lib/cc/torint.h" +#include "lib/testsupport/testsupport.h" +#ifdef _WIN32 +#include <winsock2.h> +#endif + +#if defined(HAVE_SECCOMP_H) && defined(__linux__) +#define USE_SANDBOX_GETADDRINFO +#endif + +MOCK_DECL(int,tor_lookup_hostname,(const char *name, uint32_t *addr)); + +struct addrinfo; +#ifdef USE_SANDBOX_GETADDRINFO +/** Pre-calls getaddrinfo in order to pre-record result. */ +int sandbox_add_addrinfo(const char *addr); + +// XXXX rename these. They are named as though they were sandbox-only, +// XXXX but in fact they're the only allowed entry point to getaddrinfo. +// XXXX They don't invoke the sandbox code; they only have an internal cache. +struct addrinfo; +/** Replacement for getaddrinfo(), using pre-recorded results. */ +int sandbox_getaddrinfo(const char *name, const char *servname, + const struct addrinfo *hints, + struct addrinfo **res); +void sandbox_freeaddrinfo(struct addrinfo *addrinfo); +void sandbox_free_getaddrinfo_cache(void); +void sandbox_make_getaddrinfo_cache_active(void); +#else /* !(defined(USE_SANDBOX_GETADDRINFO)) */ +#define sandbox_getaddrinfo(name, servname, hints, res) \ + getaddrinfo((name),(servname), (hints),(res)) +#define sandbox_add_addrinfo(name) \ + ((void)(name)) +#define sandbox_freeaddrinfo(addrinfo) \ + freeaddrinfo((addrinfo)) +#define sandbox_free_getaddrinfo_cache() +#endif /* defined(USE_SANDBOX_GETADDRINFO) */ + +void sandbox_disable_getaddrinfo_cache(void); + +#endif diff --git a/src/lib/net/socket.c b/src/lib/net/socket.c new file mode 100644 index 000000000..4c14a4289 --- /dev/null +++ b/src/lib/net/socket.c @@ -0,0 +1,649 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define SOCKET_PRIVATE +#include "lib/net/socket.h" +#include "lib/net/address.h" +#include "lib/cc/compat_compiler.h" +#include "lib/lock/compat_mutex.h" +#include "lib/log/torlog.h" +#include "lib/log/util_bug.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#include <stddef.h> +#include <string.h> + +/* When set_max_file_sockets() is called, update this with the max file + * descriptor value so we can use it to check the limit when opening a new + * socket. Default value is what Debian sets as the default hard limit. */ +static int max_sockets = 1024; + +/** Return the maximum number of allowed sockets. */ +int +get_max_sockets(void) +{ + return max_sockets; +} + +/** Set the maximum number of allowed sockets to <b>n</b> */ +void +set_max_sockets(int n) +{ + max_sockets = n; +} + +#undef DEBUG_SOCKET_COUNTING +#ifdef DEBUG_SOCKET_COUNTING +#include "lib/container/bitarray.h" + +/** A bitarray of all fds that should be passed to tor_socket_close(). Only + * used if DEBUG_SOCKET_COUNTING is defined. */ +static bitarray_t *open_sockets = NULL; +/** The size of <b>open_sockets</b>, in bits. */ +static int max_socket = -1; +#endif /* defined(DEBUG_SOCKET_COUNTING) */ + +/** Count of number of sockets currently open. (Undercounts sockets opened by + * eventdns and libevent.) */ +static int n_sockets_open = 0; + +/** Mutex to protect open_sockets, max_socket, and n_sockets_open. */ +static tor_mutex_t *socket_accounting_mutex = NULL; + +/** Helper: acquire the socket accounting lock. */ +static inline void +socket_accounting_lock(void) +{ + if (PREDICT_UNLIKELY(!socket_accounting_mutex)) + socket_accounting_mutex = tor_mutex_new(); + tor_mutex_acquire(socket_accounting_mutex); +} + +/** Helper: release the socket accounting lock. */ +static inline void +socket_accounting_unlock(void) +{ + tor_mutex_release(socket_accounting_mutex); +} + +/** As close(), but guaranteed to work for sockets across platforms (including + * Windows, where close()ing a socket doesn't work. Returns 0 on success and + * the socket error code on failure. */ +int +tor_close_socket_simple(tor_socket_t s) +{ + int r = 0; + + /* On Windows, you have to call close() on fds returned by open(), + * and closesocket() on fds returned by socket(). On Unix, everything + * gets close()'d. We abstract this difference by always using + * tor_close_socket to close sockets, and always using close() on + * files. + */ + #if defined(_WIN32) + r = closesocket(s); + #else + r = close(s); + #endif + + if (r != 0) { + int err = tor_socket_errno(-1); + log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err)); + return err; + } + + return r; +} + +/** As tor_close_socket_simple(), but keeps track of the number + * of open sockets. Returns 0 on success, -1 on failure. */ +MOCK_IMPL(int, +tor_close_socket,(tor_socket_t s)) +{ + int r = tor_close_socket_simple(s); + + socket_accounting_lock(); +#ifdef DEBUG_SOCKET_COUNTING + if (s > max_socket || ! bitarray_is_set(open_sockets, s)) { + log_warn(LD_BUG, "Closing a socket (%d) that wasn't returned by tor_open_" + "socket(), or that was already closed or something.", s); + } else { + tor_assert(open_sockets && s <= max_socket); + bitarray_clear(open_sockets, s); + } +#endif /* defined(DEBUG_SOCKET_COUNTING) */ + if (r == 0) { + --n_sockets_open; + } else { +#ifdef _WIN32 + if (r != WSAENOTSOCK) + --n_sockets_open; +#else + if (r != EBADF) + --n_sockets_open; // LCOV_EXCL_LINE -- EIO and EINTR too hard to force. +#endif /* defined(_WIN32) */ + r = -1; + } + + tor_assert_nonfatal(n_sockets_open >= 0); + socket_accounting_unlock(); + return r; +} + +/** @{ */ +#ifdef DEBUG_SOCKET_COUNTING +/** Helper: if DEBUG_SOCKET_COUNTING is enabled, remember that <b>s</b> is + * now an open socket. */ +static inline void +mark_socket_open(tor_socket_t s) +{ + /* XXXX This bitarray business will NOT work on windows: sockets aren't + small ints there. */ + if (s > max_socket) { + if (max_socket == -1) { + open_sockets = bitarray_init_zero(s+128); + max_socket = s+128; + } else { + open_sockets = bitarray_expand(open_sockets, max_socket, s+128); + max_socket = s+128; + } + } + if (bitarray_is_set(open_sockets, s)) { + log_warn(LD_BUG, "I thought that %d was already open, but socket() just " + "gave it to me!", s); + } + bitarray_set(open_sockets, s); +} +#else /* !(defined(DEBUG_SOCKET_COUNTING)) */ +#define mark_socket_open(s) ((void) (s)) +#endif /* defined(DEBUG_SOCKET_COUNTING) */ +/** @} */ + +/** As socket(), but counts the number of open sockets. */ +MOCK_IMPL(tor_socket_t, +tor_open_socket,(int domain, int type, int protocol)) +{ + return tor_open_socket_with_extensions(domain, type, protocol, 1, 0); +} + +/** Mockable wrapper for connect(). */ +MOCK_IMPL(tor_socket_t, +tor_connect_socket,(tor_socket_t sock, const struct sockaddr *address, + socklen_t address_len)) +{ + return connect(sock,address,address_len); +} + +/** As socket(), but creates a nonblocking socket and + * counts the number of open sockets. */ +tor_socket_t +tor_open_socket_nonblocking(int domain, int type, int protocol) +{ + return tor_open_socket_with_extensions(domain, type, protocol, 1, 1); +} + +/** As socket(), but counts the number of open sockets and handles + * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. + * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate + * if the corresponding extension should be used.*/ +tor_socket_t +tor_open_socket_with_extensions(int domain, int type, int protocol, + int cloexec, int nonblock) +{ + tor_socket_t s; + + /* We are about to create a new file descriptor so make sure we have + * enough of them. */ + if (get_n_open_sockets() >= max_sockets - 1) { +#ifdef _WIN32 + WSASetLastError(WSAEMFILE); +#else + errno = EMFILE; +#endif + return TOR_INVALID_SOCKET; + } + +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | + (nonblock ? SOCK_NONBLOCK : 0); + s = socket(domain, type|ext_flags, protocol); + if (SOCKET_OK(s)) + goto socket_ok; + /* If we got an error, see if it is EINVAL. EINVAL might indicate that, + * even though we were built on a system with SOCK_CLOEXEC and SOCK_NONBLOCK + * support, we are running on one without. */ + if (errno != EINVAL) + return s; +#endif /* defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) */ + + s = socket(domain, type, protocol); + if (! SOCKET_OK(s)) + return s; + +#if defined(FD_CLOEXEC) + if (cloexec) { + if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { + log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } +#else /* !(defined(FD_CLOEXEC)) */ + (void)cloexec; +#endif /* defined(FD_CLOEXEC) */ + + if (nonblock) { + if (set_socket_nonblocking(s) == -1) { + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } + + goto socket_ok; /* So that socket_ok will not be unused. */ + + socket_ok: + tor_take_socket_ownership(s); + return s; +} + +/** + * For socket accounting: remember that we are the owner of the socket + * <b>s</b>. This will prevent us from overallocating sockets, and prevent us + * from asserting later when we close the socket <b>s</b>. + */ +void +tor_take_socket_ownership(tor_socket_t s) +{ + socket_accounting_lock(); + ++n_sockets_open; + mark_socket_open(s); + socket_accounting_unlock(); +} + +/** As accept(), but counts the number of open sockets. */ +tor_socket_t +tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) +{ + return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 0); +} + +/** As accept(), but returns a nonblocking socket and + * counts the number of open sockets. */ +tor_socket_t +tor_accept_socket_nonblocking(tor_socket_t sockfd, struct sockaddr *addr, + socklen_t *len) +{ + return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 1); +} + +/** As accept(), but counts the number of open sockets and handles + * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. + * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate + * if the corresponding extension should be used.*/ +tor_socket_t +tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr, + socklen_t *len, int cloexec, int nonblock) +{ + tor_socket_t s; + + /* We are about to create a new file descriptor so make sure we have + * enough of them. */ + if (get_n_open_sockets() >= max_sockets - 1) { +#ifdef _WIN32 + WSASetLastError(WSAEMFILE); +#else + errno = EMFILE; +#endif + return TOR_INVALID_SOCKET; + } + +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) \ + && defined(SOCK_NONBLOCK) + int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | + (nonblock ? SOCK_NONBLOCK : 0); + s = accept4(sockfd, addr, len, ext_flags); + if (SOCKET_OK(s)) + goto socket_ok; + /* If we got an error, see if it is ENOSYS. ENOSYS indicates that, + * even though we were built on a system with accept4 support, we + * are running on one without. Also, check for EINVAL, which indicates that + * we are missing SOCK_CLOEXEC/SOCK_NONBLOCK support. */ + if (errno != EINVAL && errno != ENOSYS) + return s; +#endif /* defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) ... */ + + s = accept(sockfd, addr, len); + if (!SOCKET_OK(s)) + return s; + +#if defined(FD_CLOEXEC) + if (cloexec) { + if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { + log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno)); + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } +#else /* !(defined(FD_CLOEXEC)) */ + (void)cloexec; +#endif /* defined(FD_CLOEXEC) */ + + if (nonblock) { + if (set_socket_nonblocking(s) == -1) { + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } + + goto socket_ok; /* So that socket_ok will not be unused. */ + + socket_ok: + tor_take_socket_ownership(s); + return s; +} + +/** Return the number of sockets we currently have opened. */ +int +get_n_open_sockets(void) +{ + int n; + socket_accounting_lock(); + n = n_sockets_open; + socket_accounting_unlock(); + return n; +} + +/** + * Allocate a pair of connected sockets. (Like socketpair(family, + * type,protocol,fd), but works on systems that don't have + * socketpair.) + * + * Currently, only (AF_UNIX, SOCK_STREAM, 0) sockets are supported. + * + * Note that on systems without socketpair, this call will fail if + * localhost is inaccessible (for example, if the networking + * stack is down). And even if it succeeds, the socket pair will not + * be able to read while localhost is down later (the socket pair may + * even close, depending on OS-specific timeouts). + * + * Returns 0 on success and -errno on failure; do not rely on the value + * of errno or WSAGetLastError(). + **/ +/* It would be nicer just to set errno, but that won't work for windows. */ +int +tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) +{ +//don't use win32 socketpairs (they are always bad) +#if defined(HAVE_SOCKETPAIR) && !defined(_WIN32) + int r; + +#ifdef SOCK_CLOEXEC + r = socketpair(family, type|SOCK_CLOEXEC, protocol, fd); + if (r == 0) + goto sockets_ok; + /* If we got an error, see if it is EINVAL. EINVAL might indicate that, + * even though we were built on a system with SOCK_CLOEXEC support, we + * are running on one without. */ + if (errno != EINVAL) + return -errno; +#endif /* defined(SOCK_CLOEXEC) */ + + r = socketpair(family, type, protocol, fd); + if (r < 0) + return -errno; + +#if defined(FD_CLOEXEC) + if (SOCKET_OK(fd[0])) { + r = fcntl(fd[0], F_SETFD, FD_CLOEXEC); + if (r == -1) { + close(fd[0]); + close(fd[1]); + return -errno; + } + } + if (SOCKET_OK(fd[1])) { + r = fcntl(fd[1], F_SETFD, FD_CLOEXEC); + if (r == -1) { + close(fd[0]); + close(fd[1]); + return -errno; + } + } +#endif /* defined(FD_CLOEXEC) */ + goto sockets_ok; /* So that sockets_ok will not be unused. */ + + sockets_ok: + socket_accounting_lock(); + if (SOCKET_OK(fd[0])) { + ++n_sockets_open; + mark_socket_open(fd[0]); + } + if (SOCKET_OK(fd[1])) { + ++n_sockets_open; + mark_socket_open(fd[1]); + } + socket_accounting_unlock(); + + return 0; +#else /* !(defined(HAVE_SOCKETPAIR) && !defined(_WIN32)) */ + return tor_ersatz_socketpair(family, type, protocol, fd); +#endif /* defined(HAVE_SOCKETPAIR) && !defined(_WIN32) */ +} + +#ifdef NEED_ERSATZ_SOCKETPAIR + +static inline socklen_t +SIZEOF_SOCKADDR(int domain) +{ + switch (domain) { + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + default: + return 0; + } +} + +/** + * Helper used to implement socketpair on systems that lack it, by + * making a direct connection to localhost. + */ +STATIC int +tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) +{ + /* This socketpair does not work when localhost is down. So + * it's really not the same thing at all. But it's close enough + * for now, and really, when localhost is down sometimes, we + * have other problems too. + */ + tor_socket_t listener = TOR_INVALID_SOCKET; + tor_socket_t connector = TOR_INVALID_SOCKET; + tor_socket_t acceptor = TOR_INVALID_SOCKET; + tor_addr_t listen_tor_addr; + struct sockaddr_storage connect_addr_ss, listen_addr_ss; + struct sockaddr *listen_addr = (struct sockaddr *) &listen_addr_ss; + uint16_t listen_port = 0; + tor_addr_t connect_tor_addr; + uint16_t connect_port = 0; + struct sockaddr *connect_addr = (struct sockaddr *) &connect_addr_ss; + socklen_t size; + int saved_errno = -1; + int ersatz_domain = AF_INET; + + memset(&connect_tor_addr, 0, sizeof(connect_tor_addr)); + memset(&connect_addr_ss, 0, sizeof(connect_addr_ss)); + memset(&listen_tor_addr, 0, sizeof(listen_tor_addr)); + memset(&listen_addr_ss, 0, sizeof(listen_addr_ss)); + + if (protocol +#ifdef AF_UNIX + || family != AF_UNIX +#endif + ) { +#ifdef _WIN32 + return -WSAEAFNOSUPPORT; +#else + return -EAFNOSUPPORT; +#endif + } + if (!fd) { + return -EINVAL; + } + + listener = tor_open_socket(ersatz_domain, type, 0); + if (!SOCKET_OK(listener)) { + int first_errno = tor_socket_errno(-1); + if (first_errno == SOCK_ERRNO(EPROTONOSUPPORT) + && ersatz_domain == AF_INET) { + /* Assume we're on an IPv6-only system */ + ersatz_domain = AF_INET6; + listener = tor_open_socket(ersatz_domain, type, 0); + if (!SOCKET_OK(listener)) { + /* Keep the previous behaviour, which was to return the IPv4 error. + * (This may be less informative on IPv6-only systems.) + * XX/teor - is there a better way to decide which errno to return? + * (I doubt we care much either way, once there is an error.) + */ + return -first_errno; + } + } + } + /* If there is no 127.0.0.1 or ::1, this will and must fail. Otherwise, we + * risk exposing a socketpair on a routable IP address. (Some BSD jails + * use a routable address for localhost. Fortunately, they have the real + * AF_UNIX socketpair.) */ + if (ersatz_domain == AF_INET) { + tor_addr_from_ipv4h(&listen_tor_addr, INADDR_LOOPBACK); + } else { + tor_addr_parse(&listen_tor_addr, "[::1]"); + } + tor_assert(tor_addr_is_loopback(&listen_tor_addr)); + size = tor_addr_to_sockaddr(&listen_tor_addr, + 0 /* kernel chooses port. */, + listen_addr, + sizeof(listen_addr_ss)); + if (bind(listener, listen_addr, size) == -1) + goto tidy_up_and_fail; + if (listen(listener, 1) == -1) + goto tidy_up_and_fail; + + connector = tor_open_socket(ersatz_domain, type, 0); + if (!SOCKET_OK(connector)) + goto tidy_up_and_fail; + /* We want to find out the port number to connect to. */ + size = sizeof(connect_addr_ss); + if (getsockname(listener, connect_addr, &size) == -1) + goto tidy_up_and_fail; + if (size != SIZEOF_SOCKADDR (connect_addr->sa_family)) + goto abort_tidy_up_and_fail; + if (connect(connector, connect_addr, size) == -1) + goto tidy_up_and_fail; + + size = sizeof(listen_addr_ss); + acceptor = tor_accept_socket(listener, listen_addr, &size); + if (!SOCKET_OK(acceptor)) + goto tidy_up_and_fail; + if (size != SIZEOF_SOCKADDR(listen_addr->sa_family)) + goto abort_tidy_up_and_fail; + /* Now check we are talking to ourself by matching port and host on the + two sockets. */ + if (getsockname(connector, connect_addr, &size) == -1) + goto tidy_up_and_fail; + /* Set *_tor_addr and *_port to the address and port that was used */ + tor_addr_from_sockaddr(&listen_tor_addr, listen_addr, &listen_port); + tor_addr_from_sockaddr(&connect_tor_addr, connect_addr, &connect_port); + if (size != SIZEOF_SOCKADDR (connect_addr->sa_family) + || tor_addr_compare(&listen_tor_addr, &connect_tor_addr, CMP_SEMANTIC) + || listen_port != connect_port) { + goto abort_tidy_up_and_fail; + } + tor_close_socket(listener); + fd[0] = connector; + fd[1] = acceptor; + + return 0; + + abort_tidy_up_and_fail: +#ifdef _WIN32 + saved_errno = WSAECONNABORTED; +#else + saved_errno = ECONNABORTED; /* I hope this is portable and appropriate. */ +#endif + tidy_up_and_fail: + if (saved_errno < 0) + saved_errno = errno; + if (SOCKET_OK(listener)) + tor_close_socket(listener); + if (SOCKET_OK(connector)) + tor_close_socket(connector); + if (SOCKET_OK(acceptor)) + tor_close_socket(acceptor); + return -saved_errno; +} + +#endif /* defined(NEED_ERSATZ_SOCKETPAIR) */ + +/** Mockable wrapper for getsockname(). */ +MOCK_IMPL(int, +tor_getsockname,(tor_socket_t sock, struct sockaddr *address, + socklen_t *address_len)) +{ + return getsockname(sock, address, address_len); +} + +/** + * Find the local address associated with the socket <b>sock</b>, and + * place it in *<b>addr_out</b>. Return 0 on success, -1 on failure. + * + * (As tor_getsockname, but instead places the result in a tor_addr_t.) */ +int +tor_addr_from_getsockname(struct tor_addr_t *addr_out, tor_socket_t sock) +{ + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); + memset(&ss, 0, sizeof(ss)); + + if (tor_getsockname(sock, (struct sockaddr *) &ss, &ss_len) < 0) + return -1; + + return tor_addr_from_sockaddr(addr_out, (struct sockaddr *)&ss, NULL); +} + +/** Turn <b>socket</b> into a nonblocking socket. Return 0 on success, -1 + * on failure. + */ +int +set_socket_nonblocking(tor_socket_t sock) +{ +#if defined(_WIN32) + unsigned long nonblocking = 1; + ioctlsocket(sock, FIONBIO, (unsigned long*) &nonblocking); +#else + int flags; + + flags = fcntl(sock, F_GETFL, 0); + if (flags == -1) { + log_warn(LD_NET, "Couldn't get file status flags: %s", strerror(errno)); + return -1; + } + flags |= O_NONBLOCK; + if (fcntl(sock, F_SETFL, flags) == -1) { + log_warn(LD_NET, "Couldn't set file status flags: %s", strerror(errno)); + return -1; + } +#endif /* defined(_WIN32) */ + + return 0; +} diff --git a/src/lib/net/socket.h b/src/lib/net/socket.h new file mode 100644 index 000000000..93af92983 --- /dev/null +++ b/src/lib/net/socket.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_SOCKET_H +#define TOR_SOCKET_H + +#include "orconfig.h" +#include "lib/cc/torint.h" +#include "lib/net/nettypes.h" +#include "lib/testsupport/testsupport.h" + +#include <errno.h> + +struct sockaddr; + +int tor_close_socket_simple(tor_socket_t s); +MOCK_DECL(int, tor_close_socket, (tor_socket_t s)); +void tor_take_socket_ownership(tor_socket_t s); +tor_socket_t tor_open_socket_with_extensions( + int domain, int type, int protocol, + int cloexec, int nonblock); +MOCK_DECL(tor_socket_t,tor_open_socket,(int domain, int type, int protocol)); +tor_socket_t tor_open_socket_nonblocking(int domain, int type, int protocol); +tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, + socklen_t *len); +tor_socket_t tor_accept_socket_nonblocking(tor_socket_t sockfd, + struct sockaddr *addr, + socklen_t *len); +tor_socket_t tor_accept_socket_with_extensions(tor_socket_t sockfd, + struct sockaddr *addr, + socklen_t *len, + int cloexec, int nonblock); +MOCK_DECL(tor_socket_t, tor_connect_socket,(tor_socket_t socket, + const struct sockaddr *address, + socklen_t address_len)); +int get_n_open_sockets(void); + +MOCK_DECL(int,tor_getsockname,(tor_socket_t socket, struct sockaddr *address, + socklen_t *address_len)); +struct tor_addr_t; +int tor_addr_from_getsockname(struct tor_addr_t *addr_out, tor_socket_t sock); + +#define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags) +#define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags) + +int set_socket_nonblocking(tor_socket_t socket); +int tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]); +int network_init(void); + +int get_max_sockets(void); +void set_max_sockets(int); + +/* For stupid historical reasons, windows sockets have an independent + * set of errnos, and an independent way to get them. Also, you can't + * always believe WSAEWOULDBLOCK. Use the macros below to compare + * errnos against expected values, and use tor_socket_errno to find + * the actual errno after a socket operation fails. + */ +#if defined(_WIN32) +/** Expands to WSA<b>e</b> on Windows, and to <b>e</b> elsewhere. */ +#define SOCK_ERRNO(e) WSA##e +/** Return true if e is EAGAIN or the local equivalent. */ +#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == WSAEWOULDBLOCK) +/** Return true if e is EINPROGRESS or the local equivalent. */ +#define ERRNO_IS_EINPROGRESS(e) ((e) == WSAEINPROGRESS) +/** Return true if e is EINPROGRESS or the local equivalent as returned by + * a call to connect(). */ +#define ERRNO_IS_CONN_EINPROGRESS(e) \ + ((e) == WSAEINPROGRESS || (e)== WSAEINVAL || (e) == WSAEWOULDBLOCK) +/** Return true if e is EAGAIN or another error indicating that a call to + * accept() has no pending connections to return. */ +#define ERRNO_IS_ACCEPT_EAGAIN(e) ERRNO_IS_EAGAIN(e) +/** Return true if e is EMFILE or another error indicating that a call to + * accept() has failed because we're out of fds or something. */ +#define ERRNO_IS_RESOURCE_LIMIT(e) \ + ((e) == WSAEMFILE || (e) == WSAENOBUFS) +/** Return true if e is EADDRINUSE or the local equivalent. */ +#define ERRNO_IS_EADDRINUSE(e) ((e) == WSAEADDRINUSE) +/** Return true if e is EINTR or the local equivalent */ +#define ERRNO_IS_EINTR(e) ((e) == WSAEINTR || 0) +int tor_socket_errno(tor_socket_t sock); +const char *tor_socket_strerror(int e); +#else /* !(defined(_WIN32)) */ +#define SOCK_ERRNO(e) e +#if EAGAIN == EWOULDBLOCK +/* || 0 is for -Wparentheses-equality (-Wall?) appeasement under clang */ +#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || 0) +#else +#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == EWOULDBLOCK) +#endif /* EAGAIN == EWOULDBLOCK */ +#define ERRNO_IS_EINTR(e) ((e) == EINTR || 0) +#define ERRNO_IS_EINPROGRESS(e) ((e) == EINPROGRESS || 0) +#define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == EINPROGRESS || 0) +#define ERRNO_IS_ACCEPT_EAGAIN(e) \ + (ERRNO_IS_EAGAIN(e) || (e) == ECONNABORTED) +#define ERRNO_IS_RESOURCE_LIMIT(e) \ + ((e) == EMFILE || (e) == ENFILE || (e) == ENOBUFS || (e) == ENOMEM) +#define ERRNO_IS_EADDRINUSE(e) (((e) == EADDRINUSE) || 0) +#define tor_socket_errno(sock) (errno) +#define tor_socket_strerror(e) strerror(e) +#endif /* defined(_WIN32) */ + +#ifdef SOCKET_PRIVATE +#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS) +#define NEED_ERSATZ_SOCKETPAIR +STATIC int tor_ersatz_socketpair(int family, int type, int protocol, + tor_socket_t fd[2]); +#endif +#endif /* defined(COMPAT_PRIVATE) */ + +#endif diff --git a/src/rust/build.rs b/src/rust/build.rs index b43fbdceb..b51a87ab1 100644 --- a/src/rust/build.rs +++ b/src/rust/build.rs @@ -151,6 +151,7 @@ pub fn main() { // moving forward! cfg.component("tor-crypt-ops-testing"); cfg.component("or-testing"); + cfg.component("tor-net"); cfg.component("tor-log"); cfg.component("tor-lock"); cfg.component("tor-fdio"); diff --git a/src/test/test_util.c b/src/test/test_util.c index 41570e14d..0c2febd46 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -9,6 +9,7 @@ #define CONTROL_PRIVATE #define UTIL_PRIVATE #define UTIL_MALLOC_PRIVATE +#define SOCKET_PRIVATE #include "or/or.h" #include "common/buffers.h" #include "or/config.h"