diff options
author | Nick Mathewson <nickm@torproject.org> | 2018-09-04 11:04:21 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2018-09-04 11:04:21 -0400 |
commit | 3507fead10ef4f990c0373f4405bd235c0f0b35c (patch) | |
tree | 2eda3631bd503be2731f320f8ca46d60d2b86a16 /src | |
parent | 94b04d6c64ec998a9117d65a156888fa3af188e5 (diff) | |
parent | 820aba70efcc6f50f23cf676832412852a01141a (diff) | |
download | tor-3507fead10ef4f990c0373f4405bd235c0f0b35c.tar.gz tor-3507fead10ef4f990c0373f4405bd235c0f0b35c.zip |
Merge branch 'tor_api_owning_control'
Diffstat (limited to 'src')
-rw-r--r-- | src/app/config/config.c | 15 | ||||
-rw-r--r-- | src/app/config/confparse.c | 15 | ||||
-rw-r--r-- | src/app/config/confparse.h | 2 | ||||
-rw-r--r-- | src/app/config/or_options_st.h | 2 | ||||
-rw-r--r-- | src/core/mainloop/main.c | 21 | ||||
-rw-r--r-- | src/feature/api/tor_api.c | 77 | ||||
-rw-r--r-- | src/feature/api/tor_api.h | 12 | ||||
-rw-r--r-- | src/feature/api/tor_api_internal.h | 11 | ||||
-rw-r--r-- | src/lib/net/.may_include | 1 | ||||
-rw-r--r-- | src/lib/net/include.am | 4 | ||||
-rw-r--r-- | src/lib/net/socket.c | 165 | ||||
-rw-r--r-- | src/lib/net/socket.h | 8 | ||||
-rw-r--r-- | src/lib/net/socketpair.c | 213 | ||||
-rw-r--r-- | src/lib/net/socketpair.h | 19 | ||||
-rw-r--r-- | src/test/test_util.c | 25 | ||||
-rw-r--r-- | src/tools/tor_runner.c | 8 |
16 files changed, 406 insertions, 192 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c index 339f8e2475..f8492f6cdc 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -259,6 +259,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. @@ -646,7 +649,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"), @@ -1925,12 +1928,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; @@ -1940,7 +1939,6 @@ options_act(const or_options_t *old_options) "given FD."); return -1; } -#endif /* defined(_WIN32) */ } /* Load state */ @@ -8115,6 +8113,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 8ef01f80e7..9876a41614 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -506,7 +506,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? */ diff --git a/src/core/mainloop/main.c b/src/core/mainloop/main.c index c648d236bb..27c4a9b3bf 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,18 +4239,29 @@ 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) { tor_free_all(0); - return (init_rv < 0) ? -1 : 0; + result = (init_rv < 0) ? -1 : 0; + goto done; } } @@ -4261,6 +4269,7 @@ tor_run_main(const tor_main_configuration_t *tor_cfg) sandbox_cfg_t* cfg = sandbox_init_filter(); if (sandbox_init(cfg)) { + tor_free(argv); log_err(LD_BUG,"Failed to create syscall sandbox filter"); tor_free_all(0); return -1; @@ -4309,5 +4318,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 a53ade1f3e..efc4fdf8d9 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 <winsock2.h> +#include <windows.h> +#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 <stdio.h> #include <stdlib.h> @@ -25,6 +32,42 @@ #define raw_malloc malloc #define raw_free free +#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 <unistd.h> +#endif + +/** + * Helper: Add a copy of <b>arg</b> to the owned arguments of <b>cfg</b>. + * 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) @@ -39,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; } @@ -53,11 +98,41 @@ 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; + 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); + } + 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 ad2d3166c1..1ac9d892f2 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 <b>cfg</b>. * diff --git a/src/feature/api/tor_api_internal.h b/src/feature/api/tor_api_internal.h index 2c392a68de..1e32012d01 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,14 @@ 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; + + /** Socket that Tor will use as an owning control socket. Owned. */ + tor_socket_t owning_controller_socket; }; #endif /* !defined(TOR_API_INTERNAL_H) */ - 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/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..06421b080d 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" @@ -419,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); @@ -437,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])) { @@ -471,165 +477,8 @@ 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) */ -} - -#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..8dd04d0486 --- /dev/null +++ b/src/lib/net/socketpair.c @@ -0,0 +1,213 @@ +/* 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/inaddr_st.h" +#include "lib/arch/bytes.h" + +#include <errno.h> +#include <string.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#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 + +// 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 <b>family</b> for a socket of type <b>type</b>. On failure return + * TOR_INVALID_SOCKET. + */ +static tor_socket_t +get_local_listener(int family, int type) +{ + 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 = 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: + closesocket(sock); + 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) +{ + 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; + } +} + +/** + * Helper used to implement socketpair on systems that lack it, by + * making a direct connection to localhost. + */ +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); + + memset(&accepted_addr_ss, 0, sizeof(accepted_addr_ss)); + memset(&connect_addr_ss, 0, sizeof(connect_addr_ss)); + + if (protocol +#ifdef AF_UNIX + || family != AF_UNIX +#endif + ) { +#ifdef _WIN32 + return -WSAEAFNOSUPPORT; +#else + return -EAFNOSUPPORT; +#endif + } + if (!fd) { + return -EINVAL; + } + + 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; + } + } + + 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; + + 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)) + closesocket(listener); + if (SOCKET_OK(connector)) + closesocket(connector); + if (SOCKET_OK(acceptor)) + closesocket(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..6eecc0737a --- /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 +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 6cbd504e34..b5a231dac6 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 <pwd.h> @@ -5612,10 +5613,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); @@ -5623,10 +5627,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 diff --git a/src/tools/tor_runner.c b/src/tools/tor_runner.c index f8805e6500..b07b72a095 100644 --- a/src/tools/tor_runner.c +++ b/src/tools/tor_runner.c @@ -92,9 +92,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); |