From 2884639ad63583d2a5a3dd14185c77212e566b5f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Aug 2018 08:47:27 -0400 Subject: [PATCH 1/9] Extract tor_ersatz_socketpair into a new c file I'm doing this because I want to make it a lower-level function again, so that we can use it without linking in the rest of the universe. --- src/lib/net/include.am | 4 +- src/lib/net/socket.c | 155 +----------------------------------- src/lib/net/socket.h | 8 -- src/lib/net/socketpair.c | 164 +++++++++++++++++++++++++++++++++++++++ src/lib/net/socketpair.h | 19 +++++ src/test/test_util.c | 1 + 6 files changed, 188 insertions(+), 163 deletions(-) create mode 100644 src/lib/net/socketpair.c create mode 100644 src/lib/net/socketpair.h diff --git a/src/lib/net/include.am b/src/lib/net/include.am index 67db0d5af2..ff0967e786 100644 --- a/src/lib/net/include.am +++ b/src/lib/net/include.am @@ -12,7 +12,8 @@ src_lib_libtor_net_a_SOURCES = \ src/lib/net/gethostname.c \ src/lib/net/inaddr.c \ src/lib/net/resolve.c \ - src/lib/net/socket.c + src/lib/net/socket.c \ + src/lib/net/socketpair.c src_lib_libtor_net_testing_a_SOURCES = \ $(src_lib_libtor_net_a_SOURCES) @@ -29,4 +30,5 @@ noinst_HEADERS += \ src/lib/net/nettypes.h \ src/lib/net/resolve.h \ src/lib/net/socket.h \ + src/lib/net/socketpair.h \ src/lib/net/socks5_status.h diff --git a/src/lib/net/socket.c b/src/lib/net/socket.c index 5847bb8734..5487b8d9df 100644 --- a/src/lib/net/socket.c +++ b/src/lib/net/socket.c @@ -11,6 +11,7 @@ #define SOCKET_PRIVATE #include "lib/net/socket.h" +#include "lib/net/socketpair.h" #include "lib/net/address.h" #include "lib/cc/compat_compiler.h" #include "lib/err/torerr.h" @@ -476,160 +477,6 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) #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, diff --git a/src/lib/net/socket.h b/src/lib/net/socket.h index e2092c727a..5b7d6dbbc6 100644 --- a/src/lib/net/socket.h +++ b/src/lib/net/socket.h @@ -110,14 +110,6 @@ const char *tor_socket_strerror(int e); #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) */ - #if defined(_WIN32) && !defined(SIO_IDEAL_SEND_BACKLOG_QUERY) #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747b #endif diff --git a/src/lib/net/socketpair.c b/src/lib/net/socketpair.c new file mode 100644 index 0000000000..945bdee694 --- /dev/null +++ b/src/lib/net/socketpair.c @@ -0,0 +1,164 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ + +#include "lib/net/socketpair.h" +#include "lib/net/socket.h" +#include "lib/net/address.h" + +#include +#include + +#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) */ diff --git a/src/lib/net/socketpair.h b/src/lib/net/socketpair.h new file mode 100644 index 0000000000..3efa65a675 --- /dev/null +++ b/src/lib/net/socketpair.h @@ -0,0 +1,19 @@ +/* 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_SOCKETPAIR_H +#define TOR_SOCKETPAIR_H + +#include "orconfig.h" +#include "lib/testsupport/testsupport.h" +#include "lib/net/nettypes.h" + +#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 diff --git a/src/test/test_util.c b/src/test/test_util.c index 99fee4c5a5..7afe83c692 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -38,6 +38,7 @@ #include "lib/meminfo/meminfo.h" #include "lib/time/tvdiff.h" #include "lib/encoding/confline.h" +#include "lib/net/socketpair.h" #ifdef HAVE_PWD_H #include From 9b24609af003cb79091e628c179cf617ff41aae7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Aug 2018 09:24:06 -0400 Subject: [PATCH 2/9] Remove dependency from socketpair.c on address.h Also refactor some annoying code in tor_ersatz_socketpair. --- src/lib/net/.may_include | 1 + src/lib/net/socketpair.c | 151 +++++++++++++++++++++++---------------- 2 files changed, 92 insertions(+), 60 deletions(-) diff --git a/src/lib/net/.may_include b/src/lib/net/.may_include index 1458dad990..13b209bbed 100644 --- a/src/lib/net/.may_include +++ b/src/lib/net/.may_include @@ -2,6 +2,7 @@ orconfig.h siphash.h ht.h +lib/arch/*.h lib/cc/*.h lib/container/*.h lib/ctime/*.h diff --git a/src/lib/net/socketpair.c b/src/lib/net/socketpair.c index 945bdee694..b656938c4c 100644 --- a/src/lib/net/socketpair.c +++ b/src/lib/net/socketpair.c @@ -4,23 +4,82 @@ #include "lib/net/socketpair.h" #include "lib/net/socket.h" -#include "lib/net/address.h" +#include "lib/arch/bytes.h" #include #include +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef _WIN32 +#include +#include +#endif + #ifdef NEED_ERSATZ_SOCKETPAIR -static inline socklen_t -SIZEOF_SOCKADDR(int domain) +// Avoid warning about call to memcmp. +#define raw_memcmp memcmp + +static tor_socket_t +get_local_listener(int family, int type) { - switch (domain) { - case AF_INET: - return sizeof(struct sockaddr_in); - case AF_INET6: - return sizeof(struct sockaddr_in6); - default: - return 0; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + struct sockaddr *sa; + int len; + + memset(&sin, 0, sizeof(sin)); + memset(&sin6, 0, sizeof(sin6)); + + tor_socket_t sock = TOR_INVALID_SOCKET; + sock = tor_open_socket(family, type, 0); + if (!SOCKET_OK(sock)) { + return TOR_INVALID_SOCKET; + } + + if (family == AF_INET) { + sa = (struct sockaddr *) &sin; + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = tor_htonl(0x7f000001); + len = sizeof(sin); + } else { + sa = (struct sockaddr *) &sin6; + sin6.sin6_family = AF_INET; + sin6.sin6_addr.s6_addr[15] = 1; + len = sizeof(sin6); + } + + if (bind(sock, sa, len) == -1) + goto err; + if (listen(sock, 1) == -1) + goto err; + + return sock; + err: + tor_close_socket(sock); + return TOR_INVALID_SOCKET; +} + +static int +sockaddr_eq(struct sockaddr *sa1, struct sockaddr *sa2) +{ + if (sa1->sa_family != sa2->sa_family) + return 0; + + if (sa1->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6_1 = (struct sockaddr_in6 *) sa1; + struct sockaddr_in6 *sin6_2 = (struct sockaddr_in6 *) sa2; + return sin6_1->sin6_port == sin6_2->sin6_port && + 0==raw_memcmp(sin6_1->sin6_addr.s6_addr, sin6_2->sin6_addr.s6_addr, 16); + } else if (sa1->sa_family == AF_INET) { + struct sockaddr_in *sin_1 = (struct sockaddr_in *) sa1; + struct sockaddr_in *sin_2 = (struct sockaddr_in *) sa2; + return sin_1->sin_port == sin_2->sin_port && + sin_1->sin_addr.s_addr == sin_2->sin_addr.s_addr; + } else { + return 0; } } @@ -39,21 +98,17 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) 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_storage accepted_addr_ss; + struct sockaddr_storage connect_addr_ss; struct sockaddr *connect_addr = (struct sockaddr *) &connect_addr_ss; + struct sockaddr *accepted_addr = (struct sockaddr *) &accepted_addr_ss; socklen_t size; int saved_errno = -1; int ersatz_domain = AF_INET; + socklen_t addrlen = sizeof(struct sockaddr_in); - memset(&connect_tor_addr, 0, sizeof(connect_tor_addr)); + memset(&accepted_addr_ss, 0, sizeof(accepted_addr_ss)); 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 @@ -70,42 +125,24 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) return -EINVAL; } - listener = tor_open_socket(ersatz_domain, type, 0); + listener = get_local_listener(ersatz_domain, type); if (!SOCKET_OK(listener)) { int first_errno = tor_socket_errno(-1); - if (first_errno == SOCK_ERRNO(EPROTONOSUPPORT) - && ersatz_domain == AF_INET) { + if (first_errno == SOCK_ERRNO(EPROTONOSUPPORT)) { /* 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; - } + addrlen = sizeof(struct sockaddr_in6); + listener = get_local_listener(ersatz_domain, type); + } + 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)) @@ -114,33 +151,27 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) 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)) + if (size != addrlen) 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); + size = sizeof(accepted_addr_ss); + acceptor = tor_accept_socket(listener, accepted_addr, &size); if (!SOCKET_OK(acceptor)) goto tidy_up_and_fail; - if (size != SIZEOF_SOCKADDR(listen_addr->sa_family)) + if (size != addrlen) 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) { + if (!sockaddr_eq(accepted_addr, connect_addr)) goto abort_tidy_up_and_fail; - } tor_close_socket(listener); fd[0] = connector; fd[1] = acceptor; - return 0; abort_tidy_up_and_fail: From fc0dc5aa9ef526a8ed776fe4896fe4007173c638 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Aug 2018 09:40:02 -0400 Subject: [PATCH 3/9] Refactor tor_ersatz_socketpair() not to need socket. This change also makes tor_ersatz_socketpair() follow the same interface as socketpair() rather than tor_socketpair(), so it now needs to be wrapped in the same code as socketpair() does. --- src/lib/net/socket.c | 10 ++++++---- src/lib/net/socketpair.c | 34 ++++++++++++++++++++++------------ src/lib/net/socketpair.h | 4 ++-- src/test/test_util.c | 24 +++++++++++++++++------- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/lib/net/socket.c b/src/lib/net/socket.c index 5487b8d9df..06421b080d 100644 --- a/src/lib/net/socket.c +++ b/src/lib/net/socket.c @@ -420,9 +420,9 @@ get_n_open_sockets(void) int tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) { + int r; //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); @@ -438,6 +438,11 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) r = socketpair(family, type, protocol, fd); if (r < 0) return -errno; +#else + r = tor_ersatz_socketpair(family, type, protocol, fd); + if (r < 0) + return -r; +#endif #if defined(FD_CLOEXEC) if (SOCKET_OK(fd[0])) { @@ -472,9 +477,6 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) 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) */ } /** Mockable wrapper for getsockname(). */ diff --git a/src/lib/net/socketpair.c b/src/lib/net/socketpair.c index b656938c4c..c4e0d1eed0 100644 --- a/src/lib/net/socketpair.c +++ b/src/lib/net/socketpair.c @@ -3,18 +3,28 @@ * Copyright (c) 2007-2018, The Tor Project, Inc. */ #include "lib/net/socketpair.h" -#include "lib/net/socket.h" +#include "lib/net/inaddr_st.h" #include "lib/arch/bytes.h" #include #include +#ifdef HAVE_UNISTD_H +#include +#endif #ifdef HAVE_NETINET_IN_H #include #endif + #ifdef _WIN32 #include #include +#define socket_errno() (WSAGetLastError()) +#define SOCKET_EPROTONOSUPPORT WSAEPROTONOSUPPORT +#else +#define closesocket(x) close(x) +#define socket_errno() (errno) +#define SOCKET_EPROTONOSUPPORT EPROTONOSUPPORT #endif #ifdef NEED_ERSATZ_SOCKETPAIR @@ -34,7 +44,7 @@ get_local_listener(int family, int type) memset(&sin6, 0, sizeof(sin6)); tor_socket_t sock = TOR_INVALID_SOCKET; - sock = tor_open_socket(family, type, 0); + sock = socket(family, type, 0); if (!SOCKET_OK(sock)) { return TOR_INVALID_SOCKET; } @@ -58,7 +68,7 @@ get_local_listener(int family, int type) return sock; err: - tor_close_socket(sock); + closesocket(sock); return TOR_INVALID_SOCKET; } @@ -87,7 +97,7 @@ sockaddr_eq(struct sockaddr *sa1, struct sockaddr *sa2) * Helper used to implement socketpair on systems that lack it, by * making a direct connection to localhost. */ -STATIC int +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 @@ -127,8 +137,8 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) listener = get_local_listener(ersatz_domain, type); if (!SOCKET_OK(listener)) { - int first_errno = tor_socket_errno(-1); - if (first_errno == SOCK_ERRNO(EPROTONOSUPPORT)) { + int first_errno = socket_errno(); + if (first_errno == SOCKET_EPROTONOSUPPORT) { /* Assume we're on an IPv6-only system */ ersatz_domain = AF_INET6; addrlen = sizeof(struct sockaddr_in6); @@ -144,7 +154,7 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) } } - connector = tor_open_socket(ersatz_domain, type, 0); + connector = 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. */ @@ -157,7 +167,7 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) goto tidy_up_and_fail; size = sizeof(accepted_addr_ss); - acceptor = tor_accept_socket(listener, accepted_addr, &size); + acceptor = accept(listener, accepted_addr, &size); if (!SOCKET_OK(acceptor)) goto tidy_up_and_fail; if (size != addrlen) @@ -169,7 +179,7 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) /* Set *_tor_addr and *_port to the address and port that was used */ if (!sockaddr_eq(accepted_addr, connect_addr)) goto abort_tidy_up_and_fail; - tor_close_socket(listener); + closesocket(listener); fd[0] = connector; fd[1] = acceptor; return 0; @@ -184,11 +194,11 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) if (saved_errno < 0) saved_errno = errno; if (SOCKET_OK(listener)) - tor_close_socket(listener); + closesocket(listener); if (SOCKET_OK(connector)) - tor_close_socket(connector); + closesocket(connector); if (SOCKET_OK(acceptor)) - tor_close_socket(acceptor); + closesocket(acceptor); return -saved_errno; } diff --git a/src/lib/net/socketpair.h b/src/lib/net/socketpair.h index 3efa65a675..6eecc0737a 100644 --- a/src/lib/net/socketpair.h +++ b/src/lib/net/socketpair.h @@ -12,8 +12,8 @@ #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]); +int tor_ersatz_socketpair(int family, int type, int protocol, + tor_socket_t fd[2]); #endif #endif diff --git a/src/test/test_util.c b/src/test/test_util.c index 7afe83c692..d8246941e2 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -5540,10 +5540,13 @@ test_util_socketpair(void *arg) tt_assert(SOCKET_OK(fds[0])); tt_assert(SOCKET_OK(fds[1])); - tt_int_op(get_n_open_sockets(), OP_EQ, n + 2); + if (ersatz) + tt_int_op(get_n_open_sockets(), OP_EQ, n); + else + tt_int_op(get_n_open_sockets(), OP_EQ, n + 2); #ifdef CAN_CHECK_CLOEXEC - tt_int_op(fd_is_cloexec(fds[0]), OP_EQ, 1); - tt_int_op(fd_is_cloexec(fds[1]), OP_EQ, 1); + tt_int_op(fd_is_cloexec(fds[0]), OP_EQ, !ersatz); + tt_int_op(fd_is_cloexec(fds[1]), OP_EQ, !ersatz); #endif #ifdef CAN_CHECK_NONBLOCK tt_int_op(fd_is_nonblocking(fds[0]), OP_EQ, 0); @@ -5551,10 +5554,17 @@ test_util_socketpair(void *arg) #endif done: - if (SOCKET_OK(fds[0])) - tor_close_socket(fds[0]); - if (SOCKET_OK(fds[1])) - tor_close_socket(fds[1]); + if (ersatz) { + if (SOCKET_OK(fds[0])) + tor_close_socket_simple(fds[0]); + if (SOCKET_OK(fds[1])) + tor_close_socket_simple(fds[1]); + } else { + if (SOCKET_OK(fds[0])) + tor_close_socket(fds[0]); + if (SOCKET_OK(fds[1])) + tor_close_socket(fds[1]); + } } #undef SOCKET_EPROTO From 1b7b9c7e63848a125c05efc3c1b9a5868946a9cc Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Aug 2018 09:41:45 -0400 Subject: [PATCH 4/9] Reindent tor_ersatz_socketpair --- src/lib/net/socketpair.c | 168 +++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/src/lib/net/socketpair.c b/src/lib/net/socketpair.c index c4e0d1eed0..a12c25cc8a 100644 --- a/src/lib/net/socketpair.c +++ b/src/lib/net/socketpair.c @@ -100,106 +100,106 @@ sockaddr_eq(struct sockaddr *sa1, struct sockaddr *sa2) 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; - struct sockaddr_storage accepted_addr_ss; - struct sockaddr_storage connect_addr_ss; - struct sockaddr *connect_addr = (struct sockaddr *) &connect_addr_ss; - struct sockaddr *accepted_addr = (struct sockaddr *) &accepted_addr_ss; - socklen_t size; - int saved_errno = -1; - int ersatz_domain = AF_INET; - socklen_t addrlen = sizeof(struct sockaddr_in); + /* 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; + struct sockaddr_storage accepted_addr_ss; + struct sockaddr_storage connect_addr_ss; + struct sockaddr *connect_addr = (struct sockaddr *) &connect_addr_ss; + struct sockaddr *accepted_addr = (struct sockaddr *) &accepted_addr_ss; + socklen_t size; + int saved_errno = -1; + int ersatz_domain = AF_INET; + socklen_t addrlen = sizeof(struct sockaddr_in); - memset(&accepted_addr_ss, 0, sizeof(accepted_addr_ss)); - memset(&connect_addr_ss, 0, sizeof(connect_addr_ss)); + memset(&accepted_addr_ss, 0, sizeof(accepted_addr_ss)); + memset(&connect_addr_ss, 0, sizeof(connect_addr_ss)); - if (protocol + if (protocol #ifdef AF_UNIX - || family != AF_UNIX + || family != AF_UNIX #endif - ) { + ) { #ifdef _WIN32 - return -WSAEAFNOSUPPORT; + return -WSAEAFNOSUPPORT; #else - return -EAFNOSUPPORT; + return -EAFNOSUPPORT; #endif - } - if (!fd) { - return -EINVAL; - } + } + if (!fd) { + return -EINVAL; + } - listener = get_local_listener(ersatz_domain, type); + listener = get_local_listener(ersatz_domain, type); + if (!SOCKET_OK(listener)) { + int first_errno = socket_errno(); + if (first_errno == SOCKET_EPROTONOSUPPORT) { + /* Assume we're on an IPv6-only system */ + ersatz_domain = AF_INET6; + addrlen = sizeof(struct sockaddr_in6); + listener = get_local_listener(ersatz_domain, type); + } if (!SOCKET_OK(listener)) { - int first_errno = socket_errno(); - if (first_errno == SOCKET_EPROTONOSUPPORT) { - /* Assume we're on an IPv6-only system */ - ersatz_domain = AF_INET6; - addrlen = sizeof(struct sockaddr_in6); - listener = get_local_listener(ersatz_domain, type); - } - 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; - } + /* 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; } + } - connector = 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 != addrlen) - goto abort_tidy_up_and_fail; - if (connect(connector, connect_addr, size) == -1) - goto tidy_up_and_fail; + connector = 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 != addrlen) + goto abort_tidy_up_and_fail; + if (connect(connector, connect_addr, size) == -1) + goto tidy_up_and_fail; - size = sizeof(accepted_addr_ss); - acceptor = accept(listener, accepted_addr, &size); - if (!SOCKET_OK(acceptor)) - goto tidy_up_and_fail; - if (size != addrlen) - 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 */ - if (!sockaddr_eq(accepted_addr, connect_addr)) - goto abort_tidy_up_and_fail; - closesocket(listener); - fd[0] = connector; - fd[1] = acceptor; - return 0; + size = sizeof(accepted_addr_ss); + acceptor = accept(listener, accepted_addr, &size); + if (!SOCKET_OK(acceptor)) + goto tidy_up_and_fail; + if (size != addrlen) + 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 */ + if (!sockaddr_eq(accepted_addr, connect_addr)) + goto abort_tidy_up_and_fail; + closesocket(listener); + fd[0] = connector; + fd[1] = acceptor; + return 0; - abort_tidy_up_and_fail: + abort_tidy_up_and_fail: #ifdef _WIN32 - saved_errno = WSAECONNABORTED; + saved_errno = WSAECONNABORTED; #else - saved_errno = ECONNABORTED; /* I hope this is portable and appropriate. */ + 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)) - closesocket(listener); - if (SOCKET_OK(connector)) - closesocket(connector); - if (SOCKET_OK(acceptor)) - closesocket(acceptor); - return -saved_errno; + tidy_up_and_fail: + if (saved_errno < 0) + saved_errno = errno; + if (SOCKET_OK(listener)) + closesocket(listener); + if (SOCKET_OK(connector)) + closesocket(connector); + if (SOCKET_OK(acceptor)) + closesocket(acceptor); + return -saved_errno; } #endif /* defined(NEED_ERSATZ_SOCKETPAIR) */ From ff7229b32cb2f5a3c384200caac35e0626199087 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Aug 2018 09:43:25 -0400 Subject: [PATCH 5/9] Document new helper functions in socketpair.c --- src/lib/net/socketpair.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/net/socketpair.c b/src/lib/net/socketpair.c index a12c25cc8a..8dd04d0486 100644 --- a/src/lib/net/socketpair.c +++ b/src/lib/net/socketpair.c @@ -32,6 +32,11 @@ // Avoid warning about call to memcmp. #define raw_memcmp memcmp +/** + * Return a new socket that is bound and listening on the loopback interface + * of family family for a socket of type type. On failure return + * TOR_INVALID_SOCKET. + */ static tor_socket_t get_local_listener(int family, int type) { @@ -72,6 +77,9 @@ get_local_listener(int family, int type) return TOR_INVALID_SOCKET; } +/** + * Return true iff sa1 and sa2 are equivalent AF_INET or AF_INET6 addresses. + */ static int sockaddr_eq(struct sockaddr *sa1, struct sockaddr *sa2) { From 9a89450b6d1734fb8e015093353a5d0568b41b79 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Aug 2018 10:15:44 -0400 Subject: [PATCH 6/9] tor_api: Extend tor_api code so it can pass extra arguments to main. We need this so that the tor_api user can specify some arguments, while the tor_api implementation adds others. This implementation detail should not be visible to tor_api users. --- src/core/mainloop/main.c | 29 ++++++++++++++++++++-------- src/feature/api/tor_api.c | 31 +++++++++++++++++++++++++++++- src/feature/api/tor_api.h | 1 - src/feature/api/tor_api_internal.h | 8 +++++++- src/tools/tor_runner.c | 9 ++++++--- 5 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/core/mainloop/main.c b/src/core/mainloop/main.c index 048397a2da..4bb55c7ba6 100644 --- a/src/core/mainloop/main.c +++ b/src/core/mainloop/main.c @@ -4204,9 +4204,6 @@ tor_run_main(const tor_main_configuration_t *tor_cfg) { int result = 0; - int argc = tor_cfg->argc; - char **argv = tor_cfg->argv; - #ifdef _WIN32 #ifndef HeapEnableTerminationOnCorruption #define HeapEnableTerminationOnCorruption 1 @@ -4242,25 +4239,39 @@ tor_run_main(const tor_main_configuration_t *tor_cfg) tor_compress_init(); init_logging(0); monotime_init(); + + int argc = tor_cfg->argc + tor_cfg->argc_owned; + char **argv = tor_calloc(argc, sizeof(char*)); + memcpy(argv, tor_cfg->argv, tor_cfg->argc*sizeof(char*)); + if (tor_cfg->argc_owned) + memcpy(argv + tor_cfg->argc, tor_cfg->argv_owned, + tor_cfg->argc_owned*sizeof(char*)); + #ifdef NT_SERVICE { int done = 0; result = nt_service_parse_options(argc, argv, &done); - if (done) return result; + if (done) { + goto done; + } } #endif /* defined(NT_SERVICE) */ { int init_rv = tor_init(argc, argv); - if (init_rv < 0) - return -1; - else if (init_rv > 0) - return 0; + if (init_rv < 0) { + result = -1; + goto done; + } else if (init_rv > 0) { + result = 0; + goto done; + } } if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) { sandbox_cfg_t* cfg = sandbox_init_filter(); if (sandbox_init(cfg)) { + tor_free(argv); log_err(LD_BUG,"Failed to create syscall sandbox filter"); return -1; } @@ -4308,5 +4319,7 @@ tor_run_main(const tor_main_configuration_t *tor_cfg) result = -1; } tor_cleanup(); + done: + tor_free(argv); return result; } diff --git a/src/feature/api/tor_api.c b/src/feature/api/tor_api.c index 2a4458bf69..755d28aad6 100644 --- a/src/feature/api/tor_api.c +++ b/src/feature/api/tor_api.c @@ -25,6 +25,28 @@ #define raw_malloc malloc #define raw_free free +#define raw_realloc realloc +#define raw_strdup strdup + +/** + * Helper: Add a copy of arg to the owned arguments of cfg. + * Return 0 on success, -1 on failure. + */ +static int +cfg_add_owned_arg(tor_main_configuration_t *cfg, const char *arg) +{ + /* We aren't using amortized realloc here, because libc should do it for us, + * and because this function is not critical-path. */ + char **new_argv = raw_realloc(cfg->argv_owned, + sizeof(char*) * (cfg->argc_owned+1)); + if (new_argv == NULL) + return -1; + cfg->argv_owned = new_argv; + if (NULL == (cfg->argv_owned[cfg->argc_owned] = raw_strdup(arg))) + return -1; + ++cfg->argc_owned; + return 0; +} tor_main_configuration_t * tor_main_configuration_new(void) @@ -58,6 +80,14 @@ tor_main_configuration_free(tor_main_configuration_t *cfg) { if (cfg == NULL) return; + cfg_add_owned_arg(cfg, "bye");//XXXX + if (cfg->argv_owned) { + int i; + for (i = 0; i < cfg->argc_owned; ++i) { + raw_free(cfg->argv_owned[i]); + } + raw_free(cfg->argv_owned); + } raw_free(cfg); } @@ -85,4 +115,3 @@ tor_main(int argc, char *argv[]) tor_main_configuration_free(cfg); return rv; } - diff --git a/src/feature/api/tor_api.h b/src/feature/api/tor_api.h index ead9493c1f..5133e3cec6 100644 --- a/src/feature/api/tor_api.h +++ b/src/feature/api/tor_api.h @@ -98,4 +98,3 @@ int tor_run_main(const tor_main_configuration_t *); int tor_main(int argc, char **argv); #endif /* !defined(TOR_API_H) */ - diff --git a/src/feature/api/tor_api_internal.h b/src/feature/api/tor_api_internal.h index 2c392a68de..30924b8b04 100644 --- a/src/feature/api/tor_api_internal.h +++ b/src/feature/api/tor_api_internal.h @@ -7,6 +7,8 @@ #ifndef TOR_API_INTERNAL_H #define TOR_API_INTERNAL_H +#include "lib/net/nettypes.h" + /* The contents of this type are private; don't mess with them from outside * Tor. */ struct tor_main_configuration_t { @@ -14,7 +16,11 @@ struct tor_main_configuration_t { int argc; /** As in main(). This pointer is owned by the caller */ char **argv; + + /** As argc, but describes the number of elements in argv_owned */ + int argc_owned; + /** As argv, but is owned by the tor_main_configuration_t object. */ + char **argv_owned; }; #endif /* !defined(TOR_API_INTERNAL_H) */ - diff --git a/src/tools/tor_runner.c b/src/tools/tor_runner.c index dd90af3df5..4b21d7bf9b 100644 --- a/src/tools/tor_runner.c +++ b/src/tools/tor_runner.c @@ -86,9 +86,13 @@ child(const tor_main_configuration_t *cfg) { /* XXXX Close unused file descriptors. */ - char **args = real_calloc(cfg->argc+1, sizeof(char *)); + char **args = real_calloc(cfg->argc + cfg->argc_owned+1, sizeof(char *)); memcpy(args, cfg->argv, cfg->argc * sizeof(char *)); - args[cfg->argc] = NULL; + if (cfg->argc_owned) + memcpy(args + cfg->argc, cfg->argv_owned, + cfg->argc_owned * sizeof(char *)); + + args[cfg->argc + cfg->argc_owned] = NULL; int rv = execv(BINDIR "/tor", args); @@ -98,4 +102,3 @@ child(const tor_main_configuration_t *cfg) abort(); /* Unreachable */ } } - From c77fe82155c7ed862358f9869e0dcb1ba8772f83 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Aug 2018 10:36:10 -0400 Subject: [PATCH 7/9] Add API for creating an owning controller FD and passing it to tor_main --- src/feature/api/tor_api.c | 49 ++++++++++++++++++++++++++++-- src/feature/api/tor_api.h | 12 ++++++++ src/feature/api/tor_api_internal.h | 3 ++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/feature/api/tor_api.c b/src/feature/api/tor_api.c index 755d28aad6..664a3a0d8f 100644 --- a/src/feature/api/tor_api.c +++ b/src/feature/api/tor_api.c @@ -8,12 +8,19 @@ * \file tor_api.c **/ +#ifdef _WIN32 +#include +#include +#endif + #include "feature/api/tor_api.h" -#include "feature/api/tor_api_internal.h" // Include this after the above headers, to insure that they don't // depend on anything else. #include "orconfig.h" +#include "feature/api/tor_api_internal.h" +#include "lib/cc/torint.h" +#include "lib/cc/compat_compiler.h" #include #include @@ -28,6 +35,20 @@ #define raw_realloc realloc #define raw_strdup strdup +#ifdef _WIN32 +#include "lib/net/socketpair.h" +#define raw_socketpair tor_ersatz_socketpair +#define raw_closesocket closesocket +#define snprintf _snprintf +#else +#define raw_socketpair socketpair +#define raw_closesocket close +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + /** * Helper: Add a copy of arg to the owned arguments of cfg. * Return 0 on success, -1 on failure. @@ -61,6 +82,8 @@ tor_main_configuration_new(void) cfg->argc = 1; cfg->argv = (char **) fake_argv; + cfg->owning_controller_socket = TOR_INVALID_SOCKET; + return cfg; } @@ -75,12 +98,31 @@ tor_main_configuration_set_command_line(tor_main_configuration_t *cfg, return 0; } +tor_control_socket_t +tor_main_configuration_setup_control_socket(tor_main_configuration_t *cfg) +{ + if (SOCKET_OK(cfg->owning_controller_socket)) + return INVALID_TOR_CONTROL_SOCKET; + + tor_socket_t fds[2]; + if (raw_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + return INVALID_TOR_CONTROL_SOCKET; + } + char buf[32]; + snprintf(buf, sizeof(buf), "%"PRIu64, (uint64_t)fds[1]); + + cfg_add_owned_arg(cfg, "__OwningControllerFD"); + cfg_add_owned_arg(cfg, buf); + + cfg->owning_controller_socket = fds[1]; + return fds[0]; +} + void tor_main_configuration_free(tor_main_configuration_t *cfg) { if (cfg == NULL) return; - cfg_add_owned_arg(cfg, "bye");//XXXX if (cfg->argv_owned) { int i; for (i = 0; i < cfg->argc_owned; ++i) { @@ -88,6 +130,9 @@ tor_main_configuration_free(tor_main_configuration_t *cfg) } raw_free(cfg->argv_owned); } + if (SOCKET_OK(cfg->owning_controller_socket)) { + raw_closesocket(cfg->owning_controller_socket); + } raw_free(cfg); } diff --git a/src/feature/api/tor_api.h b/src/feature/api/tor_api.h index 5133e3cec6..0e22ced2d7 100644 --- a/src/feature/api/tor_api.h +++ b/src/feature/api/tor_api.h @@ -49,6 +49,18 @@ tor_main_configuration_t *tor_main_configuration_new(void); int tor_main_configuration_set_command_line(tor_main_configuration_t *cfg, int argc, char *argv[]); +#ifdef _WIN32 +typedef SOCKET tor_control_socket_t; +#define INVALID_TOR_CONTROL_SOCKET INVALID_SOCKET +#else +typedef int tor_control_socket_t; +#define INVALID_TOR_CONTROL_SOCKET (-1) +#endif + +/** DOCDOC */ +tor_control_socket_t tor_main_configuration_setup_control_socket( + tor_main_configuration_t *cfg); + /** * Release all storage held in cfg. * diff --git a/src/feature/api/tor_api_internal.h b/src/feature/api/tor_api_internal.h index 30924b8b04..1e32012d01 100644 --- a/src/feature/api/tor_api_internal.h +++ b/src/feature/api/tor_api_internal.h @@ -21,6 +21,9 @@ struct tor_main_configuration_t { int argc_owned; /** As argv, but is owned by the tor_main_configuration_t object. */ char **argv_owned; + + /** Socket that Tor will use as an owning control socket. Owned. */ + tor_socket_t owning_controller_socket; }; #endif /* !defined(TOR_API_INTERNAL_H) */ From fe3bacf50a8d6e7fcec5f8bb1d5c0ac32c41a57c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Aug 2018 10:49:08 -0400 Subject: [PATCH 8/9] Convert __OwningControllerFD to a 64-bit value This lets us potentially use it for internal passing of windows sockets. --- src/app/config/config.c | 15 +++++++-------- src/app/config/confparse.c | 15 +++++++++++++++ src/app/config/confparse.h | 2 ++ src/app/config/or_options_st.h | 2 +- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/app/config/config.c b/src/app/config/config.c index 1b1889779d..463405d196 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -260,6 +260,9 @@ DUMMY_TYPECHECK_INSTANCE(or_options_t); VAR(#member, LINELIST_S, member ## _lines, NULL), \ VAR("__" #member, LINELIST_S, member ## _lines, NULL) +/** UINT64_MAX as a decimal string */ +#define UINT64_MAX_STRING "18446744073709551615" + /** Array of configuration options. Until we disallow nonstandard * abbreviations, order is significant, since the first matching option will * be chosen first. @@ -647,7 +650,7 @@ static config_var_t option_vars_[] = { VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, NULL), VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), - VAR("__OwningControllerFD",INT,OwningControllerFD, "-1"), + VAR("__OwningControllerFD", UINT64, OwningControllerFD, UINT64_MAX_STRING), V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"), V(TestingServerDownloadInitialDelay, CSV_INTERVAL, "0"), V(TestingClientDownloadInitialDelay, CSV_INTERVAL, "0"), @@ -1948,12 +1951,8 @@ options_act(const or_options_t *old_options) // LCOV_EXCL_STOP } - if (running_tor && !old_options && options->OwningControllerFD != -1) { -#ifdef _WIN32 - log_warn(LD_CONFIG, "OwningControllerFD is not supported on Windows. " - "If you need it, tell the Tor developers."); - return -1; -#else + if (running_tor && !old_options && + options->OwningControllerFD != UINT64_MAX) { const unsigned ctrl_flags = CC_LOCAL_FD_IS_OWNER | CC_LOCAL_FD_IS_AUTHENTICATED; @@ -1963,7 +1962,6 @@ options_act(const or_options_t *old_options) "given FD."); return -1; } -#endif /* defined(_WIN32) */ } /* Load state */ @@ -8197,6 +8195,7 @@ getinfo_helper_config(control_connection_t *conn, case CONFIG_TYPE_STRING: type = "String"; break; case CONFIG_TYPE_FILENAME: type = "Filename"; break; case CONFIG_TYPE_UINT: type = "Integer"; break; + case CONFIG_TYPE_UINT64: type = "Integer"; break; case CONFIG_TYPE_INT: type = "SignedInteger"; break; case CONFIG_TYPE_PORT: type = "Port"; break; case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break; diff --git a/src/app/config/confparse.c b/src/app/config/confparse.c index 6fa4fd1ea8..045cbc94fe 100644 --- a/src/app/config/confparse.c +++ b/src/app/config/confparse.c @@ -195,6 +195,19 @@ config_assign_value(const config_format_t *fmt, void *options, *(int *)lvalue = i; break; + case CONFIG_TYPE_UINT64: { + uint64_t u64 = tor_parse_uint64(c->value, 10, + 0, UINT64_MAX, &ok, NULL); + if (!ok) { + tor_asprintf(msg, + "uint64 keyword '%s %s' is malformed or out of bounds.", + c->key, c->value); + return -1; + } + *(uint64_t *)lvalue = u64; + break; + } + case CONFIG_TYPE_CSV_INTERVAL: { /* We used to have entire smartlists here. But now that all of our * download schedules use exponential backoff, only the first part @@ -574,6 +587,7 @@ config_get_assigned_option(const config_format_t *fmt, const void *options, tor_asprintf(&result->value, "%d", *(int*)value); escape_val = 0; /* Can't need escape. */ break; + case CONFIG_TYPE_UINT64: /* Fall through */ case CONFIG_TYPE_MEMUNIT: tor_asprintf(&result->value, "%"PRIu64, (*(uint64_t*)value)); @@ -781,6 +795,7 @@ config_clear(const config_format_t *fmt, void *options, case CONFIG_TYPE_AUTOBOOL: *(int*)lvalue = -1; break; + case CONFIG_TYPE_UINT64: case CONFIG_TYPE_MEMUNIT: *(uint64_t*)lvalue = 0; break; diff --git a/src/app/config/confparse.h b/src/app/config/confparse.h index 570428c904..aebd035c56 100644 --- a/src/app/config/confparse.h +++ b/src/app/config/confparse.h @@ -19,6 +19,7 @@ typedef enum config_type_t { CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */ CONFIG_TYPE_UINT, /**< A non-negative integer less than MAX_INT */ CONFIG_TYPE_INT, /**< Any integer. */ + CONFIG_TYPE_UINT64, /**< A value in range 0..UINT64_MAX */ CONFIG_TYPE_PORT, /**< A port from 1...65535, 0 for "not set", or * "auto". */ CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/ @@ -60,6 +61,7 @@ typedef union { * "UINT", it still uses the C int type -- it just enforces that * the values are in range [0,INT_MAX]. */ + uint64_t *UINT64; int *INT; int *PORT; int *INTERVAL; diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index 627b39aea3..e5a7a01f2c 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -514,7 +514,7 @@ struct or_options_t { * instance. Tor will terminate if its owning controller does. */ char *OwningControllerProcess; /** FD specifier for a controller that owns this Tor instance. */ - int OwningControllerFD; + uint64_t OwningControllerFD; int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how * long do we wait before exiting? */ From 820aba70efcc6f50f23cf676832412852a01141a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Aug 2018 11:09:15 -0400 Subject: [PATCH 9/9] Changes file for owningcontrollerfd api --- changes/ticket24204 | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changes/ticket24204 diff --git a/changes/ticket24204 b/changes/ticket24204 new file mode 100644 index 0000000000..835952045b --- /dev/null +++ b/changes/ticket24204 @@ -0,0 +1,5 @@ + o Minor features (embedding API): + - The Tor controller API now supports a function to launch Tor with a + preconstructed owning controller FD, so that embedding applications + don't need to manage controller ports and authentication. Closes + ticket 24204.