diff options
author | Nick Mathewson <nickm@torproject.org> | 2018-07-05 16:31:38 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2018-07-05 17:15:50 -0400 |
commit | 63b4ea22af8e8314dd718f02046de5f4b91edf9d (patch) | |
tree | af52b6fba37f22c86447fd5267dd5eb557807c8b /src/core/mainloop | |
parent | ce84200542f48a92e8b56a8d032401ecd153e90c (diff) | |
download | tor-63b4ea22af8e8314dd718f02046de5f4b91edf9d.tar.gz tor-63b4ea22af8e8314dd718f02046de5f4b91edf9d.zip |
Move literally everything out of src/or
This commit won't build yet -- it just puts everything in a slightly
more logical place.
The reasoning here is that "src/core" will hold the stuff that every (or
nearly every) tor instance will need in order to do onion routing.
Other features (including some necessary ones) will live in
"src/feature". The "src/app" directory will hold the stuff needed
to have Tor be an application you can actually run.
This commit DOES NOT refactor the former contents of src/or into a
logical set of acyclic libraries, or change any code at all. That
will have to come in the future.
We will continue to move things around and split them in the future,
but I hope this lays a reasonable groundwork for doing so.
Diffstat (limited to 'src/core/mainloop')
-rw-r--r-- | src/core/mainloop/connection.c | 5331 | ||||
-rw-r--r-- | src/core/mainloop/connection.h | 343 | ||||
-rw-r--r-- | src/core/mainloop/cpuworker.c | 599 | ||||
-rw-r--r-- | src/core/mainloop/cpuworker.h | 37 | ||||
-rw-r--r-- | src/core/mainloop/main.c | 4311 | ||||
-rw-r--r-- | src/core/mainloop/main.h | 122 | ||||
-rw-r--r-- | src/core/mainloop/periodic.c | 172 | ||||
-rw-r--r-- | src/core/mainloop/periodic.h | 88 |
8 files changed, 11003 insertions, 0 deletions
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c new file mode 100644 index 0000000000..6fd8fbf771 --- /dev/null +++ b/src/core/mainloop/connection.c @@ -0,0 +1,5331 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file connection.c + * \brief General high-level functions to handle reading and writing + * on connections. + * + * Each connection (ideally) represents a TLS connection, a TCP socket, a unix + * socket, or a UDP socket on which reads and writes can occur. (But see + * connection_edge.c for cases where connections can also represent streams + * that do not have a corresponding socket.) + * + * The module implements the abstract type, connection_t. The subtypes are: + * <ul> + * <li>listener_connection_t, implemented here in connection.c + * <li>dir_connection_t, implemented in directory.c + * <li>or_connection_t, implemented in connection_or.c + * <li>edge_connection_t, implemented in connection_edge.c, along with + * its subtype(s): + * <ul><li>entry_connection_t, also implemented in connection_edge.c + * </ul> + * <li>control_connection_t, implemented in control.c + * </ul> + * + * The base type implemented in this module is responsible for basic + * rate limiting, flow control, and marshalling bytes onto and off of the + * network (either directly or via TLS). + * + * Connections are registered with the main loop with connection_add(). As + * they become able to read or write register the fact with the event main + * loop by calling connection_watch_events(), connection_start_reading(), or + * connection_start_writing(). When they no longer want to read or write, + * they call connection_stop_reading() or connection_stop_writing(). + * + * To queue data to be written on a connection, call + * connection_buf_add(). When data arrives, the + * connection_process_inbuf() callback is invoked, which dispatches to a + * type-specific function (such as connection_edge_process_inbuf() for + * example). Connection types that need notice of when data has been written + * receive notification via connection_flushed_some() and + * connection_finished_flushing(). These functions all delegate to + * type-specific implementations. + * + * Additionally, beyond the core of connection_t, this module also implements: + * <ul> + * <li>Listeners, which wait for incoming sockets and launch connections + * <li>Outgoing SOCKS proxy support + * <li>Outgoing HTTP proxy support + * <li>An out-of-sockets handler for dealing with socket exhaustion + * </ul> + **/ + +#define CONNECTION_PRIVATE +#include "or/or.h" +#include "or/bridges.h" +#include "lib/container/buffers.h" +#include "lib/tls/buffers_tls.h" +/* + * Define this so we get channel internal functions, since we're implementing + * part of a subclass (channel_tls_t). + */ +#define TOR_CHANNEL_INTERNAL_ +#define CONNECTION_PRIVATE +#include "lib/err/backtrace.h" +#include "or/channel.h" +#include "or/channeltls.h" +#include "or/circuitbuild.h" +#include "or/circuitlist.h" +#include "or/circuituse.h" +#include "or/config.h" +#include "or/connection.h" +#include "or/connection_edge.h" +#include "or/connection_or.h" +#include "or/control.h" +#include "lib/crypt_ops/crypto_util.h" +#include "or/directory.h" +#include "or/dirserv.h" +#include "or/dns.h" +#include "or/dnsserv.h" +#include "or/dos.h" +#include "or/entrynodes.h" +#include "or/ext_orport.h" +#include "or/geoip.h" +#include "or/main.h" +#include "or/hibernate.h" +#include "or/hs_common.h" +#include "or/hs_ident.h" +#include "or/nodelist.h" +#include "or/proto_http.h" +#include "or/proto_socks.h" +#include "or/policies.h" +#include "or/reasons.h" +#include "or/relay.h" +#include "or/rendclient.h" +#include "or/rendcommon.h" +#include "or/rephist.h" +#include "or/router.h" +#include "or/routerlist.h" +#include "or/transports.h" +#include "or/routerparse.h" +#include "lib/sandbox/sandbox.h" +#include "lib/net/buffers_net.h" +#include "lib/tls/tortls.h" +#include "lib/evloop/compat_libevent.h" +#include "lib/compress/compress.h" + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_SYS_UN_H +#include <sys/socket.h> +#include <sys/un.h> +#endif + +#include "or/dir_connection_st.h" +#include "or/control_connection_st.h" +#include "or/entry_connection_st.h" +#include "or/listener_connection_st.h" +#include "or/or_connection_st.h" +#include "or/port_cfg_st.h" +#include "or/routerinfo_st.h" +#include "or/socks_request_st.h" + +static connection_t *connection_listener_new( + const struct sockaddr *listensockaddr, + socklen_t listensocklen, int type, + const char *address, + const port_cfg_t *portcfg); +static void connection_init(time_t now, connection_t *conn, int type, + int socket_family); +static int connection_handle_listener_read(connection_t *conn, int new_type); +static int connection_finished_flushing(connection_t *conn); +static int connection_flushed_some(connection_t *conn); +static int connection_finished_connecting(connection_t *conn); +static int connection_reached_eof(connection_t *conn); +static int connection_buf_read_from_socket(connection_t *conn, + ssize_t *max_to_read, + int *socket_error); +static int connection_process_inbuf(connection_t *conn, int package_partial); +static void client_check_address_changed(tor_socket_t sock); +static void set_constrained_socket_buffers(tor_socket_t sock, int size); + +static const char *connection_proxy_state_to_string(int state); +static int connection_read_https_proxy_response(connection_t *conn); +static void connection_send_socks5_connect(connection_t *conn); +static const char *proxy_type_to_string(int proxy_type); +static int get_proxy_type(void); +const tor_addr_t *conn_get_outbound_address(sa_family_t family, + const or_options_t *options, unsigned int conn_type); +static void reenable_blocked_connection_init(const or_options_t *options); +static void reenable_blocked_connection_schedule(void); + +/** The last addresses that our network interface seemed to have been + * binding to. We use this as one way to detect when our IP changes. + * + * XXXX+ We should really use the entire list of interfaces here. + **/ +static tor_addr_t *last_interface_ipv4 = NULL; +/* DOCDOC last_interface_ipv6 */ +static tor_addr_t *last_interface_ipv6 = NULL; +/** A list of tor_addr_t for addresses we've used in outgoing connections. + * Used to detect IP address changes. */ +static smartlist_t *outgoing_addrs = NULL; + +#define CASE_ANY_LISTENER_TYPE \ + case CONN_TYPE_OR_LISTENER: \ + case CONN_TYPE_EXT_OR_LISTENER: \ + case CONN_TYPE_AP_LISTENER: \ + case CONN_TYPE_DIR_LISTENER: \ + case CONN_TYPE_CONTROL_LISTENER: \ + case CONN_TYPE_AP_TRANS_LISTENER: \ + case CONN_TYPE_AP_NATD_LISTENER: \ + case CONN_TYPE_AP_DNS_LISTENER: \ + case CONN_TYPE_AP_HTTP_CONNECT_LISTENER + +/**************************************************************/ + +/** Convert a connection_t* to an listener_connection_t*; assert if the cast + * is invalid. */ +listener_connection_t * +TO_LISTENER_CONN(connection_t *c) +{ + tor_assert(c->magic == LISTENER_CONNECTION_MAGIC); + return DOWNCAST(listener_connection_t, c); +} + +size_t +connection_get_inbuf_len(connection_t *conn) +{ + return conn->inbuf ? buf_datalen(conn->inbuf) : 0; +} + +size_t +connection_get_outbuf_len(connection_t *conn) +{ + return conn->outbuf ? buf_datalen(conn->outbuf) : 0; +} + +/** + * Return the human-readable name for the connection type <b>type</b> + */ +const char * +conn_type_to_string(int type) +{ + static char buf[64]; + switch (type) { + case CONN_TYPE_OR_LISTENER: return "OR listener"; + case CONN_TYPE_OR: return "OR"; + case CONN_TYPE_EXIT: return "Exit"; + case CONN_TYPE_AP_LISTENER: return "Socks listener"; + case CONN_TYPE_AP_TRANS_LISTENER: + return "Transparent pf/netfilter listener"; + case CONN_TYPE_AP_NATD_LISTENER: return "Transparent natd listener"; + case CONN_TYPE_AP_DNS_LISTENER: return "DNS listener"; + case CONN_TYPE_AP: return "Socks"; + case CONN_TYPE_DIR_LISTENER: return "Directory listener"; + case CONN_TYPE_DIR: return "Directory"; + case CONN_TYPE_CONTROL_LISTENER: return "Control listener"; + case CONN_TYPE_CONTROL: return "Control"; + case CONN_TYPE_EXT_OR: return "Extended OR"; + case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener"; + case CONN_TYPE_AP_HTTP_CONNECT_LISTENER: return "HTTP tunnel listener"; + default: + log_warn(LD_BUG, "unknown connection type %d", type); + tor_snprintf(buf, sizeof(buf), "unknown [%d]", type); + return buf; + } +} + +/** + * Return the human-readable name for the connection state <b>state</b> + * for the connection type <b>type</b> + */ +const char * +conn_state_to_string(int type, int state) +{ + static char buf[96]; + switch (type) { + CASE_ANY_LISTENER_TYPE: + if (state == LISTENER_STATE_READY) + return "ready"; + break; + case CONN_TYPE_OR: + switch (state) { + case OR_CONN_STATE_CONNECTING: return "connect()ing"; + case OR_CONN_STATE_PROXY_HANDSHAKING: return "handshaking (proxy)"; + case OR_CONN_STATE_TLS_HANDSHAKING: return "handshaking (TLS)"; + case OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING: + return "renegotiating (TLS, v2 handshake)"; + case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: + return "waiting for renegotiation or V3 handshake"; + case OR_CONN_STATE_OR_HANDSHAKING_V2: + return "handshaking (Tor, v2 handshake)"; + case OR_CONN_STATE_OR_HANDSHAKING_V3: + return "handshaking (Tor, v3 handshake)"; + case OR_CONN_STATE_OPEN: return "open"; + } + break; + case CONN_TYPE_EXT_OR: + switch (state) { + case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE: + return "waiting for authentication type"; + case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE: + return "waiting for client nonce"; + case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH: + return "waiting for client hash"; + case EXT_OR_CONN_STATE_OPEN: return "open"; + case EXT_OR_CONN_STATE_FLUSHING: return "flushing final OKAY"; + } + break; + case CONN_TYPE_EXIT: + switch (state) { + case EXIT_CONN_STATE_RESOLVING: return "waiting for dest info"; + case EXIT_CONN_STATE_CONNECTING: return "connecting"; + case EXIT_CONN_STATE_OPEN: return "open"; + case EXIT_CONN_STATE_RESOLVEFAILED: return "resolve failed"; + } + break; + case CONN_TYPE_AP: + switch (state) { + case AP_CONN_STATE_SOCKS_WAIT: return "waiting for socks info"; + case AP_CONN_STATE_NATD_WAIT: return "waiting for natd dest info"; + case AP_CONN_STATE_RENDDESC_WAIT: return "waiting for rendezvous desc"; + case AP_CONN_STATE_CONTROLLER_WAIT: return "waiting for controller"; + case AP_CONN_STATE_CIRCUIT_WAIT: return "waiting for circuit"; + case AP_CONN_STATE_CONNECT_WAIT: return "waiting for connect response"; + case AP_CONN_STATE_RESOLVE_WAIT: return "waiting for resolve response"; + case AP_CONN_STATE_OPEN: return "open"; + } + break; + case CONN_TYPE_DIR: + switch (state) { + case DIR_CONN_STATE_CONNECTING: return "connecting"; + case DIR_CONN_STATE_CLIENT_SENDING: return "client sending"; + case DIR_CONN_STATE_CLIENT_READING: return "client reading"; + case DIR_CONN_STATE_CLIENT_FINISHED: return "client finished"; + case DIR_CONN_STATE_SERVER_COMMAND_WAIT: return "waiting for command"; + case DIR_CONN_STATE_SERVER_WRITING: return "writing"; + } + break; + case CONN_TYPE_CONTROL: + switch (state) { + case CONTROL_CONN_STATE_OPEN: return "open (protocol v1)"; + case CONTROL_CONN_STATE_NEEDAUTH: + return "waiting for authentication (protocol v1)"; + } + break; + } + + log_warn(LD_BUG, "unknown connection state %d (type %d)", state, type); + tor_snprintf(buf, sizeof(buf), + "unknown state [%d] on unknown [%s] connection", + state, conn_type_to_string(type)); + return buf; +} + +/** Allocate and return a new dir_connection_t, initialized as by + * connection_init(). */ +dir_connection_t * +dir_connection_new(int socket_family) +{ + dir_connection_t *dir_conn = tor_malloc_zero(sizeof(dir_connection_t)); + connection_init(time(NULL), TO_CONN(dir_conn), CONN_TYPE_DIR, socket_family); + return dir_conn; +} + +/** Allocate and return a new or_connection_t, initialized as by + * connection_init(). + * + * Initialize active_circuit_pqueue. + * + * Set active_circuit_pqueue_last_recalibrated to current cell_ewma tick. + */ +or_connection_t * +or_connection_new(int type, int socket_family) +{ + or_connection_t *or_conn = tor_malloc_zero(sizeof(or_connection_t)); + time_t now = time(NULL); + tor_assert(type == CONN_TYPE_OR || type == CONN_TYPE_EXT_OR); + connection_init(now, TO_CONN(or_conn), type, socket_family); + + connection_or_set_canonical(or_conn, 0); + + if (type == CONN_TYPE_EXT_OR) + connection_or_set_ext_or_identifier(or_conn); + + return or_conn; +} + +/** Allocate and return a new entry_connection_t, initialized as by + * connection_init(). + * + * Allocate space to store the socks_request. + */ +entry_connection_t * +entry_connection_new(int type, int socket_family) +{ + entry_connection_t *entry_conn = tor_malloc_zero(sizeof(entry_connection_t)); + tor_assert(type == CONN_TYPE_AP); + connection_init(time(NULL), ENTRY_TO_CONN(entry_conn), type, socket_family); + entry_conn->socks_request = socks_request_new(); + /* If this is coming from a listener, we'll set it up based on the listener + * in a little while. Otherwise, we're doing this as a linked connection + * of some kind, and we should set it up here based on the socket family */ + if (socket_family == AF_INET) + entry_conn->entry_cfg.ipv4_traffic = 1; + else if (socket_family == AF_INET6) + entry_conn->entry_cfg.ipv6_traffic = 1; + return entry_conn; +} + +/** Allocate and return a new edge_connection_t, initialized as by + * connection_init(). */ +edge_connection_t * +edge_connection_new(int type, int socket_family) +{ + edge_connection_t *edge_conn = tor_malloc_zero(sizeof(edge_connection_t)); + tor_assert(type == CONN_TYPE_EXIT); + connection_init(time(NULL), TO_CONN(edge_conn), type, socket_family); + return edge_conn; +} + +/** Allocate and return a new control_connection_t, initialized as by + * connection_init(). */ +control_connection_t * +control_connection_new(int socket_family) +{ + control_connection_t *control_conn = + tor_malloc_zero(sizeof(control_connection_t)); + connection_init(time(NULL), + TO_CONN(control_conn), CONN_TYPE_CONTROL, socket_family); + return control_conn; +} + +/** Allocate and return a new listener_connection_t, initialized as by + * connection_init(). */ +listener_connection_t * +listener_connection_new(int type, int socket_family) +{ + listener_connection_t *listener_conn = + tor_malloc_zero(sizeof(listener_connection_t)); + connection_init(time(NULL), TO_CONN(listener_conn), type, socket_family); + return listener_conn; +} + +/** Allocate, initialize, and return a new connection_t subtype of <b>type</b> + * to make or receive connections of address family <b>socket_family</b>. The + * type should be one of the CONN_TYPE_* constants. */ +connection_t * +connection_new(int type, int socket_family) +{ + switch (type) { + case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: + return TO_CONN(or_connection_new(type, socket_family)); + + case CONN_TYPE_EXIT: + return TO_CONN(edge_connection_new(type, socket_family)); + + case CONN_TYPE_AP: + return ENTRY_TO_CONN(entry_connection_new(type, socket_family)); + + case CONN_TYPE_DIR: + return TO_CONN(dir_connection_new(socket_family)); + + case CONN_TYPE_CONTROL: + return TO_CONN(control_connection_new(socket_family)); + + CASE_ANY_LISTENER_TYPE: + return TO_CONN(listener_connection_new(type, socket_family)); + + default: { + connection_t *conn = tor_malloc_zero(sizeof(connection_t)); + connection_init(time(NULL), conn, type, socket_family); + return conn; + } + } +} + +/** Initializes conn. (you must call connection_add() to link it into the main + * array). + * + * Set conn-\>magic to the correct value. + * + * Set conn-\>type to <b>type</b>. Set conn-\>s and conn-\>conn_array_index to + * -1 to signify they are not yet assigned. + * + * Initialize conn's timestamps to now. + */ +static void +connection_init(time_t now, connection_t *conn, int type, int socket_family) +{ + static uint64_t n_connections_allocated = 1; + + switch (type) { + case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: + conn->magic = OR_CONNECTION_MAGIC; + break; + case CONN_TYPE_EXIT: + conn->magic = EDGE_CONNECTION_MAGIC; + break; + case CONN_TYPE_AP: + conn->magic = ENTRY_CONNECTION_MAGIC; + break; + case CONN_TYPE_DIR: + conn->magic = DIR_CONNECTION_MAGIC; + break; + case CONN_TYPE_CONTROL: + conn->magic = CONTROL_CONNECTION_MAGIC; + break; + CASE_ANY_LISTENER_TYPE: + conn->magic = LISTENER_CONNECTION_MAGIC; + break; + default: + conn->magic = BASE_CONNECTION_MAGIC; + break; + } + + conn->s = TOR_INVALID_SOCKET; /* give it a default of 'not used' */ + conn->conn_array_index = -1; /* also default to 'not used' */ + conn->global_identifier = n_connections_allocated++; + + conn->type = type; + conn->socket_family = socket_family; + if (!connection_is_listener(conn)) { + /* listeners never use their buf */ + conn->inbuf = buf_new(); + conn->outbuf = buf_new(); + } + + conn->timestamp_created = now; + conn->timestamp_last_read_allowed = now; + conn->timestamp_last_write_allowed = now; +} + +/** Create a link between <b>conn_a</b> and <b>conn_b</b>. */ +void +connection_link_connections(connection_t *conn_a, connection_t *conn_b) +{ + tor_assert(! SOCKET_OK(conn_a->s)); + tor_assert(! SOCKET_OK(conn_b->s)); + + conn_a->linked = 1; + conn_b->linked = 1; + conn_a->linked_conn = conn_b; + conn_b->linked_conn = conn_a; +} + +/** Return true iff the provided connection listener type supports AF_UNIX + * sockets. */ +int +conn_listener_type_supports_af_unix(int type) +{ + /* For now only control ports or SOCKS ports can be Unix domain sockets + * and listeners at the same time */ + switch (type) { + case CONN_TYPE_CONTROL_LISTENER: + case CONN_TYPE_AP_LISTENER: + return 1; + default: + return 0; + } +} + +/** Deallocate memory used by <b>conn</b>. Deallocate its buffers if + * necessary, close its socket if necessary, and mark the directory as dirty + * if <b>conn</b> is an OR or OP connection. + */ +STATIC void +connection_free_minimal(connection_t *conn) +{ + void *mem; + size_t memlen; + if (!conn) + return; + + switch (conn->type) { + case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: + tor_assert(conn->magic == OR_CONNECTION_MAGIC); + mem = TO_OR_CONN(conn); + memlen = sizeof(or_connection_t); + break; + case CONN_TYPE_AP: + tor_assert(conn->magic == ENTRY_CONNECTION_MAGIC); + mem = TO_ENTRY_CONN(conn); + memlen = sizeof(entry_connection_t); + break; + case CONN_TYPE_EXIT: + tor_assert(conn->magic == EDGE_CONNECTION_MAGIC); + mem = TO_EDGE_CONN(conn); + memlen = sizeof(edge_connection_t); + break; + case CONN_TYPE_DIR: + tor_assert(conn->magic == DIR_CONNECTION_MAGIC); + mem = TO_DIR_CONN(conn); + memlen = sizeof(dir_connection_t); + break; + case CONN_TYPE_CONTROL: + tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC); + mem = TO_CONTROL_CONN(conn); + memlen = sizeof(control_connection_t); + break; + CASE_ANY_LISTENER_TYPE: + tor_assert(conn->magic == LISTENER_CONNECTION_MAGIC); + mem = TO_LISTENER_CONN(conn); + memlen = sizeof(listener_connection_t); + break; + default: + tor_assert(conn->magic == BASE_CONNECTION_MAGIC); + mem = conn; + memlen = sizeof(connection_t); + break; + } + + if (conn->linked) { + log_info(LD_GENERAL, "Freeing linked %s connection [%s] with %d " + "bytes on inbuf, %d on outbuf.", + conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state), + (int)connection_get_inbuf_len(conn), + (int)connection_get_outbuf_len(conn)); + } + + if (!connection_is_listener(conn)) { + buf_free(conn->inbuf); + buf_free(conn->outbuf); + } else { + if (conn->socket_family == AF_UNIX) { + /* For now only control and SOCKS ports can be Unix domain sockets + * and listeners at the same time */ + tor_assert(conn_listener_type_supports_af_unix(conn->type)); + + if (unlink(conn->address) < 0 && errno != ENOENT) { + log_warn(LD_NET, "Could not unlink %s: %s", conn->address, + strerror(errno)); + } + } + } + + tor_free(conn->address); + + if (connection_speaks_cells(conn)) { + or_connection_t *or_conn = TO_OR_CONN(conn); + tor_tls_free(or_conn->tls); + or_conn->tls = NULL; + or_handshake_state_free(or_conn->handshake_state); + or_conn->handshake_state = NULL; + tor_free(or_conn->nickname); + if (or_conn->chan) { + /* Owww, this shouldn't happen, but... */ + log_info(LD_CHANNEL, + "Freeing orconn at %p, saw channel %p with ID " + "%"PRIu64 " left un-NULLed", + or_conn, TLS_CHAN_TO_BASE(or_conn->chan), + ( + TLS_CHAN_TO_BASE(or_conn->chan)->global_identifier)); + if (!CHANNEL_FINISHED(TLS_CHAN_TO_BASE(or_conn->chan))) { + channel_close_for_error(TLS_CHAN_TO_BASE(or_conn->chan)); + } + + or_conn->chan->conn = NULL; + or_conn->chan = NULL; + } + } + if (conn->type == CONN_TYPE_AP) { + entry_connection_t *entry_conn = TO_ENTRY_CONN(conn); + tor_free(entry_conn->chosen_exit_name); + tor_free(entry_conn->original_dest_address); + if (entry_conn->socks_request) + socks_request_free(entry_conn->socks_request); + if (entry_conn->pending_optimistic_data) { + buf_free(entry_conn->pending_optimistic_data); + } + if (entry_conn->sending_optimistic_data) { + buf_free(entry_conn->sending_optimistic_data); + } + } + if (CONN_IS_EDGE(conn)) { + rend_data_free(TO_EDGE_CONN(conn)->rend_data); + hs_ident_edge_conn_free(TO_EDGE_CONN(conn)->hs_ident); + } + if (conn->type == CONN_TYPE_CONTROL) { + control_connection_t *control_conn = TO_CONTROL_CONN(conn); + tor_free(control_conn->safecookie_client_hash); + tor_free(control_conn->incoming_cmd); + if (control_conn->ephemeral_onion_services) { + SMARTLIST_FOREACH(control_conn->ephemeral_onion_services, char *, cp, { + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + }); + smartlist_free(control_conn->ephemeral_onion_services); + } + } + + /* Probably already freed by connection_free. */ + tor_event_free(conn->read_event); + tor_event_free(conn->write_event); + conn->read_event = conn->write_event = NULL; + + if (conn->type == CONN_TYPE_DIR) { + dir_connection_t *dir_conn = TO_DIR_CONN(conn); + tor_free(dir_conn->requested_resource); + + tor_compress_free(dir_conn->compress_state); + if (dir_conn->spool) { + SMARTLIST_FOREACH(dir_conn->spool, spooled_resource_t *, spooled, + spooled_resource_free(spooled)); + smartlist_free(dir_conn->spool); + } + + rend_data_free(dir_conn->rend_data); + hs_ident_dir_conn_free(dir_conn->hs_ident); + if (dir_conn->guard_state) { + /* Cancel before freeing, if it's still there. */ + entry_guard_cancel(&dir_conn->guard_state); + } + circuit_guard_state_free(dir_conn->guard_state); + } + + if (SOCKET_OK(conn->s)) { + log_debug(LD_NET,"closing fd %d.",(int)conn->s); + tor_close_socket(conn->s); + conn->s = TOR_INVALID_SOCKET; + } + + if (conn->type == CONN_TYPE_OR && + !tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) { + log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest"); + connection_or_clear_identity(TO_OR_CONN(conn)); + } + if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) { + connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn)); + tor_free(TO_OR_CONN(conn)->ext_or_conn_id); + tor_free(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash); + tor_free(TO_OR_CONN(conn)->ext_or_transport); + } + + memwipe(mem, 0xCC, memlen); /* poison memory */ + tor_free(mem); +} + +/** Make sure <b>conn</b> isn't in any of the global conn lists; then free it. + */ +MOCK_IMPL(void, +connection_free_,(connection_t *conn)) +{ + if (!conn) + return; + tor_assert(!connection_is_on_closeable_list(conn)); + tor_assert(!connection_in_array(conn)); + if (BUG(conn->linked_conn)) { + conn->linked_conn->linked_conn = NULL; + if (! conn->linked_conn->marked_for_close && + conn->linked_conn->reading_from_linked_conn) + connection_start_reading(conn->linked_conn); + conn->linked_conn = NULL; + } + if (connection_speaks_cells(conn)) { + if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) { + connection_or_clear_identity(TO_OR_CONN(conn)); + } + } + if (conn->type == CONN_TYPE_CONTROL) { + connection_control_closed(TO_CONTROL_CONN(conn)); + } +#if 1 + /* DEBUGGING */ + if (conn->type == CONN_TYPE_AP) { + connection_ap_warn_and_unmark_if_pending_circ(TO_ENTRY_CONN(conn), + "connection_free"); + } +#endif /* 1 */ + + /* Notify the circuit creation DoS mitigation subsystem that an OR client + * connection has been closed. And only do that if we track it. */ + if (conn->type == CONN_TYPE_OR) { + dos_close_client_conn(TO_OR_CONN(conn)); + } + + connection_unregister_events(conn); + connection_free_minimal(conn); +} + +/** + * Called when we're about to finally unlink and free a connection: + * perform necessary accounting and cleanup + * - Directory conns that failed to fetch a rendezvous descriptor + * need to inform pending rendezvous streams. + * - OR conns need to call rep_hist_note_*() to record status. + * - AP conns need to send a socks reject if necessary. + * - Exit conns need to call connection_dns_remove() if necessary. + * - AP and Exit conns need to send an end cell if they can. + * - DNS conns need to fail any resolves that are pending on them. + * - OR and edge connections need to be unlinked from circuits. + */ +void +connection_about_to_close_connection(connection_t *conn) +{ + tor_assert(conn->marked_for_close); + + switch (conn->type) { + case CONN_TYPE_DIR: + connection_dir_about_to_close(TO_DIR_CONN(conn)); + break; + case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: + connection_or_about_to_close(TO_OR_CONN(conn)); + break; + case CONN_TYPE_AP: + connection_ap_about_to_close(TO_ENTRY_CONN(conn)); + break; + case CONN_TYPE_EXIT: + connection_exit_about_to_close(TO_EDGE_CONN(conn)); + break; + } +} + +/** Return true iff connection_close_immediate() has been called on this + * connection. */ +#define CONN_IS_CLOSED(c) \ + ((c)->linked ? ((c)->linked_conn_is_closed) : (! SOCKET_OK(c->s))) + +/** Close the underlying socket for <b>conn</b>, so we don't try to + * flush it. Must be used in conjunction with (right before) + * connection_mark_for_close(). + */ +void +connection_close_immediate(connection_t *conn) +{ + assert_connection_ok(conn,0); + if (CONN_IS_CLOSED(conn)) { + log_err(LD_BUG,"Attempt to close already-closed connection."); + tor_fragile_assert(); + return; + } + if (conn->outbuf_flushlen) { + log_info(LD_NET,"fd %d, type %s, state %s, %d bytes on outbuf.", + (int)conn->s, conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state), + (int)conn->outbuf_flushlen); + } + + connection_unregister_events(conn); + + /* Prevent the event from getting unblocked. */ + conn->read_blocked_on_bw = 0; + conn->write_blocked_on_bw = 0; + + if (SOCKET_OK(conn->s)) + tor_close_socket(conn->s); + conn->s = TOR_INVALID_SOCKET; + if (conn->linked) + conn->linked_conn_is_closed = 1; + if (conn->outbuf) + buf_clear(conn->outbuf); + conn->outbuf_flushlen = 0; +} + +/** Mark <b>conn</b> to be closed next time we loop through + * conn_close_if_marked() in main.c. */ +void +connection_mark_for_close_(connection_t *conn, int line, const char *file) +{ + assert_connection_ok(conn,0); + tor_assert(line); + tor_assert(line < 1<<16); /* marked_for_close can only fit a uint16_t. */ + tor_assert(file); + + if (conn->type == CONN_TYPE_OR) { + /* + * An or_connection should have been closed through one of the channel- + * aware functions in connection_or.c. We'll assume this is an error + * close and do that, and log a bug warning. + */ + log_warn(LD_CHANNEL | LD_BUG, + "Something tried to close an or_connection_t without going " + "through channels at %s:%d", + file, line); + connection_or_close_for_error(TO_OR_CONN(conn), 0); + } else { + /* Pass it down to the real function */ + connection_mark_for_close_internal_(conn, line, file); + } +} + +/** Mark <b>conn</b> to be closed next time we loop through + * conn_close_if_marked() in main.c; the _internal version bypasses the + * CONN_TYPE_OR checks; this should be called when you either are sure that + * if this is an or_connection_t the controlling channel has been notified + * (e.g. with connection_or_notify_error()), or you actually are the + * connection_or_close_for_error() or connection_or_close_normally() function. + * For all other cases, use connection_mark_and_flush() instead, which + * checks for or_connection_t properly, instead. See below. + */ +MOCK_IMPL(void, +connection_mark_for_close_internal_, (connection_t *conn, + int line, const char *file)) +{ + assert_connection_ok(conn,0); + tor_assert(line); + tor_assert(line < 1<<16); /* marked_for_close can only fit a uint16_t. */ + tor_assert(file); + + if (conn->marked_for_close) { + log_warn(LD_BUG,"Duplicate call to connection_mark_for_close at %s:%d" + " (first at %s:%d)", file, line, conn->marked_for_close_file, + conn->marked_for_close); + tor_fragile_assert(); + return; + } + + if (conn->type == CONN_TYPE_OR) { + /* + * Bad news if this happens without telling the controlling channel; do + * this so we can find things that call this wrongly when the asserts hit. + */ + log_debug(LD_CHANNEL, + "Calling connection_mark_for_close_internal_() on an OR conn " + "at %s:%d", + file, line); + } + + conn->marked_for_close = line; + conn->marked_for_close_file = file; + add_connection_to_closeable_list(conn); + + /* in case we're going to be held-open-til-flushed, reset + * the number of seconds since last successful write, so + * we get our whole 15 seconds */ + conn->timestamp_last_write_allowed = time(NULL); +} + +/** Find each connection that has hold_open_until_flushed set to + * 1 but hasn't written in the past 15 seconds, and set + * hold_open_until_flushed to 0. This means it will get cleaned + * up in the next loop through close_if_marked() in main.c. + */ +void +connection_expire_held_open(void) +{ + time_t now; + smartlist_t *conns = get_connection_array(); + + now = time(NULL); + + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { + /* If we've been holding the connection open, but we haven't written + * for 15 seconds... + */ + if (conn->hold_open_until_flushed) { + tor_assert(conn->marked_for_close); + if (now - conn->timestamp_last_write_allowed >= 15) { + int severity; + if (conn->type == CONN_TYPE_EXIT || + (conn->type == CONN_TYPE_DIR && + conn->purpose == DIR_PURPOSE_SERVER)) + severity = LOG_INFO; + else + severity = LOG_NOTICE; + log_fn(severity, LD_NET, + "Giving up on marked_for_close conn that's been flushing " + "for 15s (fd %d, type %s, state %s).", + (int)conn->s, conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state)); + conn->hold_open_until_flushed = 0; + } + } + } SMARTLIST_FOREACH_END(conn); +} + +#if defined(HAVE_SYS_UN_H) || defined(RUNNING_DOXYGEN) +/** Create an AF_UNIX listenaddr struct. + * <b>listenaddress</b> provides the path to the Unix socket. + * + * Eventually <b>listenaddress</b> will also optionally contain user, group, + * and file permissions for the new socket. But not yet. XXX + * Also, since we do not create the socket here the information doesn't help + * here. + * + * If not NULL <b>readable_address</b> will contain a copy of the path part of + * <b>listenaddress</b>. + * + * The listenaddr struct has to be freed by the caller. + */ +static struct sockaddr_un * +create_unix_sockaddr(const char *listenaddress, char **readable_address, + socklen_t *len_out) +{ + struct sockaddr_un *sockaddr = NULL; + + sockaddr = tor_malloc_zero(sizeof(struct sockaddr_un)); + sockaddr->sun_family = AF_UNIX; + if (strlcpy(sockaddr->sun_path, listenaddress, sizeof(sockaddr->sun_path)) + >= sizeof(sockaddr->sun_path)) { + log_warn(LD_CONFIG, "Unix socket path '%s' is too long to fit.", + escaped(listenaddress)); + tor_free(sockaddr); + return NULL; + } + + if (readable_address) + *readable_address = tor_strdup(listenaddress); + + *len_out = sizeof(struct sockaddr_un); + return sockaddr; +} +#else /* !(defined(HAVE_SYS_UN_H) || defined(RUNNING_DOXYGEN)) */ +static struct sockaddr * +create_unix_sockaddr(const char *listenaddress, char **readable_address, + socklen_t *len_out) +{ + (void)listenaddress; + (void)readable_address; + log_fn(LOG_ERR, LD_BUG, + "Unix domain sockets not supported, yet we tried to create one."); + *len_out = 0; + tor_fragile_assert(); + return NULL; +} +#endif /* defined(HAVE_SYS_UN_H) || defined(RUNNING_DOXYGEN) */ + +/** Warn that an accept or a connect has failed because we're running out of + * TCP sockets we can use on current system. Rate-limit these warnings so + * that we don't spam the log. */ +static void +warn_too_many_conns(void) +{ +#define WARN_TOO_MANY_CONNS_INTERVAL (6*60*60) + static ratelim_t last_warned = RATELIM_INIT(WARN_TOO_MANY_CONNS_INTERVAL); + char *m; + if ((m = rate_limit_log(&last_warned, approx_time()))) { + int n_conns = get_n_open_sockets(); + log_warn(LD_NET,"Failing because we have %d connections already. Please " + "read doc/TUNING for guidance.%s", n_conns, m); + tor_free(m); + control_event_general_status(LOG_WARN, "TOO_MANY_CONNECTIONS CURRENT=%d", + n_conns); + } +} + +#ifdef HAVE_SYS_UN_H + +#define UNIX_SOCKET_PURPOSE_CONTROL_SOCKET 0 +#define UNIX_SOCKET_PURPOSE_SOCKS_SOCKET 1 + +/** Check if the purpose isn't one of the ones we know what to do with */ + +static int +is_valid_unix_socket_purpose(int purpose) +{ + int valid = 0; + + switch (purpose) { + case UNIX_SOCKET_PURPOSE_CONTROL_SOCKET: + case UNIX_SOCKET_PURPOSE_SOCKS_SOCKET: + valid = 1; + break; + } + + return valid; +} + +/** Return a string description of a unix socket purpose */ +static const char * +unix_socket_purpose_to_string(int purpose) +{ + const char *s = "unknown-purpose socket"; + + switch (purpose) { + case UNIX_SOCKET_PURPOSE_CONTROL_SOCKET: + s = "control socket"; + break; + case UNIX_SOCKET_PURPOSE_SOCKS_SOCKET: + s = "SOCKS socket"; + break; + } + + return s; +} + +/** Check whether we should be willing to open an AF_UNIX socket in + * <b>path</b>. Return 0 if we should go ahead and -1 if we shouldn't. */ +static int +check_location_for_unix_socket(const or_options_t *options, const char *path, + int purpose, const port_cfg_t *port) +{ + int r = -1; + char *p = NULL; + + tor_assert(is_valid_unix_socket_purpose(purpose)); + + p = tor_strdup(path); + cpd_check_t flags = CPD_CHECK_MODE_ONLY; + if (get_parent_directory(p)<0 || p[0] != '/') { + log_warn(LD_GENERAL, "Bad unix socket address '%s'. Tor does not support " + "relative paths for unix sockets.", path); + goto done; + } + + if (port->is_world_writable) { + /* World-writable sockets can go anywhere. */ + r = 0; + goto done; + } + + if (port->is_group_writable) { + flags |= CPD_GROUP_OK; + } + + if (port->relax_dirmode_check) { + flags |= CPD_RELAX_DIRMODE_CHECK; + } + + if (check_private_dir(p, flags, options->User) < 0) { + char *escpath, *escdir; + escpath = esc_for_log(path); + escdir = esc_for_log(p); + log_warn(LD_GENERAL, "Before Tor can create a %s in %s, the directory " + "%s needs to exist, and to be accessible only by the user%s " + "account that is running Tor. (On some Unix systems, anybody " + "who can list a socket can connect to it, so Tor is being " + "careful.)", + unix_socket_purpose_to_string(purpose), escpath, escdir, + port->is_group_writable ? " and group" : ""); + tor_free(escpath); + tor_free(escdir); + goto done; + } + + r = 0; + done: + tor_free(p); + return r; +} +#endif /* defined(HAVE_SYS_UN_H) */ + +/** Tell the TCP stack that it shouldn't wait for a long time after + * <b>sock</b> has closed before reusing its port. Return 0 on success, + * -1 on failure. */ +static int +make_socket_reuseable(tor_socket_t sock) +{ +#ifdef _WIN32 + (void) sock; + return 0; +#else + int one=1; + + /* REUSEADDR on normal places means you can rebind to the port + * right after somebody else has let it go. But REUSEADDR on win32 + * means you can bind to the port _even when somebody else + * already has it bound_. So, don't do that on Win32. */ + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &one, + (socklen_t)sizeof(one)) == -1) { + return -1; + } + return 0; +#endif /* defined(_WIN32) */ +} + +#ifdef _WIN32 +/** Tell the Windows TCP stack to prevent other applications from receiving + * traffic from tor's open ports. Return 0 on success, -1 on failure. */ +static int +make_win32_socket_exclusive(tor_socket_t sock) +{ +#ifdef SO_EXCLUSIVEADDRUSE + int one=1; + + /* Any socket that sets REUSEADDR on win32 can bind to a port _even when + * somebody else already has it bound_, and _even if the original socket + * didn't set REUSEADDR_. Use EXCLUSIVEADDRUSE to prevent this port-stealing + * on win32. */ + if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void*) &one, + (socklen_t)sizeof(one))) { + return -1; + } + return 0; +#else /* !(defined(SO_EXCLUSIVEADDRUSE)) */ + (void) sock; + return 0; +#endif /* defined(SO_EXCLUSIVEADDRUSE) */ +} +#endif /* defined(_WIN32) */ + +/** Max backlog to pass to listen. We start at */ +static int listen_limit = INT_MAX; + +/* Listen on <b>fd</b> with appropriate backlog. Return as for listen. */ +static int +tor_listen(tor_socket_t fd) +{ + int r; + + if ((r = listen(fd, listen_limit)) < 0) { + if (listen_limit == SOMAXCONN) + return r; + if ((r = listen(fd, SOMAXCONN)) == 0) { + listen_limit = SOMAXCONN; + log_warn(LD_NET, "Setting listen backlog to INT_MAX connections " + "didn't work, but SOMAXCONN did. Lowering backlog limit."); + } + } + return r; +} + +/** Bind a new non-blocking socket listening to the socket described + * by <b>listensockaddr</b>. + * + * <b>address</b> is only used for logging purposes and to add the information + * to the conn. + */ +static connection_t * +connection_listener_new(const struct sockaddr *listensockaddr, + socklen_t socklen, + int type, const char *address, + const port_cfg_t *port_cfg) +{ + listener_connection_t *lis_conn; + connection_t *conn = NULL; + tor_socket_t s = TOR_INVALID_SOCKET; /* the socket we're going to make */ + or_options_t const *options = get_options(); + (void) options; /* Windows doesn't use this. */ +#if defined(HAVE_PWD_H) && defined(HAVE_SYS_UN_H) + const struct passwd *pw = NULL; +#endif + uint16_t usePort = 0, gotPort = 0; + int start_reading = 0; + static int global_next_session_group = SESSION_GROUP_FIRST_AUTO; + tor_addr_t addr; + int exhaustion = 0; + + if (listensockaddr->sa_family == AF_INET || + listensockaddr->sa_family == AF_INET6) { + int is_stream = (type != CONN_TYPE_AP_DNS_LISTENER); + if (is_stream) + start_reading = 1; + + tor_addr_from_sockaddr(&addr, listensockaddr, &usePort); + log_notice(LD_NET, "Opening %s on %s", + conn_type_to_string(type), fmt_addrport(&addr, usePort)); + + s = tor_open_socket_nonblocking(tor_addr_family(&addr), + is_stream ? SOCK_STREAM : SOCK_DGRAM, + is_stream ? IPPROTO_TCP: IPPROTO_UDP); + if (!SOCKET_OK(s)) { + int e = tor_socket_errno(s); + if (ERRNO_IS_RESOURCE_LIMIT(e)) { + warn_too_many_conns(); + /* + * We'll call the OOS handler at the error exit, so set the + * exhaustion flag for it. + */ + exhaustion = 1; + } else { + log_warn(LD_NET, "Socket creation failed: %s", + tor_socket_strerror(e)); + } + goto err; + } + + if (make_socket_reuseable(s) < 0) { + log_warn(LD_NET, "Error setting SO_REUSEADDR flag on %s: %s", + conn_type_to_string(type), + tor_socket_strerror(errno)); + } + +#ifdef _WIN32 + if (make_win32_socket_exclusive(s) < 0) { + log_warn(LD_NET, "Error setting SO_EXCLUSIVEADDRUSE flag on %s: %s", + conn_type_to_string(type), + tor_socket_strerror(errno)); + } +#endif /* defined(_WIN32) */ + +#if defined(USE_TRANSPARENT) && defined(IP_TRANSPARENT) + if (options->TransProxyType_parsed == TPT_TPROXY && + type == CONN_TYPE_AP_TRANS_LISTENER) { + int one = 1; + if (setsockopt(s, SOL_IP, IP_TRANSPARENT, (void*)&one, + (socklen_t)sizeof(one)) < 0) { + const char *extra = ""; + int e = tor_socket_errno(s); + if (e == EPERM) + extra = "TransTPROXY requires root privileges or similar" + " capabilities."; + log_warn(LD_NET, "Error setting IP_TRANSPARENT flag: %s.%s", + tor_socket_strerror(e), extra); + } + } +#endif /* defined(USE_TRANSPARENT) && defined(IP_TRANSPARENT) */ + +#ifdef IPV6_V6ONLY + if (listensockaddr->sa_family == AF_INET6) { + int one = 1; + /* We need to set IPV6_V6ONLY so that this socket can't get used for + * IPv4 connections. */ + if (setsockopt(s,IPPROTO_IPV6, IPV6_V6ONLY, + (void*)&one, (socklen_t)sizeof(one)) < 0) { + int e = tor_socket_errno(s); + log_warn(LD_NET, "Error setting IPV6_V6ONLY flag: %s", + tor_socket_strerror(e)); + /* Keep going; probably not harmful. */ + } + } +#endif /* defined(IPV6_V6ONLY) */ + + if (bind(s,listensockaddr,socklen) < 0) { + const char *helpfulhint = ""; + int e = tor_socket_errno(s); + if (ERRNO_IS_EADDRINUSE(e)) + helpfulhint = ". Is Tor already running?"; + log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort, + tor_socket_strerror(e), helpfulhint); + goto err; + } + + if (is_stream) { + if (tor_listen(s) < 0) { + log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, + tor_socket_strerror(tor_socket_errno(s))); + goto err; + } + } + + if (usePort != 0) { + gotPort = usePort; + } else { + tor_addr_t addr2; + struct sockaddr_storage ss; + socklen_t ss_len=sizeof(ss); + if (getsockname(s, (struct sockaddr*)&ss, &ss_len)<0) { + log_warn(LD_NET, "getsockname() couldn't learn address for %s: %s", + conn_type_to_string(type), + tor_socket_strerror(tor_socket_errno(s))); + gotPort = 0; + } + tor_addr_from_sockaddr(&addr2, (struct sockaddr*)&ss, &gotPort); + } +#ifdef HAVE_SYS_UN_H + /* + * AF_UNIX generic setup stuff + */ + } else if (listensockaddr->sa_family == AF_UNIX) { + /* We want to start reading for both AF_UNIX cases */ + start_reading = 1; + + tor_assert(conn_listener_type_supports_af_unix(type)); + + if (check_location_for_unix_socket(options, address, + (type == CONN_TYPE_CONTROL_LISTENER) ? + UNIX_SOCKET_PURPOSE_CONTROL_SOCKET : + UNIX_SOCKET_PURPOSE_SOCKS_SOCKET, port_cfg) < 0) { + goto err; + } + + log_notice(LD_NET, "Opening %s on %s", + conn_type_to_string(type), address); + + tor_addr_make_unspec(&addr); + + if (unlink(address) < 0 && errno != ENOENT) { + log_warn(LD_NET, "Could not unlink %s: %s", address, + strerror(errno)); + goto err; + } + + s = tor_open_socket_nonblocking(AF_UNIX, SOCK_STREAM, 0); + if (! SOCKET_OK(s)) { + int e = tor_socket_errno(s); + if (ERRNO_IS_RESOURCE_LIMIT(e)) { + warn_too_many_conns(); + /* + * We'll call the OOS handler at the error exit, so set the + * exhaustion flag for it. + */ + exhaustion = 1; + } else { + log_warn(LD_NET,"Socket creation failed: %s.", strerror(e)); + } + goto err; + } + + if (bind(s, listensockaddr, + (socklen_t)sizeof(struct sockaddr_un)) == -1) { + log_warn(LD_NET,"Bind to %s failed: %s.", address, + tor_socket_strerror(tor_socket_errno(s))); + goto err; + } + +#ifdef HAVE_PWD_H + if (options->User) { + pw = tor_getpwnam(options->User); + struct stat st; + if (pw == NULL) { + log_warn(LD_NET,"Unable to chown() %s socket: user %s not found.", + address, options->User); + goto err; + } else if (fstat(s, &st) == 0 && + st.st_uid == pw->pw_uid && st.st_gid == pw->pw_gid) { + /* No change needed */ + } else if (chown(sandbox_intern_string(address), + pw->pw_uid, pw->pw_gid) < 0) { + log_warn(LD_NET,"Unable to chown() %s socket: %s.", + address, strerror(errno)); + goto err; + } + } +#endif /* defined(HAVE_PWD_H) */ + + { + unsigned mode; + const char *status; + struct stat st; + if (port_cfg->is_world_writable) { + mode = 0666; + status = "world-writable"; + } else if (port_cfg->is_group_writable) { + mode = 0660; + status = "group-writable"; + } else { + mode = 0600; + status = "private"; + } + /* We need to use chmod; fchmod doesn't work on sockets on all + * platforms. */ + if (fstat(s, &st) == 0 && (st.st_mode & 0777) == mode) { + /* no change needed */ + } else if (chmod(sandbox_intern_string(address), mode) < 0) { + log_warn(LD_FS,"Unable to make %s %s.", address, status); + goto err; + } + } + + if (listen(s, SOMAXCONN) < 0) { + log_warn(LD_NET, "Could not listen on %s: %s", address, + tor_socket_strerror(tor_socket_errno(s))); + goto err; + } +#endif /* defined(HAVE_SYS_UN_H) */ + } else { + log_err(LD_BUG, "Got unexpected address family %d.", + listensockaddr->sa_family); + tor_assert(0); + } + + lis_conn = listener_connection_new(type, listensockaddr->sa_family); + conn = TO_CONN(lis_conn); + conn->socket_family = listensockaddr->sa_family; + conn->s = s; + s = TOR_INVALID_SOCKET; /* Prevent double-close */ + conn->address = tor_strdup(address); + conn->port = gotPort; + tor_addr_copy(&conn->addr, &addr); + + memcpy(&lis_conn->entry_cfg, &port_cfg->entry_cfg, sizeof(entry_port_cfg_t)); + + if (port_cfg->entry_cfg.isolation_flags) { + lis_conn->entry_cfg.isolation_flags = port_cfg->entry_cfg.isolation_flags; + if (port_cfg->entry_cfg.session_group >= 0) { + lis_conn->entry_cfg.session_group = port_cfg->entry_cfg.session_group; + } else { + /* This can wrap after around INT_MAX listeners are opened. But I don't + * believe that matters, since you would need to open a ridiculous + * number of listeners while keeping the early ones open before you ever + * hit this. An OR with a dozen ports open, for example, would have to + * close and re-open its listeners every second for 4 years nonstop. + */ + lis_conn->entry_cfg.session_group = global_next_session_group--; + } + } + + if (type != CONN_TYPE_AP_LISTENER) { + lis_conn->entry_cfg.ipv4_traffic = 1; + lis_conn->entry_cfg.ipv6_traffic = 1; + lis_conn->entry_cfg.prefer_ipv6 = 0; + } + + if (connection_add(conn) < 0) { /* no space, forget it */ + log_warn(LD_NET,"connection_add for listener failed. Giving up."); + goto err; + } + + log_fn(usePort==gotPort ? LOG_DEBUG : LOG_NOTICE, LD_NET, + "%s listening on port %u.", + conn_type_to_string(type), gotPort); + + conn->state = LISTENER_STATE_READY; + if (start_reading) { + connection_start_reading(conn); + } else { + tor_assert(type == CONN_TYPE_AP_DNS_LISTENER); + dnsserv_configure_listener(conn); + } + + /* + * Normal exit; call the OOS handler since connection count just changed; + * the exhaustion flag will always be zero here though. + */ + connection_check_oos(get_n_open_sockets(), 0); + + return conn; + + err: + if (SOCKET_OK(s)) + tor_close_socket(s); + if (conn) + connection_free(conn); + + /* Call the OOS handler, indicate if we saw an exhaustion-related error */ + connection_check_oos(get_n_open_sockets(), exhaustion); + + return NULL; +} + +/** Do basic sanity checking on a newly received socket. Return 0 + * if it looks ok, else return -1. + * + * Notably, some TCP stacks can erroneously have accept() return successfully + * with socklen 0, when the client sends an RST before the accept call (as + * nmap does). We want to detect that, and not go on with the connection. + */ +static int +check_sockaddr(const struct sockaddr *sa, int len, int level) +{ + int ok = 1; + + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin=(struct sockaddr_in*)sa; + if (len != sizeof(struct sockaddr_in)) { + log_fn(level, LD_NET, "Length of address not as expected: %d vs %d", + len,(int)sizeof(struct sockaddr_in)); + ok = 0; + } + if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) { + log_fn(level, LD_NET, + "Address for new connection has address/port equal to zero."); + ok = 0; + } + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6=(struct sockaddr_in6*)sa; + if (len != sizeof(struct sockaddr_in6)) { + log_fn(level, LD_NET, "Length of address not as expected: %d vs %d", + len,(int)sizeof(struct sockaddr_in6)); + ok = 0; + } + if (tor_mem_is_zero((void*)sin6->sin6_addr.s6_addr, 16) || + sin6->sin6_port == 0) { + log_fn(level, LD_NET, + "Address for new connection has address/port equal to zero."); + ok = 0; + } + } else if (sa->sa_family == AF_UNIX) { + ok = 1; + } else { + ok = 0; + } + return ok ? 0 : -1; +} + +/** Check whether the socket family from an accepted socket <b>got</b> is the + * same as the one that <b>listener</b> is waiting for. If it isn't, log + * a useful message and return -1. Else return 0. + * + * This is annoying, but can apparently happen on some Darwins. */ +static int +check_sockaddr_family_match(sa_family_t got, connection_t *listener) +{ + if (got != listener->socket_family) { + log_info(LD_BUG, "A listener connection returned a socket with a " + "mismatched family. %s for addr_family %d gave us a socket " + "with address family %d. Dropping.", + conn_type_to_string(listener->type), + (int)listener->socket_family, + (int)got); + return -1; + } + return 0; +} + +/** The listener connection <b>conn</b> told poll() it wanted to read. + * Call accept() on conn-\>s, and add the new connection if necessary. + */ +static int +connection_handle_listener_read(connection_t *conn, int new_type) +{ + tor_socket_t news; /* the new socket */ + connection_t *newconn = 0; + /* information about the remote peer when connecting to other routers */ + struct sockaddr_storage addrbuf; + struct sockaddr *remote = (struct sockaddr*)&addrbuf; + /* length of the remote address. Must be whatever accept() needs. */ + socklen_t remotelen = (socklen_t)sizeof(addrbuf); + const or_options_t *options = get_options(); + + tor_assert((size_t)remotelen >= sizeof(struct sockaddr_in)); + memset(&addrbuf, 0, sizeof(addrbuf)); + + news = tor_accept_socket_nonblocking(conn->s,remote,&remotelen); + if (!SOCKET_OK(news)) { /* accept() error */ + int e = tor_socket_errno(conn->s); + if (ERRNO_IS_ACCEPT_EAGAIN(e)) { + /* + * they hung up before we could accept(). that's fine. + * + * give the OOS handler a chance to run though + */ + connection_check_oos(get_n_open_sockets(), 0); + return 0; + } else if (ERRNO_IS_RESOURCE_LIMIT(e)) { + warn_too_many_conns(); + /* Exhaustion; tell the OOS handler */ + connection_check_oos(get_n_open_sockets(), 1); + return 0; + } + /* else there was a real error. */ + log_warn(LD_NET,"accept() failed: %s. Closing listener.", + tor_socket_strerror(e)); + connection_mark_for_close(conn); + /* Tell the OOS handler about this too */ + connection_check_oos(get_n_open_sockets(), 0); + return -1; + } + log_debug(LD_NET, + "Connection accepted on socket %d (child of fd %d).", + (int)news,(int)conn->s); + + /* We accepted a new conn; run OOS handler */ + connection_check_oos(get_n_open_sockets(), 0); + + if (make_socket_reuseable(news) < 0) { + if (tor_socket_errno(news) == EINVAL) { + /* This can happen on OSX if we get a badly timed shutdown. */ + log_debug(LD_NET, "make_socket_reuseable returned EINVAL"); + } else { + log_warn(LD_NET, "Error setting SO_REUSEADDR flag on %s: %s", + conn_type_to_string(new_type), + tor_socket_strerror(errno)); + } + tor_close_socket(news); + return 0; + } + + if (options->ConstrainedSockets) + set_constrained_socket_buffers(news, (int)options->ConstrainedSockSize); + + if (check_sockaddr_family_match(remote->sa_family, conn) < 0) { + tor_close_socket(news); + return 0; + } + + if (conn->socket_family == AF_INET || conn->socket_family == AF_INET6 || + (conn->socket_family == AF_UNIX && new_type == CONN_TYPE_AP)) { + tor_addr_t addr; + uint16_t port; + if (check_sockaddr(remote, remotelen, LOG_INFO)<0) { + log_info(LD_NET, + "accept() returned a strange address; closing connection."); + tor_close_socket(news); + return 0; + } + + tor_addr_from_sockaddr(&addr, remote, &port); + + /* process entrance policies here, before we even create the connection */ + if (new_type == CONN_TYPE_AP) { + /* check sockspolicy to see if we should accept it */ + if (socks_policy_permits_address(&addr) == 0) { + log_notice(LD_APP, + "Denying socks connection from untrusted address %s.", + fmt_and_decorate_addr(&addr)); + tor_close_socket(news); + return 0; + } + } + if (new_type == CONN_TYPE_DIR) { + /* check dirpolicy to see if we should accept it */ + if (dir_policy_permits_address(&addr) == 0) { + log_notice(LD_DIRSERV,"Denying dir connection from address %s.", + fmt_and_decorate_addr(&addr)); + tor_close_socket(news); + return 0; + } + } + if (new_type == CONN_TYPE_OR) { + /* Assess with the connection DoS mitigation subsystem if this address + * can open a new connection. */ + if (dos_conn_addr_get_defense_type(&addr) == DOS_CONN_DEFENSE_CLOSE) { + tor_close_socket(news); + return 0; + } + } + + newconn = connection_new(new_type, conn->socket_family); + newconn->s = news; + + /* remember the remote address */ + tor_addr_copy(&newconn->addr, &addr); + if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { + newconn->port = 0; + newconn->address = tor_strdup(conn->address); + } else { + newconn->port = port; + newconn->address = tor_addr_to_str_dup(&addr); + } + + if (new_type == CONN_TYPE_AP && conn->socket_family != AF_UNIX) { + log_info(LD_NET, "New SOCKS connection opened from %s.", + fmt_and_decorate_addr(&addr)); + } + if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { + log_info(LD_NET, "New SOCKS AF_UNIX connection opened"); + } + if (new_type == CONN_TYPE_CONTROL) { + log_notice(LD_CONTROL, "New control connection opened from %s.", + fmt_and_decorate_addr(&addr)); + } + + } else if (conn->socket_family == AF_UNIX && conn->type != CONN_TYPE_AP) { + tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER); + tor_assert(new_type == CONN_TYPE_CONTROL); + log_notice(LD_CONTROL, "New control connection opened."); + + newconn = connection_new(new_type, conn->socket_family); + newconn->s = news; + + /* remember the remote address -- do we have anything sane to put here? */ + tor_addr_make_unspec(&newconn->addr); + newconn->port = 1; + newconn->address = tor_strdup(conn->address); + } else { + tor_assert(0); + }; + + if (connection_add(newconn) < 0) { /* no space, forget it */ + connection_free(newconn); + return 0; /* no need to tear down the parent */ + } + + if (connection_init_accepted_conn(newconn, TO_LISTENER_CONN(conn)) < 0) { + if (! newconn->marked_for_close) + connection_mark_for_close(newconn); + return 0; + } + return 0; +} + +/** Initialize states for newly accepted connection <b>conn</b>. + * + * If conn is an OR, start the TLS handshake. + * + * If conn is a transparent AP, get its original destination + * and place it in circuit_wait. + * + * The <b>listener</b> parameter is only used for AP connections. + */ +int +connection_init_accepted_conn(connection_t *conn, + const listener_connection_t *listener) +{ + int rv; + + connection_start_reading(conn); + + switch (conn->type) { + case CONN_TYPE_EXT_OR: + /* Initiate Extended ORPort authentication. */ + return connection_ext_or_start_auth(TO_OR_CONN(conn)); + case CONN_TYPE_OR: + control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0); + rv = connection_tls_start_handshake(TO_OR_CONN(conn), 1); + if (rv < 0) { + connection_or_close_for_error(TO_OR_CONN(conn), 0); + } + return rv; + break; + case CONN_TYPE_AP: + memcpy(&TO_ENTRY_CONN(conn)->entry_cfg, &listener->entry_cfg, + sizeof(entry_port_cfg_t)); + TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch(); + TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->base_.type; + + switch (TO_CONN(listener)->type) { + case CONN_TYPE_AP_LISTENER: + conn->state = AP_CONN_STATE_SOCKS_WAIT; + TO_ENTRY_CONN(conn)->socks_request->socks_prefer_no_auth = + listener->entry_cfg.socks_prefer_no_auth; + break; + case CONN_TYPE_AP_TRANS_LISTENER: + TO_ENTRY_CONN(conn)->is_transparent_ap = 1; + /* XXXX028 -- is this correct still, with the addition of + * pending_entry_connections ? */ + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + return connection_ap_process_transparent(TO_ENTRY_CONN(conn)); + case CONN_TYPE_AP_NATD_LISTENER: + TO_ENTRY_CONN(conn)->is_transparent_ap = 1; + conn->state = AP_CONN_STATE_NATD_WAIT; + break; + case CONN_TYPE_AP_HTTP_CONNECT_LISTENER: + conn->state = AP_CONN_STATE_HTTP_CONNECT_WAIT; + } + break; + case CONN_TYPE_DIR: + conn->purpose = DIR_PURPOSE_SERVER; + conn->state = DIR_CONN_STATE_SERVER_COMMAND_WAIT; + break; + case CONN_TYPE_CONTROL: + conn->state = CONTROL_CONN_STATE_NEEDAUTH; + break; + } + return 0; +} + +/** Take conn, make a nonblocking socket; try to connect to + * sa, binding to bindaddr if sa is not localhost. If fail, return -1 and if + * applicable put your best guess about errno into *<b>socket_error</b>. + * If connected return 1, if EAGAIN return 0. + */ +MOCK_IMPL(STATIC int, +connection_connect_sockaddr,(connection_t *conn, + const struct sockaddr *sa, + socklen_t sa_len, + const struct sockaddr *bindaddr, + socklen_t bindaddr_len, + int *socket_error)) +{ + tor_socket_t s; + int inprogress = 0; + const or_options_t *options = get_options(); + + tor_assert(conn); + tor_assert(sa); + tor_assert(socket_error); + + if (net_is_completely_disabled()) { + /* We should never even try to connect anyplace if the network is + * completely shut off. + * + * (We don't check net_is_disabled() here, since we still sometimes + * want to open connections when we're in soft hibernation.) + */ + static ratelim_t disablenet_violated = RATELIM_INIT(30*60); + *socket_error = SOCK_ERRNO(ENETUNREACH); + log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG, + "Tried to open a socket with DisableNetwork set."); + tor_fragile_assert(); + return -1; + } + + const int protocol_family = sa->sa_family; + const int proto = (sa->sa_family == AF_INET6 || + sa->sa_family == AF_INET) ? IPPROTO_TCP : 0; + + s = tor_open_socket_nonblocking(protocol_family, SOCK_STREAM, proto); + if (! SOCKET_OK(s)) { + /* + * Early OOS handler calls; it matters if it's an exhaustion-related + * error or not. + */ + *socket_error = tor_socket_errno(s); + if (ERRNO_IS_RESOURCE_LIMIT(*socket_error)) { + warn_too_many_conns(); + connection_check_oos(get_n_open_sockets(), 1); + } else { + log_warn(LD_NET,"Error creating network socket: %s", + tor_socket_strerror(*socket_error)); + connection_check_oos(get_n_open_sockets(), 0); + } + return -1; + } + + if (make_socket_reuseable(s) < 0) { + log_warn(LD_NET, "Error setting SO_REUSEADDR flag on new connection: %s", + tor_socket_strerror(errno)); + } + + /* + * We've got the socket open; give the OOS handler a chance to check + * against configured maximum socket number, but tell it no exhaustion + * failure. + */ + connection_check_oos(get_n_open_sockets(), 0); + + if (bindaddr && bind(s, bindaddr, bindaddr_len) < 0) { + *socket_error = tor_socket_errno(s); + log_warn(LD_NET,"Error binding network socket: %s", + tor_socket_strerror(*socket_error)); + tor_close_socket(s); + return -1; + } + + tor_assert(options); + if (options->ConstrainedSockets) + set_constrained_socket_buffers(s, (int)options->ConstrainedSockSize); + + if (connect(s, sa, sa_len) < 0) { + int e = tor_socket_errno(s); + if (!ERRNO_IS_CONN_EINPROGRESS(e)) { + /* yuck. kill it. */ + *socket_error = e; + log_info(LD_NET, + "connect() to socket failed: %s", + tor_socket_strerror(e)); + tor_close_socket(s); + return -1; + } else { + inprogress = 1; + } + } + + /* it succeeded. we're connected. */ + log_fn(inprogress ? LOG_DEBUG : LOG_INFO, LD_NET, + "Connection to socket %s (sock "TOR_SOCKET_T_FORMAT").", + inprogress ? "in progress" : "established", s); + conn->s = s; + if (connection_add_connecting(conn) < 0) { + /* no space, forget it */ + *socket_error = SOCK_ERRNO(ENOBUFS); + return -1; + } + + return inprogress ? 0 : 1; +} + +/* Log a message if connection attempt is made when IPv4 or IPv6 is disabled. + * Log a less severe message if we couldn't conform to ClientPreferIPv6ORPort + * or ClientPreferIPv6ORPort. */ +static void +connection_connect_log_client_use_ip_version(const connection_t *conn) +{ + const or_options_t *options = get_options(); + + /* Only clients care about ClientUseIPv4/6, bail out early on servers, and + * on connections we don't care about */ + if (server_mode(options) || !conn || conn->type == CONN_TYPE_EXIT) { + return; + } + + /* We're only prepared to log OR and DIR connections here */ + if (conn->type != CONN_TYPE_OR && conn->type != CONN_TYPE_DIR) { + return; + } + + const int must_ipv4 = !fascist_firewall_use_ipv6(options); + const int must_ipv6 = (options->ClientUseIPv4 == 0); + const int pref_ipv6 = (conn->type == CONN_TYPE_OR + ? fascist_firewall_prefer_ipv6_orport(options) + : fascist_firewall_prefer_ipv6_dirport(options)); + tor_addr_t real_addr; + tor_addr_make_null(&real_addr, AF_UNSPEC); + + /* OR conns keep the original address in real_addr, as addr gets overwritten + * with the descriptor address */ + if (conn->type == CONN_TYPE_OR) { + const or_connection_t *or_conn = TO_OR_CONN((connection_t *)conn); + tor_addr_copy(&real_addr, &or_conn->real_addr); + } else if (conn->type == CONN_TYPE_DIR) { + tor_addr_copy(&real_addr, &conn->addr); + } + + /* Check if we broke a mandatory address family restriction */ + if ((must_ipv4 && tor_addr_family(&real_addr) == AF_INET6) + || (must_ipv6 && tor_addr_family(&real_addr) == AF_INET)) { + static int logged_backtrace = 0; + log_info(LD_BUG, "Outgoing %s connection to %s violated ClientUseIPv%s 0.", + conn->type == CONN_TYPE_OR ? "OR" : "Dir", + fmt_addr(&real_addr), + options->ClientUseIPv4 == 0 ? "4" : "6"); + if (!logged_backtrace) { + log_backtrace(LOG_INFO, LD_BUG, "Address came from"); + logged_backtrace = 1; + } + } + + /* Bridges are allowed to break IPv4/IPv6 ORPort preferences to connect to + * the node's configured address when ClientPreferIPv6ORPort is auto */ + if (options->UseBridges && conn->type == CONN_TYPE_OR + && options->ClientPreferIPv6ORPort == -1) { + return; + } + + /* Check if we couldn't satisfy an address family preference */ + if ((!pref_ipv6 && tor_addr_family(&real_addr) == AF_INET6) + || (pref_ipv6 && tor_addr_family(&real_addr) == AF_INET)) { + log_info(LD_NET, "Outgoing connection to %s doesn't satisfy " + "ClientPreferIPv6%sPort %d, with ClientUseIPv4 %d, and " + "fascist_firewall_use_ipv6 %d (ClientUseIPv6 %d and UseBridges " + "%d).", + fmt_addr(&real_addr), + conn->type == CONN_TYPE_OR ? "OR" : "Dir", + conn->type == CONN_TYPE_OR ? options->ClientPreferIPv6ORPort + : options->ClientPreferIPv6DirPort, + options->ClientUseIPv4, fascist_firewall_use_ipv6(options), + options->ClientUseIPv6, options->UseBridges); + } +} + +/** Retrieve the outbound address depending on the protocol (IPv4 or IPv6) + * and the connection type (relay, exit, ...) + * Return a socket address or NULL in case nothing is configured. + **/ +const tor_addr_t * +conn_get_outbound_address(sa_family_t family, + const or_options_t *options, unsigned int conn_type) +{ + const tor_addr_t *ext_addr = NULL; + + int fam_index; + switch (family) { + case AF_INET: + fam_index = 0; + break; + case AF_INET6: + fam_index = 1; + break; + default: + return NULL; + } + + // If an exit connection, use the exit address (if present) + if (conn_type == CONN_TYPE_EXIT) { + if (!tor_addr_is_null( + &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT][fam_index])) { + ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT] + [fam_index]; + } else if (!tor_addr_is_null( + &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + [fam_index])) { + ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + [fam_index]; + } + } else { // All non-exit connections + if (!tor_addr_is_null( + &options->OutboundBindAddresses[OUTBOUND_ADDR_OR][fam_index])) { + ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_OR] + [fam_index]; + } else if (!tor_addr_is_null( + &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + [fam_index])) { + ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + [fam_index]; + } + } + return ext_addr; +} + +/** Take conn, make a nonblocking socket; try to connect to + * addr:port (port arrives in *host order*). If fail, return -1 and if + * applicable put your best guess about errno into *<b>socket_error</b>. + * Else assign s to conn-\>s: if connected return 1, if EAGAIN return 0. + * + * addr:port can be different to conn->addr:conn->port if connecting through + * a proxy. + * + * address is used to make the logs useful. + * + * On success, add conn to the list of polled connections. + */ +int +connection_connect(connection_t *conn, const char *address, + const tor_addr_t *addr, uint16_t port, int *socket_error) +{ + struct sockaddr_storage addrbuf; + struct sockaddr_storage bind_addr_ss; + struct sockaddr *bind_addr = NULL; + struct sockaddr *dest_addr; + int dest_addr_len, bind_addr_len = 0; + + /* Log if we didn't stick to ClientUseIPv4/6 or ClientPreferIPv6OR/DirPort + */ + connection_connect_log_client_use_ip_version(conn); + + if (!tor_addr_is_loopback(addr)) { + const tor_addr_t *ext_addr = NULL; + ext_addr = conn_get_outbound_address(tor_addr_family(addr), get_options(), + conn->type); + if (ext_addr) { + memset(&bind_addr_ss, 0, sizeof(bind_addr_ss)); + bind_addr_len = tor_addr_to_sockaddr(ext_addr, 0, + (struct sockaddr *) &bind_addr_ss, + sizeof(bind_addr_ss)); + if (bind_addr_len == 0) { + log_warn(LD_NET, + "Error converting OutboundBindAddress %s into sockaddr. " + "Ignoring.", fmt_and_decorate_addr(ext_addr)); + } else { + bind_addr = (struct sockaddr *)&bind_addr_ss; + } + } + } + + memset(&addrbuf,0,sizeof(addrbuf)); + dest_addr = (struct sockaddr*) &addrbuf; + dest_addr_len = tor_addr_to_sockaddr(addr, port, dest_addr, sizeof(addrbuf)); + tor_assert(dest_addr_len > 0); + + log_debug(LD_NET, "Connecting to %s:%u.", + escaped_safe_str_client(address), port); + + return connection_connect_sockaddr(conn, dest_addr, dest_addr_len, + bind_addr, bind_addr_len, socket_error); +} + +#ifdef HAVE_SYS_UN_H + +/** Take conn, make a nonblocking socket; try to connect to + * an AF_UNIX socket at socket_path. If fail, return -1 and if applicable + * put your best guess about errno into *<b>socket_error</b>. Else assign s + * to conn-\>s: if connected return 1, if EAGAIN return 0. + * + * On success, add conn to the list of polled connections. + */ +int +connection_connect_unix(connection_t *conn, const char *socket_path, + int *socket_error) +{ + struct sockaddr_un dest_addr; + + tor_assert(socket_path); + + /* Check that we'll be able to fit it into dest_addr later */ + if (strlen(socket_path) + 1 > sizeof(dest_addr.sun_path)) { + log_warn(LD_NET, + "Path %s is too long for an AF_UNIX socket\n", + escaped_safe_str_client(socket_path)); + *socket_error = SOCK_ERRNO(ENAMETOOLONG); + return -1; + } + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.sun_family = AF_UNIX; + strlcpy(dest_addr.sun_path, socket_path, sizeof(dest_addr.sun_path)); + + log_debug(LD_NET, + "Connecting to AF_UNIX socket at %s.", + escaped_safe_str_client(socket_path)); + + return connection_connect_sockaddr(conn, + (struct sockaddr *)&dest_addr, sizeof(dest_addr), + NULL, 0, socket_error); +} + +#endif /* defined(HAVE_SYS_UN_H) */ + +/** Convert state number to string representation for logging purposes. + */ +static const char * +connection_proxy_state_to_string(int state) +{ + static const char *unknown = "???"; + static const char *states[] = { + "PROXY_NONE", + "PROXY_INFANT", + "PROXY_HTTPS_WANT_CONNECT_OK", + "PROXY_SOCKS4_WANT_CONNECT_OK", + "PROXY_SOCKS5_WANT_AUTH_METHOD_NONE", + "PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929", + "PROXY_SOCKS5_WANT_AUTH_RFC1929_OK", + "PROXY_SOCKS5_WANT_CONNECT_OK", + "PROXY_CONNECTED", + }; + + if (state < PROXY_NONE || state > PROXY_CONNECTED) + return unknown; + + return states[state]; +} + +/** Returns the global proxy type used by tor. Use this function for + * logging or high-level purposes, don't use it to fill the + * <b>proxy_type</b> field of or_connection_t; use the actual proxy + * protocol instead.*/ +static int +get_proxy_type(void) +{ + const or_options_t *options = get_options(); + + if (options->ClientTransportPlugin) + return PROXY_PLUGGABLE; + else if (options->HTTPSProxy) + return PROXY_CONNECT; + else if (options->Socks4Proxy) + return PROXY_SOCKS4; + else if (options->Socks5Proxy) + return PROXY_SOCKS5; + else + return PROXY_NONE; +} + +/* One byte for the version, one for the command, two for the + port, and four for the addr... and, one more for the + username NUL: */ +#define SOCKS4_STANDARD_BUFFER_SIZE (1 + 1 + 2 + 4 + 1) + +/** Write a proxy request of <b>type</b> (socks4, socks5, https) to conn + * for conn->addr:conn->port, authenticating with the auth details given + * in the configuration (if available). SOCKS 5 and HTTP CONNECT proxies + * support authentication. + * + * Returns -1 if conn->addr is incompatible with the proxy protocol, and + * 0 otherwise. + * + * Use connection_read_proxy_handshake() to complete the handshake. + */ +int +connection_proxy_connect(connection_t *conn, int type) +{ + const or_options_t *options; + + tor_assert(conn); + + options = get_options(); + + switch (type) { + case PROXY_CONNECT: { + char buf[1024]; + char *base64_authenticator=NULL; + const char *authenticator = options->HTTPSProxyAuthenticator; + + /* Send HTTP CONNECT and authentication (if available) in + * one request */ + + if (authenticator) { + base64_authenticator = alloc_http_authenticator(authenticator); + if (!base64_authenticator) + log_warn(LD_OR, "Encoding https authenticator failed"); + } + + if (base64_authenticator) { + const char *addrport = fmt_addrport(&conn->addr, conn->port); + tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Proxy-Authorization: Basic %s\r\n\r\n", + addrport, + addrport, + base64_authenticator); + tor_free(base64_authenticator); + } else { + tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.0\r\n\r\n", + fmt_addrport(&conn->addr, conn->port)); + } + + connection_buf_add(buf, strlen(buf), conn); + conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK; + break; + } + + case PROXY_SOCKS4: { + unsigned char *buf; + uint16_t portn; + uint32_t ip4addr; + size_t buf_size = 0; + char *socks_args_string = NULL; + + /* Send a SOCKS4 connect request */ + + if (tor_addr_family(&conn->addr) != AF_INET) { + log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6"); + return -1; + } + + { /* If we are here because we are trying to connect to a + pluggable transport proxy, check if we have any SOCKS + arguments to transmit. If we do, compress all arguments to + a single string in 'socks_args_string': */ + + if (get_proxy_type() == PROXY_PLUGGABLE) { + socks_args_string = + pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port); + if (socks_args_string) + log_debug(LD_NET, "Sending out '%s' as our SOCKS argument string.", + socks_args_string); + } + } + + { /* Figure out the buffer size we need for the SOCKS message: */ + + buf_size = SOCKS4_STANDARD_BUFFER_SIZE; + + /* If we have a SOCKS argument string, consider its size when + calculating the buffer size: */ + if (socks_args_string) + buf_size += strlen(socks_args_string); + } + + buf = tor_malloc_zero(buf_size); + + ip4addr = tor_addr_to_ipv4n(&conn->addr); + portn = htons(conn->port); + + buf[0] = 4; /* version */ + buf[1] = SOCKS_COMMAND_CONNECT; /* command */ + memcpy(buf + 2, &portn, 2); /* port */ + memcpy(buf + 4, &ip4addr, 4); /* addr */ + + /* Next packet field is the userid. If we have pluggable + transport SOCKS arguments, we have to embed them + there. Otherwise, we use an empty userid. */ + if (socks_args_string) { /* place the SOCKS args string: */ + tor_assert(strlen(socks_args_string) > 0); + tor_assert(buf_size >= + SOCKS4_STANDARD_BUFFER_SIZE + strlen(socks_args_string)); + strlcpy((char *)buf + 8, socks_args_string, buf_size - 8); + tor_free(socks_args_string); + } else { + buf[8] = 0; /* no userid */ + } + + connection_buf_add((char *)buf, buf_size, conn); + tor_free(buf); + + conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK; + break; + } + + case PROXY_SOCKS5: { + unsigned char buf[4]; /* fields: vers, num methods, method list */ + + /* Send a SOCKS5 greeting (connect request must wait) */ + + buf[0] = 5; /* version */ + + /* We have to use SOCKS5 authentication, if we have a + Socks5ProxyUsername or if we want to pass arguments to our + pluggable transport proxy: */ + if ((options->Socks5ProxyUsername) || + (get_proxy_type() == PROXY_PLUGGABLE && + (get_socks_args_by_bridge_addrport(&conn->addr, conn->port)))) { + /* number of auth methods */ + buf[1] = 2; + buf[2] = 0x00; /* no authentication */ + buf[3] = 0x02; /* rfc1929 Username/Passwd auth */ + conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929; + } else { + buf[1] = 1; + buf[2] = 0x00; /* no authentication */ + conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_NONE; + } + + connection_buf_add((char *)buf, 2 + buf[1], conn); + break; + } + + default: + log_err(LD_BUG, "Invalid proxy protocol, %d", type); + tor_fragile_assert(); + return -1; + } + + log_debug(LD_NET, "set state %s", + connection_proxy_state_to_string(conn->proxy_state)); + + return 0; +} + +/** Read conn's inbuf. If the http response from the proxy is all + * here, make sure it's good news, then return 1. If it's bad news, + * return -1. Else return 0 and hope for better luck next time. + */ +static int +connection_read_https_proxy_response(connection_t *conn) +{ + char *headers; + char *reason=NULL; + int status_code; + time_t date_header; + + switch (fetch_from_buf_http(conn->inbuf, + &headers, MAX_HEADERS_SIZE, + NULL, NULL, 10000, 0)) { + case -1: /* overflow */ + log_warn(LD_PROTOCOL, + "Your https proxy sent back an oversized response. Closing."); + return -1; + case 0: + log_info(LD_NET,"https proxy response not all here yet. Waiting."); + return 0; + /* case 1, fall through */ + } + + if (parse_http_response(headers, &status_code, &date_header, + NULL, &reason) < 0) { + log_warn(LD_NET, + "Unparseable headers from proxy (connecting to '%s'). Closing.", + conn->address); + tor_free(headers); + return -1; + } + tor_free(headers); + if (!reason) reason = tor_strdup("[no reason given]"); + + if (status_code == 200) { + log_info(LD_NET, + "HTTPS connect to '%s' successful! (200 %s) Starting TLS.", + conn->address, escaped(reason)); + tor_free(reason); + return 1; + } + /* else, bad news on the status code */ + switch (status_code) { + case 403: + log_warn(LD_NET, + "The https proxy refused to allow connection to %s " + "(status code %d, %s). Closing.", + conn->address, status_code, escaped(reason)); + break; + default: + log_warn(LD_NET, + "The https proxy sent back an unexpected status code %d (%s). " + "Closing.", + status_code, escaped(reason)); + break; + } + tor_free(reason); + return -1; +} + +/** Send SOCKS5 CONNECT command to <b>conn</b>, copying <b>conn->addr</b> + * and <b>conn->port</b> into the request. + */ +static void +connection_send_socks5_connect(connection_t *conn) +{ + unsigned char buf[1024]; + size_t reqsize = 6; + uint16_t port = htons(conn->port); + + buf[0] = 5; /* version */ + buf[1] = SOCKS_COMMAND_CONNECT; /* command */ + buf[2] = 0; /* reserved */ + + if (tor_addr_family(&conn->addr) == AF_INET) { + uint32_t addr = tor_addr_to_ipv4n(&conn->addr); + + buf[3] = 1; + reqsize += 4; + memcpy(buf + 4, &addr, 4); + memcpy(buf + 8, &port, 2); + } else { /* AF_INET6 */ + buf[3] = 4; + reqsize += 16; + memcpy(buf + 4, tor_addr_to_in6_addr8(&conn->addr), 16); + memcpy(buf + 20, &port, 2); + } + + connection_buf_add((char *)buf, reqsize, conn); + + conn->proxy_state = PROXY_SOCKS5_WANT_CONNECT_OK; +} + +/** Wrapper around fetch_from_buf_socks_client: see that functions + * for documentation of its behavior. */ +static int +connection_fetch_from_buf_socks_client(connection_t *conn, + int state, char **reason) +{ + return fetch_from_buf_socks_client(conn->inbuf, state, reason); +} + +/** Call this from connection_*_process_inbuf() to advance the proxy + * handshake. + * + * No matter what proxy protocol is used, if this function returns 1, the + * handshake is complete, and the data remaining on inbuf may contain the + * start of the communication with the requested server. + * + * Returns 0 if the current buffer contains an incomplete response, and -1 + * on error. + */ +int +connection_read_proxy_handshake(connection_t *conn) +{ + int ret = 0; + char *reason = NULL; + + log_debug(LD_NET, "enter state %s", + connection_proxy_state_to_string(conn->proxy_state)); + + switch (conn->proxy_state) { + case PROXY_HTTPS_WANT_CONNECT_OK: + ret = connection_read_https_proxy_response(conn); + if (ret == 1) + conn->proxy_state = PROXY_CONNECTED; + break; + + case PROXY_SOCKS4_WANT_CONNECT_OK: + ret = connection_fetch_from_buf_socks_client(conn, + conn->proxy_state, + &reason); + if (ret == 1) + conn->proxy_state = PROXY_CONNECTED; + break; + + case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE: + ret = connection_fetch_from_buf_socks_client(conn, + conn->proxy_state, + &reason); + /* no auth needed, do connect */ + if (ret == 1) { + connection_send_socks5_connect(conn); + ret = 0; + } + break; + + case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929: + ret = connection_fetch_from_buf_socks_client(conn, + conn->proxy_state, + &reason); + + /* send auth if needed, otherwise do connect */ + if (ret == 1) { + connection_send_socks5_connect(conn); + ret = 0; + } else if (ret == 2) { + unsigned char buf[1024]; + size_t reqsize, usize, psize; + const char *user, *pass; + char *socks_args_string = NULL; + + if (get_proxy_type() == PROXY_PLUGGABLE) { + socks_args_string = + pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port); + if (!socks_args_string) { + log_warn(LD_NET, "Could not create SOCKS args string."); + ret = -1; + break; + } + + log_debug(LD_NET, "SOCKS5 arguments: %s", socks_args_string); + tor_assert(strlen(socks_args_string) > 0); + tor_assert(strlen(socks_args_string) <= MAX_SOCKS5_AUTH_SIZE_TOTAL); + + if (strlen(socks_args_string) > MAX_SOCKS5_AUTH_FIELD_SIZE) { + user = socks_args_string; + usize = MAX_SOCKS5_AUTH_FIELD_SIZE; + pass = socks_args_string + MAX_SOCKS5_AUTH_FIELD_SIZE; + psize = strlen(socks_args_string) - MAX_SOCKS5_AUTH_FIELD_SIZE; + } else { + user = socks_args_string; + usize = strlen(socks_args_string); + pass = "\0"; + psize = 1; + } + } else if (get_options()->Socks5ProxyUsername) { + user = get_options()->Socks5ProxyUsername; + pass = get_options()->Socks5ProxyPassword; + tor_assert(user && pass); + usize = strlen(user); + psize = strlen(pass); + } else { + log_err(LD_BUG, "We entered %s for no reason!", __func__); + tor_fragile_assert(); + ret = -1; + break; + } + + /* Username and password lengths should have been checked + above and during torrc parsing. */ + tor_assert(usize <= MAX_SOCKS5_AUTH_FIELD_SIZE && + psize <= MAX_SOCKS5_AUTH_FIELD_SIZE); + reqsize = 3 + usize + psize; + + buf[0] = 1; /* negotiation version */ + buf[1] = usize; + memcpy(buf + 2, user, usize); + buf[2 + usize] = psize; + memcpy(buf + 3 + usize, pass, psize); + + if (socks_args_string) + tor_free(socks_args_string); + + connection_buf_add((char *)buf, reqsize, conn); + + conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_RFC1929_OK; + ret = 0; + } + break; + + case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK: + ret = connection_fetch_from_buf_socks_client(conn, + conn->proxy_state, + &reason); + /* send the connect request */ + if (ret == 1) { + connection_send_socks5_connect(conn); + ret = 0; + } + break; + + case PROXY_SOCKS5_WANT_CONNECT_OK: + ret = connection_fetch_from_buf_socks_client(conn, + conn->proxy_state, + &reason); + if (ret == 1) + conn->proxy_state = PROXY_CONNECTED; + break; + + default: + log_err(LD_BUG, "Invalid proxy_state for reading, %d", + conn->proxy_state); + tor_fragile_assert(); + ret = -1; + break; + } + + log_debug(LD_NET, "leaving state %s", + connection_proxy_state_to_string(conn->proxy_state)); + + if (ret < 0) { + if (reason) { + log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d (%s)", + conn->address, conn->port, escaped(reason)); + tor_free(reason); + } else { + log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d", + conn->address, conn->port); + } + } else if (ret == 1) { + log_info(LD_NET, "Proxy Client: connection to %s:%d successful", + conn->address, conn->port); + } + + return ret; +} + +/** Given a list of listener connections in <b>old_conns</b>, and list of + * port_cfg_t entries in <b>ports</b>, open a new listener for every port in + * <b>ports</b> that does not already have a listener in <b>old_conns</b>. + * + * Remove from <b>old_conns</b> every connection that has a corresponding + * entry in <b>ports</b>. Add to <b>new_conns</b> new every connection we + * launch. + * + * If <b>control_listeners_only</b> is true, then we only open control + * listeners, and we do not remove any noncontrol listeners from old_conns. + * + * Return 0 on success, -1 on failure. + **/ +static int +retry_listener_ports(smartlist_t *old_conns, + const smartlist_t *ports, + smartlist_t *new_conns, + int control_listeners_only) +{ + smartlist_t *launch = smartlist_new(); + int r = 0; + + if (control_listeners_only) { + SMARTLIST_FOREACH(ports, port_cfg_t *, p, { + if (p->type == CONN_TYPE_CONTROL_LISTENER) + smartlist_add(launch, p); + }); + } else { + smartlist_add_all(launch, ports); + } + + /* Iterate through old_conns, comparing it to launch: remove from both lists + * each pair of elements that corresponds to the same port. */ + SMARTLIST_FOREACH_BEGIN(old_conns, connection_t *, conn) { + const port_cfg_t *found_port = NULL; + + /* Okay, so this is a listener. Is it configured? */ + SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, wanted) { + if (conn->type != wanted->type) + continue; + if ((conn->socket_family != AF_UNIX && wanted->is_unix_addr) || + (conn->socket_family == AF_UNIX && ! wanted->is_unix_addr)) + continue; + + if (wanted->server_cfg.no_listen) + continue; /* We don't want to open a listener for this one */ + + if (wanted->is_unix_addr) { + if (conn->socket_family == AF_UNIX && + !strcmp(wanted->unix_addr, conn->address)) { + found_port = wanted; + break; + } + } else { + int port_matches; + if (wanted->port == CFG_AUTO_PORT) { + port_matches = 1; + } else { + port_matches = (wanted->port == conn->port); + } + if (port_matches && tor_addr_eq(&wanted->addr, &conn->addr)) { + found_port = wanted; + break; + } + } + } SMARTLIST_FOREACH_END(wanted); + + if (found_port) { + /* This listener is already running; we don't need to launch it. */ + //log_debug(LD_NET, "Already have %s on %s:%d", + // conn_type_to_string(found_port->type), conn->address, conn->port); + smartlist_remove(launch, found_port); + /* And we can remove the connection from old_conns too. */ + SMARTLIST_DEL_CURRENT(old_conns, conn); + } + } SMARTLIST_FOREACH_END(conn); + + /* Now open all the listeners that are configured but not opened. */ + SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, port) { + struct sockaddr *listensockaddr; + socklen_t listensocklen = 0; + char *address=NULL; + connection_t *conn; + int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port; + tor_assert(real_port <= UINT16_MAX); + if (port->server_cfg.no_listen) + continue; + +#ifndef _WIN32 + /* We don't need to be root to create a UNIX socket, so defer until after + * setuid. */ + const or_options_t *options = get_options(); + if (port->is_unix_addr && !geteuid() && (options->User) && + strcmp(options->User, "root")) + continue; +#endif /* !defined(_WIN32) */ + + if (port->is_unix_addr) { + listensockaddr = (struct sockaddr *) + create_unix_sockaddr(port->unix_addr, + &address, &listensocklen); + } else { + listensockaddr = tor_malloc(sizeof(struct sockaddr_storage)); + listensocklen = tor_addr_to_sockaddr(&port->addr, + real_port, + listensockaddr, + sizeof(struct sockaddr_storage)); + address = tor_addr_to_str_dup(&port->addr); + } + + if (listensockaddr) { + conn = connection_listener_new(listensockaddr, listensocklen, + port->type, address, port); + tor_free(listensockaddr); + tor_free(address); + } else { + conn = NULL; + } + + if (!conn) { + r = -1; + } else { + if (new_conns) + smartlist_add(new_conns, conn); + } + } SMARTLIST_FOREACH_END(port); + + smartlist_free(launch); + + return r; +} + +/** Launch listeners for each port you should have open. Only launch + * listeners who are not already open, and only close listeners we no longer + * want. + * + * Add all old conns that should be closed to <b>replaced_conns</b>. + * Add all new connections to <b>new_conns</b>. + * + * If <b>close_all_noncontrol</b> is true, then we only open control + * listeners, and we close all other listeners. + */ +int +retry_all_listeners(smartlist_t *replaced_conns, + smartlist_t *new_conns, int close_all_noncontrol) +{ + smartlist_t *listeners = smartlist_new(); + const or_options_t *options = get_options(); + int retval = 0; + const uint16_t old_or_port = router_get_advertised_or_port(options); + const uint16_t old_or_port_ipv6 = + router_get_advertised_or_port_by_af(options,AF_INET6); + const uint16_t old_dir_port = router_get_advertised_dir_port(options, 0); + + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (connection_is_listener(conn) && !conn->marked_for_close) + smartlist_add(listeners, conn); + } SMARTLIST_FOREACH_END(conn); + + if (retry_listener_ports(listeners, + get_configured_ports(), + new_conns, + close_all_noncontrol) < 0) + retval = -1; + + /* Any members that were still in 'listeners' don't correspond to + * any configured port. Kill 'em. */ + SMARTLIST_FOREACH_BEGIN(listeners, connection_t *, conn) { + log_notice(LD_NET, "Closing no-longer-configured %s on %s:%d", + conn_type_to_string(conn->type), conn->address, conn->port); + if (replaced_conns) { + smartlist_add(replaced_conns, conn); + } else { + connection_close_immediate(conn); + connection_mark_for_close(conn); + } + } SMARTLIST_FOREACH_END(conn); + + smartlist_free(listeners); + + if (old_or_port != router_get_advertised_or_port(options) || + old_or_port_ipv6 != router_get_advertised_or_port_by_af(options, + AF_INET6) || + old_dir_port != router_get_advertised_dir_port(options, 0)) { + /* Our chosen ORPort or DirPort is not what it used to be: the + * descriptor we had (if any) should be regenerated. (We won't + * automatically notice this because of changes in the option, + * since the value could be "auto".) */ + mark_my_descriptor_dirty("Chosen Or/DirPort changed"); + } + + return retval; +} + +/** Mark every listener of type other than CONTROL_LISTENER to be closed. */ +void +connection_mark_all_noncontrol_listeners(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + if (conn->type == CONN_TYPE_CONTROL_LISTENER) + continue; + if (connection_is_listener(conn)) + connection_mark_for_close(conn); + } SMARTLIST_FOREACH_END(conn); +} + +/** Mark every external connection not used for controllers for close. */ +void +connection_mark_all_noncontrol_connections(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + switch (conn->type) { + case CONN_TYPE_CONTROL_LISTENER: + case CONN_TYPE_CONTROL: + break; + case CONN_TYPE_AP: + connection_mark_unattached_ap(TO_ENTRY_CONN(conn), + END_STREAM_REASON_HIBERNATING); + break; + case CONN_TYPE_OR: + { + or_connection_t *orconn = TO_OR_CONN(conn); + if (orconn->chan) { + connection_or_close_normally(orconn, 0); + } else { + /* + * There should have been one, but mark for close and hope + * for the best.. + */ + connection_mark_for_close(conn); + } + } + break; + default: + connection_mark_for_close(conn); + break; + } + } SMARTLIST_FOREACH_END(conn); +} + +/** Return 1 if we should apply rate limiting to <b>conn</b>, and 0 + * otherwise. + * Right now this just checks if it's an internal IP address or an + * internal connection. We also should, but don't, check if the connection + * uses pluggable transports, since we should then limit it even if it + * comes from an internal IP address. */ +static int +connection_is_rate_limited(connection_t *conn) +{ + const or_options_t *options = get_options(); + if (conn->linked) + return 0; /* Internal connection */ + else if (! options->CountPrivateBandwidth && + (tor_addr_family(&conn->addr) == AF_UNSPEC || /* no address */ + tor_addr_family(&conn->addr) == AF_UNIX || /* no address */ + tor_addr_is_internal(&conn->addr, 0))) + return 0; /* Internal address */ + else + return 1; +} + +/** When was either global write bucket last empty? If this was recent, then + * we're probably low on bandwidth, and we should be stingy with our bandwidth + * usage. */ +static time_t write_buckets_last_empty_at = -100; + +/** How many seconds of no active local circuits will make the + * connection revert to the "relayed" bandwidth class? */ +#define CLIENT_IDLE_TIME_FOR_PRIORITY 30 + +/** Return 1 if <b>conn</b> should use tokens from the "relayed" + * bandwidth rates, else 0. Currently, only OR conns with bandwidth + * class 1, and directory conns that are serving data out, count. + */ +static int +connection_counts_as_relayed_traffic(connection_t *conn, time_t now) +{ + if (conn->type == CONN_TYPE_OR && + connection_or_client_used(TO_OR_CONN(conn)) + + CLIENT_IDLE_TIME_FOR_PRIORITY < now) + return 1; + if (conn->type == CONN_TYPE_DIR && DIR_CONN_IS_SERVER(conn)) + return 1; + return 0; +} + +/** Helper function to decide how many bytes out of <b>global_bucket</b> + * we're willing to use for this transaction. <b>base</b> is the size + * of a cell on the network; <b>priority</b> says whether we should + * write many of them or just a few; and <b>conn_bucket</b> (if + * non-negative) provides an upper limit for our answer. */ +static ssize_t +connection_bucket_get_share(int base, int priority, + ssize_t global_bucket_val, ssize_t conn_bucket) +{ + ssize_t at_most; + ssize_t num_bytes_high = (priority ? 32 : 16) * base; + ssize_t num_bytes_low = (priority ? 4 : 2) * base; + + /* Do a rudimentary limiting so one circuit can't hog a connection. + * Pick at most 32 cells, at least 4 cells if possible, and if we're in + * the middle pick 1/8 of the available bandwidth. */ + at_most = global_bucket_val / 8; + at_most -= (at_most % base); /* round down */ + if (at_most > num_bytes_high) /* 16 KB, or 8 KB for low-priority */ + at_most = num_bytes_high; + else if (at_most < num_bytes_low) /* 2 KB, or 1 KB for low-priority */ + at_most = num_bytes_low; + + if (at_most > global_bucket_val) + at_most = global_bucket_val; + + if (conn_bucket >= 0 && at_most > conn_bucket) + at_most = conn_bucket; + + if (at_most < 0) + return 0; + return at_most; +} + +/** How many bytes at most can we read onto this connection? */ +static ssize_t +connection_bucket_read_limit(connection_t *conn, time_t now) +{ + int base = RELAY_PAYLOAD_SIZE; + int priority = conn->type != CONN_TYPE_DIR; + ssize_t conn_bucket = -1; + size_t global_bucket_val = token_bucket_rw_get_read(&global_bucket); + + if (connection_speaks_cells(conn)) { + or_connection_t *or_conn = TO_OR_CONN(conn); + if (conn->state == OR_CONN_STATE_OPEN) + conn_bucket = token_bucket_rw_get_read(&or_conn->bucket); + base = get_cell_network_size(or_conn->wide_circ_ids); + } + + if (!connection_is_rate_limited(conn)) { + /* be willing to read on local conns even if our buckets are empty */ + return conn_bucket>=0 ? conn_bucket : 1<<14; + } + + if (connection_counts_as_relayed_traffic(conn, now)) { + size_t relayed = token_bucket_rw_get_read(&global_relayed_bucket); + global_bucket_val = MIN(global_bucket_val, relayed); + } + + return connection_bucket_get_share(base, priority, + global_bucket_val, conn_bucket); +} + +/** How many bytes at most can we write onto this connection? */ +ssize_t +connection_bucket_write_limit(connection_t *conn, time_t now) +{ + int base = RELAY_PAYLOAD_SIZE; + int priority = conn->type != CONN_TYPE_DIR; + size_t conn_bucket = conn->outbuf_flushlen; + size_t global_bucket_val = token_bucket_rw_get_write(&global_bucket); + + if (!connection_is_rate_limited(conn)) { + /* be willing to write to local conns even if our buckets are empty */ + return conn->outbuf_flushlen; + } + + if (connection_speaks_cells(conn)) { + /* use the per-conn write limit if it's lower */ + or_connection_t *or_conn = TO_OR_CONN(conn); + if (conn->state == OR_CONN_STATE_OPEN) + conn_bucket = MIN(conn_bucket, + token_bucket_rw_get_write(&or_conn->bucket)); + base = get_cell_network_size(or_conn->wide_circ_ids); + } + + if (connection_counts_as_relayed_traffic(conn, now)) { + size_t relayed = token_bucket_rw_get_write(&global_relayed_bucket); + global_bucket_val = MIN(global_bucket_val, relayed); + } + + return connection_bucket_get_share(base, priority, + global_bucket_val, conn_bucket); +} + +/** Return 1 if the global write buckets are low enough that we + * shouldn't send <b>attempt</b> bytes of low-priority directory stuff + * out to <b>conn</b>. Else return 0. + + * Priority was 1 for v1 requests (directories and running-routers), + * and 2 for v2 requests and later (statuses and descriptors). + * + * There are a lot of parameters we could use here: + * - global_relayed_write_bucket. Low is bad. + * - global_write_bucket. Low is bad. + * - bandwidthrate. Low is bad. + * - bandwidthburst. Not a big factor? + * - attempt. High is bad. + * - total bytes queued on outbufs. High is bad. But I'm wary of + * using this, since a few slow-flushing queues will pump up the + * number without meaning what we meant to mean. What we really + * mean is "total directory bytes added to outbufs recently", but + * that's harder to quantify and harder to keep track of. + */ +int +global_write_bucket_low(connection_t *conn, size_t attempt, int priority) +{ + size_t smaller_bucket = + MIN(token_bucket_rw_get_write(&global_bucket), + token_bucket_rw_get_write(&global_relayed_bucket)); + if (authdir_mode(get_options()) && priority>1) + return 0; /* there's always room to answer v2 if we're an auth dir */ + + if (!connection_is_rate_limited(conn)) + return 0; /* local conns don't get limited */ + + if (smaller_bucket < attempt) + return 1; /* not enough space no matter the priority */ + + { + const time_t diff = approx_time() - write_buckets_last_empty_at; + if (diff <= 1) + return 1; /* we're already hitting our limits, no more please */ + } + + if (priority == 1) { /* old-style v1 query */ + /* Could we handle *two* of these requests within the next two seconds? */ + const or_options_t *options = get_options(); + size_t can_write = (size_t) (smaller_bucket + + 2*(options->RelayBandwidthRate ? options->RelayBandwidthRate : + options->BandwidthRate)); + if (can_write < 2*attempt) + return 1; + } else { /* v2 query */ + /* no further constraints yet */ + } + return 0; +} + +/** When did we last tell the accounting subsystem about transmitted + * bandwidth? */ +static time_t last_recorded_accounting_at = 0; + +/** Helper: adjusts our bandwidth history and informs the controller as + * appropriate, given that we have just read <b>num_read</b> bytes and written + * <b>num_written</b> bytes on <b>conn</b>. */ +static void +record_num_bytes_transferred_impl(connection_t *conn, + time_t now, size_t num_read, size_t num_written) +{ + /* Count bytes of answering direct and tunneled directory requests */ + if (conn->type == CONN_TYPE_DIR && conn->purpose == DIR_PURPOSE_SERVER) { + if (num_read > 0) + rep_hist_note_dir_bytes_read(num_read, now); + if (num_written > 0) + rep_hist_note_dir_bytes_written(num_written, now); + } + + if (!connection_is_rate_limited(conn)) + return; /* local IPs are free */ + + if (conn->type == CONN_TYPE_OR) + rep_hist_note_or_conn_bytes(conn->global_identifier, num_read, + num_written, now); + + if (num_read > 0) { + rep_hist_note_bytes_read(num_read, now); + } + if (num_written > 0) { + rep_hist_note_bytes_written(num_written, now); + } + if (conn->type == CONN_TYPE_EXIT) + rep_hist_note_exit_bytes(conn->port, num_written, num_read); + + /* Remember these bytes towards statistics. */ + stats_increment_bytes_read_and_written(num_read, num_written); + + /* Remember these bytes towards accounting. */ + if (accounting_is_enabled(get_options())) { + if (now > last_recorded_accounting_at && last_recorded_accounting_at) { + accounting_add_bytes(num_read, num_written, + (int)(now - last_recorded_accounting_at)); + } else { + accounting_add_bytes(num_read, num_written, 0); + } + last_recorded_accounting_at = now; + } +} + +/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes + * onto <b>conn</b>. Decrement buckets appropriately. */ +static void +connection_buckets_decrement(connection_t *conn, time_t now, + size_t num_read, size_t num_written) +{ + if (num_written >= INT_MAX || num_read >= INT_MAX) { + log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, " + "connection type=%s, state=%s", + (unsigned long)num_read, (unsigned long)num_written, + conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state)); + tor_assert_nonfatal_unreached(); + if (num_written >= INT_MAX) + num_written = 1; + if (num_read >= INT_MAX) + num_read = 1; + } + + record_num_bytes_transferred_impl(conn, now, num_read, num_written); + + if (!connection_is_rate_limited(conn)) + return; /* local IPs are free */ + + unsigned flags = 0; + if (connection_counts_as_relayed_traffic(conn, now)) { + flags = token_bucket_rw_dec(&global_relayed_bucket, num_read, num_written); + } + flags |= token_bucket_rw_dec(&global_bucket, num_read, num_written); + + if (flags & TB_WRITE) { + write_buckets_last_empty_at = now; + } + if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) { + or_connection_t *or_conn = TO_OR_CONN(conn); + token_bucket_rw_dec(&or_conn->bucket, num_read, num_written); + } +} + +/** + * Mark <b>conn</b> as needing to stop reading because bandwidth has been + * exhausted. If <b>is_global_bw</b>, it is closing because global bandwidth + * limit has been exhausted. Otherwise, it is closing because its own + * bandwidth limit has been exhausted. + */ +void +connection_read_bw_exhausted(connection_t *conn, bool is_global_bw) +{ + (void)is_global_bw; + conn->read_blocked_on_bw = 1; + connection_stop_reading(conn); + reenable_blocked_connection_schedule(); +} + +/** + * Mark <b>conn</b> as needing to stop reading because write bandwidth has + * been exhausted. If <b>is_global_bw</b>, it is closing because global + * bandwidth limit has been exhausted. Otherwise, it is closing because its + * own bandwidth limit has been exhausted. +*/ +void +connection_write_bw_exhausted(connection_t *conn, bool is_global_bw) +{ + (void)is_global_bw; + conn->write_blocked_on_bw = 1; + connection_stop_reading(conn); + reenable_blocked_connection_schedule(); +} + +/** If we have exhausted our global buckets, or the buckets for conn, + * stop reading. */ +void +connection_consider_empty_read_buckets(connection_t *conn) +{ + const char *reason; + + if (!connection_is_rate_limited(conn)) + return; /* Always okay. */ + + int is_global = 1; + + if (token_bucket_rw_get_read(&global_bucket) <= 0) { + reason = "global read bucket exhausted. Pausing."; + } else if (connection_counts_as_relayed_traffic(conn, approx_time()) && + token_bucket_rw_get_read(&global_relayed_bucket) <= 0) { + reason = "global relayed read bucket exhausted. Pausing."; + } else if (connection_speaks_cells(conn) && + conn->state == OR_CONN_STATE_OPEN && + token_bucket_rw_get_read(&TO_OR_CONN(conn)->bucket) <= 0) { + reason = "connection read bucket exhausted. Pausing."; + is_global = false; + } else + return; /* all good, no need to stop it */ + + LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "%s", reason)); + connection_read_bw_exhausted(conn, is_global); +} + +/** If we have exhausted our global buckets, or the buckets for conn, + * stop writing. */ +void +connection_consider_empty_write_buckets(connection_t *conn) +{ + const char *reason; + + if (!connection_is_rate_limited(conn)) + return; /* Always okay. */ + + bool is_global = true; + if (token_bucket_rw_get_write(&global_bucket) <= 0) { + reason = "global write bucket exhausted. Pausing."; + } else if (connection_counts_as_relayed_traffic(conn, approx_time()) && + token_bucket_rw_get_write(&global_relayed_bucket) <= 0) { + reason = "global relayed write bucket exhausted. Pausing."; + } else if (connection_speaks_cells(conn) && + conn->state == OR_CONN_STATE_OPEN && + token_bucket_rw_get_write(&TO_OR_CONN(conn)->bucket) <= 0) { + reason = "connection write bucket exhausted. Pausing."; + is_global = false; + } else + return; /* all good, no need to stop it */ + + LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "%s", reason)); + connection_write_bw_exhausted(conn, is_global); +} + +/** Initialize the global buckets to the values configured in the + * options */ +void +connection_bucket_init(void) +{ + const or_options_t *options = get_options(); + const uint32_t now_ts = monotime_coarse_get_stamp(); + token_bucket_rw_init(&global_bucket, + (int32_t)options->BandwidthRate, + (int32_t)options->BandwidthBurst, + now_ts); + if (options->RelayBandwidthRate) { + token_bucket_rw_init(&global_relayed_bucket, + (int32_t)options->RelayBandwidthRate, + (int32_t)options->RelayBandwidthBurst, + now_ts); + } else { + token_bucket_rw_init(&global_relayed_bucket, + (int32_t)options->BandwidthRate, + (int32_t)options->BandwidthBurst, + now_ts); + } + + reenable_blocked_connection_init(options); +} + +/** Update the global connection bucket settings to a new value. */ +void +connection_bucket_adjust(const or_options_t *options) +{ + token_bucket_rw_adjust(&global_bucket, + (int32_t)options->BandwidthRate, + (int32_t)options->BandwidthBurst); + if (options->RelayBandwidthRate) { + token_bucket_rw_adjust(&global_relayed_bucket, + (int32_t)options->RelayBandwidthRate, + (int32_t)options->RelayBandwidthBurst); + } else { + token_bucket_rw_adjust(&global_relayed_bucket, + (int32_t)options->BandwidthRate, + (int32_t)options->BandwidthBurst); + } +} + +/** + * Cached value of the last coarse-timestamp when we refilled the + * global buckets. + */ +static uint32_t last_refilled_global_buckets_ts=0; +/** + * Refill the token buckets for a single connection <b>conn</b>, and the + * global token buckets as appropriate. Requires that <b>now_ts</b> is + * the time in coarse timestamp units. + */ +static void +connection_bucket_refill_single(connection_t *conn, uint32_t now_ts) +{ + /* Note that we only check for equality here: the underlying + * token bucket functions can handle moving backwards in time if they + * need to. */ + if (now_ts != last_refilled_global_buckets_ts) { + token_bucket_rw_refill(&global_bucket, now_ts); + token_bucket_rw_refill(&global_relayed_bucket, now_ts); + last_refilled_global_buckets_ts = now_ts; + } + + if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) { + or_connection_t *or_conn = TO_OR_CONN(conn); + token_bucket_rw_refill(&or_conn->bucket, now_ts); + } +} + +/** + * Event to re-enable all connections that were previously blocked on read or + * write. + */ +static mainloop_event_t *reenable_blocked_connections_ev = NULL; + +/** True iff reenable_blocked_connections_ev is currently scheduled. */ +static int reenable_blocked_connections_is_scheduled = 0; + +/** Delay after which to run reenable_blocked_connections_ev. */ +static struct timeval reenable_blocked_connections_delay; + +/** + * Re-enable all connections that were previously blocked on read or write. + * This event is scheduled after enough time has elapsed to be sure + * that the buckets will refill when the connections have something to do. + */ +static void +reenable_blocked_connections_cb(mainloop_event_t *ev, void *arg) +{ + (void)ev; + (void)arg; + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->read_blocked_on_bw == 1) { + connection_start_reading(conn); + conn->read_blocked_on_bw = 0; + } + if (conn->write_blocked_on_bw == 1) { + connection_start_writing(conn); + conn->write_blocked_on_bw = 0; + } + } SMARTLIST_FOREACH_END(conn); + + reenable_blocked_connections_is_scheduled = 0; +} + +/** + * Initialize the mainloop event that we use to wake up connections that + * find themselves blocked on bandwidth. + */ +static void +reenable_blocked_connection_init(const or_options_t *options) +{ + if (! reenable_blocked_connections_ev) { + reenable_blocked_connections_ev = + mainloop_event_new(reenable_blocked_connections_cb, NULL); + reenable_blocked_connections_is_scheduled = 0; + } + time_t sec = options->TokenBucketRefillInterval / 1000; + int msec = (options->TokenBucketRefillInterval % 1000); + reenable_blocked_connections_delay.tv_sec = sec; + reenable_blocked_connections_delay.tv_usec = msec * 1000; +} + +/** + * Called when we have blocked a connection for being low on bandwidth: + * schedule an event to reenable such connections, if it is not already + * scheduled. + */ +static void +reenable_blocked_connection_schedule(void) +{ + if (reenable_blocked_connections_is_scheduled) + return; + if (BUG(reenable_blocked_connections_ev == NULL)) { + reenable_blocked_connection_init(get_options()); + } + mainloop_event_schedule(reenable_blocked_connections_ev, + &reenable_blocked_connections_delay); + reenable_blocked_connections_is_scheduled = 1; +} + +/** Read bytes from conn-\>s and process them. + * + * It calls connection_buf_read_from_socket() to bring in any new bytes, + * and then calls connection_process_inbuf() to process them. + * + * Mark the connection and return -1 if you want to close it, else + * return 0. + */ +static int +connection_handle_read_impl(connection_t *conn) +{ + ssize_t max_to_read=-1, try_to_read; + size_t before, n_read = 0; + int socket_error = 0; + + if (conn->marked_for_close) + return 0; /* do nothing */ + + conn->timestamp_last_read_allowed = approx_time(); + + connection_bucket_refill_single(conn, monotime_coarse_get_stamp()); + + switch (conn->type) { + case CONN_TYPE_OR_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_OR); + case CONN_TYPE_EXT_OR_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_EXT_OR); + case CONN_TYPE_AP_LISTENER: + case CONN_TYPE_AP_TRANS_LISTENER: + case CONN_TYPE_AP_NATD_LISTENER: + case CONN_TYPE_AP_HTTP_CONNECT_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_AP); + case CONN_TYPE_DIR_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_DIR); + case CONN_TYPE_CONTROL_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_CONTROL); + case CONN_TYPE_AP_DNS_LISTENER: + /* This should never happen; eventdns.c handles the reads here. */ + tor_fragile_assert(); + return 0; + } + + loop_again: + try_to_read = max_to_read; + tor_assert(!conn->marked_for_close); + + before = buf_datalen(conn->inbuf); + if (connection_buf_read_from_socket(conn, &max_to_read, &socket_error) < 0) { + /* There's a read error; kill the connection.*/ + if (conn->type == CONN_TYPE_OR) { + connection_or_notify_error(TO_OR_CONN(conn), + socket_error != 0 ? + errno_to_orconn_end_reason(socket_error) : + END_OR_CONN_REASON_CONNRESET, + socket_error != 0 ? + tor_socket_strerror(socket_error) : + "(unknown, errno was 0)"); + } + if (CONN_IS_EDGE(conn)) { + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + connection_edge_end_errno(edge_conn); + if (conn->type == CONN_TYPE_AP && TO_ENTRY_CONN(conn)->socks_request) { + /* broken, don't send a socks reply back */ + TO_ENTRY_CONN(conn)->socks_request->has_finished = 1; + } + } + connection_close_immediate(conn); /* Don't flush; connection is dead. */ + /* + * This can bypass normal channel checking since we did + * connection_or_notify_error() above. + */ + connection_mark_for_close_internal(conn); + return -1; + } + n_read += buf_datalen(conn->inbuf) - before; + if (CONN_IS_EDGE(conn) && try_to_read != max_to_read) { + /* instruct it not to try to package partial cells. */ + if (connection_process_inbuf(conn, 0) < 0) { + return -1; + } + if (!conn->marked_for_close && + connection_is_reading(conn) && + !conn->inbuf_reached_eof && + max_to_read > 0) + goto loop_again; /* try reading again, in case more is here now */ + } + /* one last try, packaging partial cells and all. */ + if (!conn->marked_for_close && + connection_process_inbuf(conn, 1) < 0) { + return -1; + } + if (conn->linked_conn) { + /* The other side's handle_write() will never actually get called, so + * we need to invoke the appropriate callbacks ourself. */ + connection_t *linked = conn->linked_conn; + + if (n_read) { + /* Probably a no-op, since linked conns typically don't count for + * bandwidth rate limiting. But do it anyway so we can keep stats + * accurately. Note that since we read the bytes from conn, and + * we're writing the bytes onto the linked connection, we count + * these as <i>written</i> bytes. */ + connection_buckets_decrement(linked, approx_time(), 0, n_read); + + if (connection_flushed_some(linked) < 0) + connection_mark_for_close(linked); + if (!connection_wants_to_flush(linked)) + connection_finished_flushing(linked); + } + + if (!buf_datalen(linked->outbuf) && conn->active_on_link) + connection_stop_reading_from_linked_conn(conn); + } + /* If we hit the EOF, call connection_reached_eof(). */ + if (!conn->marked_for_close && + conn->inbuf_reached_eof && + connection_reached_eof(conn) < 0) { + return -1; + } + return 0; +} + +/* DOCDOC connection_handle_read */ +int +connection_handle_read(connection_t *conn) +{ + int res; + update_current_time(time(NULL)); + res = connection_handle_read_impl(conn); + return res; +} + +/** Pull in new bytes from conn-\>s or conn-\>linked_conn onto conn-\>inbuf, + * either directly or via TLS. Reduce the token buckets by the number of bytes + * read. + * + * If *max_to_read is -1, then decide it ourselves, else go with the + * value passed to us. When returning, if it's changed, subtract the + * number of bytes we read from *max_to_read. + * + * Return -1 if we want to break conn, else return 0. + */ +static int +connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read, + int *socket_error) +{ + int result; + ssize_t at_most = *max_to_read; + size_t slack_in_buf, more_to_read; + size_t n_read = 0, n_written = 0; + + if (at_most == -1) { /* we need to initialize it */ + /* how many bytes are we allowed to read? */ + at_most = connection_bucket_read_limit(conn, approx_time()); + } + + slack_in_buf = buf_slack(conn->inbuf); + again: + if ((size_t)at_most > slack_in_buf && slack_in_buf >= 1024) { + more_to_read = at_most - slack_in_buf; + at_most = slack_in_buf; + } else { + more_to_read = 0; + } + + if (connection_speaks_cells(conn) && + conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) { + int pending; + or_connection_t *or_conn = TO_OR_CONN(conn); + size_t initial_size; + if (conn->state == OR_CONN_STATE_TLS_HANDSHAKING || + conn->state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) { + /* continue handshaking even if global token bucket is empty */ + return connection_tls_continue_handshake(or_conn); + } + + log_debug(LD_NET, + "%d: starting, inbuf_datalen %ld (%d pending in tls object)." + " at_most %ld.", + (int)conn->s,(long)buf_datalen(conn->inbuf), + tor_tls_get_pending_bytes(or_conn->tls), (long)at_most); + + initial_size = buf_datalen(conn->inbuf); + /* else open, or closing */ + result = buf_read_from_tls(conn->inbuf, or_conn->tls, at_most); + if (TOR_TLS_IS_ERROR(result) || result == TOR_TLS_CLOSE) + or_conn->tls_error = result; + else + or_conn->tls_error = 0; + + switch (result) { + case TOR_TLS_CLOSE: + case TOR_TLS_ERROR_IO: + log_debug(LD_NET,"TLS connection closed %son read. Closing. " + "(Nickname %s, address %s)", + result == TOR_TLS_CLOSE ? "cleanly " : "", + or_conn->nickname ? or_conn->nickname : "not set", + conn->address); + return result; + CASE_TOR_TLS_ERROR_ANY_NONIO: + log_debug(LD_NET,"tls error [%s]. breaking (nickname %s, address %s).", + tor_tls_err_to_string(result), + or_conn->nickname ? or_conn->nickname : "not set", + conn->address); + return result; + case TOR_TLS_WANTWRITE: + connection_start_writing(conn); + return 0; + case TOR_TLS_WANTREAD: + if (conn->in_connection_handle_write) { + /* We've been invoked from connection_handle_write, because we're + * waiting for a TLS renegotiation, the renegotiation started, and + * SSL_read returned WANTWRITE. But now SSL_read is saying WANTREAD + * again. Stop waiting for write events now, or else we'll + * busy-loop until data arrives for us to read. */ + connection_stop_writing(conn); + if (!connection_is_reading(conn)) + connection_start_reading(conn); + } + /* we're already reading, one hopes */ + break; + case TOR_TLS_DONE: /* no data read, so nothing to process */ + break; /* so we call bucket_decrement below */ + default: + break; + } + pending = tor_tls_get_pending_bytes(or_conn->tls); + if (pending) { + /* If we have any pending bytes, we read them now. This *can* + * take us over our read allotment, but really we shouldn't be + * believing that SSL bytes are the same as TCP bytes anyway. */ + int r2 = buf_read_from_tls(conn->inbuf, or_conn->tls, pending); + if (BUG(r2<0)) { + log_warn(LD_BUG, "apparently, reading pending bytes can fail."); + return -1; + } + } + result = (int)(buf_datalen(conn->inbuf)-initial_size); + tor_tls_get_n_raw_bytes(or_conn->tls, &n_read, &n_written); + log_debug(LD_GENERAL, "After TLS read of %d: %ld read, %ld written", + result, (long)n_read, (long)n_written); + } else if (conn->linked) { + if (conn->linked_conn) { + result = buf_move_to_buf(conn->inbuf, conn->linked_conn->outbuf, + &conn->linked_conn->outbuf_flushlen); + } else { + result = 0; + } + //log_notice(LD_GENERAL, "Moved %d bytes on an internal link!", result); + /* If the other side has disappeared, or if it's been marked for close and + * we flushed its outbuf, then we should set our inbuf_reached_eof. */ + if (!conn->linked_conn || + (conn->linked_conn->marked_for_close && + buf_datalen(conn->linked_conn->outbuf) == 0)) + conn->inbuf_reached_eof = 1; + + n_read = (size_t) result; + } else { + /* !connection_speaks_cells, !conn->linked_conn. */ + int reached_eof = 0; + CONN_LOG_PROTECT(conn, + result = buf_read_from_socket(conn->inbuf, conn->s, + at_most, + &reached_eof, + socket_error)); + if (reached_eof) + conn->inbuf_reached_eof = 1; + +// log_fn(LOG_DEBUG,"read_to_buf returned %d.",read_result); + + if (result < 0) + return -1; + n_read = (size_t) result; + } + + if (n_read > 0) { + /* change *max_to_read */ + *max_to_read = at_most - n_read; + + /* Update edge_conn->n_read */ + if (conn->type == CONN_TYPE_AP) { + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + + /* Check for overflow: */ + if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_read > n_read)) + edge_conn->n_read += (int)n_read; + else + edge_conn->n_read = UINT32_MAX; + } + + /* If CONN_BW events are enabled, update conn->n_read_conn_bw for + * OR/DIR/EXIT connections, checking for overflow. */ + if (get_options()->TestingEnableConnBwEvent && + (conn->type == CONN_TYPE_OR || + conn->type == CONN_TYPE_DIR || + conn->type == CONN_TYPE_EXIT)) { + if (PREDICT_LIKELY(UINT32_MAX - conn->n_read_conn_bw > n_read)) + conn->n_read_conn_bw += (int)n_read; + else + conn->n_read_conn_bw = UINT32_MAX; + } + } + + connection_buckets_decrement(conn, approx_time(), n_read, n_written); + + if (more_to_read && result == at_most) { + slack_in_buf = buf_slack(conn->inbuf); + at_most = more_to_read; + goto again; + } + + /* Call even if result is 0, since the global read bucket may + * have reached 0 on a different conn, and this connection needs to + * know to stop reading. */ + connection_consider_empty_read_buckets(conn); + if (n_written > 0 && connection_is_writing(conn)) + connection_consider_empty_write_buckets(conn); + + return 0; +} + +/** A pass-through to fetch_from_buf. */ +int +connection_buf_get_bytes(char *string, size_t len, connection_t *conn) +{ + return buf_get_bytes(conn->inbuf, string, len); +} + +/** As buf_get_line(), but read from a connection's input buffer. */ +int +connection_buf_get_line(connection_t *conn, char *data, + size_t *data_len) +{ + return buf_get_line(conn->inbuf, data, data_len); +} + +/** As fetch_from_buf_http, but fetches from a connection's input buffer_t as + * appropriate. */ +int +connection_fetch_from_buf_http(connection_t *conn, + char **headers_out, size_t max_headerlen, + char **body_out, size_t *body_used, + size_t max_bodylen, int force_complete) +{ + return fetch_from_buf_http(conn->inbuf, headers_out, max_headerlen, + body_out, body_used, max_bodylen, force_complete); +} + +/** Return conn-\>outbuf_flushlen: how many bytes conn wants to flush + * from its outbuf. */ +int +connection_wants_to_flush(connection_t *conn) +{ + return conn->outbuf_flushlen > 0; +} + +/** Are there too many bytes on edge connection <b>conn</b>'s outbuf to + * send back a relay-level sendme yet? Return 1 if so, 0 if not. Used by + * connection_edge_consider_sending_sendme(). + */ +int +connection_outbuf_too_full(connection_t *conn) +{ + return (conn->outbuf_flushlen > 10*CELL_PAYLOAD_SIZE); +} + +/** + * On Windows Vista and Windows 7, tune the send buffer size according to a + * hint from the OS. + * + * This should help fix slow upload rates. + */ +static void +update_send_buffer_size(tor_socket_t sock) +{ +#ifdef _WIN32 + /* We only do this on Vista and 7, because earlier versions of Windows + * don't have the SIO_IDEAL_SEND_BACKLOG_QUERY functionality, and on + * later versions it isn't necessary. */ + static int isVistaOr7 = -1; + if (isVistaOr7 == -1) { + isVistaOr7 = 0; + OSVERSIONINFO osvi = { 0 }; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion < 2) + isVistaOr7 = 1; + } + if (!isVistaOr7) + return; + if (get_options()->ConstrainedSockets) + return; + ULONG isb = 0; + DWORD bytesReturned = 0; + if (!WSAIoctl(sock, SIO_IDEAL_SEND_BACKLOG_QUERY, NULL, 0, + &isb, sizeof(isb), &bytesReturned, NULL, NULL)) { + setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&isb, sizeof(isb)); + } +#else + (void) sock; +#endif +} + +/** Try to flush more bytes onto <b>conn</b>-\>s. + * + * This function gets called either from conn_write_callback() in main.c + * when libevent tells us that conn wants to write, or below + * from connection_buf_add() when an entire TLS record is ready. + * + * Update <b>conn</b>-\>timestamp_last_write_allowed to now, and call flush_buf + * or flush_buf_tls appropriately. If it succeeds and there are no more + * more bytes on <b>conn</b>-\>outbuf, then call connection_finished_flushing + * on it too. + * + * If <b>force</b>, then write as many bytes as possible, ignoring bandwidth + * limits. (Used for flushing messages to controller connections on fatal + * errors.) + * + * Mark the connection and return -1 if you want to close it, else + * return 0. + */ +static int +connection_handle_write_impl(connection_t *conn, int force) +{ + int e; + socklen_t len=(socklen_t)sizeof(e); + int result; + ssize_t max_to_write; + time_t now = approx_time(); + size_t n_read = 0, n_written = 0; + int dont_stop_writing = 0; + + tor_assert(!connection_is_listener(conn)); + + if (conn->marked_for_close || !SOCKET_OK(conn->s)) + return 0; /* do nothing */ + + if (conn->in_flushed_some) { + log_warn(LD_BUG, "called recursively from inside conn->in_flushed_some"); + return 0; + } + + conn->timestamp_last_write_allowed = now; + + connection_bucket_refill_single(conn, monotime_coarse_get_stamp()); + + /* Sometimes, "writable" means "connected". */ + if (connection_state_is_connecting(conn)) { + if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { + log_warn(LD_BUG, "getsockopt() syscall failed"); + if (conn->type == CONN_TYPE_OR) { + or_connection_t *orconn = TO_OR_CONN(conn); + connection_or_close_for_error(orconn, 0); + } else { + if (CONN_IS_EDGE(conn)) { + connection_edge_end_errno(TO_EDGE_CONN(conn)); + } + connection_mark_for_close(conn); + } + return -1; + } + if (e) { + /* some sort of error, but maybe just inprogress still */ + if (!ERRNO_IS_CONN_EINPROGRESS(e)) { + log_info(LD_NET,"in-progress connect failed. Removing. (%s)", + tor_socket_strerror(e)); + if (CONN_IS_EDGE(conn)) + connection_edge_end_errno(TO_EDGE_CONN(conn)); + if (conn->type == CONN_TYPE_OR) + connection_or_notify_error(TO_OR_CONN(conn), + errno_to_orconn_end_reason(e), + tor_socket_strerror(e)); + + connection_close_immediate(conn); + /* + * This can bypass normal channel checking since we did + * connection_or_notify_error() above. + */ + connection_mark_for_close_internal(conn); + return -1; + } else { + return 0; /* no change, see if next time is better */ + } + } + /* The connection is successful. */ + if (connection_finished_connecting(conn)<0) + return -1; + } + + max_to_write = force ? (ssize_t)conn->outbuf_flushlen + : connection_bucket_write_limit(conn, now); + + if (connection_speaks_cells(conn) && + conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) { + or_connection_t *or_conn = TO_OR_CONN(conn); + size_t initial_size; + if (conn->state == OR_CONN_STATE_TLS_HANDSHAKING || + conn->state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) { + connection_stop_writing(conn); + if (connection_tls_continue_handshake(or_conn) < 0) { + /* Don't flush; connection is dead. */ + connection_or_notify_error(or_conn, + END_OR_CONN_REASON_MISC, + "TLS error in connection_tls_" + "continue_handshake()"); + connection_close_immediate(conn); + /* + * This can bypass normal channel checking since we did + * connection_or_notify_error() above. + */ + connection_mark_for_close_internal(conn); + return -1; + } + return 0; + } else if (conn->state == OR_CONN_STATE_TLS_SERVER_RENEGOTIATING) { + return connection_handle_read(conn); + } + + /* else open, or closing */ + initial_size = buf_datalen(conn->outbuf); + result = buf_flush_to_tls(conn->outbuf, or_conn->tls, + max_to_write, &conn->outbuf_flushlen); + + if (result >= 0) + update_send_buffer_size(conn->s); + + /* If we just flushed the last bytes, tell the channel on the + * or_conn to check if it needs to geoip_change_dirreq_state() */ + /* XXXX move this to flushed_some or finished_flushing -NM */ + if (buf_datalen(conn->outbuf) == 0 && or_conn->chan) + channel_notify_flushed(TLS_CHAN_TO_BASE(or_conn->chan)); + + switch (result) { + CASE_TOR_TLS_ERROR_ANY: + case TOR_TLS_CLOSE: + log_info(LD_NET, result != TOR_TLS_CLOSE ? + "tls error. breaking.":"TLS connection closed on flush"); + /* Don't flush; connection is dead. */ + connection_or_notify_error(or_conn, + END_OR_CONN_REASON_MISC, + result != TOR_TLS_CLOSE ? + "TLS error in during flush" : + "TLS closed during flush"); + connection_close_immediate(conn); + /* + * This can bypass normal channel checking since we did + * connection_or_notify_error() above. + */ + connection_mark_for_close_internal(conn); + return -1; + case TOR_TLS_WANTWRITE: + log_debug(LD_NET,"wanted write."); + /* we're already writing */ + dont_stop_writing = 1; + break; + case TOR_TLS_WANTREAD: + /* Make sure to avoid a loop if the receive buckets are empty. */ + log_debug(LD_NET,"wanted read."); + if (!connection_is_reading(conn)) { + connection_write_bw_exhausted(conn, true); + /* we'll start reading again when we get more tokens in our + * read bucket; then we'll start writing again too. + */ + } + /* else no problem, we're already reading */ + return 0; + /* case TOR_TLS_DONE: + * for TOR_TLS_DONE, fall through to check if the flushlen + * is empty, so we can stop writing. + */ + } + + tor_tls_get_n_raw_bytes(or_conn->tls, &n_read, &n_written); + log_debug(LD_GENERAL, "After TLS write of %d: %ld read, %ld written", + result, (long)n_read, (long)n_written); + or_conn->bytes_xmitted += result; + or_conn->bytes_xmitted_by_tls += n_written; + /* So we notice bytes were written even on error */ + /* XXXX This cast is safe since we can never write INT_MAX bytes in a + * single set of TLS operations. But it looks kinda ugly. If we refactor + * the *_buf_tls functions, we should make them return ssize_t or size_t + * or something. */ + result = (int)(initial_size-buf_datalen(conn->outbuf)); + } else { + CONN_LOG_PROTECT(conn, + result = buf_flush_to_socket(conn->outbuf, conn->s, + max_to_write, &conn->outbuf_flushlen)); + if (result < 0) { + if (CONN_IS_EDGE(conn)) + connection_edge_end_errno(TO_EDGE_CONN(conn)); + if (conn->type == CONN_TYPE_AP) { + /* writing failed; we couldn't send a SOCKS reply if we wanted to */ + TO_ENTRY_CONN(conn)->socks_request->has_finished = 1; + } + + connection_close_immediate(conn); /* Don't flush; connection is dead. */ + connection_mark_for_close(conn); + return -1; + } + update_send_buffer_size(conn->s); + n_written = (size_t) result; + } + + if (n_written && conn->type == CONN_TYPE_AP) { + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + + /* Check for overflow: */ + if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_written > n_written)) + edge_conn->n_written += (int)n_written; + else + edge_conn->n_written = UINT32_MAX; + } + + /* If CONN_BW events are enabled, update conn->n_written_conn_bw for + * OR/DIR/EXIT connections, checking for overflow. */ + if (n_written && get_options()->TestingEnableConnBwEvent && + (conn->type == CONN_TYPE_OR || + conn->type == CONN_TYPE_DIR || + conn->type == CONN_TYPE_EXIT)) { + if (PREDICT_LIKELY(UINT32_MAX - conn->n_written_conn_bw > n_written)) + conn->n_written_conn_bw += (int)n_written; + else + conn->n_written_conn_bw = UINT32_MAX; + } + + connection_buckets_decrement(conn, approx_time(), n_read, n_written); + + if (result > 0) { + /* If we wrote any bytes from our buffer, then call the appropriate + * functions. */ + if (connection_flushed_some(conn) < 0) { + if (connection_speaks_cells(conn)) { + connection_or_notify_error(TO_OR_CONN(conn), + END_OR_CONN_REASON_MISC, + "Got error back from " + "connection_flushed_some()"); + } + + /* + * This can bypass normal channel checking since we did + * connection_or_notify_error() above. + */ + connection_mark_for_close_internal(conn); + } + } + + if (!connection_wants_to_flush(conn) && + !dont_stop_writing) { /* it's done flushing */ + if (connection_finished_flushing(conn) < 0) { + /* already marked */ + return -1; + } + return 0; + } + + /* Call even if result is 0, since the global write bucket may + * have reached 0 on a different conn, and this connection needs to + * know to stop writing. */ + connection_consider_empty_write_buckets(conn); + if (n_read > 0 && connection_is_reading(conn)) + connection_consider_empty_read_buckets(conn); + + return 0; +} + +/* DOCDOC connection_handle_write */ +int +connection_handle_write(connection_t *conn, int force) +{ + int res; + update_current_time(time(NULL)); + conn->in_connection_handle_write = 1; + res = connection_handle_write_impl(conn, force); + conn->in_connection_handle_write = 0; + return res; +} + +/** + * Try to flush data that's waiting for a write on <b>conn</b>. Return + * -1 on failure, 0 on success. + * + * Don't use this function for regular writing; the buffers + * system should be good enough at scheduling writes there. Instead, this + * function is for cases when we're about to exit or something and we want + * to report it right away. + */ +int +connection_flush(connection_t *conn) +{ + return connection_handle_write(conn, 1); +} + +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Return true iff it is okay to queue bytes on <b>conn</b>'s outbuf for + * writing. + */ +static int +connection_may_write_to_buf(connection_t *conn) +{ + /* if it's marked for close, only allow write if we mean to flush it */ + if (conn->marked_for_close && !conn->hold_open_until_flushed) + return 0; + + return 1; +} + +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Called when an attempt to add bytes on <b>conn</b>'s outbuf has failed; + * mark the connection and warn as appropriate. + */ +static void +connection_write_to_buf_failed(connection_t *conn) +{ + if (CONN_IS_EDGE(conn)) { + /* if it failed, it means we have our package/delivery windows set + wrong compared to our max outbuf size. close the whole circuit. */ + log_warn(LD_NET, + "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s); + circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)), + END_CIRC_REASON_INTERNAL); + } else if (conn->type == CONN_TYPE_OR) { + or_connection_t *orconn = TO_OR_CONN(conn); + log_warn(LD_NET, + "write_to_buf failed on an orconn; notifying of error " + "(fd %d)", (int)(conn->s)); + connection_or_close_for_error(orconn, 0); + } else { + log_warn(LD_NET, + "write_to_buf failed. Closing connection (fd %d).", + (int)conn->s); + connection_mark_for_close(conn); + } +} + +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Called when an attempt to add bytes on <b>conn</b>'s outbuf has succeeded: + * record the number of bytes added. + */ +static void +connection_write_to_buf_commit(connection_t *conn, size_t len) +{ + /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING + * state, we don't want to try to write it right away, since + * conn->write_event won't be set yet. Otherwise, write data from + * this conn as the socket is available. */ + if (conn->write_event) { + connection_start_writing(conn); + } + conn->outbuf_flushlen += len; +} + +/** Append <b>len</b> bytes of <b>string</b> onto <b>conn</b>'s + * outbuf, and ask it to start writing. + * + * If <b>zlib</b> is nonzero, this is a directory connection that should get + * its contents compressed or decompressed as they're written. If zlib is + * negative, this is the last data to be compressed, and the connection's zlib + * state should be flushed. + */ +MOCK_IMPL(void, +connection_write_to_buf_impl_,(const char *string, size_t len, + connection_t *conn, int zlib)) +{ + /* XXXX This function really needs to return -1 on failure. */ + int r; + if (!len && !(zlib<0)) + return; + + if (!connection_may_write_to_buf(conn)) + return; + + size_t written; + + if (zlib) { + size_t old_datalen = buf_datalen(conn->outbuf); + dir_connection_t *dir_conn = TO_DIR_CONN(conn); + int done = zlib < 0; + CONN_LOG_PROTECT(conn, r = buf_add_compress(conn->outbuf, + dir_conn->compress_state, + string, len, done)); + written = buf_datalen(conn->outbuf) - old_datalen; + } else { + CONN_LOG_PROTECT(conn, r = buf_add(conn->outbuf, string, len)); + written = len; + } + if (r < 0) { + connection_write_to_buf_failed(conn); + return; + } + connection_write_to_buf_commit(conn, written); +} + +void +connection_buf_add_compress(const char *string, size_t len, + dir_connection_t *conn, int done) +{ + connection_write_to_buf_impl_(string, len, TO_CONN(conn), done ? -1 : 1); +} + +/** + * Add all bytes from <b>buf</b> to <b>conn</b>'s outbuf, draining them + * from <b>buf</b>. (If the connection is marked and will soon be closed, + * nothing is drained.) + */ +void +connection_buf_add_buf(connection_t *conn, buf_t *buf) +{ + tor_assert(conn); + tor_assert(buf); + size_t len = buf_datalen(buf); + if (len == 0) + return; + + if (!connection_may_write_to_buf(conn)) + return; + + buf_move_all(conn->outbuf, buf); + connection_write_to_buf_commit(conn, len); +} + +#define CONN_GET_ALL_TEMPLATE(var, test) \ + STMT_BEGIN \ + smartlist_t *conns = get_connection_array(); \ + smartlist_t *ret_conns = smartlist_new(); \ + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, var) { \ + if (var && (test) && !var->marked_for_close) \ + smartlist_add(ret_conns, var); \ + } SMARTLIST_FOREACH_END(var); \ + return ret_conns; \ + STMT_END + +/* Return a list of connections that aren't close and matches the given type + * and state. The returned list can be empty and must be freed using + * smartlist_free(). The caller does NOT have ownership of the objects in the + * list so it must not free them nor reference them as they can disappear. */ +smartlist_t * +connection_list_by_type_state(int type, int state) +{ + CONN_GET_ALL_TEMPLATE(conn, (conn->type == type && conn->state == state)); +} + +/* Return a list of connections that aren't close and matches the given type + * and purpose. The returned list can be empty and must be freed using + * smartlist_free(). The caller does NOT have ownership of the objects in the + * list so it must not free them nor reference them as they can disappear. */ +smartlist_t * +connection_list_by_type_purpose(int type, int purpose) +{ + CONN_GET_ALL_TEMPLATE(conn, + (conn->type == type && conn->purpose == purpose)); +} + +/** Return a connection_t * from get_connection_array() that satisfies test on + * var, and that is not marked for close. */ +#define CONN_GET_TEMPLATE(var, test) \ + STMT_BEGIN \ + smartlist_t *conns = get_connection_array(); \ + SMARTLIST_FOREACH(conns, connection_t *, var, \ + { \ + if (var && (test) && !var->marked_for_close) \ + return var; \ + }); \ + return NULL; \ + STMT_END + +/** Return a connection with given type, address, port, and purpose; + * or NULL if no such connection exists (or if all such connections are marked + * for close). */ +MOCK_IMPL(connection_t *, +connection_get_by_type_addr_port_purpose,(int type, + const tor_addr_t *addr, uint16_t port, + int purpose)) +{ + CONN_GET_TEMPLATE(conn, + (conn->type == type && + tor_addr_eq(&conn->addr, addr) && + conn->port == port && + conn->purpose == purpose)); +} + +/** Return the stream with id <b>id</b> if it is not already marked for + * close. + */ +connection_t * +connection_get_by_global_id(uint64_t id) +{ + CONN_GET_TEMPLATE(conn, conn->global_identifier == id); +} + +/** Return a connection of type <b>type</b> that is not marked for close. + */ +connection_t * +connection_get_by_type(int type) +{ + CONN_GET_TEMPLATE(conn, conn->type == type); +} + +/** Return a connection of type <b>type</b> that is in state <b>state</b>, + * and that is not marked for close. + */ +connection_t * +connection_get_by_type_state(int type, int state) +{ + CONN_GET_TEMPLATE(conn, conn->type == type && conn->state == state); +} + +/** Return a connection of type <b>type</b> that has rendquery equal + * to <b>rendquery</b>, and that is not marked for close. If state + * is non-zero, conn must be of that state too. + */ +connection_t * +connection_get_by_type_state_rendquery(int type, int state, + const char *rendquery) +{ + tor_assert(type == CONN_TYPE_DIR || + type == CONN_TYPE_AP || type == CONN_TYPE_EXIT); + tor_assert(rendquery); + + CONN_GET_TEMPLATE(conn, + (conn->type == type && + (!state || state == conn->state)) && + ( + (type == CONN_TYPE_DIR && + TO_DIR_CONN(conn)->rend_data && + !rend_cmp_service_ids(rendquery, + rend_data_get_address(TO_DIR_CONN(conn)->rend_data))) + || + (CONN_IS_EDGE(conn) && + TO_EDGE_CONN(conn)->rend_data && + !rend_cmp_service_ids(rendquery, + rend_data_get_address(TO_EDGE_CONN(conn)->rend_data))) + )); +} + +/** Return a new smartlist of dir_connection_t * from get_connection_array() + * that satisfy conn_test on connection_t *conn_var, and dirconn_test on + * dir_connection_t *dirconn_var. conn_var must be of CONN_TYPE_DIR and not + * marked for close to be included in the list. */ +#define DIR_CONN_LIST_TEMPLATE(conn_var, conn_test, \ + dirconn_var, dirconn_test) \ + STMT_BEGIN \ + smartlist_t *conns = get_connection_array(); \ + smartlist_t *dir_conns = smartlist_new(); \ + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn_var) { \ + if (conn_var && (conn_test) \ + && conn_var->type == CONN_TYPE_DIR \ + && !conn_var->marked_for_close) { \ + dir_connection_t *dirconn_var = TO_DIR_CONN(conn_var); \ + if (dirconn_var && (dirconn_test)) { \ + smartlist_add(dir_conns, dirconn_var); \ + } \ + } \ + } SMARTLIST_FOREACH_END(conn_var); \ + return dir_conns; \ + STMT_END + +/** Return a list of directory connections that are fetching the item + * described by <b>purpose</b>/<b>resource</b>. If there are none, + * return an empty list. This list must be freed using smartlist_free, + * but the pointers in it must not be freed. + * Note that this list should not be cached, as the pointers in it can be + * freed if their connections close. */ +smartlist_t * +connection_dir_list_by_purpose_and_resource( + int purpose, + const char *resource) +{ + DIR_CONN_LIST_TEMPLATE(conn, + conn->purpose == purpose, + dirconn, + 0 == strcmp_opt(resource, + dirconn->requested_resource)); +} + +/** Return a list of directory connections that are fetching the item + * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. If there are + * none, return an empty list. This list must be freed using smartlist_free, + * but the pointers in it must not be freed. + * Note that this list should not be cached, as the pointers in it can be + * freed if their connections close. */ +smartlist_t * +connection_dir_list_by_purpose_resource_and_state( + int purpose, + const char *resource, + int state) +{ + DIR_CONN_LIST_TEMPLATE(conn, + conn->purpose == purpose && conn->state == state, + dirconn, + 0 == strcmp_opt(resource, + dirconn->requested_resource)); +} + +#undef DIR_CONN_LIST_TEMPLATE + +/** Return an arbitrary active OR connection that isn't <b>this_conn</b>. + * + * We use this to guess if we should tell the controller that we + * didn't manage to connect to any of our bridges. */ +static connection_t * +connection_get_another_active_or_conn(const or_connection_t *this_conn) +{ + CONN_GET_TEMPLATE(conn, + conn != TO_CONN(this_conn) && conn->type == CONN_TYPE_OR); +} + +/** Return 1 if there are any active OR connections apart from + * <b>this_conn</b>. + * + * We use this to guess if we should tell the controller that we + * didn't manage to connect to any of our bridges. */ +int +any_other_active_or_conns(const or_connection_t *this_conn) +{ + connection_t *conn = connection_get_another_active_or_conn(this_conn); + if (conn != NULL) { + log_debug(LD_DIR, "%s: Found an OR connection: %s", + __func__, conn->address); + return 1; + } + + return 0; +} + +#undef CONN_GET_TEMPLATE + +/** Return 1 if <b>conn</b> is a listener conn, else return 0. */ +int +connection_is_listener(connection_t *conn) +{ + if (conn->type == CONN_TYPE_OR_LISTENER || + conn->type == CONN_TYPE_EXT_OR_LISTENER || + conn->type == CONN_TYPE_AP_LISTENER || + conn->type == CONN_TYPE_AP_TRANS_LISTENER || + conn->type == CONN_TYPE_AP_DNS_LISTENER || + conn->type == CONN_TYPE_AP_NATD_LISTENER || + conn->type == CONN_TYPE_AP_HTTP_CONNECT_LISTENER || + conn->type == CONN_TYPE_DIR_LISTENER || + conn->type == CONN_TYPE_CONTROL_LISTENER) + return 1; + return 0; +} + +/** Return 1 if <b>conn</b> is in state "open" and is not marked + * for close, else return 0. + */ +int +connection_state_is_open(connection_t *conn) +{ + tor_assert(conn); + + if (conn->marked_for_close) + return 0; + + if ((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) || + (conn->type == CONN_TYPE_EXT_OR) || + (conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) || + (conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN) || + (conn->type == CONN_TYPE_CONTROL && + conn->state == CONTROL_CONN_STATE_OPEN)) + return 1; + + return 0; +} + +/** Return 1 if conn is in 'connecting' state, else return 0. */ +int +connection_state_is_connecting(connection_t *conn) +{ + tor_assert(conn); + + if (conn->marked_for_close) + return 0; + switch (conn->type) + { + case CONN_TYPE_OR: + return conn->state == OR_CONN_STATE_CONNECTING; + case CONN_TYPE_EXIT: + return conn->state == EXIT_CONN_STATE_CONNECTING; + case CONN_TYPE_DIR: + return conn->state == DIR_CONN_STATE_CONNECTING; + } + + return 0; +} + +/** Allocates a base64'ed authenticator for use in http or https + * auth, based on the input string <b>authenticator</b>. Returns it + * if success, else returns NULL. */ +char * +alloc_http_authenticator(const char *authenticator) +{ + /* an authenticator in Basic authentication + * is just the string "username:password" */ + const size_t authenticator_length = strlen(authenticator); + const size_t base64_authenticator_length = + base64_encode_size(authenticator_length, 0) + 1; + char *base64_authenticator = tor_malloc(base64_authenticator_length); + if (base64_encode(base64_authenticator, base64_authenticator_length, + authenticator, authenticator_length, 0) < 0) { + tor_free(base64_authenticator); /* free and set to null */ + } + return base64_authenticator; +} + +/** Given a socket handle, check whether the local address (sockname) of the + * socket is one that we've connected from before. If so, double-check + * whether our address has changed and we need to generate keys. If we do, + * call init_keys(). + */ +static void +client_check_address_changed(tor_socket_t sock) +{ + tor_addr_t out_addr, iface_addr; + tor_addr_t **last_interface_ip_ptr; + sa_family_t family; + + if (!outgoing_addrs) + outgoing_addrs = smartlist_new(); + + if (tor_addr_from_getsockname(&out_addr, sock) < 0) { + int e = tor_socket_errno(sock); + log_warn(LD_NET, "getsockname() to check for address change failed: %s", + tor_socket_strerror(e)); + return; + } + family = tor_addr_family(&out_addr); + + if (family == AF_INET) + last_interface_ip_ptr = &last_interface_ipv4; + else if (family == AF_INET6) + last_interface_ip_ptr = &last_interface_ipv6; + else + return; + + if (! *last_interface_ip_ptr) { + tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t)); + if (get_interface_address6(LOG_INFO, family, a)==0) { + *last_interface_ip_ptr = a; + } else { + tor_free(a); + } + } + + /* If we've used this address previously, we're okay. */ + SMARTLIST_FOREACH(outgoing_addrs, const tor_addr_t *, a_ptr, + if (tor_addr_eq(a_ptr, &out_addr)) + return; + ); + + /* Uh-oh. We haven't connected from this address before. Has the interface + * address changed? */ + if (get_interface_address6(LOG_INFO, family, &iface_addr)<0) + return; + + if (tor_addr_eq(&iface_addr, *last_interface_ip_ptr)) { + /* Nope, it hasn't changed. Add this address to the list. */ + smartlist_add(outgoing_addrs, tor_memdup(&out_addr, sizeof(tor_addr_t))); + } else { + /* The interface changed. We're a client, so we need to regenerate our + * keys. First, reset the state. */ + log_notice(LD_NET, "Our IP address has changed. Rotating keys..."); + tor_addr_copy(*last_interface_ip_ptr, &iface_addr); + SMARTLIST_FOREACH(outgoing_addrs, tor_addr_t*, a_ptr, tor_free(a_ptr)); + smartlist_clear(outgoing_addrs); + smartlist_add(outgoing_addrs, tor_memdup(&out_addr, sizeof(tor_addr_t))); + /* We'll need to resolve ourselves again. */ + reset_last_resolved_addr(); + /* Okay, now change our keys. */ + ip_address_changed(1); + } +} + +/** Some systems have limited system buffers for recv and xmit on + * sockets allocated in a virtual server or similar environment. For a Tor + * server this can produce the "Error creating network socket: No buffer + * space available" error once all available TCP buffer space is consumed. + * This method will attempt to constrain the buffers allocated for the socket + * to the desired size to stay below system TCP buffer limits. + */ +static void +set_constrained_socket_buffers(tor_socket_t sock, int size) +{ + void *sz = (void*)&size; + socklen_t sz_sz = (socklen_t) sizeof(size); + if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, sz, sz_sz) < 0) { + int e = tor_socket_errno(sock); + log_warn(LD_NET, "setsockopt() to constrain send " + "buffer to %d bytes failed: %s", size, tor_socket_strerror(e)); + } + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, sz, sz_sz) < 0) { + int e = tor_socket_errno(sock); + log_warn(LD_NET, "setsockopt() to constrain recv " + "buffer to %d bytes failed: %s", size, tor_socket_strerror(e)); + } +} + +/** Process new bytes that have arrived on conn-\>inbuf. + * + * This function just passes conn to the connection-specific + * connection_*_process_inbuf() function. It also passes in + * package_partial if wanted. + */ +static int +connection_process_inbuf(connection_t *conn, int package_partial) +{ + tor_assert(conn); + + switch (conn->type) { + case CONN_TYPE_OR: + return connection_or_process_inbuf(TO_OR_CONN(conn)); + case CONN_TYPE_EXT_OR: + return connection_ext_or_process_inbuf(TO_OR_CONN(conn)); + case CONN_TYPE_EXIT: + case CONN_TYPE_AP: + return connection_edge_process_inbuf(TO_EDGE_CONN(conn), + package_partial); + case CONN_TYPE_DIR: + return connection_dir_process_inbuf(TO_DIR_CONN(conn)); + case CONN_TYPE_CONTROL: + return connection_control_process_inbuf(TO_CONTROL_CONN(conn)); + default: + log_err(LD_BUG,"got unexpected conn type %d.", conn->type); + tor_fragile_assert(); + return -1; + } +} + +/** Called whenever we've written data on a connection. */ +static int +connection_flushed_some(connection_t *conn) +{ + int r = 0; + tor_assert(!conn->in_flushed_some); + conn->in_flushed_some = 1; + if (conn->type == CONN_TYPE_DIR && + conn->state == DIR_CONN_STATE_SERVER_WRITING) { + r = connection_dirserv_flushed_some(TO_DIR_CONN(conn)); + } else if (conn->type == CONN_TYPE_OR) { + r = connection_or_flushed_some(TO_OR_CONN(conn)); + } else if (CONN_IS_EDGE(conn)) { + r = connection_edge_flushed_some(TO_EDGE_CONN(conn)); + } + conn->in_flushed_some = 0; + return r; +} + +/** We just finished flushing bytes to the appropriately low network layer, + * and there are no more bytes remaining in conn-\>outbuf or + * conn-\>tls to be flushed. + * + * This function just passes conn to the connection-specific + * connection_*_finished_flushing() function. + */ +static int +connection_finished_flushing(connection_t *conn) +{ + tor_assert(conn); + + /* If the connection is closed, don't try to do anything more here. */ + if (CONN_IS_CLOSED(conn)) + return 0; + +// log_fn(LOG_DEBUG,"entered. Socket %u.", conn->s); + + connection_stop_writing(conn); + + switch (conn->type) { + case CONN_TYPE_OR: + return connection_or_finished_flushing(TO_OR_CONN(conn)); + case CONN_TYPE_EXT_OR: + return connection_ext_or_finished_flushing(TO_OR_CONN(conn)); + case CONN_TYPE_AP: + case CONN_TYPE_EXIT: + return connection_edge_finished_flushing(TO_EDGE_CONN(conn)); + case CONN_TYPE_DIR: + return connection_dir_finished_flushing(TO_DIR_CONN(conn)); + case CONN_TYPE_CONTROL: + return connection_control_finished_flushing(TO_CONTROL_CONN(conn)); + default: + log_err(LD_BUG,"got unexpected conn type %d.", conn->type); + tor_fragile_assert(); + return -1; + } +} + +/** Called when our attempt to connect() to another server has just + * succeeded. + * + * This function just passes conn to the connection-specific + * connection_*_finished_connecting() function. + */ +static int +connection_finished_connecting(connection_t *conn) +{ + tor_assert(conn); + + if (!server_mode(get_options())) { + /* See whether getsockname() says our address changed. We need to do this + * now that the connection has finished, because getsockname() on Windows + * won't work until then. */ + client_check_address_changed(conn->s); + } + + switch (conn->type) + { + case CONN_TYPE_OR: + return connection_or_finished_connecting(TO_OR_CONN(conn)); + case CONN_TYPE_EXIT: + return connection_edge_finished_connecting(TO_EDGE_CONN(conn)); + case CONN_TYPE_DIR: + return connection_dir_finished_connecting(TO_DIR_CONN(conn)); + default: + log_err(LD_BUG,"got unexpected conn type %d.", conn->type); + tor_fragile_assert(); + return -1; + } +} + +/** Callback: invoked when a connection reaches an EOF event. */ +static int +connection_reached_eof(connection_t *conn) +{ + switch (conn->type) { + case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: + return connection_or_reached_eof(TO_OR_CONN(conn)); + case CONN_TYPE_AP: + case CONN_TYPE_EXIT: + return connection_edge_reached_eof(TO_EDGE_CONN(conn)); + case CONN_TYPE_DIR: + return connection_dir_reached_eof(TO_DIR_CONN(conn)); + case CONN_TYPE_CONTROL: + return connection_control_reached_eof(TO_CONTROL_CONN(conn)); + default: + log_err(LD_BUG,"got unexpected conn type %d.", conn->type); + tor_fragile_assert(); + return -1; + } +} + +/** Comparator for the two-orconn case in OOS victim sort */ +static int +oos_victim_comparator_for_orconns(or_connection_t *a, or_connection_t *b) +{ + int a_circs, b_circs; + /* Fewer circuits == higher priority for OOS kill, sort earlier */ + + a_circs = connection_or_get_num_circuits(a); + b_circs = connection_or_get_num_circuits(b); + + if (a_circs < b_circs) return 1; + else if (a_circs > b_circs) return -1; + else return 0; +} + +/** Sort comparator for OOS victims; better targets sort before worse + * ones. */ +static int +oos_victim_comparator(const void **a_v, const void **b_v) +{ + connection_t *a = NULL, *b = NULL; + + /* Get connection pointers out */ + + a = (connection_t *)(*a_v); + b = (connection_t *)(*b_v); + + tor_assert(a != NULL); + tor_assert(b != NULL); + + /* + * We always prefer orconns as victims currently; we won't even see + * these non-orconn cases, but if we do, sort them after orconns. + */ + if (a->type == CONN_TYPE_OR && b->type == CONN_TYPE_OR) { + return oos_victim_comparator_for_orconns(TO_OR_CONN(a), TO_OR_CONN(b)); + } else { + /* + * One isn't an orconn; if one is, it goes first. We currently have no + * opinions about cases where neither is an orconn. + */ + if (a->type == CONN_TYPE_OR) return -1; + else if (b->type == CONN_TYPE_OR) return 1; + else return 0; + } +} + +/** Pick n victim connections for the OOS handler and return them in a + * smartlist. + */ +MOCK_IMPL(STATIC smartlist_t *, +pick_oos_victims, (int n)) +{ + smartlist_t *eligible = NULL, *victims = NULL; + smartlist_t *conns; + int conn_counts_by_type[CONN_TYPE_MAX_ + 1], i; + + /* + * Big damn assumption (someone improve this someday!): + * + * Socket exhaustion normally happens on high-volume relays, and so + * most of the connections involved are orconns. We should pick victims + * by assembling a list of all orconns, and sorting them in order of + * how much 'damage' by some metric we'd be doing by dropping them. + * + * If we move on from orconns, we should probably think about incoming + * directory connections next, or exit connections. Things we should + * probably never kill are controller connections and listeners. + * + * This function will count how many connections of different types + * exist and log it for purposes of gathering data on typical OOS + * situations to guide future improvements. + */ + + /* First, get the connection array */ + conns = get_connection_array(); + /* + * Iterate it and pick out eligible connection types, and log some stats + * along the way. + */ + eligible = smartlist_new(); + memset(conn_counts_by_type, 0, sizeof(conn_counts_by_type)); + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) { + /* Bump the counter */ + tor_assert(c->type <= CONN_TYPE_MAX_); + ++(conn_counts_by_type[c->type]); + + /* Skip anything without a socket we can free */ + if (!(SOCKET_OK(c->s))) { + continue; + } + + /* Skip anything we would count as moribund */ + if (connection_is_moribund(c)) { + continue; + } + + switch (c->type) { + case CONN_TYPE_OR: + /* We've got an orconn, it's eligible to be OOSed */ + smartlist_add(eligible, c); + break; + default: + /* We don't know what to do with it, ignore it */ + break; + } + } SMARTLIST_FOREACH_END(c); + + /* Log some stats */ + if (smartlist_len(conns) > 0) { + /* At least one counter must be non-zero */ + log_info(LD_NET, "Some stats on conn types seen during OOS follow"); + for (i = CONN_TYPE_MIN_; i <= CONN_TYPE_MAX_; ++i) { + /* Did we see any? */ + if (conn_counts_by_type[i] > 0) { + log_info(LD_NET, "%s: %d conns", + conn_type_to_string(i), + conn_counts_by_type[i]); + } + } + log_info(LD_NET, "Done with OOS conn type stats"); + } + + /* Did we find more eligible targets than we want to kill? */ + if (smartlist_len(eligible) > n) { + /* Sort the list in order of target preference */ + smartlist_sort(eligible, oos_victim_comparator); + /* Pick first n as victims */ + victims = smartlist_new(); + for (i = 0; i < n; ++i) { + smartlist_add(victims, smartlist_get(eligible, i)); + } + /* Free the original list */ + smartlist_free(eligible); + } else { + /* No, we can just call them all victims */ + victims = eligible; + } + + return victims; +} + +/** Kill a list of connections for the OOS handler. */ +MOCK_IMPL(STATIC void, +kill_conn_list_for_oos, (smartlist_t *conns)) +{ + if (!conns) return; + + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) { + /* Make sure the channel layer gets told about orconns */ + if (c->type == CONN_TYPE_OR) { + connection_or_close_for_error(TO_OR_CONN(c), 1); + } else { + connection_mark_for_close(c); + } + } SMARTLIST_FOREACH_END(c); + + log_notice(LD_NET, + "OOS handler marked %d connections", + smartlist_len(conns)); +} + +/** Check if a connection is on the way out so the OOS handler doesn't try + * to kill more than it needs. */ +int +connection_is_moribund(connection_t *conn) +{ + if (conn != NULL && + (conn->conn_array_index < 0 || + conn->marked_for_close)) { + return 1; + } else { + return 0; + } +} + +/** Out-of-Sockets handler; n_socks is the current number of open + * sockets, and failed is non-zero if a socket exhaustion related + * error immediately preceded this call. This is where to do + * circuit-killing heuristics as needed. + */ +void +connection_check_oos(int n_socks, int failed) +{ + int target_n_socks = 0, moribund_socks, socks_to_kill; + smartlist_t *conns; + + /* Early exit: is OOS checking disabled? */ + if (get_options()->DisableOOSCheck) { + return; + } + + /* Sanity-check args */ + tor_assert(n_socks >= 0); + + /* + * Make some log noise; keep it at debug level since this gets a chance + * to run on every connection attempt. + */ + log_debug(LD_NET, + "Running the OOS handler (%d open sockets, %s)", + n_socks, (failed != 0) ? "exhaustion seen" : "no exhaustion"); + + /* + * Check if we're really handling an OOS condition, and if so decide how + * many sockets we want to get down to. Be sure we check if the threshold + * is distinct from zero first; it's possible for this to be called a few + * times before we've finished reading the config. + */ + if (n_socks >= get_options()->ConnLimit_high_thresh && + get_options()->ConnLimit_high_thresh != 0 && + get_options()->ConnLimit_ != 0) { + /* Try to get down to the low threshold */ + target_n_socks = get_options()->ConnLimit_low_thresh; + log_notice(LD_NET, + "Current number of sockets %d is greater than configured " + "limit %d; OOS handler trying to get down to %d", + n_socks, get_options()->ConnLimit_high_thresh, + target_n_socks); + } else if (failed) { + /* + * If we're not at the limit but we hit a socket exhaustion error, try to + * drop some (but not as aggressively as ConnLimit_low_threshold, which is + * 3/4 of ConnLimit_) + */ + target_n_socks = (n_socks * 9) / 10; + log_notice(LD_NET, + "We saw socket exhaustion at %d open sockets; OOS handler " + "trying to get down to %d", + n_socks, target_n_socks); + } + + if (target_n_socks > 0) { + /* + * It's an OOS! + * + * Count moribund sockets; it's be important that anything we decide + * to get rid of here but don't immediately close get counted as moribund + * on subsequent invocations so we don't try to kill too many things if + * connection_check_oos() gets called multiple times. + */ + moribund_socks = connection_count_moribund(); + + if (moribund_socks < n_socks - target_n_socks) { + socks_to_kill = n_socks - target_n_socks - moribund_socks; + + conns = pick_oos_victims(socks_to_kill); + if (conns) { + kill_conn_list_for_oos(conns); + log_notice(LD_NET, + "OOS handler killed %d conns", smartlist_len(conns)); + smartlist_free(conns); + } else { + log_notice(LD_NET, "OOS handler failed to pick any victim conns"); + } + } else { + log_notice(LD_NET, + "Not killing any sockets for OOS because there are %d " + "already moribund, and we only want to eliminate %d", + moribund_socks, n_socks - target_n_socks); + } + } +} + +/** Log how many bytes are used by buffers of different kinds and sizes. */ +void +connection_dump_buffer_mem_stats(int severity) +{ + uint64_t used_by_type[CONN_TYPE_MAX_+1]; + uint64_t alloc_by_type[CONN_TYPE_MAX_+1]; + int n_conns_by_type[CONN_TYPE_MAX_+1]; + uint64_t total_alloc = 0; + uint64_t total_used = 0; + int i; + smartlist_t *conns = get_connection_array(); + + memset(used_by_type, 0, sizeof(used_by_type)); + memset(alloc_by_type, 0, sizeof(alloc_by_type)); + memset(n_conns_by_type, 0, sizeof(n_conns_by_type)); + + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) { + int tp = c->type; + ++n_conns_by_type[tp]; + if (c->inbuf) { + used_by_type[tp] += buf_datalen(c->inbuf); + alloc_by_type[tp] += buf_allocation(c->inbuf); + } + if (c->outbuf) { + used_by_type[tp] += buf_datalen(c->outbuf); + alloc_by_type[tp] += buf_allocation(c->outbuf); + } + } SMARTLIST_FOREACH_END(c); + for (i=0; i <= CONN_TYPE_MAX_; ++i) { + total_used += used_by_type[i]; + total_alloc += alloc_by_type[i]; + } + + tor_log(severity, LD_GENERAL, + "In buffers for %d connections: %"PRIu64" used/%"PRIu64" allocated", + smartlist_len(conns), + (total_used), (total_alloc)); + for (i=CONN_TYPE_MIN_; i <= CONN_TYPE_MAX_; ++i) { + if (!n_conns_by_type[i]) + continue; + tor_log(severity, LD_GENERAL, + " For %d %s connections: %"PRIu64" used/%"PRIu64" allocated", + n_conns_by_type[i], conn_type_to_string(i), + (used_by_type[i]), (alloc_by_type[i])); + } +} + +/** Verify that connection <b>conn</b> has all of its invariants + * correct. Trigger an assert if anything is invalid. + */ +void +assert_connection_ok(connection_t *conn, time_t now) +{ + (void) now; /* XXXX unused. */ + tor_assert(conn); + tor_assert(conn->type >= CONN_TYPE_MIN_); + tor_assert(conn->type <= CONN_TYPE_MAX_); + + switch (conn->type) { + case CONN_TYPE_OR: + case CONN_TYPE_EXT_OR: + tor_assert(conn->magic == OR_CONNECTION_MAGIC); + break; + case CONN_TYPE_AP: + tor_assert(conn->magic == ENTRY_CONNECTION_MAGIC); + break; + case CONN_TYPE_EXIT: + tor_assert(conn->magic == EDGE_CONNECTION_MAGIC); + break; + case CONN_TYPE_DIR: + tor_assert(conn->magic == DIR_CONNECTION_MAGIC); + break; + case CONN_TYPE_CONTROL: + tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC); + break; + CASE_ANY_LISTENER_TYPE: + tor_assert(conn->magic == LISTENER_CONNECTION_MAGIC); + break; + default: + tor_assert(conn->magic == BASE_CONNECTION_MAGIC); + break; + } + + if (conn->linked_conn) { + tor_assert(conn->linked_conn->linked_conn == conn); + tor_assert(conn->linked); + } + if (conn->linked) + tor_assert(!SOCKET_OK(conn->s)); + + if (conn->outbuf_flushlen > 0) { + /* With optimistic data, we may have queued data in + * EXIT_CONN_STATE_RESOLVING while the conn is not yet marked to writing. + * */ + tor_assert((conn->type == CONN_TYPE_EXIT && + conn->state == EXIT_CONN_STATE_RESOLVING) || + connection_is_writing(conn) || + conn->write_blocked_on_bw || + (CONN_IS_EDGE(conn) && + TO_EDGE_CONN(conn)->edge_blocked_on_circ)); + } + + if (conn->hold_open_until_flushed) + tor_assert(conn->marked_for_close); + + /* XXXX check: read_blocked_on_bw, write_blocked_on_bw, s, conn_array_index, + * marked_for_close. */ + + /* buffers */ + if (conn->inbuf) + buf_assert_ok(conn->inbuf); + if (conn->outbuf) + buf_assert_ok(conn->outbuf); + + if (conn->type == CONN_TYPE_OR) { + or_connection_t *or_conn = TO_OR_CONN(conn); + if (conn->state == OR_CONN_STATE_OPEN) { + /* tor_assert(conn->bandwidth > 0); */ + /* the above isn't necessarily true: if we just did a TLS + * handshake but we didn't recognize the other peer, or it + * gave a bad cert/etc, then we won't have assigned bandwidth, + * yet it will be open. -RD + */ +// tor_assert(conn->read_bucket >= 0); + } +// tor_assert(conn->addr && conn->port); + tor_assert(conn->address); + if (conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) + tor_assert(or_conn->tls); + } + + if (CONN_IS_EDGE(conn)) { + /* XXX unchecked: package window, deliver window. */ + if (conn->type == CONN_TYPE_AP) { + entry_connection_t *entry_conn = TO_ENTRY_CONN(conn); + if (entry_conn->chosen_exit_optional || entry_conn->chosen_exit_retries) + tor_assert(entry_conn->chosen_exit_name); + + tor_assert(entry_conn->socks_request); + if (conn->state == AP_CONN_STATE_OPEN) { + tor_assert(entry_conn->socks_request->has_finished); + if (!conn->marked_for_close) { + tor_assert(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer); + assert_cpath_layer_ok(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer); + } + } + } + if (conn->type == CONN_TYPE_EXIT) { + tor_assert(conn->purpose == EXIT_PURPOSE_CONNECT || + conn->purpose == EXIT_PURPOSE_RESOLVE); + } + } else if (conn->type == CONN_TYPE_DIR) { + } else { + /* Purpose is only used for dir and exit types currently */ + tor_assert(!conn->purpose); + } + + switch (conn->type) + { + CASE_ANY_LISTENER_TYPE: + tor_assert(conn->state == LISTENER_STATE_READY); + break; + case CONN_TYPE_OR: + tor_assert(conn->state >= OR_CONN_STATE_MIN_); + tor_assert(conn->state <= OR_CONN_STATE_MAX_); + break; + case CONN_TYPE_EXT_OR: + tor_assert(conn->state >= EXT_OR_CONN_STATE_MIN_); + tor_assert(conn->state <= EXT_OR_CONN_STATE_MAX_); + break; + case CONN_TYPE_EXIT: + tor_assert(conn->state >= EXIT_CONN_STATE_MIN_); + tor_assert(conn->state <= EXIT_CONN_STATE_MAX_); + tor_assert(conn->purpose >= EXIT_PURPOSE_MIN_); + tor_assert(conn->purpose <= EXIT_PURPOSE_MAX_); + break; + case CONN_TYPE_AP: + tor_assert(conn->state >= AP_CONN_STATE_MIN_); + tor_assert(conn->state <= AP_CONN_STATE_MAX_); + tor_assert(TO_ENTRY_CONN(conn)->socks_request); + break; + case CONN_TYPE_DIR: + tor_assert(conn->state >= DIR_CONN_STATE_MIN_); + tor_assert(conn->state <= DIR_CONN_STATE_MAX_); + tor_assert(conn->purpose >= DIR_PURPOSE_MIN_); + tor_assert(conn->purpose <= DIR_PURPOSE_MAX_); + break; + case CONN_TYPE_CONTROL: + tor_assert(conn->state >= CONTROL_CONN_STATE_MIN_); + tor_assert(conn->state <= CONTROL_CONN_STATE_MAX_); + break; + default: + tor_assert(0); + } +} + +/** Fills <b>addr</b> and <b>port</b> with the details of the global + * proxy server we are using. + * <b>conn</b> contains the connection we are using the proxy for. + * + * Return 0 on success, -1 on failure. + */ +int +get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, + const connection_t *conn) +{ + const or_options_t *options = get_options(); + + /* Client Transport Plugins can use another proxy, but that should be hidden + * from the rest of tor (as the plugin is responsible for dealing with the + * proxy), check it first, then check the rest of the proxy types to allow + * the config to have unused ClientTransportPlugin entries. + */ + if (options->ClientTransportPlugin) { + const transport_t *transport = NULL; + int r; + r = get_transport_by_bridge_addrport(&conn->addr, conn->port, &transport); + if (r<0) + return -1; + if (transport) { /* transport found */ + tor_addr_copy(addr, &transport->addr); + *port = transport->port; + *proxy_type = transport->socks_version; + return 0; + } + + /* Unused ClientTransportPlugin. */ + } + + if (options->HTTPSProxy) { + tor_addr_copy(addr, &options->HTTPSProxyAddr); + *port = options->HTTPSProxyPort; + *proxy_type = PROXY_CONNECT; + return 0; + } else if (options->Socks4Proxy) { + tor_addr_copy(addr, &options->Socks4ProxyAddr); + *port = options->Socks4ProxyPort; + *proxy_type = PROXY_SOCKS4; + return 0; + } else if (options->Socks5Proxy) { + tor_addr_copy(addr, &options->Socks5ProxyAddr); + *port = options->Socks5ProxyPort; + *proxy_type = PROXY_SOCKS5; + return 0; + } + + tor_addr_make_unspec(addr); + *port = 0; + *proxy_type = PROXY_NONE; + return 0; +} + +/** Log a failed connection to a proxy server. + * <b>conn</b> is the connection we use the proxy server for. */ +void +log_failed_proxy_connection(connection_t *conn) +{ + tor_addr_t proxy_addr; + uint16_t proxy_port; + int proxy_type; + + if (get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, conn) != 0) + return; /* if we have no proxy set up, leave this function. */ + + log_warn(LD_NET, + "The connection to the %s proxy server at %s just failed. " + "Make sure that the proxy server is up and running.", + proxy_type_to_string(proxy_type), + fmt_addrport(&proxy_addr, proxy_port)); +} + +/** Return string representation of <b>proxy_type</b>. */ +static const char * +proxy_type_to_string(int proxy_type) +{ + switch (proxy_type) { + case PROXY_CONNECT: return "HTTP"; + case PROXY_SOCKS4: return "SOCKS4"; + case PROXY_SOCKS5: return "SOCKS5"; + case PROXY_PLUGGABLE: return "pluggable transports SOCKS"; + case PROXY_NONE: return "NULL"; + default: tor_assert(0); + } + return NULL; /*Unreached*/ +} + +/** Call connection_free_minimal() on every connection in our array, and + * release all storage held by connection.c. + * + * Don't do the checks in connection_free(), because they will + * fail. + */ +void +connection_free_all(void) +{ + smartlist_t *conns = get_connection_array(); + + /* We don't want to log any messages to controllers. */ + SMARTLIST_FOREACH(conns, connection_t *, conn, + if (conn->type == CONN_TYPE_CONTROL) + TO_CONTROL_CONN(conn)->event_mask = 0); + + control_update_global_event_mask(); + + /* Unlink everything from the identity map. */ + connection_or_clear_identity_map(); + connection_or_clear_ext_or_id_map(); + + /* Clear out our list of broken connections */ + clear_broken_connection_map(0); + + SMARTLIST_FOREACH(conns, connection_t *, conn, + connection_free_minimal(conn)); + + if (outgoing_addrs) { + SMARTLIST_FOREACH(outgoing_addrs, tor_addr_t *, addr, tor_free(addr)); + smartlist_free(outgoing_addrs); + outgoing_addrs = NULL; + } + + tor_free(last_interface_ipv4); + tor_free(last_interface_ipv6); + last_recorded_accounting_at = 0; + + mainloop_event_free(reenable_blocked_connections_ev); + reenable_blocked_connections_is_scheduled = 0; + memset(&reenable_blocked_connections_delay, 0, sizeof(struct timeval)); +} + +/** Log a warning, and possibly emit a control event, that <b>received</b> came + * at a skewed time. <b>trusted</b> indicates that the <b>source</b> was one + * that we had more faith in and therefore the warning level should have higher + * severity. + */ +MOCK_IMPL(void, +clock_skew_warning, (const connection_t *conn, long apparent_skew, int trusted, + log_domain_mask_t domain, const char *received, + const char *source)) +{ + char dbuf[64]; + char *ext_source = NULL, *warn = NULL; + format_time_interval(dbuf, sizeof(dbuf), apparent_skew); + if (conn) + tor_asprintf(&ext_source, "%s:%s:%d", source, conn->address, conn->port); + else + ext_source = tor_strdup(source); + log_fn(trusted ? LOG_WARN : LOG_INFO, domain, + "Received %s with skewed time (%s): " + "It seems that our clock is %s by %s, or that theirs is %s%s. " + "Tor requires an accurate clock to work: please check your time, " + "timezone, and date settings.", received, ext_source, + apparent_skew > 0 ? "ahead" : "behind", dbuf, + apparent_skew > 0 ? "behind" : "ahead", + (!conn || trusted) ? "" : ", or they are sending us the wrong time"); + if (trusted) { + control_event_general_status(LOG_WARN, "CLOCK_SKEW SKEW=%ld SOURCE=%s", + apparent_skew, ext_source); + tor_asprintf(&warn, "Clock skew %ld in %s from %s", apparent_skew, + received, source); + control_event_bootstrap_problem(warn, "CLOCK_SKEW", conn, 1); + } + tor_free(warn); + tor_free(ext_source); +} diff --git a/src/core/mainloop/connection.h b/src/core/mainloop/connection.h new file mode 100644 index 0000000000..3419ee65e8 --- /dev/null +++ b/src/core/mainloop/connection.h @@ -0,0 +1,343 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file connection.h + * \brief Header file for connection.c. + **/ + +#ifndef TOR_CONNECTION_H +#define TOR_CONNECTION_H + +listener_connection_t *TO_LISTENER_CONN(connection_t *); + +struct buf_t; + +#define CONN_TYPE_MIN_ 3 +/** Type for sockets listening for OR connections. */ +#define CONN_TYPE_OR_LISTENER 3 +/** A bidirectional TLS connection transmitting a sequence of cells. + * May be from an OR to an OR, or from an OP to an OR. */ +#define CONN_TYPE_OR 4 +/** A TCP connection from an onion router to a stream's destination. */ +#define CONN_TYPE_EXIT 5 +/** Type for sockets listening for SOCKS connections. */ +#define CONN_TYPE_AP_LISTENER 6 +/** A SOCKS proxy connection from the user application to the onion + * proxy. */ +#define CONN_TYPE_AP 7 +/** Type for sockets listening for HTTP connections to the directory server. */ +#define CONN_TYPE_DIR_LISTENER 8 +/** Type for HTTP connections to the directory server. */ +#define CONN_TYPE_DIR 9 +/* Type 10 is unused. */ +/** Type for listening for connections from user interface process. */ +#define CONN_TYPE_CONTROL_LISTENER 11 +/** Type for connections from user interface process. */ +#define CONN_TYPE_CONTROL 12 +/** Type for sockets listening for transparent connections redirected by pf or + * netfilter. */ +#define CONN_TYPE_AP_TRANS_LISTENER 13 +/** Type for sockets listening for transparent connections redirected by + * natd. */ +#define CONN_TYPE_AP_NATD_LISTENER 14 +/** Type for sockets listening for DNS requests. */ +#define CONN_TYPE_AP_DNS_LISTENER 15 + +/** Type for connections from the Extended ORPort. */ +#define CONN_TYPE_EXT_OR 16 +/** Type for sockets listening for Extended ORPort connections. */ +#define CONN_TYPE_EXT_OR_LISTENER 17 +/** Type for sockets listening for HTTP CONNECT tunnel connections. */ +#define CONN_TYPE_AP_HTTP_CONNECT_LISTENER 18 + +#define CONN_TYPE_MAX_ 19 +/* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in + * connection_t. */ + +/* Proxy client handshake states */ +/* We use a proxy but we haven't even connected to it yet. */ +#define PROXY_INFANT 1 +/* We use an HTTP proxy and we've sent the CONNECT command. */ +#define PROXY_HTTPS_WANT_CONNECT_OK 2 +/* We use a SOCKS4 proxy and we've sent the CONNECT command. */ +#define PROXY_SOCKS4_WANT_CONNECT_OK 3 +/* We use a SOCKS5 proxy and we try to negotiate without + any authentication . */ +#define PROXY_SOCKS5_WANT_AUTH_METHOD_NONE 4 +/* We use a SOCKS5 proxy and we try to negotiate with + Username/Password authentication . */ +#define PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929 5 +/* We use a SOCKS5 proxy and we just sent our credentials. */ +#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 6 +/* We use a SOCKS5 proxy and we just sent our CONNECT command. */ +#define PROXY_SOCKS5_WANT_CONNECT_OK 7 +/* We use a proxy and we CONNECTed successfully!. */ +#define PROXY_CONNECTED 8 + +/** State for any listener connection. */ +#define LISTENER_STATE_READY 0 + +const char *conn_type_to_string(int type); +const char *conn_state_to_string(int type, int state); +int conn_listener_type_supports_af_unix(int type); + +dir_connection_t *dir_connection_new(int socket_family); +or_connection_t *or_connection_new(int type, int socket_family); +edge_connection_t *edge_connection_new(int type, int socket_family); +entry_connection_t *entry_connection_new(int type, int socket_family); +control_connection_t *control_connection_new(int socket_family); +listener_connection_t *listener_connection_new(int type, int socket_family); +connection_t *connection_new(int type, int socket_family); +int connection_init_accepted_conn(connection_t *conn, + const listener_connection_t *listener); +void connection_link_connections(connection_t *conn_a, connection_t *conn_b); +MOCK_DECL(void,connection_free_,(connection_t *conn)); +#define connection_free(conn) \ + FREE_AND_NULL(connection_t, connection_free_, (conn)) +void connection_free_all(void); +void connection_about_to_close_connection(connection_t *conn); +void connection_close_immediate(connection_t *conn); +void connection_mark_for_close_(connection_t *conn, + int line, const char *file); +MOCK_DECL(void, connection_mark_for_close_internal_, + (connection_t *conn, int line, const char *file)); + +#define connection_mark_for_close(c) \ + connection_mark_for_close_((c), __LINE__, SHORT_FILE__) +#define connection_mark_for_close_internal(c) \ + connection_mark_for_close_internal_((c), __LINE__, SHORT_FILE__) + +/** + * Mark 'c' for close, but try to hold it open until all the data is written. + * Use the _internal versions of connection_mark_for_close; this should be + * called when you either are sure that if this is an or_connection_t the + * controlling channel has been notified (e.g. with + * connection_or_notify_error()), or you actually are the + * connection_or_close_for_error() or connection_or_close_normally function. + * For all other cases, use connection_mark_and_flush() instead, which + * checks for or_connection_t properly, instead. See below. + */ +#define connection_mark_and_flush_internal_(c,line,file) \ + do { \ + connection_t *tmp_conn__ = (c); \ + connection_mark_for_close_internal_(tmp_conn__, (line), (file)); \ + tmp_conn__->hold_open_until_flushed = 1; \ + } while (0) + +#define connection_mark_and_flush_internal(c) \ + connection_mark_and_flush_internal_((c), __LINE__, SHORT_FILE__) + +/** + * Mark 'c' for close, but try to hold it open until all the data is written. + */ +#define connection_mark_and_flush_(c,line,file) \ + do { \ + connection_t *tmp_conn_ = (c); \ + if (tmp_conn_->type == CONN_TYPE_OR) { \ + log_warn(LD_CHANNEL | LD_BUG, \ + "Something tried to close (and flush) an or_connection_t" \ + " without going through channels at %s:%d", \ + file, line); \ + connection_or_close_for_error(TO_OR_CONN(tmp_conn_), 1); \ + } else { \ + connection_mark_and_flush_internal_(c, line, file); \ + } \ + } while (0) + +#define connection_mark_and_flush(c) \ + connection_mark_and_flush_((c), __LINE__, SHORT_FILE__) + +void connection_expire_held_open(void); + +int connection_connect(connection_t *conn, const char *address, + const tor_addr_t *addr, + uint16_t port, int *socket_error); + +#ifdef HAVE_SYS_UN_H + +int connection_connect_unix(connection_t *conn, const char *socket_path, + int *socket_error); + +#endif /* defined(HAVE_SYS_UN_H) */ + +/** Maximum size of information that we can fit into SOCKS5 username + or password fields. */ +#define MAX_SOCKS5_AUTH_FIELD_SIZE 255 + +/** Total maximum size of information that we can fit into SOCKS5 + username and password fields. */ +#define MAX_SOCKS5_AUTH_SIZE_TOTAL 2*MAX_SOCKS5_AUTH_FIELD_SIZE + +int connection_proxy_connect(connection_t *conn, int type); +int connection_read_proxy_handshake(connection_t *conn); +void log_failed_proxy_connection(connection_t *conn); +int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, + const connection_t *conn); + +int retry_all_listeners(smartlist_t *replaced_conns, + smartlist_t *new_conns, + int close_all_noncontrol); + +void connection_mark_all_noncontrol_listeners(void); +void connection_mark_all_noncontrol_connections(void); + +ssize_t connection_bucket_write_limit(connection_t *conn, time_t now); +int global_write_bucket_low(connection_t *conn, size_t attempt, int priority); +void connection_bucket_init(void); +void connection_bucket_adjust(const or_options_t *options); +void connection_bucket_refill_all(time_t now, + uint32_t now_ts); +void connection_read_bw_exhausted(connection_t *conn, bool is_global_bw); +void connection_write_bw_exhausted(connection_t *conn, bool is_global_bw); +void connection_consider_empty_read_buckets(connection_t *conn); +void connection_consider_empty_write_buckets(connection_t *conn); + +int connection_handle_read(connection_t *conn); + +int connection_buf_get_bytes(char *string, size_t len, connection_t *conn); +int connection_buf_get_line(connection_t *conn, char *data, + size_t *data_len); +int connection_fetch_from_buf_http(connection_t *conn, + char **headers_out, size_t max_headerlen, + char **body_out, size_t *body_used, + size_t max_bodylen, int force_complete); + +int connection_wants_to_flush(connection_t *conn); +int connection_outbuf_too_full(connection_t *conn); +int connection_handle_write(connection_t *conn, int force); +int connection_flush(connection_t *conn); + +MOCK_DECL(void, connection_write_to_buf_impl_, + (const char *string, size_t len, connection_t *conn, int zlib)); +/* DOCDOC connection_write_to_buf */ +static void connection_buf_add(const char *string, size_t len, + connection_t *conn); +static inline void +connection_buf_add(const char *string, size_t len, connection_t *conn) +{ + connection_write_to_buf_impl_(string, len, conn, 0); +} +void connection_buf_add_compress(const char *string, size_t len, + dir_connection_t *conn, int done); +void connection_buf_add_buf(connection_t *conn, struct buf_t *buf); + +size_t connection_get_inbuf_len(connection_t *conn); +size_t connection_get_outbuf_len(connection_t *conn); +connection_t *connection_get_by_global_id(uint64_t id); + +connection_t *connection_get_by_type(int type); +MOCK_DECL(connection_t *,connection_get_by_type_addr_port_purpose,(int type, + const tor_addr_t *addr, + uint16_t port, int purpose)); +connection_t *connection_get_by_type_state(int type, int state); +connection_t *connection_get_by_type_state_rendquery(int type, int state, + const char *rendquery); +smartlist_t *connection_list_by_type_state(int type, int state); +smartlist_t *connection_list_by_type_purpose(int type, int purpose); +smartlist_t *connection_dir_list_by_purpose_and_resource( + int purpose, + const char *resource); +smartlist_t *connection_dir_list_by_purpose_resource_and_state( + int purpose, + const char *resource, + int state); + +#define CONN_LEN_AND_FREE_TEMPLATE(sl) \ + STMT_BEGIN \ + int len = smartlist_len(sl); \ + smartlist_free(sl); \ + return len; \ + STMT_END + +/** Return a count of directory connections that are fetching the item + * described by <b>purpose</b>/<b>resource</b>. */ +static inline int +connection_dir_count_by_purpose_and_resource( + int purpose, + const char *resource) +{ + smartlist_t *conns = connection_dir_list_by_purpose_and_resource( + purpose, + resource); + CONN_LEN_AND_FREE_TEMPLATE(conns); +} + +/** Return a count of directory connections that are fetching the item + * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. */ +static inline int +connection_dir_count_by_purpose_resource_and_state( + int purpose, + const char *resource, + int state) +{ + smartlist_t *conns = + connection_dir_list_by_purpose_resource_and_state( + purpose, + resource, + state); + CONN_LEN_AND_FREE_TEMPLATE(conns); +} + +#undef CONN_LEN_AND_FREE_TEMPLATE + +int any_other_active_or_conns(const or_connection_t *this_conn); + +/* || 0 is for -Wparentheses-equality (-Wall?) appeasement under clang */ +#define connection_speaks_cells(conn) (((conn)->type == CONN_TYPE_OR) || 0) +int connection_is_listener(connection_t *conn); +int connection_state_is_open(connection_t *conn); +int connection_state_is_connecting(connection_t *conn); + +char *alloc_http_authenticator(const char *authenticator); + +void assert_connection_ok(connection_t *conn, time_t now); +int connection_or_nonopen_was_started_here(or_connection_t *conn); +void connection_dump_buffer_mem_stats(int severity); + +MOCK_DECL(void, clock_skew_warning, + (const connection_t *conn, long apparent_skew, int trusted, + log_domain_mask_t domain, const char *received, + const char *source)); + +int connection_is_moribund(connection_t *conn); +void connection_check_oos(int n_socks, int failed); + +/** Execute the statement <b>stmt</b>, which may log events concerning the + * connection <b>conn</b>. To prevent infinite loops, disable log messages + * being sent to controllers if <b>conn</b> is a control connection. + * + * Stmt must not contain any return or goto statements. + */ +#define CONN_LOG_PROTECT(conn, stmt) \ + STMT_BEGIN \ + int _log_conn_is_control; \ + tor_assert(conn); \ + _log_conn_is_control = (conn->type == CONN_TYPE_CONTROL); \ + if (_log_conn_is_control) \ + disable_control_logging(); \ + STMT_BEGIN stmt; STMT_END; \ + if (_log_conn_is_control) \ + enable_control_logging(); \ + STMT_END + +#ifdef CONNECTION_PRIVATE +STATIC void connection_free_minimal(connection_t *conn); + +/* Used only by connection.c and test*.c */ +MOCK_DECL(STATIC int,connection_connect_sockaddr, + (connection_t *conn, + const struct sockaddr *sa, + socklen_t sa_len, + const struct sockaddr *bindaddr, + socklen_t bindaddr_len, + int *socket_error)); +MOCK_DECL(STATIC void, kill_conn_list_for_oos, (smartlist_t *conns)); +MOCK_DECL(STATIC smartlist_t *, pick_oos_victims, (int n)); + +#endif /* defined(CONNECTION_PRIVATE) */ + +#endif /* !defined(TOR_CONNECTION_H) */ diff --git a/src/core/mainloop/cpuworker.c b/src/core/mainloop/cpuworker.c new file mode 100644 index 0000000000..8b58e4c68c --- /dev/null +++ b/src/core/mainloop/cpuworker.c @@ -0,0 +1,599 @@ +/* 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 */ + +/** + * \file cpuworker.c + * \brief Uses the workqueue/threadpool code to farm CPU-intensive activities + * out to subprocesses. + * + * The multithreading backend for this module is in workqueue.c; this module + * specializes workqueue.c. + * + * Right now, we use this infrastructure + * <ul><li>for processing onionskins in onion.c + * <li>for compressing consensuses in consdiffmgr.c, + * <li>and for calculating diffs and compressing them in consdiffmgr.c. + * </ul> + **/ +#include "or/or.h" +#include "or/channel.h" +#include "or/circuitbuild.h" +#include "or/circuitlist.h" +#include "or/connection_or.h" +#include "or/config.h" +#include "or/cpuworker.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "lib/crypt_ops/crypto_util.h" +#include "or/main.h" +#include "or/onion.h" +#include "or/rephist.h" +#include "or/router.h" +#include "lib/evloop/workqueue.h" + +#include "or/or_circuit_st.h" +#include "lib/intmath/weakrng.h" + +static void queue_pending_tasks(void); + +typedef struct worker_state_s { + int generation; + server_onion_keys_t *onion_keys; +} worker_state_t; + +static void * +worker_state_new(void *arg) +{ + worker_state_t *ws; + (void)arg; + ws = tor_malloc_zero(sizeof(worker_state_t)); + ws->onion_keys = server_onion_keys_new(); + return ws; +} + +#define worker_state_free(ws) \ + FREE_AND_NULL(worker_state_t, worker_state_free_, (ws)) + +static void +worker_state_free_(worker_state_t *ws) +{ + if (!ws) + return; + server_onion_keys_free(ws->onion_keys); + tor_free(ws); +} + +static void +worker_state_free_void(void *arg) +{ + worker_state_free_(arg); +} + +static replyqueue_t *replyqueue = NULL; +static threadpool_t *threadpool = NULL; + +static tor_weak_rng_t request_sample_rng = TOR_WEAK_RNG_INIT; + +static int total_pending_tasks = 0; +static int max_pending_tasks = 128; + +/** Initialize the cpuworker subsystem. It is OK to call this more than once + * during Tor's lifetime. + */ +void +cpu_init(void) +{ + if (!replyqueue) { + replyqueue = replyqueue_new(0); + } + if (!threadpool) { + /* + In our threadpool implementation, half the threads are permissive and + half are strict (when it comes to running lower-priority tasks). So we + always make sure we have at least two threads, so that there will be at + least one thread of each kind. + */ + const int n_threads = get_num_cpus(get_options()) + 1; + threadpool = threadpool_new(n_threads, + replyqueue, + worker_state_new, + worker_state_free_void, + NULL); + + int r = threadpool_register_reply_event(threadpool, NULL); + + tor_assert(r == 0); + } + + /* Total voodoo. Can we make this more sensible? */ + max_pending_tasks = get_num_cpus(get_options()) * 64; + crypto_seed_weak_rng(&request_sample_rng); +} + +/** Magic numbers to make sure our cpuworker_requests don't grow any + * mis-framing bugs. */ +#define CPUWORKER_REQUEST_MAGIC 0xda4afeed +#define CPUWORKER_REPLY_MAGIC 0x5eedf00d + +/** A request sent to a cpuworker. */ +typedef struct cpuworker_request_t { + /** Magic number; must be CPUWORKER_REQUEST_MAGIC. */ + uint32_t magic; + + /** Flag: Are we timing this request? */ + unsigned timed : 1; + /** If we're timing this request, when was it sent to the cpuworker? */ + struct timeval started_at; + + /** A create cell for the cpuworker to process. */ + create_cell_t create_cell; + + /* Turn the above into a tagged union if needed. */ +} cpuworker_request_t; + +/** A reply sent by a cpuworker. */ +typedef struct cpuworker_reply_t { + /** Magic number; must be CPUWORKER_REPLY_MAGIC. */ + uint32_t magic; + + /** True iff we got a successful request. */ + uint8_t success; + + /** Are we timing this request? */ + unsigned int timed : 1; + /** What handshake type was the request? (Used for timing) */ + uint16_t handshake_type; + /** When did we send the request to the cpuworker? */ + struct timeval started_at; + /** Once the cpuworker received the request, how many microseconds did it + * take? (This shouldn't overflow; 4 billion micoseconds is over an hour, + * and we'll never have an onion handshake that takes so long.) */ + uint32_t n_usec; + + /** Output of processing a create cell + * + * @{ + */ + /** The created cell to send back. */ + created_cell_t created_cell; + /** The keys to use on this circuit. */ + uint8_t keys[CPATH_KEY_MATERIAL_LEN]; + /** Input to use for authenticating introduce1 cells. */ + uint8_t rend_auth_material[DIGEST_LEN]; +} cpuworker_reply_t; + +typedef struct cpuworker_job_u { + or_circuit_t *circ; + union { + cpuworker_request_t request; + cpuworker_reply_t reply; + } u; +} cpuworker_job_t; + +static workqueue_reply_t +update_state_threadfn(void *state_, void *work_) +{ + worker_state_t *state = state_; + worker_state_t *update = work_; + server_onion_keys_free(state->onion_keys); + state->onion_keys = update->onion_keys; + update->onion_keys = NULL; + worker_state_free(update); + ++state->generation; + return WQ_RPL_REPLY; +} + +/** Called when the onion key has changed so update all CPU worker(s) with + * new function pointers with which a new state will be generated. + */ +void +cpuworkers_rotate_keyinfo(void) +{ + if (!threadpool) { + /* If we're a client, then we won't have cpuworkers, and we won't need + * to tell them to rotate their state. + */ + return; + } + if (threadpool_queue_update(threadpool, + worker_state_new, + update_state_threadfn, + worker_state_free_void, + NULL)) { + log_warn(LD_OR, "Failed to queue key update for worker threads."); + } +} + +/** Indexed by handshake type: how many onionskins have we processed and + * counted of that type? */ +static uint64_t onionskins_n_processed[MAX_ONION_HANDSHAKE_TYPE+1]; +/** Indexed by handshake type, corresponding to the onionskins counted in + * onionskins_n_processed: how many microseconds have we spent in cpuworkers + * processing that kind of onionskin? */ +static uint64_t onionskins_usec_internal[MAX_ONION_HANDSHAKE_TYPE+1]; +/** Indexed by handshake type, corresponding to onionskins counted in + * onionskins_n_processed: how many microseconds have we spent waiting for + * cpuworkers to give us answers for that kind of onionskin? + */ +static uint64_t onionskins_usec_roundtrip[MAX_ONION_HANDSHAKE_TYPE+1]; + +/** If any onionskin takes longer than this, we clip them to this + * time. (microseconds) */ +#define MAX_BELIEVABLE_ONIONSKIN_DELAY (2*1000*1000) + +/** Return true iff we'd like to measure a handshake of type + * <b>onionskin_type</b>. Call only from the main thread. */ +static int +should_time_request(uint16_t onionskin_type) +{ + /* If we've never heard of this type, we shouldn't even be here. */ + if (onionskin_type > MAX_ONION_HANDSHAKE_TYPE) + return 0; + /* Measure the first N handshakes of each type, to ensure we have a + * sample */ + if (onionskins_n_processed[onionskin_type] < 4096) + return 1; + /** Otherwise, measure with P=1/128. We avoid doing this for every + * handshake, since the measurement itself can take a little time. */ + return tor_weak_random_one_in_n(&request_sample_rng, 128); +} + +/** Return an estimate of how many microseconds we will need for a single + * cpuworker to process <b>n_requests</b> onionskins of type + * <b>onionskin_type</b>. */ +uint64_t +estimated_usec_for_onionskins(uint32_t n_requests, uint16_t onionskin_type) +{ + if (onionskin_type > MAX_ONION_HANDSHAKE_TYPE) /* should be impossible */ + return 1000 * (uint64_t)n_requests; + if (PREDICT_UNLIKELY(onionskins_n_processed[onionskin_type] < 100)) { + /* Until we have 100 data points, just asssume everything takes 1 msec. */ + return 1000 * (uint64_t)n_requests; + } else { + /* This can't overflow: we'll never have more than 500000 onionskins + * measured in onionskin_usec_internal, and they won't take anything near + * 1 sec each, and we won't have anything like 1 million queued + * onionskins. But that's 5e5 * 1e6 * 1e6, which is still less than + * UINT64_MAX. */ + return (onionskins_usec_internal[onionskin_type] * n_requests) / + onionskins_n_processed[onionskin_type]; + } +} + +/** Compute the absolute and relative overhead of using the cpuworker + * framework for onionskins of type <b>onionskin_type</b>.*/ +static int +get_overhead_for_onionskins(uint32_t *usec_out, double *frac_out, + uint16_t onionskin_type) +{ + uint64_t overhead; + + *usec_out = 0; + *frac_out = 0.0; + + if (onionskin_type > MAX_ONION_HANDSHAKE_TYPE) /* should be impossible */ + return -1; + if (onionskins_n_processed[onionskin_type] == 0 || + onionskins_usec_internal[onionskin_type] == 0 || + onionskins_usec_roundtrip[onionskin_type] == 0) + return -1; + + overhead = onionskins_usec_roundtrip[onionskin_type] - + onionskins_usec_internal[onionskin_type]; + + *usec_out = (uint32_t)(overhead / onionskins_n_processed[onionskin_type]); + *frac_out = ((double)overhead) / onionskins_usec_internal[onionskin_type]; + + return 0; +} + +/** If we've measured overhead for onionskins of type <b>onionskin_type</b>, + * log it. */ +void +cpuworker_log_onionskin_overhead(int severity, int onionskin_type, + const char *onionskin_type_name) +{ + uint32_t overhead; + double relative_overhead; + int r; + + r = get_overhead_for_onionskins(&overhead, &relative_overhead, + onionskin_type); + if (!overhead || r<0) + return; + + log_fn(severity, LD_OR, + "%s onionskins have averaged %u usec overhead (%.2f%%) in " + "cpuworker code ", + onionskin_type_name, (unsigned)overhead, relative_overhead*100); +} + +/** Handle a reply from the worker threads. */ +static void +cpuworker_onion_handshake_replyfn(void *work_) +{ + cpuworker_job_t *job = work_; + cpuworker_reply_t rpl; + or_circuit_t *circ = NULL; + + tor_assert(total_pending_tasks > 0); + --total_pending_tasks; + + /* Could avoid this, but doesn't matter. */ + memcpy(&rpl, &job->u.reply, sizeof(rpl)); + + tor_assert(rpl.magic == CPUWORKER_REPLY_MAGIC); + + if (rpl.timed && rpl.success && + rpl.handshake_type <= MAX_ONION_HANDSHAKE_TYPE) { + /* Time how long this request took. The handshake_type check should be + needless, but let's leave it in to be safe. */ + struct timeval tv_end, tv_diff; + int64_t usec_roundtrip; + tor_gettimeofday(&tv_end); + timersub(&tv_end, &rpl.started_at, &tv_diff); + usec_roundtrip = ((int64_t)tv_diff.tv_sec)*1000000 + tv_diff.tv_usec; + if (usec_roundtrip >= 0 && + usec_roundtrip < MAX_BELIEVABLE_ONIONSKIN_DELAY) { + ++onionskins_n_processed[rpl.handshake_type]; + onionskins_usec_internal[rpl.handshake_type] += rpl.n_usec; + onionskins_usec_roundtrip[rpl.handshake_type] += usec_roundtrip; + if (onionskins_n_processed[rpl.handshake_type] >= 500000) { + /* Scale down every 500000 handshakes. On a busy server, that's + * less impressive than it sounds. */ + onionskins_n_processed[rpl.handshake_type] /= 2; + onionskins_usec_internal[rpl.handshake_type] /= 2; + onionskins_usec_roundtrip[rpl.handshake_type] /= 2; + } + } + } + + circ = job->circ; + + log_debug(LD_OR, + "Unpacking cpuworker reply %p, circ=%p, success=%d", + job, circ, rpl.success); + + if (circ->base_.magic == DEAD_CIRCUIT_MAGIC) { + /* The circuit was supposed to get freed while the reply was + * pending. Instead, it got left for us to free so that we wouldn't freak + * out when the job->circ field wound up pointing to nothing. */ + log_debug(LD_OR, "Circuit died while reply was pending. Freeing memory."); + circ->base_.magic = 0; + tor_free(circ); + goto done_processing; + } + + circ->workqueue_entry = NULL; + + if (TO_CIRCUIT(circ)->marked_for_close) { + /* We already marked this circuit; we can't call it open. */ + log_debug(LD_OR,"circuit is already marked."); + goto done_processing; + } + + if (rpl.success == 0) { + log_debug(LD_OR, + "decoding onionskin failed. " + "(Old key or bad software.) Closing."); + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); + goto done_processing; + } + + if (onionskin_answer(circ, + &rpl.created_cell, + (const char*)rpl.keys, sizeof(rpl.keys), + rpl.rend_auth_material) < 0) { + log_warn(LD_OR,"onionskin_answer failed. Closing."); + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); + goto done_processing; + } + log_debug(LD_OR,"onionskin_answer succeeded. Yay."); + + done_processing: + memwipe(&rpl, 0, sizeof(rpl)); + memwipe(job, 0, sizeof(*job)); + tor_free(job); + queue_pending_tasks(); +} + +/** Implementation function for onion handshake requests. */ +static workqueue_reply_t +cpuworker_onion_handshake_threadfn(void *state_, void *work_) +{ + worker_state_t *state = state_; + cpuworker_job_t *job = work_; + + /* variables for onion processing */ + server_onion_keys_t *onion_keys = state->onion_keys; + cpuworker_request_t req; + cpuworker_reply_t rpl; + + memcpy(&req, &job->u.request, sizeof(req)); + + tor_assert(req.magic == CPUWORKER_REQUEST_MAGIC); + memset(&rpl, 0, sizeof(rpl)); + + const create_cell_t *cc = &req.create_cell; + created_cell_t *cell_out = &rpl.created_cell; + struct timeval tv_start = {0,0}, tv_end; + int n; + rpl.timed = req.timed; + rpl.started_at = req.started_at; + rpl.handshake_type = cc->handshake_type; + if (req.timed) + tor_gettimeofday(&tv_start); + n = onion_skin_server_handshake(cc->handshake_type, + cc->onionskin, cc->handshake_len, + onion_keys, + cell_out->reply, + rpl.keys, CPATH_KEY_MATERIAL_LEN, + rpl.rend_auth_material); + if (n < 0) { + /* failure */ + log_debug(LD_OR,"onion_skin_server_handshake failed."); + memset(&rpl, 0, sizeof(rpl)); + rpl.success = 0; + } else { + /* success */ + log_debug(LD_OR,"onion_skin_server_handshake succeeded."); + cell_out->handshake_len = n; + switch (cc->cell_type) { + case CELL_CREATE: + cell_out->cell_type = CELL_CREATED; break; + case CELL_CREATE2: + cell_out->cell_type = CELL_CREATED2; break; + case CELL_CREATE_FAST: + cell_out->cell_type = CELL_CREATED_FAST; break; + default: + tor_assert(0); + return WQ_RPL_SHUTDOWN; + } + rpl.success = 1; + } + rpl.magic = CPUWORKER_REPLY_MAGIC; + if (req.timed) { + struct timeval tv_diff; + int64_t usec; + tor_gettimeofday(&tv_end); + timersub(&tv_end, &tv_start, &tv_diff); + usec = ((int64_t)tv_diff.tv_sec)*1000000 + tv_diff.tv_usec; + if (usec < 0 || usec > MAX_BELIEVABLE_ONIONSKIN_DELAY) + rpl.n_usec = MAX_BELIEVABLE_ONIONSKIN_DELAY; + else + rpl.n_usec = (uint32_t) usec; + } + + memcpy(&job->u.reply, &rpl, sizeof(rpl)); + + memwipe(&req, 0, sizeof(req)); + memwipe(&rpl, 0, sizeof(req)); + return WQ_RPL_REPLY; +} + +/** Take pending tasks from the queue and assign them to cpuworkers. */ +static void +queue_pending_tasks(void) +{ + or_circuit_t *circ; + create_cell_t *onionskin = NULL; + + while (total_pending_tasks < max_pending_tasks) { + circ = onion_next_task(&onionskin); + + if (!circ) + return; + + if (assign_onionskin_to_cpuworker(circ, onionskin) < 0) + log_info(LD_OR,"assign_to_cpuworker failed. Ignoring."); + } +} + +/** DOCDOC */ +MOCK_IMPL(workqueue_entry_t *, +cpuworker_queue_work,(workqueue_priority_t priority, + workqueue_reply_t (*fn)(void *, void *), + void (*reply_fn)(void *), + void *arg)) +{ + tor_assert(threadpool); + + return threadpool_queue_work_priority(threadpool, + priority, + fn, + reply_fn, + arg); +} + +/** Try to tell a cpuworker to perform the public key operations necessary to + * respond to <b>onionskin</b> for the circuit <b>circ</b>. + * + * Return 0 if we successfully assign the task, or -1 on failure. + */ +int +assign_onionskin_to_cpuworker(or_circuit_t *circ, + create_cell_t *onionskin) +{ + workqueue_entry_t *queue_entry; + cpuworker_job_t *job; + cpuworker_request_t req; + int should_time; + + tor_assert(threadpool); + + if (!circ->p_chan) { + log_info(LD_OR,"circ->p_chan gone. Failing circ."); + tor_free(onionskin); + return -1; + } + + if (total_pending_tasks >= max_pending_tasks) { + log_debug(LD_OR,"No idle cpuworkers. Queuing."); + if (onion_pending_add(circ, onionskin) < 0) { + tor_free(onionskin); + return -1; + } + return 0; + } + + if (!channel_is_client(circ->p_chan)) + rep_hist_note_circuit_handshake_assigned(onionskin->handshake_type); + + should_time = should_time_request(onionskin->handshake_type); + memset(&req, 0, sizeof(req)); + req.magic = CPUWORKER_REQUEST_MAGIC; + req.timed = should_time; + + memcpy(&req.create_cell, onionskin, sizeof(create_cell_t)); + + tor_free(onionskin); + + if (should_time) + tor_gettimeofday(&req.started_at); + + job = tor_malloc_zero(sizeof(cpuworker_job_t)); + job->circ = circ; + memcpy(&job->u.request, &req, sizeof(req)); + memwipe(&req, 0, sizeof(req)); + + ++total_pending_tasks; + queue_entry = threadpool_queue_work_priority(threadpool, + WQ_PRI_HIGH, + cpuworker_onion_handshake_threadfn, + cpuworker_onion_handshake_replyfn, + job); + if (!queue_entry) { + log_warn(LD_BUG, "Couldn't queue work on threadpool"); + tor_free(job); + return -1; + } + + log_debug(LD_OR, "Queued task %p (qe=%p, circ=%p)", + job, queue_entry, job->circ); + + circ->workqueue_entry = queue_entry; + + return 0; +} + +/** If <b>circ</b> has a pending handshake that hasn't been processed yet, + * remove it from the worker queue. */ +void +cpuworker_cancel_circ_handshake(or_circuit_t *circ) +{ + cpuworker_job_t *job; + if (circ->workqueue_entry == NULL) + return; + + job = workqueue_entry_cancel(circ->workqueue_entry); + if (job) { + /* It successfully cancelled. */ + memwipe(job, 0xe0, sizeof(*job)); + tor_free(job); + tor_assert(total_pending_tasks > 0); + --total_pending_tasks; + /* if (!job), this is done in cpuworker_onion_handshake_replyfn. */ + circ->workqueue_entry = NULL; + } +} diff --git a/src/core/mainloop/cpuworker.h b/src/core/mainloop/cpuworker.h new file mode 100644 index 0000000000..50812b2dab --- /dev/null +++ b/src/core/mainloop/cpuworker.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file cpuworker.h + * \brief Header file for cpuworker.c. + **/ + +#ifndef TOR_CPUWORKER_H +#define TOR_CPUWORKER_H + +void cpu_init(void); +void cpuworkers_rotate_keyinfo(void); +struct workqueue_entry_s; +enum workqueue_reply_t; +enum workqueue_priority_t; +MOCK_DECL(struct workqueue_entry_s *, cpuworker_queue_work, ( + enum workqueue_priority_t priority, + enum workqueue_reply_t (*fn)(void *, void *), + void (*reply_fn)(void *), + void *arg)); + +struct create_cell_t; +int assign_onionskin_to_cpuworker(or_circuit_t *circ, + struct create_cell_t *onionskin); + +uint64_t estimated_usec_for_onionskins(uint32_t n_requests, + uint16_t onionskin_type); +void cpuworker_log_onionskin_overhead(int severity, int onionskin_type, + const char *onionskin_type_name); +void cpuworker_cancel_circ_handshake(or_circuit_t *circ); + +#endif /* !defined(TOR_CPUWORKER_H) */ + diff --git a/src/core/mainloop/main.c b/src/core/mainloop/main.c new file mode 100644 index 0000000000..2a57aa587a --- /dev/null +++ b/src/core/mainloop/main.c @@ -0,0 +1,4311 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file main.c + * \brief Toplevel module. Handles signals, multiplexes between + * connections, implements main loop, and drives scheduled events. + * + * For the main loop itself; see run_main_loop_once(). It invokes the rest of + * Tor mostly through Libevent callbacks. Libevent callbacks can happen when + * a timer elapses, a signal is received, a socket is ready to read or write, + * or an event is manually activated. + * + * Most events in Tor are driven from these callbacks: + * <ul> + * <li>conn_read_callback() and conn_write_callback() here, which are + * invoked when a socket is ready to read or write respectively. + * <li>signal_callback(), which handles incoming signals. + * </ul> + * Other events are used for specific purposes, or for building more complex + * control structures. If you search for usage of tor_libevent_new(), you + * will find all the events that we construct in Tor. + * + * Tor has numerous housekeeping operations that need to happen + * regularly. They are handled in different ways: + * <ul> + * <li>The most frequent operations are handled after every read or write + * event, at the end of connection_handle_read() and + * connection_handle_write(). + * + * <li>The next most frequent operations happen after each invocation of the + * main loop, in run_main_loop_once(). + * + * <li>Once per second, we run all of the operations listed in + * second_elapsed_callback(), and in its child, run_scheduled_events(). + * + * <li>Once-a-second operations are handled in second_elapsed_callback(). + * + * <li>More infrequent operations take place based on the periodic event + * driver in periodic.c . These are stored in the periodic_events[] + * table. + * </ul> + * + **/ + +#define MAIN_PRIVATE +#include "or/or.h" +#include "or/addressmap.h" +#include "lib/err/backtrace.h" +#include "or/bridges.h" +#include "lib/container/buffers.h" +#include "lib/tls/buffers_tls.h" +#include "or/channel.h" +#include "or/channeltls.h" +#include "or/channelpadding.h" +#include "or/circuitbuild.h" +#include "or/circuitlist.h" +#include "or/circuituse.h" +#include "or/circuitmux_ewma.h" +#include "or/command.h" +#include "lib/compress/compress.h" +#include "or/config.h" +#include "or/confparse.h" +#include "or/connection.h" +#include "or/connection_edge.h" +#include "or/connection_or.h" +#include "or/consdiffmgr.h" +#include "or/control.h" +#include "or/cpuworker.h" +#include "lib/crypt_ops/crypto_s2k.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "or/directory.h" +#include "or/dirserv.h" +#include "or/dns.h" +#include "or/dnsserv.h" +#include "or/dos.h" +#include "or/entrynodes.h" +#include "or/geoip.h" +#include "or/hibernate.h" +#include "or/hs_cache.h" +#include "or/hs_circuitmap.h" +#include "or/hs_client.h" +#include "or/keypin.h" +#include "or/main.h" +#include "or/microdesc.h" +#include "or/networkstatus.h" +#include "or/nodelist.h" +#include "app/ntmain.h" +#include "or/onion.h" +#include "or/periodic.h" +#include "or/policies.h" +#include "or/protover.h" +#include "or/transports.h" +#include "or/relay.h" +#include "or/rendclient.h" +#include "or/rendcommon.h" +#include "or/rendservice.h" +#include "or/rephist.h" +#include "or/router.h" +#include "or/routerkeys.h" +#include "or/routerlist.h" +#include "or/routerparse.h" +#include "or/scheduler.h" +#include "or/statefile.h" +#include "or/status.h" +#include "or/tor_api.h" +#include "or/tor_api_internal.h" +#include "lib/process/waitpid.h" +#include "or/ext_orport.h" +#include "lib/memarea/memarea.h" +#include "lib/meminfo/meminfo.h" +#include "lib/osinfo/uname.h" +#include "lib/sandbox/sandbox.h" +#include "lib/fs/lockfile.h" +#include "lib/net/buffers_net.h" +#include "lib/tls/tortls.h" +#include "lib/evloop/compat_libevent.h" +#include "lib/encoding/confline.h" +#include "lib/evloop/timers.h" + +#include <event2/event.h> + +#include "or/dirauth/dirvote.h" +#include "or/dirauth/mode.h" +#include "or/dirauth/shared_random.h" + +#include "or/cell_st.h" +#include "or/entry_connection_st.h" +#include "or/networkstatus_st.h" +#include "or/or_connection_st.h" +#include "or/or_state_st.h" +#include "or/port_cfg_st.h" +#include "or/routerinfo_st.h" +#include "or/socks_request_st.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_SYSTEMD +# if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) +/* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse + * Coverity. Here's a kludge to unconfuse it. + */ +# define __INCLUDE_LEVEL__ 2 +#endif /* defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) */ +#include <systemd/sd-daemon.h> +#endif /* defined(HAVE_SYSTEMD) */ + +void evdns_shutdown(int); + +#ifdef HAVE_RUST +// helper function defined in Rust to output a log message indicating if tor is +// running with Rust enabled. See src/rust/tor_util +void rust_log_welcome_string(void); +#endif + +/********* PROTOTYPES **********/ + +static void dumpmemusage(int severity); +static void dumpstats(int severity); /* log stats */ +static void conn_read_callback(evutil_socket_t fd, short event, void *_conn); +static void conn_write_callback(evutil_socket_t fd, short event, void *_conn); +static void second_elapsed_callback(periodic_timer_t *timer, void *args); +static int conn_close_if_marked(int i); +static void connection_start_reading_from_linked_conn(connection_t *conn); +static int connection_should_read_from_linked_conn(connection_t *conn); +static int run_main_loop_until_done(void); +static void process_signal(int sig); +static void shutdown_did_not_work_callback(evutil_socket_t fd, short event, + void *arg) ATTR_NORETURN; + +/********* START VARIABLES **********/ + +/* Token bucket for all traffic. */ +token_bucket_rw_t global_bucket; + +/* Token bucket for relayed traffic. */ +token_bucket_rw_t global_relayed_bucket; + +/* XXX we might want to keep stats about global_relayed_*_bucket too. Or not.*/ +/** How many bytes have we read since we started the process? */ +static uint64_t stats_n_bytes_read = 0; +/** How many bytes have we written since we started the process? */ +static uint64_t stats_n_bytes_written = 0; +/** What time did this process start up? */ +time_t time_of_process_start = 0; +/** How many seconds have we been running? */ +static long stats_n_seconds_working = 0; +/** How many times have we returned from the main loop successfully? */ +static uint64_t stats_n_main_loop_successes = 0; +/** How many times have we received an error from the main loop? */ +static uint64_t stats_n_main_loop_errors = 0; +/** How many times have we returned from the main loop with no events. */ +static uint64_t stats_n_main_loop_idle = 0; + +/** How often will we honor SIGNEWNYM requests? */ +#define MAX_SIGNEWNYM_RATE 10 +/** When did we last process a SIGNEWNYM request? */ +static time_t time_of_last_signewnym = 0; +/** Is there a signewnym request we're currently waiting to handle? */ +static int signewnym_is_pending = 0; +/** Mainloop event for the deferred signewnym call. */ +static mainloop_event_t *handle_deferred_signewnym_ev = NULL; +/** How many times have we called newnym? */ +static unsigned newnym_epoch = 0; + +/** Smartlist of all open connections. */ +STATIC smartlist_t *connection_array = NULL; +/** List of connections that have been marked for close and need to be freed + * and removed from connection_array. */ +static smartlist_t *closeable_connection_lst = NULL; +/** List of linked connections that are currently reading data into their + * inbuf from their partner's outbuf. */ +static smartlist_t *active_linked_connection_lst = NULL; +/** Flag: Set to true iff we entered the current libevent main loop via + * <b>loop_once</b>. If so, there's no need to trigger a loopexit in order + * to handle linked connections. */ +static int called_loop_once = 0; +/** Flag: if true, it's time to shut down, so the main loop should exit as + * soon as possible. + */ +static int main_loop_should_exit = 0; +/** The return value that the main loop should yield when it exits, if + * main_loop_should_exit is true. + */ +static int main_loop_exit_value = 0; + +/** We set this to 1 when we've opened a circuit, so we can print a log + * entry to inform the user that Tor is working. We set it to 0 when + * we think the fact that we once opened a circuit doesn't mean we can do so + * any longer (a big time jump happened, when we notice our directory is + * heinously out-of-date, etc. + */ +static int can_complete_circuits = 0; + +/** How often do we check for router descriptors that we should download + * when we have too little directory info? */ +#define GREEDY_DESCRIPTOR_RETRY_INTERVAL (10) +/** How often do we check for router descriptors that we should download + * when we have enough directory info? */ +#define LAZY_DESCRIPTOR_RETRY_INTERVAL (60) + +/** Decides our behavior when no logs are configured/before any + * logs have been configured. For 0, we log notice to stdout as normal. + * For 1, we log warnings only. For 2, we log nothing. + */ +int quiet_level = 0; + +/********* END VARIABLES ************/ + +/**************************************************************************** + * + * This section contains accessors and other methods on the connection_array + * variables (which are global within this file and unavailable outside it). + * + ****************************************************************************/ + +/** Return 1 if we have successfully built a circuit, and nothing has changed + * to make us think that maybe we can't. + */ +int +have_completed_a_circuit(void) +{ + return can_complete_circuits; +} + +/** Note that we have successfully built a circuit, so that reachability + * testing and introduction points and so on may be attempted. */ +void +note_that_we_completed_a_circuit(void) +{ + can_complete_circuits = 1; +} + +/** Note that something has happened (like a clock jump, or DisableNetwork) to + * make us think that maybe we can't complete circuits. */ +void +note_that_we_maybe_cant_complete_circuits(void) +{ + can_complete_circuits = 0; +} + +/** Add <b>conn</b> to the array of connections that we can poll on. The + * connection's socket must be set; the connection starts out + * non-reading and non-writing. + */ +int +connection_add_impl(connection_t *conn, int is_connecting) +{ + tor_assert(conn); + tor_assert(SOCKET_OK(conn->s) || + conn->linked || + (conn->type == CONN_TYPE_AP && + TO_EDGE_CONN(conn)->is_dns_request)); + + tor_assert(conn->conn_array_index == -1); /* can only connection_add once */ + conn->conn_array_index = smartlist_len(connection_array); + smartlist_add(connection_array, conn); + + (void) is_connecting; + + if (SOCKET_OK(conn->s) || conn->linked) { + conn->read_event = tor_event_new(tor_libevent_get_base(), + conn->s, EV_READ|EV_PERSIST, conn_read_callback, conn); + conn->write_event = tor_event_new(tor_libevent_get_base(), + conn->s, EV_WRITE|EV_PERSIST, conn_write_callback, conn); + /* XXXX CHECK FOR NULL RETURN! */ + } + + log_debug(LD_NET,"new conn type %s, socket %d, address %s, n_conns %d.", + conn_type_to_string(conn->type), (int)conn->s, conn->address, + smartlist_len(connection_array)); + + return 0; +} + +/** Tell libevent that we don't care about <b>conn</b> any more. */ +void +connection_unregister_events(connection_t *conn) +{ + if (conn->read_event) { + if (event_del(conn->read_event)) + log_warn(LD_BUG, "Error removing read event for %d", (int)conn->s); + tor_free(conn->read_event); + } + if (conn->write_event) { + if (event_del(conn->write_event)) + log_warn(LD_BUG, "Error removing write event for %d", (int)conn->s); + tor_free(conn->write_event); + } + if (conn->type == CONN_TYPE_AP_DNS_LISTENER) { + dnsserv_close_listener(conn); + } +} + +/** Remove the connection from the global list, and remove the + * corresponding poll entry. Calling this function will shift the last + * connection (if any) into the position occupied by conn. + */ +int +connection_remove(connection_t *conn) +{ + int current_index; + connection_t *tmp; + + tor_assert(conn); + + log_debug(LD_NET,"removing socket %d (type %s), n_conns now %d", + (int)conn->s, conn_type_to_string(conn->type), + smartlist_len(connection_array)); + + if (conn->type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { + log_info(LD_NET, "Closing SOCKS Unix socket connection"); + } + + control_event_conn_bandwidth(conn); + + tor_assert(conn->conn_array_index >= 0); + current_index = conn->conn_array_index; + connection_unregister_events(conn); /* This is redundant, but cheap. */ + if (current_index == smartlist_len(connection_array)-1) { /* at the end */ + smartlist_del(connection_array, current_index); + return 0; + } + + /* replace this one with the one at the end */ + smartlist_del(connection_array, current_index); + tmp = smartlist_get(connection_array, current_index); + tmp->conn_array_index = current_index; + + return 0; +} + +/** If <b>conn</b> is an edge conn, remove it from the list + * of conn's on this circuit. If it's not on an edge, + * flush and send destroys for all circuits on this conn. + * + * Remove it from connection_array (if applicable) and + * from closeable_connection_list. + * + * Then free it. + */ +static void +connection_unlink(connection_t *conn) +{ + connection_about_to_close_connection(conn); + if (conn->conn_array_index >= 0) { + connection_remove(conn); + } + if (conn->linked_conn) { + conn->linked_conn->linked_conn = NULL; + if (! conn->linked_conn->marked_for_close && + conn->linked_conn->reading_from_linked_conn) + connection_start_reading(conn->linked_conn); + conn->linked_conn = NULL; + } + smartlist_remove(closeable_connection_lst, conn); + smartlist_remove(active_linked_connection_lst, conn); + if (conn->type == CONN_TYPE_EXIT) { + assert_connection_edge_not_dns_pending(TO_EDGE_CONN(conn)); + } + if (conn->type == CONN_TYPE_OR) { + if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) + connection_or_clear_identity(TO_OR_CONN(conn)); + /* connection_unlink() can only get called if the connection + * was already on the closeable list, and it got there by + * connection_mark_for_close(), which was called from + * connection_or_close_normally() or + * connection_or_close_for_error(), so the channel should + * already be in CHANNEL_STATE_CLOSING, and then the + * connection_about_to_close_connection() goes to + * connection_or_about_to_close(), which calls channel_closed() + * to notify the channel_t layer, and closed the channel, so + * nothing more to do here to deal with the channel associated + * with an orconn. + */ + } + connection_free(conn); +} + +/** + * Callback: used to activate read events for all linked connections, so + * libevent knows to call their read callbacks. This callback run as a + * postloop event, so that the events _it_ activates don't happen until + * Libevent has a chance to check for other events. + */ +static void +schedule_active_linked_connections_cb(mainloop_event_t *event, void *arg) +{ + (void)event; + (void)arg; + + /* All active linked conns should get their read events activated, + * so that libevent knows to run their callbacks. */ + SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn, + event_active(conn->read_event, EV_READ, 1)); +} + +/** Event that invokes schedule_active_linked_connections_cb. */ +static mainloop_event_t *schedule_active_linked_connections_event = NULL; + +/** Initialize the global connection list, closeable connection list, + * and active connection list. */ +STATIC void +init_connection_lists(void) +{ + if (!connection_array) + connection_array = smartlist_new(); + if (!closeable_connection_lst) + closeable_connection_lst = smartlist_new(); + if (!active_linked_connection_lst) + active_linked_connection_lst = smartlist_new(); +} + +/** Schedule <b>conn</b> to be closed. **/ +void +add_connection_to_closeable_list(connection_t *conn) +{ + tor_assert(!smartlist_contains(closeable_connection_lst, conn)); + tor_assert(conn->marked_for_close); + assert_connection_ok(conn, time(NULL)); + smartlist_add(closeable_connection_lst, conn); + mainloop_schedule_postloop_cleanup(); +} + +/** Return 1 if conn is on the closeable list, else return 0. */ +int +connection_is_on_closeable_list(connection_t *conn) +{ + return smartlist_contains(closeable_connection_lst, conn); +} + +/** Return true iff conn is in the current poll array. */ +int +connection_in_array(connection_t *conn) +{ + return smartlist_contains(connection_array, conn); +} + +/** Set <b>*array</b> to an array of all connections. <b>*array</b> must not + * be modified. + */ +MOCK_IMPL(smartlist_t *, +get_connection_array, (void)) +{ + if (!connection_array) + connection_array = smartlist_new(); + return connection_array; +} + +/** + * Return the amount of network traffic read, in bytes, over the life of this + * process. + */ +MOCK_IMPL(uint64_t, +get_bytes_read,(void)) +{ + return stats_n_bytes_read; +} + +/** + * Return the amount of network traffic read, in bytes, over the life of this + * process. + */ +MOCK_IMPL(uint64_t, +get_bytes_written,(void)) +{ + return stats_n_bytes_written; +} + +/** + * Increment the amount of network traffic read and written, over the life of + * this process. + */ +void +stats_increment_bytes_read_and_written(uint64_t r, uint64_t w) +{ + stats_n_bytes_read += r; + stats_n_bytes_written += w; +} + +/** Set the event mask on <b>conn</b> to <b>events</b>. (The event + * mask is a bitmask whose bits are READ_EVENT and WRITE_EVENT) + */ +void +connection_watch_events(connection_t *conn, watchable_events_t events) +{ + if (events & READ_EVENT) + connection_start_reading(conn); + else + connection_stop_reading(conn); + + if (events & WRITE_EVENT) + connection_start_writing(conn); + else + connection_stop_writing(conn); +} + +/** Return true iff <b>conn</b> is listening for read events. */ +int +connection_is_reading(connection_t *conn) +{ + tor_assert(conn); + + return conn->reading_from_linked_conn || + (conn->read_event && event_pending(conn->read_event, EV_READ, NULL)); +} + +/** Reset our main loop counters. */ +void +reset_main_loop_counters(void) +{ + stats_n_main_loop_successes = 0; + stats_n_main_loop_errors = 0; + stats_n_main_loop_idle = 0; +} + +/** Increment the main loop success counter. */ +static void +increment_main_loop_success_count(void) +{ + ++stats_n_main_loop_successes; +} + +/** Get the main loop success counter. */ +uint64_t +get_main_loop_success_count(void) +{ + return stats_n_main_loop_successes; +} + +/** Increment the main loop error counter. */ +static void +increment_main_loop_error_count(void) +{ + ++stats_n_main_loop_errors; +} + +/** Get the main loop error counter. */ +uint64_t +get_main_loop_error_count(void) +{ + return stats_n_main_loop_errors; +} + +/** Increment the main loop idle counter. */ +static void +increment_main_loop_idle_count(void) +{ + ++stats_n_main_loop_idle; +} + +/** Get the main loop idle counter. */ +uint64_t +get_main_loop_idle_count(void) +{ + return stats_n_main_loop_idle; +} + +/** Check whether <b>conn</b> is correct in having (or not having) a + * read/write event (passed in <b>ev</b>). On success, return 0. On failure, + * log a warning and return -1. */ +static int +connection_check_event(connection_t *conn, struct event *ev) +{ + int bad; + + if (conn->type == CONN_TYPE_AP && TO_EDGE_CONN(conn)->is_dns_request) { + /* DNS requests which we launch through the dnsserv.c module do not have + * any underlying socket or any underlying linked connection, so they + * shouldn't have any attached events either. + */ + bad = ev != NULL; + } else { + /* Everything else should have an underlying socket, or a linked + * connection (which is also tracked with a read_event/write_event pair). + */ + bad = ev == NULL; + } + + if (bad) { + log_warn(LD_BUG, "Event missing on connection %p [%s;%s]. " + "socket=%d. linked=%d. " + "is_dns_request=%d. Marked_for_close=%s:%d", + conn, + conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state), + (int)conn->s, (int)conn->linked, + (conn->type == CONN_TYPE_AP && + TO_EDGE_CONN(conn)->is_dns_request), + conn->marked_for_close_file ? conn->marked_for_close_file : "-", + conn->marked_for_close + ); + log_backtrace(LOG_WARN, LD_BUG, "Backtrace attached."); + return -1; + } + return 0; +} + +/** Tell the main loop to stop notifying <b>conn</b> of any read events. */ +MOCK_IMPL(void, +connection_stop_reading,(connection_t *conn)) +{ + tor_assert(conn); + + if (connection_check_event(conn, conn->read_event) < 0) { + return; + } + + if (conn->linked) { + conn->reading_from_linked_conn = 0; + connection_stop_reading_from_linked_conn(conn); + } else { + if (event_del(conn->read_event)) + log_warn(LD_NET, "Error from libevent setting read event state for %d " + "to unwatched: %s", + (int)conn->s, + tor_socket_strerror(tor_socket_errno(conn->s))); + } +} + +/** Tell the main loop to start notifying <b>conn</b> of any read events. */ +MOCK_IMPL(void, +connection_start_reading,(connection_t *conn)) +{ + tor_assert(conn); + + if (connection_check_event(conn, conn->read_event) < 0) { + return; + } + + if (conn->linked) { + conn->reading_from_linked_conn = 1; + if (connection_should_read_from_linked_conn(conn)) + connection_start_reading_from_linked_conn(conn); + } else { + if (event_add(conn->read_event, NULL)) + log_warn(LD_NET, "Error from libevent setting read event state for %d " + "to watched: %s", + (int)conn->s, + tor_socket_strerror(tor_socket_errno(conn->s))); + } +} + +/** Return true iff <b>conn</b> is listening for write events. */ +int +connection_is_writing(connection_t *conn) +{ + tor_assert(conn); + + return conn->writing_to_linked_conn || + (conn->write_event && event_pending(conn->write_event, EV_WRITE, NULL)); +} + +/** Tell the main loop to stop notifying <b>conn</b> of any write events. */ +MOCK_IMPL(void, +connection_stop_writing,(connection_t *conn)) +{ + tor_assert(conn); + + if (connection_check_event(conn, conn->write_event) < 0) { + return; + } + + if (conn->linked) { + conn->writing_to_linked_conn = 0; + if (conn->linked_conn) + connection_stop_reading_from_linked_conn(conn->linked_conn); + } else { + if (event_del(conn->write_event)) + log_warn(LD_NET, "Error from libevent setting write event state for %d " + "to unwatched: %s", + (int)conn->s, + tor_socket_strerror(tor_socket_errno(conn->s))); + } +} + +/** Tell the main loop to start notifying <b>conn</b> of any write events. */ +MOCK_IMPL(void, +connection_start_writing,(connection_t *conn)) +{ + tor_assert(conn); + + if (connection_check_event(conn, conn->write_event) < 0) { + return; + } + + if (conn->linked) { + conn->writing_to_linked_conn = 1; + if (conn->linked_conn && + connection_should_read_from_linked_conn(conn->linked_conn)) + connection_start_reading_from_linked_conn(conn->linked_conn); + } else { + if (event_add(conn->write_event, NULL)) + log_warn(LD_NET, "Error from libevent setting write event state for %d " + "to watched: %s", + (int)conn->s, + tor_socket_strerror(tor_socket_errno(conn->s))); + } +} + +/** Return true iff <b>conn</b> is linked conn, and reading from the conn + * linked to it would be good and feasible. (Reading is "feasible" if the + * other conn exists and has data in its outbuf, and is "good" if we have our + * reading_from_linked_conn flag set and the other conn has its + * writing_to_linked_conn flag set.)*/ +static int +connection_should_read_from_linked_conn(connection_t *conn) +{ + if (conn->linked && conn->reading_from_linked_conn) { + if (! conn->linked_conn || + (conn->linked_conn->writing_to_linked_conn && + buf_datalen(conn->linked_conn->outbuf))) + return 1; + } + return 0; +} + +/** Event to run 'shutdown did not work callback'. */ +static struct event *shutdown_did_not_work_event = NULL; + +/** Failsafe measure that should never actually be necessary: If + * tor_shutdown_event_loop_and_exit() somehow doesn't successfully exit the + * event loop, then this callback will kill Tor with an assertion failure + * seconds later + */ +static void +shutdown_did_not_work_callback(evutil_socket_t fd, short event, void *arg) +{ + // LCOV_EXCL_START + (void) fd; + (void) event; + (void) arg; + tor_assert_unreached(); + // LCOV_EXCL_STOP +} + +#ifdef ENABLE_RESTART_DEBUGGING +static struct event *tor_shutdown_event_loop_for_restart_event = NULL; +static void +tor_shutdown_event_loop_for_restart_cb( + evutil_socket_t fd, short event, void *arg) +{ + (void)fd; + (void)event; + (void)arg; + tor_event_free(tor_shutdown_event_loop_for_restart_event); + tor_shutdown_event_loop_and_exit(0); +} +#endif + +/** + * After finishing the current callback (if any), shut down the main loop, + * clean up the process, and exit with <b>exitcode</b>. + */ +void +tor_shutdown_event_loop_and_exit(int exitcode) +{ + if (main_loop_should_exit) + return; /* Ignore multiple calls to this function. */ + + main_loop_should_exit = 1; + main_loop_exit_value = exitcode; + + /* Die with an assertion failure in ten seconds, if for some reason we don't + * exit normally. */ + /* XXXX We should consider this code if it's never used. */ + struct timeval ten_seconds = { 10, 0 }; + shutdown_did_not_work_event = tor_evtimer_new( + tor_libevent_get_base(), + shutdown_did_not_work_callback, NULL); + event_add(shutdown_did_not_work_event, &ten_seconds); + + /* Unlike exit_loop_after_delay(), exit_loop_after_callback + * prevents other callbacks from running. */ + tor_libevent_exit_loop_after_callback(tor_libevent_get_base()); +} + +/** Return true iff tor_shutdown_event_loop_and_exit() has been called. */ +int +tor_event_loop_shutdown_is_pending(void) +{ + return main_loop_should_exit; +} + +/** Helper: Tell the main loop to begin reading bytes into <b>conn</b> from + * its linked connection, if it is not doing so already. Called by + * connection_start_reading and connection_start_writing as appropriate. */ +static void +connection_start_reading_from_linked_conn(connection_t *conn) +{ + tor_assert(conn); + tor_assert(conn->linked == 1); + + if (!conn->active_on_link) { + conn->active_on_link = 1; + smartlist_add(active_linked_connection_lst, conn); + mainloop_event_activate(schedule_active_linked_connections_event); + } else { + tor_assert(smartlist_contains(active_linked_connection_lst, conn)); + } +} + +/** Tell the main loop to stop reading bytes into <b>conn</b> from its linked + * connection, if is currently doing so. Called by connection_stop_reading, + * connection_stop_writing, and connection_read. */ +void +connection_stop_reading_from_linked_conn(connection_t *conn) +{ + tor_assert(conn); + tor_assert(conn->linked == 1); + + if (conn->active_on_link) { + conn->active_on_link = 0; + /* FFFF We could keep an index here so we can smartlist_del + * cleanly. On the other hand, this doesn't show up on profiles, + * so let's leave it alone for now. */ + smartlist_remove(active_linked_connection_lst, conn); + } else { + tor_assert(!smartlist_contains(active_linked_connection_lst, conn)); + } +} + +/** Close all connections that have been scheduled to get closed. */ +STATIC void +close_closeable_connections(void) +{ + int i; + for (i = 0; i < smartlist_len(closeable_connection_lst); ) { + connection_t *conn = smartlist_get(closeable_connection_lst, i); + if (conn->conn_array_index < 0) { + connection_unlink(conn); /* blow it away right now */ + } else { + if (!conn_close_if_marked(conn->conn_array_index)) + ++i; + } + } +} + +/** Count moribund connections for the OOS handler */ +MOCK_IMPL(int, +connection_count_moribund, (void)) +{ + int moribund = 0; + + /* + * Count things we'll try to kill when close_closeable_connections() + * runs next. + */ + SMARTLIST_FOREACH_BEGIN(closeable_connection_lst, connection_t *, conn) { + if (SOCKET_OK(conn->s) && connection_is_moribund(conn)) ++moribund; + } SMARTLIST_FOREACH_END(conn); + + return moribund; +} + +/** Libevent callback: this gets invoked when (connection_t*)<b>conn</b> has + * some data to read. */ +static void +conn_read_callback(evutil_socket_t fd, short event, void *_conn) +{ + connection_t *conn = _conn; + (void)fd; + (void)event; + + log_debug(LD_NET,"socket %d wants to read.",(int)conn->s); + + /* assert_connection_ok(conn, time(NULL)); */ + + if (connection_handle_read(conn) < 0) { + if (!conn->marked_for_close) { +#ifndef _WIN32 + log_warn(LD_BUG,"Unhandled error on read for %s connection " + "(fd %d); removing", + conn_type_to_string(conn->type), (int)conn->s); + tor_fragile_assert(); +#endif /* !defined(_WIN32) */ + if (CONN_IS_EDGE(conn)) + connection_edge_end_errno(TO_EDGE_CONN(conn)); + connection_mark_for_close(conn); + } + } + assert_connection_ok(conn, time(NULL)); + + if (smartlist_len(closeable_connection_lst)) + close_closeable_connections(); +} + +/** Libevent callback: this gets invoked when (connection_t*)<b>conn</b> has + * some data to write. */ +static void +conn_write_callback(evutil_socket_t fd, short events, void *_conn) +{ + connection_t *conn = _conn; + (void)fd; + (void)events; + + LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "socket %d wants to write.", + (int)conn->s)); + + /* assert_connection_ok(conn, time(NULL)); */ + + if (connection_handle_write(conn, 0) < 0) { + if (!conn->marked_for_close) { + /* this connection is broken. remove it. */ + log_fn(LOG_WARN,LD_BUG, + "unhandled error on write for %s connection (fd %d); removing", + conn_type_to_string(conn->type), (int)conn->s); + tor_fragile_assert(); + if (CONN_IS_EDGE(conn)) { + /* otherwise we cry wolf about duplicate close */ + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + if (!edge_conn->end_reason) + edge_conn->end_reason = END_STREAM_REASON_INTERNAL; + edge_conn->edge_has_sent_end = 1; + } + connection_close_immediate(conn); /* So we don't try to flush. */ + connection_mark_for_close(conn); + } + } + assert_connection_ok(conn, time(NULL)); + + if (smartlist_len(closeable_connection_lst)) + close_closeable_connections(); +} + +/** If the connection at connection_array[i] is marked for close, then: + * - If it has data that it wants to flush, try to flush it. + * - If it _still_ has data to flush, and conn->hold_open_until_flushed is + * true, then leave the connection open and return. + * - Otherwise, remove the connection from connection_array and from + * all other lists, close it, and free it. + * Returns 1 if the connection was closed, 0 otherwise. + */ +static int +conn_close_if_marked(int i) +{ + connection_t *conn; + int retval; + time_t now; + + conn = smartlist_get(connection_array, i); + if (!conn->marked_for_close) + return 0; /* nothing to see here, move along */ + now = time(NULL); + assert_connection_ok(conn, now); + /* assert_all_pending_dns_resolves_ok(); */ + + log_debug(LD_NET,"Cleaning up connection (fd "TOR_SOCKET_T_FORMAT").", + conn->s); + + /* If the connection we are about to close was trying to connect to + a proxy server and failed, the client won't be able to use that + proxy. We should warn the user about this. */ + if (conn->proxy_state == PROXY_INFANT) + log_failed_proxy_connection(conn); + + if ((SOCKET_OK(conn->s) || conn->linked_conn) && + connection_wants_to_flush(conn)) { + /* s == -1 means it's an incomplete edge connection, or that the socket + * has already been closed as unflushable. */ + ssize_t sz = connection_bucket_write_limit(conn, now); + if (!conn->hold_open_until_flushed) + log_info(LD_NET, + "Conn (addr %s, fd %d, type %s, state %d) marked, but wants " + "to flush %d bytes. (Marked at %s:%d)", + escaped_safe_str_client(conn->address), + (int)conn->s, conn_type_to_string(conn->type), conn->state, + (int)conn->outbuf_flushlen, + conn->marked_for_close_file, conn->marked_for_close); + if (conn->linked_conn) { + retval = buf_move_to_buf(conn->linked_conn->inbuf, conn->outbuf, + &conn->outbuf_flushlen); + if (retval >= 0) { + /* The linked conn will notice that it has data when it notices that + * we're gone. */ + connection_start_reading_from_linked_conn(conn->linked_conn); + } + log_debug(LD_GENERAL, "Flushed last %d bytes from a linked conn; " + "%d left; flushlen %d; wants-to-flush==%d", retval, + (int)connection_get_outbuf_len(conn), + (int)conn->outbuf_flushlen, + connection_wants_to_flush(conn)); + } else if (connection_speaks_cells(conn)) { + if (conn->state == OR_CONN_STATE_OPEN) { + retval = buf_flush_to_tls(conn->outbuf, TO_OR_CONN(conn)->tls, sz, + &conn->outbuf_flushlen); + } else + retval = -1; /* never flush non-open broken tls connections */ + } else { + retval = buf_flush_to_socket(conn->outbuf, conn->s, sz, + &conn->outbuf_flushlen); + } + if (retval >= 0 && /* Technically, we could survive things like + TLS_WANT_WRITE here. But don't bother for now. */ + conn->hold_open_until_flushed && connection_wants_to_flush(conn)) { + if (retval > 0) { + LOG_FN_CONN(conn, (LOG_INFO,LD_NET, + "Holding conn (fd %d) open for more flushing.", + (int)conn->s)); + conn->timestamp_last_write_allowed = now; /* reset so we can flush + * more */ + } else if (sz == 0) { + /* Also, retval==0. If we get here, we didn't want to write anything + * (because of rate-limiting) and we didn't. */ + + /* Connection must flush before closing, but it's being rate-limited. + * Let's remove from Libevent, and mark it as blocked on bandwidth + * so it will be re-added on next token bucket refill. Prevents + * busy Libevent loops where we keep ending up here and returning + * 0 until we are no longer blocked on bandwidth. + */ + connection_consider_empty_read_buckets(conn); + connection_consider_empty_write_buckets(conn); + + /* Make sure that consider_empty_buckets really disabled the + * connection: */ + if (BUG(connection_is_writing(conn))) { + connection_write_bw_exhausted(conn, true); + } + if (BUG(connection_is_reading(conn))) { + /* XXXX+ We should make this code unreachable; if a connection is + * marked for close and flushing, there is no point in reading to it + * at all. Further, checking at this point is a bit of a hack: it + * would make much more sense to react in + * connection_handle_read_impl, or to just stop reading in + * mark_and_flush */ + connection_read_bw_exhausted(conn, true/* kludge. */); + } + } + return 0; + } + if (connection_wants_to_flush(conn)) { + log_fn(LOG_INFO, LD_NET, "We stalled too much while trying to write %d " + "bytes to address %s. If this happens a lot, either " + "something is wrong with your network connection, or " + "something is wrong with theirs. " + "(fd %d, type %s, state %d, marked at %s:%d).", + (int)connection_get_outbuf_len(conn), + escaped_safe_str_client(conn->address), + (int)conn->s, conn_type_to_string(conn->type), conn->state, + conn->marked_for_close_file, + conn->marked_for_close); + } + } + + connection_unlink(conn); /* unlink, remove, free */ + return 1; +} + +/** Implementation for directory_all_unreachable. This is done in a callback, + * since otherwise it would complicate Tor's control-flow graph beyond all + * reason. + */ +static void +directory_all_unreachable_cb(mainloop_event_t *event, void *arg) +{ + (void)event; + (void)arg; + + connection_t *conn; + + while ((conn = connection_get_by_type_state(CONN_TYPE_AP, + AP_CONN_STATE_CIRCUIT_WAIT))) { + entry_connection_t *entry_conn = TO_ENTRY_CONN(conn); + log_notice(LD_NET, + "Is your network connection down? " + "Failing connection to '%s:%d'.", + safe_str_client(entry_conn->socks_request->address), + entry_conn->socks_request->port); + connection_mark_unattached_ap(entry_conn, + END_STREAM_REASON_NET_UNREACHABLE); + } + control_event_general_error("DIR_ALL_UNREACHABLE"); +} + +static mainloop_event_t *directory_all_unreachable_cb_event = NULL; + +/** We've just tried every dirserver we know about, and none of + * them were reachable. Assume the network is down. Change state + * so next time an application connection arrives we'll delay it + * and try another directory fetch. Kill off all the circuit_wait + * streams that are waiting now, since they will all timeout anyway. + */ +void +directory_all_unreachable(time_t now) +{ + (void)now; + + reset_uptime(); /* reset it */ + + if (!directory_all_unreachable_cb_event) { + directory_all_unreachable_cb_event = + mainloop_event_new(directory_all_unreachable_cb, NULL); + tor_assert(directory_all_unreachable_cb_event); + } + + mainloop_event_activate(directory_all_unreachable_cb_event); +} + +/** This function is called whenever we successfully pull down some new + * network statuses or server descriptors. */ +void +directory_info_has_arrived(time_t now, int from_cache, int suppress_logs) +{ + const or_options_t *options = get_options(); + + /* if we have enough dir info, then update our guard status with + * whatever we just learned. */ + int invalidate_circs = guards_update_all(); + + if (invalidate_circs) { + circuit_mark_all_unused_circs(); + circuit_mark_all_dirty_circs_as_unusable(); + } + + if (!router_have_minimum_dir_info()) { + int quiet = suppress_logs || from_cache || + directory_too_idle_to_fetch_descriptors(options, now); + tor_log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR, + "I learned some more directory information, but not enough to " + "build a circuit: %s", get_dir_info_status_string()); + update_all_descriptor_downloads(now); + return; + } else { + if (directory_fetches_from_authorities(options)) { + update_all_descriptor_downloads(now); + } + + /* Don't even bother trying to get extrainfo until the rest of our + * directory info is up-to-date */ + if (options->DownloadExtraInfo) + update_extrainfo_downloads(now); + } + + if (server_mode(options) && !net_is_disabled() && !from_cache && + (have_completed_a_circuit() || !any_predicted_circuits(now))) + router_do_reachability_checks(1, 1); +} + +/** Perform regular maintenance tasks for a single connection. This + * function gets run once per second per connection by run_scheduled_events. + */ +static void +run_connection_housekeeping(int i, time_t now) +{ + cell_t cell; + connection_t *conn = smartlist_get(connection_array, i); + const or_options_t *options = get_options(); + or_connection_t *or_conn; + channel_t *chan = NULL; + int have_any_circuits; + int past_keepalive = + now >= conn->timestamp_last_write_allowed + options->KeepalivePeriod; + + if (conn->outbuf && !connection_get_outbuf_len(conn) && + conn->type == CONN_TYPE_OR) + TO_OR_CONN(conn)->timestamp_lastempty = now; + + if (conn->marked_for_close) { + /* nothing to do here */ + return; + } + + /* Expire any directory connections that haven't been active (sent + * if a server or received if a client) for 5 min */ + if (conn->type == CONN_TYPE_DIR && + ((DIR_CONN_IS_SERVER(conn) && + conn->timestamp_last_write_allowed + + options->TestingDirConnectionMaxStall < now) || + (!DIR_CONN_IS_SERVER(conn) && + conn->timestamp_last_read_allowed + + options->TestingDirConnectionMaxStall < now))) { + log_info(LD_DIR,"Expiring wedged directory conn (fd %d, purpose %d)", + (int)conn->s, conn->purpose); + /* This check is temporary; it's to let us know whether we should consider + * parsing partial serverdesc responses. */ + if (conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC && + connection_get_inbuf_len(conn) >= 1024) { + log_info(LD_DIR,"Trying to extract information from wedged server desc " + "download."); + connection_dir_reached_eof(TO_DIR_CONN(conn)); + } else { + connection_mark_for_close(conn); + } + return; + } + + if (!connection_speaks_cells(conn)) + return; /* we're all done here, the rest is just for OR conns */ + + /* If we haven't flushed to an OR connection for a while, then either nuke + the connection or send a keepalive, depending. */ + + or_conn = TO_OR_CONN(conn); + tor_assert(conn->outbuf); + + chan = TLS_CHAN_TO_BASE(or_conn->chan); + tor_assert(chan); + + if (channel_num_circuits(chan) != 0) { + have_any_circuits = 1; + chan->timestamp_last_had_circuits = now; + } else { + have_any_circuits = 0; + } + + if (channel_is_bad_for_new_circs(TLS_CHAN_TO_BASE(or_conn->chan)) && + ! have_any_circuits) { + /* It's bad for new circuits, and has no unmarked circuits on it: + * mark it now. */ + log_info(LD_OR, + "Expiring non-used OR connection to fd %d (%s:%d) [Too old].", + (int)conn->s, conn->address, conn->port); + if (conn->state == OR_CONN_STATE_CONNECTING) + connection_or_connect_failed(TO_OR_CONN(conn), + END_OR_CONN_REASON_TIMEOUT, + "Tor gave up on the connection"); + connection_or_close_normally(TO_OR_CONN(conn), 1); + } else if (!connection_state_is_open(conn)) { + if (past_keepalive) { + /* We never managed to actually get this connection open and happy. */ + log_info(LD_OR,"Expiring non-open OR connection to fd %d (%s:%d).", + (int)conn->s,conn->address, conn->port); + connection_or_close_normally(TO_OR_CONN(conn), 0); + } + } else if (we_are_hibernating() && + ! have_any_circuits && + !connection_get_outbuf_len(conn)) { + /* We're hibernating or shutting down, there's no circuits, and nothing to + * flush.*/ + log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) " + "[Hibernating or exiting].", + (int)conn->s,conn->address, conn->port); + connection_or_close_normally(TO_OR_CONN(conn), 1); + } else if (!have_any_circuits && + now - or_conn->idle_timeout >= + chan->timestamp_last_had_circuits) { + log_info(LD_OR,"Expiring non-used OR connection %"PRIu64" to fd %d " + "(%s:%d) [no circuits for %d; timeout %d; %scanonical].", + (chan->global_identifier), + (int)conn->s, conn->address, conn->port, + (int)(now - chan->timestamp_last_had_circuits), + or_conn->idle_timeout, + or_conn->is_canonical ? "" : "non"); + connection_or_close_normally(TO_OR_CONN(conn), 0); + } else if ( + now >= or_conn->timestamp_lastempty + options->KeepalivePeriod*10 && + now >= + conn->timestamp_last_write_allowed + options->KeepalivePeriod*10) { + log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL, + "Expiring stuck OR connection to fd %d (%s:%d). (%d bytes to " + "flush; %d seconds since last write)", + (int)conn->s, conn->address, conn->port, + (int)connection_get_outbuf_len(conn), + (int)(now-conn->timestamp_last_write_allowed)); + connection_or_close_normally(TO_OR_CONN(conn), 0); + } else if (past_keepalive && !connection_get_outbuf_len(conn)) { + /* send a padding cell */ + log_fn(LOG_DEBUG,LD_OR,"Sending keepalive to (%s:%d)", + conn->address, conn->port); + memset(&cell,0,sizeof(cell_t)); + cell.command = CELL_PADDING; + connection_or_write_cell_to_buf(&cell, or_conn); + } else { + channelpadding_decide_to_pad_channel(chan); + } +} + +/** Honor a NEWNYM request: make future requests unlinkable to past + * requests. */ +static void +signewnym_impl(time_t now) +{ + const or_options_t *options = get_options(); + if (!proxy_mode(options)) { + log_info(LD_CONTROL, "Ignoring SIGNAL NEWNYM because client functionality " + "is disabled."); + return; + } + + circuit_mark_all_dirty_circs_as_unusable(); + addressmap_clear_transient(); + hs_client_purge_state(); + time_of_last_signewnym = now; + signewnym_is_pending = 0; + + ++newnym_epoch; + + control_event_signal(SIGNEWNYM); +} + +/** Callback: run a deferred signewnym. */ +static void +handle_deferred_signewnym_cb(mainloop_event_t *event, void *arg) +{ + (void)event; + (void)arg; + log_info(LD_CONTROL, "Honoring delayed NEWNYM request"); + signewnym_impl(time(NULL)); +} + +/** Return the number of times that signewnym has been called. */ +unsigned +get_signewnym_epoch(void) +{ + return newnym_epoch; +} + +/** True iff we have initialized all the members of <b>periodic_events</b>. + * Used to prevent double-initialization. */ +static int periodic_events_initialized = 0; + +/* Declare all the timer callback functions... */ +#undef CALLBACK +#define CALLBACK(name) \ + static int name ## _callback(time_t, const or_options_t *) +CALLBACK(add_entropy); +CALLBACK(check_authority_cert); +CALLBACK(check_canonical_channels); +CALLBACK(check_descriptor); +CALLBACK(check_dns_honesty); +CALLBACK(check_ed_keys); +CALLBACK(check_expired_networkstatus); +CALLBACK(check_for_reachability_bw); +CALLBACK(check_onion_keys_expiry_time); +CALLBACK(clean_caches); +CALLBACK(clean_consdiffmgr); +CALLBACK(dirvote); +CALLBACK(downrate_stability); +CALLBACK(expire_old_ciruits_serverside); +CALLBACK(fetch_networkstatus); +CALLBACK(heartbeat); +CALLBACK(hs_service); +CALLBACK(launch_descriptor_fetches); +CALLBACK(launch_reachability_tests); +CALLBACK(reachability_warnings); +CALLBACK(record_bridge_stats); +CALLBACK(rend_cache_failure_clean); +CALLBACK(reset_padding_counts); +CALLBACK(retry_dns); +CALLBACK(retry_listeners); +CALLBACK(rotate_onion_key); +CALLBACK(rotate_x509_certificate); +CALLBACK(save_stability); +CALLBACK(save_state); +CALLBACK(write_bridge_ns); +CALLBACK(write_stats_file); + +#undef CALLBACK + +/* Now we declare an array of periodic_event_item_t for each periodic event */ +#define CALLBACK(name, r, f) PERIODIC_EVENT(name, r, f) + +STATIC periodic_event_item_t periodic_events[] = { + /* Everyone needs to run those. */ + CALLBACK(add_entropy, PERIODIC_EVENT_ROLE_ALL, 0), + CALLBACK(check_expired_networkstatus, PERIODIC_EVENT_ROLE_ALL, 0), + CALLBACK(clean_caches, PERIODIC_EVENT_ROLE_ALL, 0), + CALLBACK(fetch_networkstatus, PERIODIC_EVENT_ROLE_ALL, + PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(heartbeat, PERIODIC_EVENT_ROLE_ALL, 0), + CALLBACK(launch_descriptor_fetches, PERIODIC_EVENT_ROLE_ALL, + PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(reset_padding_counts, PERIODIC_EVENT_ROLE_ALL, 0), + CALLBACK(retry_listeners, PERIODIC_EVENT_ROLE_ALL, + PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(save_state, PERIODIC_EVENT_ROLE_ALL, 0), + CALLBACK(rotate_x509_certificate, PERIODIC_EVENT_ROLE_ALL, 0), + CALLBACK(write_stats_file, PERIODIC_EVENT_ROLE_ALL, 0), + + /* Routers (bridge and relay) only. */ + CALLBACK(check_descriptor, PERIODIC_EVENT_ROLE_ROUTER, + PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(check_ed_keys, PERIODIC_EVENT_ROLE_ROUTER, 0), + CALLBACK(check_for_reachability_bw, PERIODIC_EVENT_ROLE_ROUTER, + PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(check_onion_keys_expiry_time, PERIODIC_EVENT_ROLE_ROUTER, 0), + CALLBACK(expire_old_ciruits_serverside, PERIODIC_EVENT_ROLE_ROUTER, + PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(reachability_warnings, PERIODIC_EVENT_ROLE_ROUTER, + PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(retry_dns, PERIODIC_EVENT_ROLE_ROUTER, 0), + CALLBACK(rotate_onion_key, PERIODIC_EVENT_ROLE_ROUTER, 0), + + /* Authorities (bridge and directory) only. */ + CALLBACK(downrate_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0), + CALLBACK(launch_reachability_tests, PERIODIC_EVENT_ROLE_AUTHORITIES, + PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(save_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0), + + /* Directory authority only. */ + CALLBACK(check_authority_cert, PERIODIC_EVENT_ROLE_DIRAUTH, 0), + CALLBACK(dirvote, PERIODIC_EVENT_ROLE_DIRAUTH, PERIODIC_EVENT_FLAG_NEED_NET), + + /* Relay only. */ + CALLBACK(check_canonical_channels, PERIODIC_EVENT_ROLE_RELAY, + PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(check_dns_honesty, PERIODIC_EVENT_ROLE_RELAY, + PERIODIC_EVENT_FLAG_NEED_NET), + + /* Hidden Service service only. */ + CALLBACK(hs_service, PERIODIC_EVENT_ROLE_HS_SERVICE, + PERIODIC_EVENT_FLAG_NEED_NET), + + /* Bridge only. */ + CALLBACK(record_bridge_stats, PERIODIC_EVENT_ROLE_BRIDGE, 0), + + /* Client only. */ + CALLBACK(rend_cache_failure_clean, PERIODIC_EVENT_ROLE_CLIENT, 0), + + /* Bridge Authority only. */ + CALLBACK(write_bridge_ns, PERIODIC_EVENT_ROLE_BRIDGEAUTH, 0), + + /* Directory server only. */ + CALLBACK(clean_consdiffmgr, PERIODIC_EVENT_ROLE_DIRSERVER, 0), + + END_OF_PERIODIC_EVENTS +}; +#undef CALLBACK + +/* These are pointers to members of periodic_events[] that are used to + * implement particular callbacks. We keep them separate here so that we + * can access them by name. We also keep them inside periodic_events[] + * so that we can implement "reset all timers" in a reasonable way. */ +static periodic_event_item_t *check_descriptor_event=NULL; +static periodic_event_item_t *dirvote_event=NULL; +static periodic_event_item_t *fetch_networkstatus_event=NULL; +static periodic_event_item_t *launch_descriptor_fetches_event=NULL; +static periodic_event_item_t *check_dns_honesty_event=NULL; +static periodic_event_item_t *save_state_event=NULL; + +/** Reset all the periodic events so we'll do all our actions again as if we + * just started up. + * Useful if our clock just moved back a long time from the future, + * so we don't wait until that future arrives again before acting. + */ +void +reset_all_main_loop_timers(void) +{ + int i; + for (i = 0; periodic_events[i].name; ++i) { + periodic_event_reschedule(&periodic_events[i]); + } +} + +/** Return the member of periodic_events[] whose name is <b>name</b>. + * Return NULL if no such event is found. + */ +static periodic_event_item_t * +find_periodic_event(const char *name) +{ + int i; + for (i = 0; periodic_events[i].name; ++i) { + if (strcmp(name, periodic_events[i].name) == 0) + return &periodic_events[i]; + } + return NULL; +} + +/** Return a bitmask of the roles this tor instance is configured for using + * the given options. */ +STATIC int +get_my_roles(const or_options_t *options) +{ + tor_assert(options); + + int roles = 0; + int is_bridge = options->BridgeRelay; + int is_client = options_any_client_port_set(options); + int is_relay = server_mode(options); + int is_dirauth = authdir_mode_v3(options); + int is_bridgeauth = authdir_mode_bridge(options); + int is_hidden_service = !!hs_service_get_num_services() || + !!rend_num_services(); + int is_dirserver = dir_server_mode(options); + + if (is_bridge) roles |= PERIODIC_EVENT_ROLE_BRIDGE; + if (is_client) roles |= PERIODIC_EVENT_ROLE_CLIENT; + if (is_relay) roles |= PERIODIC_EVENT_ROLE_RELAY; + if (is_dirauth) roles |= PERIODIC_EVENT_ROLE_DIRAUTH; + if (is_bridgeauth) roles |= PERIODIC_EVENT_ROLE_BRIDGEAUTH; + if (is_hidden_service) roles |= PERIODIC_EVENT_ROLE_HS_SERVICE; + if (is_dirserver) roles |= PERIODIC_EVENT_ROLE_DIRSERVER; + + return roles; +} + +/** Event to run initialize_periodic_events_cb */ +static struct event *initialize_periodic_events_event = NULL; + +/** Helper, run one second after setup: + * Initializes all members of periodic_events and starts them running. + * + * (We do this one second after setup for backward-compatibility reasons; + * it might not actually be necessary.) */ +static void +initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data) +{ + (void) fd; + (void) events; + (void) data; + + tor_event_free(initialize_periodic_events_event); + + rescan_periodic_events(get_options()); +} + +/** Set up all the members of periodic_events[], and configure them all to be + * launched from a callback. */ +STATIC void +initialize_periodic_events(void) +{ + tor_assert(periodic_events_initialized == 0); + periodic_events_initialized = 1; + + /* Set up all periodic events. We'll launch them by roles. */ + int i; + for (i = 0; periodic_events[i].name; ++i) { + periodic_event_setup(&periodic_events[i]); + } + +#define NAMED_CALLBACK(name) \ + STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END + + NAMED_CALLBACK(check_descriptor); + NAMED_CALLBACK(dirvote); + NAMED_CALLBACK(fetch_networkstatus); + NAMED_CALLBACK(launch_descriptor_fetches); + NAMED_CALLBACK(check_dns_honesty); + NAMED_CALLBACK(save_state); + + struct timeval one_second = { 1, 0 }; + initialize_periodic_events_event = tor_evtimer_new( + tor_libevent_get_base(), + initialize_periodic_events_cb, NULL); + event_add(initialize_periodic_events_event, &one_second); +} + +STATIC void +teardown_periodic_events(void) +{ + int i; + for (i = 0; periodic_events[i].name; ++i) { + periodic_event_destroy(&periodic_events[i]); + } + periodic_events_initialized = 0; +} + +/** Do a pass at all our periodic events, disable those we don't need anymore + * and enable those we need now using the given options. */ +void +rescan_periodic_events(const or_options_t *options) +{ + tor_assert(options); + + /* Avoid scanning the event list if we haven't initialized it yet. This is + * particularly useful for unit tests in order to avoid initializing main + * loop events everytime. */ + if (!periodic_events_initialized) { + return; + } + + int roles = get_my_roles(options); + + for (int i = 0; periodic_events[i].name; ++i) { + periodic_event_item_t *item = &periodic_events[i]; + + /* Handle the event flags. */ + if (net_is_disabled() && + (item->flags & PERIODIC_EVENT_FLAG_NEED_NET)) { + continue; + } + + /* Enable the event if needed. It is safe to enable an event that was + * already enabled. Same goes for disabling it. */ + if (item->roles & roles) { + log_debug(LD_GENERAL, "Launching periodic event %s", item->name); + periodic_event_enable(item); + } else { + log_debug(LD_GENERAL, "Disabling periodic event %s", item->name); + periodic_event_disable(item); + } + } +} + +/* We just got new options globally set, see if we need to enabled or disable + * periodic events. */ +void +periodic_events_on_new_options(const or_options_t *options) +{ + /* Only if we've already initialized the events, rescan the list which will + * enable or disable events depending on our roles. This will be called at + * bootup and we don't want this function to initialize the events because + * they aren't set up at this stage. */ + if (periodic_events_initialized) { + rescan_periodic_events(options); + } +} + +/** + * Update our schedule so that we'll check whether we need to update our + * descriptor immediately, rather than after up to CHECK_DESCRIPTOR_INTERVAL + * seconds. + */ +void +reschedule_descriptor_update_check(void) +{ + if (check_descriptor_event) { + periodic_event_reschedule(check_descriptor_event); + } +} + +/** + * Update our schedule so that we'll check whether we need to fetch directory + * info immediately. + */ +void +reschedule_directory_downloads(void) +{ + tor_assert(fetch_networkstatus_event); + tor_assert(launch_descriptor_fetches_event); + + periodic_event_reschedule(fetch_networkstatus_event); + periodic_event_reschedule(launch_descriptor_fetches_event); +} + +/** Mainloop callback: clean up circuits, channels, and connections + * that are pending close. */ +static void +postloop_cleanup_cb(mainloop_event_t *ev, void *arg) +{ + (void)ev; + (void)arg; + circuit_close_all_marked(); + close_closeable_connections(); + channel_run_cleanup(); + channel_listener_run_cleanup(); +} + +/** Event to run postloop_cleanup_cb */ +static mainloop_event_t *postloop_cleanup_ev=NULL; + +/** Schedule a post-loop event to clean up marked channels, connections, and + * circuits. */ +void +mainloop_schedule_postloop_cleanup(void) +{ + if (PREDICT_UNLIKELY(postloop_cleanup_ev == NULL)) { + // (It's possible that we can get here if we decide to close a connection + // in the earliest stages of our configuration, before we create events.) + return; + } + mainloop_event_activate(postloop_cleanup_ev); +} + +#define LONGEST_TIMER_PERIOD (30 * 86400) +/** Helper: Return the number of seconds between <b>now</b> and <b>next</b>, + * clipped to the range [1 second, LONGEST_TIMER_PERIOD]. */ +static inline int +safe_timer_diff(time_t now, time_t next) +{ + if (next > now) { + /* There were no computers at signed TIME_MIN (1902 on 32-bit systems), + * and nothing that could run Tor. It's a bug if 'next' is around then. + * On 64-bit systems with signed TIME_MIN, TIME_MIN is before the Big + * Bang. We cannot extrapolate past a singularity, but there was probably + * nothing that could run Tor then, either. + **/ + tor_assert(next > TIME_MIN + LONGEST_TIMER_PERIOD); + + if (next - LONGEST_TIMER_PERIOD > now) + return LONGEST_TIMER_PERIOD; + return (int)(next - now); + } else { + return 1; + } +} + +/** Perform regular maintenance tasks. This function gets run once per + * second by second_elapsed_callback(). + */ +static void +run_scheduled_events(time_t now) +{ + const or_options_t *options = get_options(); + + /* 0. See if we've been asked to shut down and our timeout has + * expired; or if our bandwidth limits are exhausted and we + * should hibernate; or if it's time to wake up from hibernation. + */ + consider_hibernation(now); + + /* Maybe enough time elapsed for us to reconsider a circuit. */ + circuit_upgrade_circuits_from_guard_wait(); + + if (options->UseBridges && !net_is_disabled()) { + /* Note: this check uses net_is_disabled(), not should_delay_dir_fetches() + * -- the latter is only for fetching consensus-derived directory info. */ + fetch_bridge_descriptors(options, now); + } + + if (accounting_is_enabled(options)) { + accounting_run_housekeeping(now); + } + + /* 3a. Every second, we examine pending circuits and prune the + * ones which have been pending for more than a few seconds. + * We do this before step 4, so it can try building more if + * it's not comfortable with the number of available circuits. + */ + /* (If our circuit build timeout can ever become lower than a second (which + * it can't, currently), we should do this more often.) */ + circuit_expire_building(); + circuit_expire_waiting_for_better_guard(); + + /* 3b. Also look at pending streams and prune the ones that 'began' + * a long time ago but haven't gotten a 'connected' yet. + * Do this before step 4, so we can put them back into pending + * state to be picked up by the new circuit. + */ + connection_ap_expire_beginning(); + + /* 3c. And expire connections that we've held open for too long. + */ + connection_expire_held_open(); + + /* 4. Every second, we try a new circuit if there are no valid + * circuits. Every NewCircuitPeriod seconds, we expire circuits + * that became dirty more than MaxCircuitDirtiness seconds ago, + * and we make a new circ if there are no clean circuits. + */ + const int have_dir_info = router_have_minimum_dir_info(); + if (have_dir_info && !net_is_disabled()) { + circuit_build_needed_circs(now); + } else { + circuit_expire_old_circs_as_needed(now); + } + + /* 5. We do housekeeping for each connection... */ + channel_update_bad_for_new_circs(NULL, 0); + int i; + for (i=0;i<smartlist_len(connection_array);i++) { + run_connection_housekeeping(i, now); + } + + /* 11b. check pending unconfigured managed proxies */ + if (!net_is_disabled() && pt_proxies_configuration_pending()) + pt_configure_remaining_proxies(); +} + +/* Periodic callback: rotate the onion keys after the period defined by the + * "onion-key-rotation-days" consensus parameter, shut down and restart all + * cpuworkers, and update our descriptor if necessary. + */ +static int +rotate_onion_key_callback(time_t now, const or_options_t *options) +{ + if (server_mode(options)) { + int onion_key_lifetime = get_onion_key_lifetime(); + time_t rotation_time = get_onion_key_set_at()+onion_key_lifetime; + if (rotation_time > now) { + return ONION_KEY_CONSENSUS_CHECK_INTERVAL; + } + + log_info(LD_GENERAL,"Rotating onion key."); + rotate_onion_key(); + cpuworkers_rotate_keyinfo(); + if (router_rebuild_descriptor(1)<0) { + log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); + } + if (advertised_server_mode() && !net_is_disabled()) + router_upload_dir_desc_to_dirservers(0); + return ONION_KEY_CONSENSUS_CHECK_INTERVAL; + } + return PERIODIC_EVENT_NO_UPDATE; +} + +/* Period callback: Check if our old onion keys are still valid after the + * period of time defined by the consensus parameter + * "onion-key-grace-period-days", otherwise expire them by setting them to + * NULL. + */ +static int +check_onion_keys_expiry_time_callback(time_t now, const or_options_t *options) +{ + if (server_mode(options)) { + int onion_key_grace_period = get_onion_key_grace_period(); + time_t expiry_time = get_onion_key_set_at()+onion_key_grace_period; + if (expiry_time > now) { + return ONION_KEY_CONSENSUS_CHECK_INTERVAL; + } + + log_info(LD_GENERAL, "Expiring old onion keys."); + expire_old_onion_keys(); + cpuworkers_rotate_keyinfo(); + return ONION_KEY_CONSENSUS_CHECK_INTERVAL; + } + + return PERIODIC_EVENT_NO_UPDATE; +} + +/* Periodic callback: Every 30 seconds, check whether it's time to make new + * Ed25519 subkeys. + */ +static int +check_ed_keys_callback(time_t now, const or_options_t *options) +{ + if (server_mode(options)) { + if (should_make_new_ed_keys(options, now)) { + int new_signing_key = load_ed_keys(options, now); + if (new_signing_key < 0 || + generate_ed_link_cert(options, now, new_signing_key > 0)) { + log_err(LD_OR, "Unable to update Ed25519 keys! Exiting."); + tor_shutdown_event_loop_and_exit(1); + } + } + return 30; + } + return PERIODIC_EVENT_NO_UPDATE; +} + +/** + * Periodic callback: Every {LAZY,GREEDY}_DESCRIPTOR_RETRY_INTERVAL, + * see about fetching descriptors, microdescriptors, and extrainfo + * documents. + */ +static int +launch_descriptor_fetches_callback(time_t now, const or_options_t *options) +{ + if (should_delay_dir_fetches(options, NULL)) + return PERIODIC_EVENT_NO_UPDATE; + + update_all_descriptor_downloads(now); + update_extrainfo_downloads(now); + if (router_have_minimum_dir_info()) + return LAZY_DESCRIPTOR_RETRY_INTERVAL; + else + return GREEDY_DESCRIPTOR_RETRY_INTERVAL; +} + +/** + * Periodic event: Rotate our X.509 certificates and TLS keys once every + * MAX_SSL_KEY_LIFETIME_INTERNAL. + */ +static int +rotate_x509_certificate_callback(time_t now, const or_options_t *options) +{ + static int first = 1; + (void)now; + (void)options; + if (first) { + first = 0; + return MAX_SSL_KEY_LIFETIME_INTERNAL; + } + + /* 1b. Every MAX_SSL_KEY_LIFETIME_INTERNAL seconds, we change our + * TLS context. */ + log_info(LD_GENERAL,"Rotating tls context."); + if (router_initialize_tls_context() < 0) { + log_err(LD_BUG, "Error reinitializing TLS context"); + tor_assert_unreached(); + } + if (generate_ed_link_cert(options, now, 1)) { + log_err(LD_OR, "Unable to update Ed25519->TLS link certificate for " + "new TLS context."); + tor_assert_unreached(); + } + + /* We also make sure to rotate the TLS connections themselves if they've + * been up for too long -- but that's done via is_bad_for_new_circs in + * run_connection_housekeeping() above. */ + return MAX_SSL_KEY_LIFETIME_INTERNAL; +} + +/** + * Periodic callback: once an hour, grab some more entropy from the + * kernel and feed it to our CSPRNG. + **/ +static int +add_entropy_callback(time_t now, const or_options_t *options) +{ + (void)now; + (void)options; + /* We already seeded once, so don't die on failure. */ + if (crypto_seed_rng() < 0) { + log_warn(LD_GENERAL, "Tried to re-seed RNG, but failed. We already " + "seeded once, though, so we won't exit here."); + } + + /** How often do we add more entropy to OpenSSL's RNG pool? */ +#define ENTROPY_INTERVAL (60*60) + return ENTROPY_INTERVAL; +} + +/** + * Periodic callback: if we're an authority, make sure we test + * the routers on the network for reachability. + */ +static int +launch_reachability_tests_callback(time_t now, const or_options_t *options) +{ + if (authdir_mode_tests_reachability(options) && + !net_is_disabled()) { + /* try to determine reachability of the other Tor relays */ + dirserv_test_reachability(now); + } + return REACHABILITY_TEST_INTERVAL; +} + +/** + * Periodic callback: if we're an authority, discount the stability + * information (and other rephist information) that's older. + */ +static int +downrate_stability_callback(time_t now, const or_options_t *options) +{ + (void)options; + /* 1d. Periodically, we discount older stability information so that new + * stability info counts more, and save the stability information to disk as + * appropriate. */ + time_t next = rep_hist_downrate_old_runs(now); + return safe_timer_diff(now, next); +} + +/** + * Periodic callback: if we're an authority, record our measured stability + * information from rephist in an mtbf file. + */ +static int +save_stability_callback(time_t now, const or_options_t *options) +{ + if (authdir_mode_tests_reachability(options)) { + if (rep_hist_record_mtbf_data(now, 1)<0) { + log_warn(LD_GENERAL, "Couldn't store mtbf data."); + } + } +#define SAVE_STABILITY_INTERVAL (30*60) + return SAVE_STABILITY_INTERVAL; +} + +/** + * Periodic callback: if we're an authority, check on our authority + * certificate (the one that authenticates our authority signing key). + */ +static int +check_authority_cert_callback(time_t now, const or_options_t *options) +{ + (void)now; + (void)options; + /* 1e. Periodically, if we're a v3 authority, we check whether our cert is + * close to expiring and warn the admin if it is. */ + v3_authority_check_key_expiry(); +#define CHECK_V3_CERTIFICATE_INTERVAL (5*60) + return CHECK_V3_CERTIFICATE_INTERVAL; +} + +/** + * Scheduled callback: Run directory-authority voting functionality. + * + * The schedule is a bit complicated here, so dirvote_act() manages the + * schedule itself. + **/ +static int +dirvote_callback(time_t now, const or_options_t *options) +{ + if (!authdir_mode_v3(options)) { + tor_assert_nonfatal_unreached(); + return 3600; + } + + time_t next = dirvote_act(options, now); + if (BUG(next == TIME_MAX)) { + /* This shouldn't be returned unless we called dirvote_act() without + * being an authority. If it happens, maybe our configuration will + * fix itself in an hour or so? */ + return 3600; + } + return safe_timer_diff(now, next); +} + +/** Reschedule the directory-authority voting event. Run this whenever the + * schedule has changed. */ +void +reschedule_dirvote(const or_options_t *options) +{ + if (periodic_events_initialized && authdir_mode_v3(options)) { + periodic_event_reschedule(dirvote_event); + } +} + +/** + * Periodic callback: If our consensus is too old, recalculate whether + * we can actually use it. + */ +static int +check_expired_networkstatus_callback(time_t now, const or_options_t *options) +{ + (void)options; + /* Check whether our networkstatus has expired. */ + networkstatus_t *ns = networkstatus_get_latest_consensus(); + /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in + * networkstatus_get_reasonably_live_consensus(), but that value is way + * way too high. Arma: is the bridge issue there resolved yet? -NM */ +#define NS_EXPIRY_SLOP (24*60*60) + if (ns && ns->valid_until < (now - NS_EXPIRY_SLOP) && + router_have_minimum_dir_info()) { + router_dir_info_changed(); + } +#define CHECK_EXPIRED_NS_INTERVAL (2*60) + return CHECK_EXPIRED_NS_INTERVAL; +} + +/** + * Scheduled callback: Save the state file to disk if appropriate. + */ +static int +save_state_callback(time_t now, const or_options_t *options) +{ + (void) options; + (void) or_state_save(now); // only saves if appropriate + const time_t next_write = get_or_state()->next_write; + if (next_write == TIME_MAX) { + return 86400; + } + return safe_timer_diff(now, next_write); +} + +/** Reschedule the event for saving the state file. + * + * Run this when the state becomes dirty. */ +void +reschedule_or_state_save(void) +{ + if (save_state_event == NULL) { + /* This can happen early on during startup. */ + return; + } + periodic_event_reschedule(save_state_event); +} + +/** + * Periodic callback: Write statistics to disk if appropriate. + */ +static int +write_stats_file_callback(time_t now, const or_options_t *options) +{ + /* 1g. Check whether we should write statistics to disk. + */ +#define CHECK_WRITE_STATS_INTERVAL (60*60) + time_t next_time_to_write_stats_files = now + CHECK_WRITE_STATS_INTERVAL; + if (options->CellStatistics) { + time_t next_write = + rep_hist_buffer_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->DirReqStatistics) { + time_t next_write = geoip_dirreq_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->EntryStatistics) { + time_t next_write = geoip_entry_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->HiddenServiceStatistics) { + time_t next_write = rep_hist_hs_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->ExitPortStatistics) { + time_t next_write = rep_hist_exit_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->ConnDirectionStatistics) { + time_t next_write = rep_hist_conn_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->BridgeAuthoritativeDir) { + time_t next_write = rep_hist_desc_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + + return safe_timer_diff(now, next_time_to_write_stats_files); +} + +#define CHANNEL_CHECK_INTERVAL (60*60) +static int +check_canonical_channels_callback(time_t now, const or_options_t *options) +{ + (void)now; + if (public_server_mode(options)) + channel_check_for_duplicates(); + + return CHANNEL_CHECK_INTERVAL; +} + +static int +reset_padding_counts_callback(time_t now, const or_options_t *options) +{ + if (options->PaddingStatistics) { + rep_hist_prep_published_padding_counts(now); + } + + rep_hist_reset_padding_counts(); + return REPHIST_CELL_PADDING_COUNTS_INTERVAL; +} + +static int should_init_bridge_stats = 1; + +/** + * Periodic callback: Write bridge statistics to disk if appropriate. + */ +static int +record_bridge_stats_callback(time_t now, const or_options_t *options) +{ + /* 1h. Check whether we should write bridge statistics to disk. + */ + if (should_record_bridge_info(options)) { + if (should_init_bridge_stats) { + /* (Re-)initialize bridge statistics. */ + geoip_bridge_stats_init(now); + should_init_bridge_stats = 0; + return WRITE_STATS_INTERVAL; + } else { + /* Possibly write bridge statistics to disk and ask when to write + * them next time. */ + time_t next = geoip_bridge_stats_write(now); + return safe_timer_diff(now, next); + } + } else if (!should_init_bridge_stats) { + /* Bridge mode was turned off. Ensure that stats are re-initialized + * next time bridge mode is turned on. */ + should_init_bridge_stats = 1; + } + return PERIODIC_EVENT_NO_UPDATE; +} + +/** + * Periodic callback: Clean in-memory caches every once in a while + */ +static int +clean_caches_callback(time_t now, const or_options_t *options) +{ + /* Remove old information from rephist and the rend cache. */ + rep_history_clean(now - options->RephistTrackTime); + rend_cache_clean(now, REND_CACHE_TYPE_SERVICE); + hs_cache_clean_as_client(now); + hs_cache_clean_as_dir(now); + microdesc_cache_rebuild(NULL, 0); +#define CLEAN_CACHES_INTERVAL (30*60) + return CLEAN_CACHES_INTERVAL; +} + +/** + * Periodic callback: Clean the cache of failed hidden service lookups + * frequently. + */ +static int +rend_cache_failure_clean_callback(time_t now, const or_options_t *options) +{ + (void)options; + /* We don't keep entries that are more than five minutes old so we try to + * clean it as soon as we can since we want to make sure the client waits + * as little as possible for reachability reasons. */ + rend_cache_failure_clean(now); + hs_cache_client_intro_state_clean(now); + return 30; +} + +/** + * Periodic callback: If we're a server and initializing dns failed, retry. + */ +static int +retry_dns_callback(time_t now, const or_options_t *options) +{ + (void)now; +#define RETRY_DNS_INTERVAL (10*60) + if (server_mode(options) && has_dns_init_failed()) + dns_init(); + return RETRY_DNS_INTERVAL; +} + +/** Periodic callback: consider rebuilding or and re-uploading our descriptor + * (if we've passed our internal checks). */ +static int +check_descriptor_callback(time_t now, const or_options_t *options) +{ +/** How often do we check whether part of our router info has changed in a + * way that would require an upload? That includes checking whether our IP + * address has changed. */ +#define CHECK_DESCRIPTOR_INTERVAL (60) + + (void)options; + + /* 2b. Once per minute, regenerate and upload the descriptor if the old + * one is inaccurate. */ + if (!net_is_disabled()) { + check_descriptor_bandwidth_changed(now); + check_descriptor_ipaddress_changed(now); + mark_my_descriptor_dirty_if_too_old(now); + consider_publishable_server(0); + /* If any networkstatus documents are no longer recent, we need to + * update all the descriptors' running status. */ + /* Remove dead routers. */ + /* XXXX This doesn't belong here, but it was here in the pre- + * XXXX refactoring code. */ + routerlist_remove_old_routers(); + } + + return CHECK_DESCRIPTOR_INTERVAL; +} + +/** + * Periodic callback: check whether we're reachable (as a relay), and + * whether our bandwidth has changed enough that we need to + * publish a new descriptor. + */ +static int +check_for_reachability_bw_callback(time_t now, const or_options_t *options) +{ + /* XXXX This whole thing was stuck in the middle of what is now + * XXXX check_descriptor_callback. I'm not sure it's right. */ + + static int dirport_reachability_count = 0; + /* also, check religiously for reachability, if it's within the first + * 20 minutes of our uptime. */ + if (server_mode(options) && + (have_completed_a_circuit() || !any_predicted_circuits(now)) && + !net_is_disabled()) { + if (get_uptime() < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { + router_do_reachability_checks(1, dirport_reachability_count==0); + if (++dirport_reachability_count > 5) + dirport_reachability_count = 0; + return 1; + } else { + /* If we haven't checked for 12 hours and our bandwidth estimate is + * low, do another bandwidth test. This is especially important for + * bridges, since they might go long periods without much use. */ + const routerinfo_t *me = router_get_my_routerinfo(); + static int first_time = 1; + if (!first_time && me && + me->bandwidthcapacity < me->bandwidthrate && + me->bandwidthcapacity < 51200) { + reset_bandwidth_test(); + } + first_time = 0; +#define BANDWIDTH_RECHECK_INTERVAL (12*60*60) + return BANDWIDTH_RECHECK_INTERVAL; + } + } + return CHECK_DESCRIPTOR_INTERVAL; +} + +/** + * Periodic event: once a minute, (or every second if TestingTorNetwork, or + * during client bootstrap), check whether we want to download any + * networkstatus documents. */ +static int +fetch_networkstatus_callback(time_t now, const or_options_t *options) +{ + /* How often do we check whether we should download network status + * documents? */ + const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping( + now); + const int prefer_mirrors = !directory_fetches_from_authorities( + get_options()); + int networkstatus_dl_check_interval = 60; + /* check more often when testing, or when bootstrapping from mirrors + * (connection limits prevent too many connections being made) */ + if (options->TestingTorNetwork + || (we_are_bootstrapping && prefer_mirrors)) { + networkstatus_dl_check_interval = 1; + } + + if (should_delay_dir_fetches(options, NULL)) + return PERIODIC_EVENT_NO_UPDATE; + + update_networkstatus_downloads(now); + return networkstatus_dl_check_interval; +} + +/** + * Periodic callback: Every 60 seconds, we relaunch listeners if any died. */ +static int +retry_listeners_callback(time_t now, const or_options_t *options) +{ + (void)now; + (void)options; + if (!net_is_disabled()) { + retry_all_listeners(NULL, NULL, 0); + return 60; + } + return PERIODIC_EVENT_NO_UPDATE; +} + +/** + * Periodic callback: as a server, see if we have any old unused circuits + * that should be expired */ +static int +expire_old_ciruits_serverside_callback(time_t now, const or_options_t *options) +{ + (void)options; + /* every 11 seconds, so not usually the same second as other such events */ + circuit_expire_old_circuits_serverside(now); + return 11; +} + +/** + * Callback: Send warnings if Tor doesn't find its ports reachable. + */ +static int +reachability_warnings_callback(time_t now, const or_options_t *options) +{ + (void) now; + + if (get_uptime() < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { + return (int)(TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT - get_uptime()); + } + + if (server_mode(options) && + !net_is_disabled() && + have_completed_a_circuit()) { + /* every 20 minutes, check and complain if necessary */ + const routerinfo_t *me = router_get_my_routerinfo(); + if (me && !check_whether_orport_reachable(options)) { + char *address = tor_dup_ip(me->addr); + log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that " + "its ORPort is reachable. Relays do not publish descriptors " + "until their ORPort and DirPort are reachable. Please check " + "your firewalls, ports, address, /etc/hosts file, etc.", + address, me->or_port); + control_event_server_status(LOG_WARN, + "REACHABILITY_FAILED ORADDRESS=%s:%d", + address, me->or_port); + tor_free(address); + } + + if (me && !check_whether_dirport_reachable(options)) { + char *address = tor_dup_ip(me->addr); + log_warn(LD_CONFIG, + "Your server (%s:%d) has not managed to confirm that its " + "DirPort is reachable. Relays do not publish descriptors " + "until their ORPort and DirPort are reachable. Please check " + "your firewalls, ports, address, /etc/hosts file, etc.", + address, me->dir_port); + control_event_server_status(LOG_WARN, + "REACHABILITY_FAILED DIRADDRESS=%s:%d", + address, me->dir_port); + tor_free(address); + } + } + + return TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT; +} + +static int dns_honesty_first_time = 1; + +/** + * Periodic event: if we're an exit, see if our DNS server is telling us + * obvious lies. + */ +static int +check_dns_honesty_callback(time_t now, const or_options_t *options) +{ + (void)now; + /* 9. and if we're an exit node, check whether our DNS is telling stories + * to us. */ + if (net_is_disabled() || + ! public_server_mode(options) || + router_my_exit_policy_is_reject_star()) + return PERIODIC_EVENT_NO_UPDATE; + + if (dns_honesty_first_time) { + /* Don't launch right when we start */ + dns_honesty_first_time = 0; + return crypto_rand_int_range(60, 180); + } + + dns_launch_correctness_checks(); + return 12*3600 + crypto_rand_int(12*3600); +} + +/** + * Periodic callback: if we're the bridge authority, write a networkstatus + * file to disk. + */ +static int +write_bridge_ns_callback(time_t now, const or_options_t *options) +{ + /* 10. write bridge networkstatus file to disk */ + if (options->BridgeAuthoritativeDir) { + networkstatus_dump_bridge_status_to_file(now); +#define BRIDGE_STATUSFILE_INTERVAL (30*60) + return BRIDGE_STATUSFILE_INTERVAL; + } + return PERIODIC_EVENT_NO_UPDATE; +} + +static int heartbeat_callback_first_time = 1; + +/** + * Periodic callback: write the heartbeat message in the logs. + * + * If writing the heartbeat message to the logs fails for some reason, retry + * again after <b>MIN_HEARTBEAT_PERIOD</b> seconds. + */ +static int +heartbeat_callback(time_t now, const or_options_t *options) +{ + /* Check if heartbeat is disabled */ + if (!options->HeartbeatPeriod) { + return PERIODIC_EVENT_NO_UPDATE; + } + + /* Skip the first one. */ + if (heartbeat_callback_first_time) { + heartbeat_callback_first_time = 0; + return options->HeartbeatPeriod; + } + + /* Write the heartbeat message */ + if (log_heartbeat(now) == 0) { + return options->HeartbeatPeriod; + } else { + /* If we couldn't write the heartbeat log message, try again in the minimum + * interval of time. */ + return MIN_HEARTBEAT_PERIOD; + } +} + +#define CDM_CLEAN_CALLBACK_INTERVAL 600 +static int +clean_consdiffmgr_callback(time_t now, const or_options_t *options) +{ + (void)now; + if (dir_server_mode(options)) { + consdiffmgr_cleanup(); + } + return CDM_CLEAN_CALLBACK_INTERVAL; +} + +/* + * Periodic callback: Run scheduled events for HS service. This is called + * every second. + */ +static int +hs_service_callback(time_t now, const or_options_t *options) +{ + (void) options; + + /* We need to at least be able to build circuits and that we actually have + * a working network. */ + if (!have_completed_a_circuit() || net_is_disabled() || + networkstatus_get_live_consensus(now) == NULL) { + goto end; + } + + hs_service_run_scheduled_events(now); + + end: + /* Every 1 second. */ + return 1; +} + +/** Timer: used to invoke second_elapsed_callback() once per second. */ +static periodic_timer_t *second_timer = NULL; + +/** + * Enable or disable the per-second timer as appropriate, creating it if + * necessary. + */ +void +reschedule_per_second_timer(void) +{ + struct timeval one_second; + one_second.tv_sec = 1; + one_second.tv_usec = 0; + + if (! second_timer) { + second_timer = periodic_timer_new(tor_libevent_get_base(), + &one_second, + second_elapsed_callback, + NULL); + tor_assert(second_timer); + } + + const bool run_per_second_events = + control_any_per_second_event_enabled() || ! net_is_completely_disabled(); + + if (run_per_second_events) { + periodic_timer_launch(second_timer, &one_second); + } else { + periodic_timer_disable(second_timer); + } +} + +/** Last time that update_current_time was called. */ +static time_t current_second = 0; +/** Last time that update_current_time updated current_second. */ +static monotime_coarse_t current_second_last_changed; + +/** + * Set the current time to "now", which should be the value returned by + * time(). Check for clock jumps and track the total number of seconds we + * have been running. + */ +void +update_current_time(time_t now) +{ + if (PREDICT_LIKELY(now == current_second)) { + /* We call this function a lot. Most frequently, the current second + * will not have changed, so we just return. */ + return; + } + + const time_t seconds_elapsed = current_second ? (now - current_second) : 0; + + /* Check the wall clock against the monotonic clock, so we can + * better tell idleness from clock jumps and/or other shenanigans. */ + monotime_coarse_t last_updated; + memcpy(&last_updated, ¤t_second_last_changed, sizeof(last_updated)); + monotime_coarse_get(¤t_second_last_changed); + + /** How much clock jumping do we tolerate? */ +#define NUM_JUMPED_SECONDS_BEFORE_WARN 100 + + /** How much idleness do we tolerate? */ +#define NUM_IDLE_SECONDS_BEFORE_WARN 3600 + + if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN) { + // moving back in time is always a bad sign. + circuit_note_clock_jumped(seconds_elapsed, false); + } else if (seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) { + /* Compare the monotonic clock to the result of time(). */ + const int32_t monotime_msec_passed = + monotime_coarse_diff_msec32(&last_updated, + ¤t_second_last_changed); + const int monotime_sec_passed = monotime_msec_passed / 1000; + const int discrepancy = monotime_sec_passed - (int)seconds_elapsed; + /* If the monotonic clock deviates from time(NULL), we have a couple of + * possibilities. On some systems, this means we have been suspended or + * sleeping. Everywhere, it can mean that the wall-clock time has + * been changed -- for example, with settimeofday(). + * + * On the other hand, if the monotonic time matches with the wall-clock + * time, we've probably just been idle for a while, with no events firing. + * we tolerate much more of that. + */ + const bool clock_jumped = abs(discrepancy) > 2; + + if (clock_jumped || seconds_elapsed >= NUM_IDLE_SECONDS_BEFORE_WARN) { + circuit_note_clock_jumped(seconds_elapsed, ! clock_jumped); + } + } else if (seconds_elapsed > 0) { + stats_n_seconds_working += seconds_elapsed; + } + + update_approx_time(now); + current_second = now; +} + +/** Libevent callback: invoked once every second. */ +static void +second_elapsed_callback(periodic_timer_t *timer, void *arg) +{ + /* XXXX This could be sensibly refactored into multiple callbacks, and we + * could use Libevent's timers for this rather than checking the current + * time against a bunch of timeouts every second. */ + time_t now; + (void)timer; + (void)arg; + + now = time(NULL); + + /* We don't need to do this once-per-second any more: time-updating is + * only in this callback _because it is a callback_. It should be fine + * to disable this callback, and the time will still get updated. + */ + update_current_time(now); + + /* Maybe some controller events are ready to fire */ + control_per_second_events(); + + run_scheduled_events(now); +} + +#ifdef HAVE_SYSTEMD_209 +static periodic_timer_t *systemd_watchdog_timer = NULL; + +/** Libevent callback: invoked to reset systemd watchdog. */ +static void +systemd_watchdog_callback(periodic_timer_t *timer, void *arg) +{ + (void)timer; + (void)arg; + sd_notify(0, "WATCHDOG=1"); +} +#endif /* defined(HAVE_SYSTEMD_209) */ + +#define UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST (6*60*60) + +/** Called when our IP address seems to have changed. <b>at_interface</b> + * should be true if we detected a change in our interface, and false if we + * detected a change in our published address. */ +void +ip_address_changed(int at_interface) +{ + const or_options_t *options = get_options(); + int server = server_mode(options); + int exit_reject_interfaces = (server && options->ExitRelay + && options->ExitPolicyRejectLocalInterfaces); + + if (at_interface) { + if (! server) { + /* Okay, change our keys. */ + if (init_keys_client() < 0) + log_warn(LD_GENERAL, "Unable to rotate keys after IP change!"); + } + } else { + if (server) { + if (get_uptime() > UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST) + reset_bandwidth_test(); + reset_uptime(); + router_reset_reachability(); + } + } + + /* Exit relays incorporate interface addresses in their exit policies when + * ExitPolicyRejectLocalInterfaces is set */ + if (exit_reject_interfaces || (server && !at_interface)) { + mark_my_descriptor_dirty("IP address changed"); + } + + dns_servers_relaunch_checks(); +} + +/** Forget what we've learned about the correctness of our DNS servers, and + * start learning again. */ +void +dns_servers_relaunch_checks(void) +{ + if (server_mode(get_options())) { + dns_reset_correctness_checks(); + if (periodic_events_initialized) { + tor_assert(check_dns_honesty_event); + periodic_event_reschedule(check_dns_honesty_event); + } + } +} + +/** Called when we get a SIGHUP: reload configuration files and keys, + * retry all connections, and so on. */ +static int +do_hup(void) +{ + const or_options_t *options = get_options(); + + log_notice(LD_GENERAL,"Received reload signal (hup). Reloading config and " + "resetting internal state."); + if (accounting_is_enabled(options)) + accounting_record_bandwidth_usage(time(NULL), get_or_state()); + + router_reset_warnings(); + routerlist_reset_warnings(); + /* first, reload config variables, in case they've changed */ + if (options->ReloadTorrcOnSIGHUP) { + /* no need to provide argc/v, they've been cached in init_from_config */ + int init_rv = options_init_from_torrc(0, NULL); + if (init_rv < 0) { + log_err(LD_CONFIG,"Reading config failed--see warnings above. " + "For usage, try -h."); + return -1; + } else if (BUG(init_rv > 0)) { + // LCOV_EXCL_START + /* This should be impossible: the only "return 1" cases in + * options_init_from_torrc are ones caused by command-line arguments; + * but they can't change while Tor is running. */ + return -1; + // LCOV_EXCL_STOP + } + options = get_options(); /* they have changed now */ + /* Logs are only truncated the first time they are opened, but were + probably intended to be cleaned up on signal. */ + if (options->TruncateLogFile) + truncate_logs(); + } else { + char *msg = NULL; + log_notice(LD_GENERAL, "Not reloading config file: the controller told " + "us not to."); + /* Make stuff get rescanned, reloaded, etc. */ + if (set_options((or_options_t*)options, &msg) < 0) { + if (!msg) + msg = tor_strdup("Unknown error"); + log_warn(LD_GENERAL, "Unable to re-set previous options: %s", msg); + tor_free(msg); + } + } + if (authdir_mode(options)) { + /* reload the approved-routers file */ + if (dirserv_load_fingerprint_file() < 0) { + /* warnings are logged from dirserv_load_fingerprint_file() directly */ + log_info(LD_GENERAL, "Error reloading fingerprints. " + "Continuing with old list."); + } + } + + /* Rotate away from the old dirty circuits. This has to be done + * after we've read the new options, but before we start using + * circuits for directory fetches. */ + circuit_mark_all_dirty_circs_as_unusable(); + + /* retry appropriate downloads */ + router_reset_status_download_failures(); + router_reset_descriptor_download_failures(); + if (!net_is_disabled()) + update_networkstatus_downloads(time(NULL)); + + /* We'll retry routerstatus downloads in about 10 seconds; no need to + * force a retry there. */ + + if (server_mode(options)) { + /* Maybe we've been given a new ed25519 key or certificate? + */ + time_t now = approx_time(); + int new_signing_key = load_ed_keys(options, now); + if (new_signing_key < 0 || + generate_ed_link_cert(options, now, new_signing_key > 0)) { + log_warn(LD_OR, "Problem reloading Ed25519 keys; still using old keys."); + } + + /* Update cpuworker and dnsworker processes, so they get up-to-date + * configuration options. */ + cpuworkers_rotate_keyinfo(); + dns_reset(); + } + return 0; +} + +/** Initialize some mainloop_event_t objects that we require. */ +STATIC void +initialize_mainloop_events(void) +{ + if (!schedule_active_linked_connections_event) { + schedule_active_linked_connections_event = + mainloop_event_postloop_new(schedule_active_linked_connections_cb, NULL); + } + if (!postloop_cleanup_ev) { + postloop_cleanup_ev = + mainloop_event_postloop_new(postloop_cleanup_cb, NULL); + } +} + +/** Tor main loop. */ +int +do_main_loop(void) +{ + time_t now; + + /* initialize the periodic events first, so that code that depends on the + * events being present does not assert. + */ + if (! periodic_events_initialized) { + initialize_periodic_events(); + } + + initialize_mainloop_events(); + + /* initialize dns resolve map, spawn workers if needed */ + if (dns_init() < 0) { + if (get_options()->ServerDNSAllowBrokenConfig) + log_warn(LD_GENERAL, "Couldn't set up any working nameservers. " + "Network not up yet? Will try again soon."); + else { + log_err(LD_GENERAL,"Error initializing dns subsystem; exiting. To " + "retry instead, set the ServerDNSAllowBrokenResolvConf option."); + } + } + + handle_signals(); + monotime_init(); + timers_initialize(); + + /* load the private keys, if we're supposed to have them, and set up the + * TLS context. */ + if (! client_identity_key_is_set()) { + if (init_keys() < 0) { + log_err(LD_OR, "Error initializing keys; exiting"); + return -1; + } + } + + /* Set up our buckets */ + connection_bucket_init(); + + /* initialize the bootstrap status events to know we're starting up */ + control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0); + + /* Initialize the keypinning log. */ + if (authdir_mode_v3(get_options())) { + char *fname = get_datadir_fname("key-pinning-journal"); + int r = 0; + if (keypin_load_journal(fname)<0) { + log_err(LD_DIR, "Error loading key-pinning journal: %s",strerror(errno)); + r = -1; + } + if (keypin_open_journal(fname)<0) { + log_err(LD_DIR, "Error opening key-pinning journal: %s",strerror(errno)); + r = -1; + } + tor_free(fname); + if (r) + return r; + } + { + /* This is the old name for key-pinning-journal. These got corrupted + * in a couple of cases by #16530, so we started over. See #16580 for + * the rationale and for other options we didn't take. We can remove + * this code once all the authorities that ran 0.2.7.1-alpha-dev are + * upgraded. + */ + char *fname = get_datadir_fname("key-pinning-entries"); + unlink(fname); + tor_free(fname); + } + + if (trusted_dirs_reload_certs()) { + log_warn(LD_DIR, + "Couldn't load all cached v3 certificates. Starting anyway."); + } + if (router_reload_consensus_networkstatus()) { + return -1; + } + /* load the routers file, or assign the defaults. */ + if (router_reload_router_list()) { + return -1; + } + /* load the networkstatuses. (This launches a download for new routers as + * appropriate.) + */ + now = time(NULL); + directory_info_has_arrived(now, 1, 0); + + if (server_mode(get_options()) || dir_server_mode(get_options())) { + /* launch cpuworkers. Need to do this *after* we've read the onion key. */ + cpu_init(); + } + consdiffmgr_enable_background_compression(); + + /* Setup shared random protocol subsystem. */ + if (authdir_mode_v3(get_options())) { + if (sr_init(1) < 0) { + return -1; + } + } + + /* set up once-a-second callback. */ + reschedule_per_second_timer(); + +#ifdef HAVE_SYSTEMD_209 + uint64_t watchdog_delay; + /* set up systemd watchdog notification. */ + if (sd_watchdog_enabled(1, &watchdog_delay) > 0) { + if (! systemd_watchdog_timer) { + struct timeval watchdog; + /* The manager will "act on" us if we don't send them a notification + * every 'watchdog_delay' microseconds. So, send notifications twice + * that often. */ + watchdog_delay /= 2; + watchdog.tv_sec = watchdog_delay / 1000000; + watchdog.tv_usec = watchdog_delay % 1000000; + + systemd_watchdog_timer = periodic_timer_new(tor_libevent_get_base(), + &watchdog, + systemd_watchdog_callback, + NULL); + tor_assert(systemd_watchdog_timer); + } + } +#endif /* defined(HAVE_SYSTEMD_209) */ + +#ifdef HAVE_SYSTEMD + { + const int r = sd_notify(0, "READY=1"); + if (r < 0) { + log_warn(LD_GENERAL, "Unable to send readiness to systemd: %s", + strerror(r)); + } else if (r > 0) { + log_notice(LD_GENERAL, "Signaled readiness to systemd"); + } else { + log_info(LD_GENERAL, "Systemd NOTIFY_SOCKET not present."); + } + } +#endif /* defined(HAVE_SYSTEMD) */ + + main_loop_should_exit = 0; + main_loop_exit_value = 0; + +#ifdef ENABLE_RESTART_DEBUGGING + { + static int first_time = 1; + + if (first_time && getenv("TOR_DEBUG_RESTART")) { + first_time = 0; + const char *sec_str = getenv("TOR_DEBUG_RESTART_AFTER_SECONDS"); + long sec; + int sec_ok=0; + if (sec_str && + (sec = tor_parse_long(sec_str, 10, 0, INT_MAX, &sec_ok, NULL)) && + sec_ok) { + /* Okay, we parsed the seconds. */ + } else { + sec = 5; + } + struct timeval restart_after = { (time_t) sec, 0 }; + tor_shutdown_event_loop_for_restart_event = + tor_evtimer_new(tor_libevent_get_base(), + tor_shutdown_event_loop_for_restart_cb, NULL); + event_add(tor_shutdown_event_loop_for_restart_event, &restart_after); + } + } +#endif + + return run_main_loop_until_done(); +} + +#ifndef _WIN32 +/** Rate-limiter for EINVAL-type libevent warnings. */ +static ratelim_t libevent_error_ratelim = RATELIM_INIT(10); +#endif + +/** + * Run the main loop a single time. Return 0 for "exit"; -1 for "exit with + * error", and 1 for "run this again." + */ +static int +run_main_loop_once(void) +{ + int loop_result; + + if (nt_service_is_stopping()) + return 0; + + if (main_loop_should_exit) + return 0; + +#ifndef _WIN32 + /* Make it easier to tell whether libevent failure is our fault or not. */ + errno = 0; +#endif + + if (get_options()->MainloopStats) { + /* We always enforce that EVLOOP_ONCE is passed to event_base_loop() if we + * are collecting main loop statistics. */ + called_loop_once = 1; + } else { + called_loop_once = 0; + } + + /* Make sure we know (about) what time it is. */ + update_approx_time(time(NULL)); + + /* Here it is: the main loop. Here we tell Libevent to poll until we have + * an event, or the second ends, or until we have some active linked + * connections to trigger events for. Libevent will wait till one + * of these happens, then run all the appropriate callbacks. */ + loop_result = tor_libevent_run_event_loop(tor_libevent_get_base(), + called_loop_once); + + if (get_options()->MainloopStats) { + /* Update our main loop counters. */ + if (loop_result == 0) { + // The call was successful. + increment_main_loop_success_count(); + } else if (loop_result == -1) { + // The call was erroneous. + increment_main_loop_error_count(); + } else if (loop_result == 1) { + // The call didn't have any active or pending events + // to handle. + increment_main_loop_idle_count(); + } + } + + /* Oh, the loop failed. That might be an error that we need to + * catch, but more likely, it's just an interrupted poll() call or something, + * and we should try again. */ + if (loop_result < 0) { + int e = tor_socket_errno(-1); + /* let the program survive things like ^z */ + if (e != EINTR && !ERRNO_IS_EINPROGRESS(e)) { + log_err(LD_NET,"libevent call with %s failed: %s [%d]", + tor_libevent_get_method(), tor_socket_strerror(e), e); + return -1; +#ifndef _WIN32 + } else if (e == EINVAL) { + log_fn_ratelim(&libevent_error_ratelim, LOG_WARN, LD_NET, + "EINVAL from libevent: should you upgrade libevent?"); + if (libevent_error_ratelim.n_calls_since_last_time > 8) { + log_err(LD_NET, "Too many libevent errors, too fast: dying"); + return -1; + } +#endif /* !defined(_WIN32) */ + } else { + tor_assert_nonfatal_once(! ERRNO_IS_EINPROGRESS(e)); + log_debug(LD_NET,"libevent call interrupted."); + /* You can't trust the results of this poll(). Go back to the + * top of the big for loop. */ + return 1; + } + } + + if (main_loop_should_exit) + return 0; + + return 1; +} + +/** Run the run_main_loop_once() function until it declares itself done, + * and return its final return value. + * + * Shadow won't invoke this function, so don't fill it up with things. + */ +static int +run_main_loop_until_done(void) +{ + int loop_result = 1; + do { + loop_result = run_main_loop_once(); + } while (loop_result == 1); + + if (main_loop_should_exit) + return main_loop_exit_value; + else + return loop_result; +} + +/** Libevent callback: invoked when we get a signal. + */ +static void +signal_callback(evutil_socket_t fd, short events, void *arg) +{ + const int *sigptr = arg; + const int sig = *sigptr; + (void)fd; + (void)events; + + update_current_time(time(NULL)); + process_signal(sig); +} + +/** Do the work of acting on a signal received in <b>sig</b> */ +static void +process_signal(int sig) +{ + switch (sig) + { + case SIGTERM: + log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly."); + tor_shutdown_event_loop_and_exit(0); + break; + case SIGINT: + if (!server_mode(get_options())) { /* do it now */ + log_notice(LD_GENERAL,"Interrupt: exiting cleanly."); + tor_shutdown_event_loop_and_exit(0); + return; + } +#ifdef HAVE_SYSTEMD + sd_notify(0, "STOPPING=1"); +#endif + hibernate_begin_shutdown(); + break; +#ifdef SIGPIPE + case SIGPIPE: + log_debug(LD_GENERAL,"Caught SIGPIPE. Ignoring."); + break; +#endif + case SIGUSR1: + /* prefer to log it at INFO, but make sure we always see it */ + dumpstats(get_min_log_level()<LOG_INFO ? get_min_log_level() : LOG_INFO); + control_event_signal(sig); + break; + case SIGUSR2: + switch_logs_debug(); + log_debug(LD_GENERAL,"Caught USR2, going to loglevel debug. " + "Send HUP to change back."); + control_event_signal(sig); + break; + case SIGHUP: +#ifdef HAVE_SYSTEMD + sd_notify(0, "RELOADING=1"); +#endif + if (do_hup() < 0) { + log_warn(LD_CONFIG,"Restart failed (config error?). Exiting."); + tor_shutdown_event_loop_and_exit(1); + return; + } +#ifdef HAVE_SYSTEMD + sd_notify(0, "READY=1"); +#endif + control_event_signal(sig); + break; +#ifdef SIGCHLD + case SIGCHLD: + notify_pending_waitpid_callbacks(); + break; +#endif + case SIGNEWNYM: { + time_t now = time(NULL); + if (time_of_last_signewnym + MAX_SIGNEWNYM_RATE > now) { + const time_t delay_sec = + time_of_last_signewnym + MAX_SIGNEWNYM_RATE - now; + if (! signewnym_is_pending) { + signewnym_is_pending = 1; + if (!handle_deferred_signewnym_ev) { + handle_deferred_signewnym_ev = + mainloop_event_postloop_new(handle_deferred_signewnym_cb, NULL); + } + const struct timeval delay_tv = { delay_sec, 0 }; + mainloop_event_schedule(handle_deferred_signewnym_ev, &delay_tv); + } + log_notice(LD_CONTROL, + "Rate limiting NEWNYM request: delaying by %d second(s)", + (int)(delay_sec)); + } else { + signewnym_impl(now); + } + break; + } + case SIGCLEARDNSCACHE: + addressmap_clear_transient(); + control_event_signal(sig); + break; + case SIGHEARTBEAT: + log_heartbeat(time(NULL)); + control_event_signal(sig); + break; + } +} + +/** Returns Tor's uptime. */ +MOCK_IMPL(long, +get_uptime,(void)) +{ + return stats_n_seconds_working; +} + +/** Reset Tor's uptime. */ +MOCK_IMPL(void, +reset_uptime,(void)) +{ + stats_n_seconds_working = 0; +} + +/** + * Write current memory usage information to the log. + */ +static void +dumpmemusage(int severity) +{ + connection_dump_buffer_mem_stats(severity); + tor_log(severity, LD_GENERAL, "In rephist: %"PRIu64" used by %d Tors.", + (rephist_total_alloc), rephist_total_num); + dump_routerlist_mem_usage(severity); + dump_cell_pool_usage(severity); + dump_dns_mem_usage(severity); + tor_log_mallinfo(severity); +} + +/** Write all statistics to the log, with log level <b>severity</b>. Called + * in response to a SIGUSR1. */ +static void +dumpstats(int severity) +{ + time_t now = time(NULL); + time_t elapsed; + size_t rbuf_cap, wbuf_cap, rbuf_len, wbuf_len; + + tor_log(severity, LD_GENERAL, "Dumping stats:"); + + SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) { + int i = conn_sl_idx; + tor_log(severity, LD_GENERAL, + "Conn %d (socket %d) type %d (%s), state %d (%s), created %d secs ago", + i, (int)conn->s, conn->type, conn_type_to_string(conn->type), + conn->state, conn_state_to_string(conn->type, conn->state), + (int)(now - conn->timestamp_created)); + if (!connection_is_listener(conn)) { + tor_log(severity,LD_GENERAL, + "Conn %d is to %s:%d.", i, + safe_str_client(conn->address), + conn->port); + tor_log(severity,LD_GENERAL, + "Conn %d: %d bytes waiting on inbuf (len %d, last read %d secs ago)", + i, + (int)connection_get_inbuf_len(conn), + (int)buf_allocation(conn->inbuf), + (int)(now - conn->timestamp_last_read_allowed)); + tor_log(severity,LD_GENERAL, + "Conn %d: %d bytes waiting on outbuf " + "(len %d, last written %d secs ago)",i, + (int)connection_get_outbuf_len(conn), + (int)buf_allocation(conn->outbuf), + (int)(now - conn->timestamp_last_write_allowed)); + if (conn->type == CONN_TYPE_OR) { + or_connection_t *or_conn = TO_OR_CONN(conn); + if (or_conn->tls) { + if (tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len, + &wbuf_cap, &wbuf_len) == 0) { + tor_log(severity, LD_GENERAL, + "Conn %d: %d/%d bytes used on OpenSSL read buffer; " + "%d/%d bytes used on write buffer.", + i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap); + } + } + } + } + circuit_dump_by_conn(conn, severity); /* dump info about all the circuits + * using this conn */ + } SMARTLIST_FOREACH_END(conn); + + channel_dumpstats(severity); + channel_listener_dumpstats(severity); + + tor_log(severity, LD_NET, + "Cells processed: %"PRIu64" padding\n" + " %"PRIu64" create\n" + " %"PRIu64" created\n" + " %"PRIu64" relay\n" + " (%"PRIu64" relayed)\n" + " (%"PRIu64" delivered)\n" + " %"PRIu64" destroy", + (stats_n_padding_cells_processed), + (stats_n_create_cells_processed), + (stats_n_created_cells_processed), + (stats_n_relay_cells_processed), + (stats_n_relay_cells_relayed), + (stats_n_relay_cells_delivered), + (stats_n_destroy_cells_processed)); + if (stats_n_data_cells_packaged) + tor_log(severity,LD_NET,"Average packaged cell fullness: %2.3f%%", + 100*(((double)stats_n_data_bytes_packaged) / + ((double)stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) ); + if (stats_n_data_cells_received) + tor_log(severity,LD_NET,"Average delivered cell fullness: %2.3f%%", + 100*(((double)stats_n_data_bytes_received) / + ((double)stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) ); + + cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_TAP, "TAP"); + cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_NTOR,"ntor"); + + if (now - time_of_process_start >= 0) + elapsed = now - time_of_process_start; + else + elapsed = 0; + + if (elapsed) { + tor_log(severity, LD_NET, + "Average bandwidth: %"PRIu64"/%d = %d bytes/sec reading", + (stats_n_bytes_read), + (int)elapsed, + (int) (stats_n_bytes_read/elapsed)); + tor_log(severity, LD_NET, + "Average bandwidth: %"PRIu64"/%d = %d bytes/sec writing", + (stats_n_bytes_written), + (int)elapsed, + (int) (stats_n_bytes_written/elapsed)); + } + + tor_log(severity, LD_NET, "--------------- Dumping memory information:"); + dumpmemusage(severity); + + rep_hist_dump_stats(now,severity); + rend_service_dump_stats(severity); + dump_distinct_digest_count(severity); +} + +/** Called by exit() as we shut down the process. + */ +static void +exit_function(void) +{ + /* NOTE: If we ever daemonize, this gets called immediately. That's + * okay for now, because we only use this on Windows. */ +#ifdef _WIN32 + WSACleanup(); +#endif +} + +#ifdef _WIN32 +#define UNIX_ONLY 0 +#else +#define UNIX_ONLY 1 +#endif + +static struct { + /** A numeric code for this signal. Must match the signal value if + * try_to_register is true. */ + int signal_value; + /** True if we should try to register this signal with libevent and catch + * corresponding posix signals. False otherwise. */ + int try_to_register; + /** Pointer to hold the event object constructed for this signal. */ + struct event *signal_event; +} signal_handlers[] = { +#ifdef SIGINT + { SIGINT, UNIX_ONLY, NULL }, /* do a controlled slow shutdown */ +#endif +#ifdef SIGTERM + { SIGTERM, UNIX_ONLY, NULL }, /* to terminate now */ +#endif +#ifdef SIGPIPE + { SIGPIPE, UNIX_ONLY, NULL }, /* otherwise SIGPIPE kills us */ +#endif +#ifdef SIGUSR1 + { SIGUSR1, UNIX_ONLY, NULL }, /* dump stats */ +#endif +#ifdef SIGUSR2 + { SIGUSR2, UNIX_ONLY, NULL }, /* go to loglevel debug */ +#endif +#ifdef SIGHUP + { SIGHUP, UNIX_ONLY, NULL }, /* to reload config, retry conns, etc */ +#endif +#ifdef SIGXFSZ + { SIGXFSZ, UNIX_ONLY, NULL }, /* handle file-too-big resource exhaustion */ +#endif +#ifdef SIGCHLD + { SIGCHLD, UNIX_ONLY, NULL }, /* handle dns/cpu workers that exit */ +#endif + /* These are controller-only */ + { SIGNEWNYM, 0, NULL }, + { SIGCLEARDNSCACHE, 0, NULL }, + { SIGHEARTBEAT, 0, NULL }, + { -1, -1, NULL } +}; + +/** Set up the signal handler events for this process, and register them + * with libevent if appropriate. */ +void +handle_signals(void) +{ + int i; + const int enabled = !get_options()->DisableSignalHandlers; + + for (i = 0; signal_handlers[i].signal_value >= 0; ++i) { + /* Signal handlers are only registered with libevent if they need to catch + * real POSIX signals. We construct these signal handler events in either + * case, though, so that controllers can activate them with the SIGNAL + * command. + */ + if (enabled && signal_handlers[i].try_to_register) { + signal_handlers[i].signal_event = + tor_evsignal_new(tor_libevent_get_base(), + signal_handlers[i].signal_value, + signal_callback, + &signal_handlers[i].signal_value); + if (event_add(signal_handlers[i].signal_event, NULL)) + log_warn(LD_BUG, "Error from libevent when adding " + "event for signal %d", + signal_handlers[i].signal_value); + } else { + signal_handlers[i].signal_event = + tor_event_new(tor_libevent_get_base(), -1, + EV_SIGNAL, signal_callback, + &signal_handlers[i].signal_value); + } + } +} + +/* Cause the signal handler for signal_num to be called in the event loop. */ +void +activate_signal(int signal_num) +{ + int i; + for (i = 0; signal_handlers[i].signal_value >= 0; ++i) { + if (signal_handlers[i].signal_value == signal_num) { + event_active(signal_handlers[i].signal_event, EV_SIGNAL, 1); + return; + } + } +} + +/** Main entry point for the Tor command-line client. Return 0 on "success", + * negative on "failure", and positive on "success and exit". + */ +int +tor_init(int argc, char *argv[]) +{ + char progname[256]; + int quiet = 0; + + time_of_process_start = time(NULL); + init_connection_lists(); + /* Have the log set up with our application name. */ + tor_snprintf(progname, sizeof(progname), "Tor %s", get_version()); + log_set_application_name(progname); + + /* Set up the crypto nice and early */ + if (crypto_early_init() < 0) { + log_err(LD_GENERAL, "Unable to initialize the crypto subsystem!"); + return -1; + } + + /* Initialize the history structures. */ + rep_hist_init(); + /* Initialize the service cache. */ + rend_cache_init(); + addressmap_init(); /* Init the client dns cache. Do it always, since it's + * cheap. */ + /* Initialize the HS subsystem. */ + hs_init(); + + { + /* We search for the "quiet" option first, since it decides whether we + * will log anything at all to the command line. */ + config_line_t *opts = NULL, *cmdline_opts = NULL; + const config_line_t *cl; + (void) config_parse_commandline(argc, argv, 1, &opts, &cmdline_opts); + for (cl = cmdline_opts; cl; cl = cl->next) { + if (!strcmp(cl->key, "--hush")) + quiet = 1; + if (!strcmp(cl->key, "--quiet") || + !strcmp(cl->key, "--dump-config")) + quiet = 2; + /* The following options imply --hush */ + if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") || + !strcmp(cl->key, "--list-torrc-options") || + !strcmp(cl->key, "--library-versions") || + !strcmp(cl->key, "--hash-password") || + !strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) { + if (quiet < 1) + quiet = 1; + } + } + config_free_lines(opts); + config_free_lines(cmdline_opts); + } + + /* give it somewhere to log to initially */ + switch (quiet) { + case 2: + /* no initial logging */ + break; + case 1: + add_temp_log(LOG_WARN); + break; + default: + add_temp_log(LOG_NOTICE); + } + quiet_level = quiet; + + { + const char *version = get_version(); + + log_notice(LD_GENERAL, "Tor %s running on %s with Libevent %s, " + "OpenSSL %s, Zlib %s, Liblzma %s, and Libzstd %s.", version, + get_uname(), + tor_libevent_get_version_str(), + crypto_openssl_get_version_str(), + tor_compress_supports_method(ZLIB_METHOD) ? + tor_compress_version_str(ZLIB_METHOD) : "N/A", + tor_compress_supports_method(LZMA_METHOD) ? + tor_compress_version_str(LZMA_METHOD) : "N/A", + tor_compress_supports_method(ZSTD_METHOD) ? + tor_compress_version_str(ZSTD_METHOD) : "N/A"); + + log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! " + "Learn how to be safe at " + "https://www.torproject.org/download/download#warning"); + + if (strstr(version, "alpha") || strstr(version, "beta")) + log_notice(LD_GENERAL, "This version is not a stable Tor release. " + "Expect more bugs than usual."); + + tor_compress_log_init_warnings(); + } + +#ifdef HAVE_RUST + rust_log_welcome_string(); +#endif /* defined(HAVE_RUST) */ + + if (network_init()<0) { + log_err(LD_BUG,"Error initializing network; exiting."); + return -1; + } + atexit(exit_function); + + int init_rv = options_init_from_torrc(argc,argv); + if (init_rv < 0) { + log_err(LD_CONFIG,"Reading config failed--see warnings above."); + return -1; + } else if (init_rv > 0) { + // We succeeded, and should exit anyway -- probably the user just said + // "--version" or something like that. + return 1; + } + + /* The options are now initialised */ + const or_options_t *options = get_options(); + + /* Initialize channelpadding parameters to defaults until we get + * a consensus */ + channelpadding_new_consensus_params(NULL); + + /* Initialize predicted ports list after loading options */ + predicted_ports_init(); + +#ifndef _WIN32 + if (geteuid()==0) + log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, " + "and you probably shouldn't."); +#endif + + if (crypto_global_init(options->HardwareAccel, + options->AccelName, + options->AccelDir)) { + log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting."); + return -1; + } + stream_choice_seed_weak_rng(); + if (tor_init_libevent_rng() < 0) { + log_warn(LD_NET, "Problem initializing libevent RNG."); + } + + /* Scan/clean unparseable descriptors; after reading config */ + routerparse_init(); + + return 0; +} + +/** A lockfile structure, used to prevent two Tors from messing with the + * data directory at once. If this variable is non-NULL, we're holding + * the lockfile. */ +static tor_lockfile_t *lockfile = NULL; + +/** Try to grab the lock file described in <b>options</b>, if we do not + * already have it. If <b>err_if_locked</b> is true, warn if somebody else is + * holding the lock, and exit if we can't get it after waiting. Otherwise, + * return -1 if we can't get the lockfile. Return 0 on success. + */ +int +try_locking(const or_options_t *options, int err_if_locked) +{ + if (lockfile) + return 0; + else { + char *fname = options_get_datadir_fname(options, "lock"); + int already_locked = 0; + tor_lockfile_t *lf = tor_lockfile_lock(fname, 0, &already_locked); + tor_free(fname); + if (!lf) { + if (err_if_locked && already_locked) { + int r; + log_warn(LD_GENERAL, "It looks like another Tor process is running " + "with the same data directory. Waiting 5 seconds to see " + "if it goes away."); +#ifndef _WIN32 + sleep(5); +#else + Sleep(5000); +#endif + r = try_locking(options, 0); + if (r<0) { + log_err(LD_GENERAL, "No, it's still there. Exiting."); + return -1; + } + return r; + } + return -1; + } + lockfile = lf; + return 0; + } +} + +/** Return true iff we've successfully acquired the lock file. */ +int +have_lockfile(void) +{ + return lockfile != NULL; +} + +/** If we have successfully acquired the lock file, release it. */ +void +release_lockfile(void) +{ + if (lockfile) { + tor_lockfile_unlock(lockfile); + lockfile = NULL; + } +} + +/** Free all memory that we might have allocated somewhere. + * If <b>postfork</b>, we are a worker process and we want to free + * only the parts of memory that we won't touch. If !<b>postfork</b>, + * Tor is shutting down and we should free everything. + * + * Helps us find the real leaks with sanitizers and the like. Also valgrind + * should then report 0 reachable in its leak report (in an ideal world -- + * in practice libevent, SSL, libc etc never quite free everything). */ +void +tor_free_all(int postfork) +{ + if (!postfork) { + evdns_shutdown(1); + } + geoip_free_all(); + dirvote_free_all(); + routerlist_free_all(); + networkstatus_free_all(); + addressmap_free_all(); + dirserv_free_all(); + rend_cache_free_all(); + rend_service_authorization_free_all(); + rep_hist_free_all(); + dns_free_all(); + clear_pending_onions(); + circuit_free_all(); + entry_guards_free_all(); + pt_free_all(); + channel_tls_free_all(); + channel_free_all(); + connection_free_all(); + connection_edge_free_all(); + scheduler_free_all(); + nodelist_free_all(); + microdesc_free_all(); + routerparse_free_all(); + ext_orport_free_all(); + control_free_all(); + sandbox_free_getaddrinfo_cache(); + protover_free_all(); + bridges_free_all(); + consdiffmgr_free_all(); + hs_free_all(); + dos_free_all(); + circuitmux_ewma_free_all(); + accounting_free_all(); + + if (!postfork) { + config_free_all(); + or_state_free_all(); + router_free_all(); + routerkeys_free_all(); + policies_free_all(); + } + if (!postfork) { + tor_tls_free_all(); +#ifndef _WIN32 + tor_getpwnam(NULL); +#endif + } + /* stuff in main.c */ + + smartlist_free(connection_array); + smartlist_free(closeable_connection_lst); + smartlist_free(active_linked_connection_lst); + periodic_timer_free(second_timer); + teardown_periodic_events(); + tor_event_free(shutdown_did_not_work_event); + tor_event_free(initialize_periodic_events_event); + mainloop_event_free(directory_all_unreachable_cb_event); + mainloop_event_free(schedule_active_linked_connections_event); + mainloop_event_free(postloop_cleanup_ev); + mainloop_event_free(handle_deferred_signewnym_ev); + +#ifdef HAVE_SYSTEMD_209 + periodic_timer_free(systemd_watchdog_timer); +#endif + + memset(&global_bucket, 0, sizeof(global_bucket)); + memset(&global_relayed_bucket, 0, sizeof(global_relayed_bucket)); + stats_n_bytes_read = stats_n_bytes_written = 0; + time_of_process_start = 0; + time_of_last_signewnym = 0; + signewnym_is_pending = 0; + newnym_epoch = 0; + called_loop_once = 0; + main_loop_should_exit = 0; + main_loop_exit_value = 0; + can_complete_circuits = 0; + quiet_level = 0; + should_init_bridge_stats = 1; + dns_honesty_first_time = 1; + heartbeat_callback_first_time = 1; + current_second = 0; + memset(¤t_second_last_changed, 0, + sizeof(current_second_last_changed)); + + if (!postfork) { + release_lockfile(); + } + tor_libevent_free_all(); + /* Stuff in util.c and address.c*/ + if (!postfork) { + escaped(NULL); + esc_router_info(NULL); + clean_up_backtrace_handler(); + logs_free_all(); /* free log strings. do this last so logs keep working. */ + } +} + +/** + * Remove the specified file, and log a warning if the operation fails for + * any reason other than the file not existing. Ignores NULL filenames. + */ +void +tor_remove_file(const char *filename) +{ + if (filename && tor_unlink(filename) != 0 && errno != ENOENT) { + log_warn(LD_FS, "Couldn't unlink %s: %s", + filename, strerror(errno)); + } +} + +/** Do whatever cleanup is necessary before shutting Tor down. */ +void +tor_cleanup(void) +{ + const or_options_t *options = get_options(); + if (options->command == CMD_RUN_TOR) { + time_t now = time(NULL); + /* Remove our pid file. We don't care if there was an error when we + * unlink, nothing we could do about it anyways. */ + tor_remove_file(options->PidFile); + /* Remove control port file */ + tor_remove_file(options->ControlPortWriteToFile); + /* Remove cookie authentication file */ + { + char *cookie_fname = get_controller_cookie_file_name(); + tor_remove_file(cookie_fname); + tor_free(cookie_fname); + } + /* Remove Extended ORPort cookie authentication file */ + { + char *cookie_fname = get_ext_or_auth_cookie_file_name(); + tor_remove_file(cookie_fname); + tor_free(cookie_fname); + } + if (accounting_is_enabled(options)) + accounting_record_bandwidth_usage(now, get_or_state()); + or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */ + or_state_save(now); + if (authdir_mode(options)) { + sr_save_and_cleanup(); + } + if (authdir_mode_tests_reachability(options)) + rep_hist_record_mtbf_data(now, 0); + keypin_close_journal(); + } + + timers_shutdown(); + + tor_free_all(0); /* We could move tor_free_all back into the ifdef below + later, if it makes shutdown unacceptably slow. But for + now, leave it here: it's helped us catch bugs in the + past. */ + crypto_global_cleanup(); +} + +/** Read/create keys as needed, and echo our fingerprint to stdout. */ +static int +do_list_fingerprint(void) +{ + char buf[FINGERPRINT_LEN+1]; + crypto_pk_t *k; + const char *nickname = get_options()->Nickname; + sandbox_disable_getaddrinfo_cache(); + if (!server_mode(get_options())) { + log_err(LD_GENERAL, + "Clients don't have long-term identity keys. Exiting."); + return -1; + } + tor_assert(nickname); + if (init_keys() < 0) { + log_err(LD_GENERAL,"Error initializing keys; exiting."); + return -1; + } + if (!(k = get_server_identity_key())) { + log_err(LD_GENERAL,"Error: missing identity key."); + return -1; + } + if (crypto_pk_get_fingerprint(k, buf, 1)<0) { + log_err(LD_BUG, "Error computing fingerprint"); + return -1; + } + printf("%s %s\n", nickname, buf); + return 0; +} + +/** Entry point for password hashing: take the desired password from + * the command line, and print its salted hash to stdout. **/ +static void +do_hash_password(void) +{ + + char output[256]; + char key[S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN]; + + crypto_rand(key, S2K_RFC2440_SPECIFIER_LEN-1); + key[S2K_RFC2440_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */ + secret_to_key_rfc2440(key+S2K_RFC2440_SPECIFIER_LEN, DIGEST_LEN, + get_options()->command_arg, strlen(get_options()->command_arg), + key); + base16_encode(output, sizeof(output), key, sizeof(key)); + printf("16:%s\n",output); +} + +/** Entry point for configuration dumping: write the configuration to + * stdout. */ +static int +do_dump_config(void) +{ + const or_options_t *options = get_options(); + const char *arg = options->command_arg; + int how; + char *opts; + + if (!strcmp(arg, "short")) { + how = OPTIONS_DUMP_MINIMAL; + } else if (!strcmp(arg, "non-builtin")) { + how = OPTIONS_DUMP_DEFAULTS; + } else if (!strcmp(arg, "full")) { + how = OPTIONS_DUMP_ALL; + } else { + fprintf(stderr, "No valid argument to --dump-config found!\n"); + fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n"); + + return -1; + } + + opts = options_dump(options, how); + printf("%s", opts); + tor_free(opts); + + return 0; +} + +static void +init_addrinfo(void) +{ + if (! server_mode(get_options()) || + (get_options()->Address && strlen(get_options()->Address) > 0)) { + /* We don't need to seed our own hostname, because we won't be calling + * resolve_my_address on it. + */ + return; + } + char hname[256]; + + // host name to sandbox + gethostname(hname, sizeof(hname)); + sandbox_add_addrinfo(hname); +} + +static sandbox_cfg_t* +sandbox_init_filter(void) +{ + const or_options_t *options = get_options(); + sandbox_cfg_t *cfg = sandbox_cfg_new(); + int i; + + sandbox_cfg_allow_openat_filename(&cfg, + get_cachedir_fname("cached-status")); + +#define OPEN(name) \ + sandbox_cfg_allow_open_filename(&cfg, tor_strdup(name)) + +#define OPEN_DATADIR(name) \ + sandbox_cfg_allow_open_filename(&cfg, get_datadir_fname(name)) + +#define OPEN_DATADIR2(name, name2) \ + sandbox_cfg_allow_open_filename(&cfg, get_datadir_fname2((name), (name2))) + +#define OPEN_DATADIR_SUFFIX(name, suffix) do { \ + OPEN_DATADIR(name); \ + OPEN_DATADIR(name suffix); \ + } while (0) + +#define OPEN_DATADIR2_SUFFIX(name, name2, suffix) do { \ + OPEN_DATADIR2(name, name2); \ + OPEN_DATADIR2(name, name2 suffix); \ + } while (0) + +#define OPEN_KEY_DIRECTORY() \ + sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->KeyDirectory)) +#define OPEN_CACHEDIR(name) \ + sandbox_cfg_allow_open_filename(&cfg, get_cachedir_fname(name)) +#define OPEN_CACHEDIR_SUFFIX(name, suffix) do { \ + OPEN_CACHEDIR(name); \ + OPEN_CACHEDIR(name suffix); \ + } while (0) +#define OPEN_KEYDIR(name) \ + sandbox_cfg_allow_open_filename(&cfg, get_keydir_fname(name)) +#define OPEN_KEYDIR_SUFFIX(name, suffix) do { \ + OPEN_KEYDIR(name); \ + OPEN_KEYDIR(name suffix); \ + } while (0) + + OPEN(options->DataDirectory); + OPEN_KEY_DIRECTORY(); + + OPEN_CACHEDIR_SUFFIX("cached-certs", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("unverified-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdescs", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-descriptors", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp"); + OPEN_CACHEDIR("cached-descriptors.tmp.tmp"); + OPEN_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp"); + OPEN_CACHEDIR("cached-extrainfo.tmp.tmp"); + + OPEN_DATADIR_SUFFIX("state", ".tmp"); + OPEN_DATADIR_SUFFIX("sr-state", ".tmp"); + OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp"); + OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp"); + OPEN_DATADIR("key-pinning-journal"); + OPEN("/dev/srandom"); + OPEN("/dev/urandom"); + OPEN("/dev/random"); + OPEN("/etc/hosts"); + OPEN("/proc/meminfo"); + + if (options->BridgeAuthoritativeDir) + OPEN_DATADIR_SUFFIX("networkstatus-bridges", ".tmp"); + + if (authdir_mode(options)) + OPEN_DATADIR("approved-routers"); + + if (options->ServerDNSResolvConfFile) + sandbox_cfg_allow_open_filename(&cfg, + tor_strdup(options->ServerDNSResolvConfFile)); + else + sandbox_cfg_allow_open_filename(&cfg, tor_strdup("/etc/resolv.conf")); + + for (i = 0; i < 2; ++i) { + if (get_torrc_fname(i)) { + sandbox_cfg_allow_open_filename(&cfg, tor_strdup(get_torrc_fname(i))); + } + } + + SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, { + OPEN(f); + }); + +#define RENAME_SUFFIX(name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_datadir_fname(name suffix), \ + get_datadir_fname(name)) + +#define RENAME_SUFFIX2(prefix, name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_datadir_fname2(prefix, name suffix), \ + get_datadir_fname2(prefix, name)) + +#define RENAME_CACHEDIR_SUFFIX(name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_cachedir_fname(name suffix), \ + get_cachedir_fname(name)) + +#define RENAME_KEYDIR_SUFFIX(name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_keydir_fname(name suffix), \ + get_keydir_fname(name)) + + RENAME_CACHEDIR_SUFFIX("cached-certs", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("unverified-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp"); + + RENAME_SUFFIX("state", ".tmp"); + RENAME_SUFFIX("sr-state", ".tmp"); + RENAME_SUFFIX("unparseable-desc", ".tmp"); + RENAME_SUFFIX("v3-status-votes", ".tmp"); + + if (options->BridgeAuthoritativeDir) + RENAME_SUFFIX("networkstatus-bridges", ".tmp"); + +#define STAT_DATADIR(name) \ + sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name)) + +#define STAT_CACHEDIR(name) \ + sandbox_cfg_allow_stat_filename(&cfg, get_cachedir_fname(name)) + +#define STAT_DATADIR2(name, name2) \ + sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname2((name), (name2))) + +#define STAT_KEY_DIRECTORY() \ + sandbox_cfg_allow_stat_filename(&cfg, tor_strdup(options->KeyDirectory)) + + STAT_DATADIR(NULL); + STAT_DATADIR("lock"); + STAT_DATADIR("state"); + STAT_DATADIR("router-stability"); + + STAT_CACHEDIR("cached-extrainfo.new"); + + { + smartlist_t *files = smartlist_new(); + tor_log_get_logfile_names(files); + SMARTLIST_FOREACH(files, char *, file_name, { + /* steals reference */ + sandbox_cfg_allow_open_filename(&cfg, file_name); + }); + smartlist_free(files); + } + + { + smartlist_t *files = smartlist_new(); + smartlist_t *dirs = smartlist_new(); + hs_service_lists_fnames_for_sandbox(files, dirs); + SMARTLIST_FOREACH(files, char *, file_name, { + char *tmp_name = NULL; + tor_asprintf(&tmp_name, "%s.tmp", file_name); + sandbox_cfg_allow_rename(&cfg, + tor_strdup(tmp_name), tor_strdup(file_name)); + /* steals references */ + sandbox_cfg_allow_open_filename(&cfg, file_name); + sandbox_cfg_allow_open_filename(&cfg, tmp_name); + }); + SMARTLIST_FOREACH(dirs, char *, dir, { + /* steals reference */ + sandbox_cfg_allow_stat_filename(&cfg, dir); + }); + smartlist_free(files); + smartlist_free(dirs); + } + + { + char *fname; + if ((fname = get_controller_cookie_file_name())) { + sandbox_cfg_allow_open_filename(&cfg, fname); + } + if ((fname = get_ext_or_auth_cookie_file_name())) { + sandbox_cfg_allow_open_filename(&cfg, fname); + } + } + + SMARTLIST_FOREACH_BEGIN(get_configured_ports(), port_cfg_t *, port) { + if (!port->is_unix_addr) + continue; + /* When we open an AF_UNIX address, we want permission to open the + * directory that holds it. */ + char *dirname = tor_strdup(port->unix_addr); + if (get_parent_directory(dirname) == 0) { + OPEN(dirname); + } + tor_free(dirname); + sandbox_cfg_allow_chmod_filename(&cfg, tor_strdup(port->unix_addr)); + sandbox_cfg_allow_chown_filename(&cfg, tor_strdup(port->unix_addr)); + } SMARTLIST_FOREACH_END(port); + + if (options->DirPortFrontPage) { + sandbox_cfg_allow_open_filename(&cfg, + tor_strdup(options->DirPortFrontPage)); + } + + // orport + if (server_mode(get_options())) { + + OPEN_KEYDIR_SUFFIX("secret_id_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_onion_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); + OPEN_KEYDIR("secret_id_key.old"); + OPEN_KEYDIR("secret_onion_key.old"); + OPEN_KEYDIR("secret_onion_key_ntor.old"); + + OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key_encrypted", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_public_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp"); + + OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp"); + OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp"); + + OPEN_DATADIR2_SUFFIX("stats", "entry-stats", ".tmp"); + OPEN_DATADIR2_SUFFIX("stats", "exit-stats", ".tmp"); + OPEN_DATADIR2_SUFFIX("stats", "buffer-stats", ".tmp"); + OPEN_DATADIR2_SUFFIX("stats", "conn-stats", ".tmp"); + OPEN_DATADIR2_SUFFIX("stats", "hidserv-stats", ".tmp"); + + OPEN_DATADIR("approved-routers"); + OPEN_DATADIR_SUFFIX("fingerprint", ".tmp"); + OPEN_DATADIR_SUFFIX("hashed-fingerprint", ".tmp"); + OPEN_DATADIR_SUFFIX("router-stability", ".tmp"); + + OPEN("/etc/resolv.conf"); + + RENAME_SUFFIX("fingerprint", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); + + RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_id_key.old", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key.old", ".tmp"); + + RENAME_SUFFIX2("stats", "bridge-stats", ".tmp"); + RENAME_SUFFIX2("stats", "dirreq-stats", ".tmp"); + RENAME_SUFFIX2("stats", "entry-stats", ".tmp"); + RENAME_SUFFIX2("stats", "exit-stats", ".tmp"); + RENAME_SUFFIX2("stats", "buffer-stats", ".tmp"); + RENAME_SUFFIX2("stats", "conn-stats", ".tmp"); + RENAME_SUFFIX2("stats", "hidserv-stats", ".tmp"); + RENAME_SUFFIX("hashed-fingerprint", ".tmp"); + RENAME_SUFFIX("router-stability", ".tmp"); + + RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp"); + + sandbox_cfg_allow_rename(&cfg, + get_keydir_fname("secret_onion_key"), + get_keydir_fname("secret_onion_key.old")); + sandbox_cfg_allow_rename(&cfg, + get_keydir_fname("secret_onion_key_ntor"), + get_keydir_fname("secret_onion_key_ntor.old")); + + STAT_KEY_DIRECTORY(); + OPEN_DATADIR("stats"); + STAT_DATADIR("stats"); + STAT_DATADIR2("stats", "dirreq-stats"); + + consdiffmgr_register_with_sandbox(&cfg); + } + + init_addrinfo(); + + return cfg; +} + +/* Main entry point for the Tor process. Called from tor_main(), and by + * anybody embedding Tor. */ +int +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 +#endif + /* On heap corruption, just give up; don't try to play along. */ + HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + /* Call SetProcessDEPPolicy to permanently enable DEP. + The function will not resolve on earlier versions of Windows, + and failure is not dangerous. */ + HMODULE hMod = GetModuleHandleA("Kernel32.dll"); + if (hMod) { + typedef BOOL (WINAPI *PSETDEP)(DWORD); + PSETDEP setdeppolicy = (PSETDEP)GetProcAddress(hMod, + "SetProcessDEPPolicy"); + if (setdeppolicy) { + /* PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION */ + setdeppolicy(3); + } + } +#endif /* defined(_WIN32) */ + + { + int bt_err = configure_backtrace_handler(get_version()); + if (bt_err < 0) { + log_warn(LD_BUG, "Unable to install backtrace handler: %s", + strerror(-bt_err)); + } + } + init_protocol_warning_severity_level(); + + update_approx_time(time(NULL)); + tor_threads_init(); + tor_compress_init(); + init_logging(0); + monotime_init(); +#ifdef NT_SERVICE + { + int done = 0; + result = nt_service_parse_options(argc, argv, &done); + if (done) return result; + } +#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 (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) { + sandbox_cfg_t* cfg = sandbox_init_filter(); + + if (sandbox_init(cfg)) { + log_err(LD_BUG,"Failed to create syscall sandbox filter"); + return -1; + } + + // registering libevent rng +#ifdef HAVE_EVUTIL_SECURE_RNG_SET_URANDOM_DEVICE_FILE + evutil_secure_rng_set_urandom_device_file( + (char*) sandbox_intern_string("/dev/urandom")); +#endif + } + + switch (get_options()->command) { + case CMD_RUN_TOR: +#ifdef NT_SERVICE + nt_service_set_state(SERVICE_RUNNING); +#endif + result = do_main_loop(); + break; + case CMD_KEYGEN: + result = load_ed_keys(get_options(), time(NULL)) < 0; + break; + case CMD_KEY_EXPIRATION: + init_keys(); + result = log_cert_expiration(); + break; + case CMD_LIST_FINGERPRINT: + result = do_list_fingerprint(); + break; + case CMD_HASH_PASSWORD: + do_hash_password(); + result = 0; + break; + case CMD_VERIFY_CONFIG: + if (quiet_level == 0) + printf("Configuration was valid\n"); + result = 0; + break; + case CMD_DUMP_CONFIG: + result = do_dump_config(); + break; + case CMD_RUN_UNITTESTS: /* only set by test.c */ + default: + log_warn(LD_BUG,"Illegal command number %d: internal error.", + get_options()->command); + result = -1; + } + tor_cleanup(); + return result; +} diff --git a/src/core/mainloop/main.h b/src/core/mainloop/main.h new file mode 100644 index 0000000000..90146f4bd4 --- /dev/null +++ b/src/core/mainloop/main.h @@ -0,0 +1,122 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file main.h + * \brief Header file for main.c. + **/ + +#ifndef TOR_MAIN_H +#define TOR_MAIN_H + +int have_completed_a_circuit(void); +void note_that_we_completed_a_circuit(void); +void note_that_we_maybe_cant_complete_circuits(void); + +int connection_add_impl(connection_t *conn, int is_connecting); +#define connection_add(conn) connection_add_impl((conn), 0) +#define connection_add_connecting(conn) connection_add_impl((conn), 1) +int connection_remove(connection_t *conn); +void connection_unregister_events(connection_t *conn); +int connection_in_array(connection_t *conn); +void add_connection_to_closeable_list(connection_t *conn); +int connection_is_on_closeable_list(connection_t *conn); + +MOCK_DECL(smartlist_t *, get_connection_array, (void)); +MOCK_DECL(uint64_t,get_bytes_read,(void)); +MOCK_DECL(uint64_t,get_bytes_written,(void)); +void stats_increment_bytes_read_and_written(uint64_t r, uint64_t w); + +/** Bitmask for events that we can turn on and off with + * connection_watch_events. */ +typedef enum watchable_events { + /* Yes, it is intentional that these match Libevent's EV_READ and EV_WRITE */ + READ_EVENT=0x02, /**< We want to know when a connection is readable */ + WRITE_EVENT=0x04 /**< We want to know when a connection is writable */ +} watchable_events_t; +void connection_watch_events(connection_t *conn, watchable_events_t events); +int connection_is_reading(connection_t *conn); +MOCK_DECL(void,connection_stop_reading,(connection_t *conn)); +MOCK_DECL(void,connection_start_reading,(connection_t *conn)); + +int connection_is_writing(connection_t *conn); +MOCK_DECL(void,connection_stop_writing,(connection_t *conn)); +MOCK_DECL(void,connection_start_writing,(connection_t *conn)); + +void tor_shutdown_event_loop_and_exit(int exitcode); +int tor_event_loop_shutdown_is_pending(void); + +void connection_stop_reading_from_linked_conn(connection_t *conn); + +MOCK_DECL(int, connection_count_moribund, (void)); + +void directory_all_unreachable(time_t now); +void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs); + +void ip_address_changed(int at_interface); +void dns_servers_relaunch_checks(void); +void reset_all_main_loop_timers(void); +void reschedule_descriptor_update_check(void); +void reschedule_directory_downloads(void); +void reschedule_or_state_save(void); +void reschedule_dirvote(const or_options_t *options); +void mainloop_schedule_postloop_cleanup(void); +void rescan_periodic_events(const or_options_t *options); + +void update_current_time(time_t now); + +MOCK_DECL(long,get_uptime,(void)); +MOCK_DECL(void,reset_uptime,(void)); + +unsigned get_signewnym_epoch(void); + +void handle_signals(void); +void activate_signal(int signal_num); + +int try_locking(const or_options_t *options, int err_if_locked); +int have_lockfile(void); +void release_lockfile(void); + +void tor_remove_file(const char *filename); + +void tor_cleanup(void); +void tor_free_all(int postfork); + +int do_main_loop(void); +int tor_init(int argc, char **argv); + +void reset_main_loop_counters(void); +uint64_t get_main_loop_success_count(void); +uint64_t get_main_loop_error_count(void); +uint64_t get_main_loop_idle_count(void); + +void periodic_events_on_new_options(const or_options_t *options); +void reschedule_per_second_timer(void); + +struct token_bucket_rw_t; + +extern time_t time_of_process_start; +extern int quiet_level; +extern struct token_bucket_rw_t global_bucket; +extern struct token_bucket_rw_t global_relayed_bucket; + +#ifdef MAIN_PRIVATE +STATIC void init_connection_lists(void); +STATIC void initialize_mainloop_events(void); +STATIC void close_closeable_connections(void); +STATIC void initialize_periodic_events(void); +STATIC void teardown_periodic_events(void); +STATIC int get_my_roles(const or_options_t *options); +#ifdef TOR_UNIT_TESTS +extern smartlist_t *connection_array; + +/* We need the periodic_event_item_t definition. */ +#include "or/periodic.h" +extern periodic_event_item_t periodic_events[]; +#endif +#endif /* defined(MAIN_PRIVATE) */ + +#endif /* !defined(TOR_MAIN_H) */ diff --git a/src/core/mainloop/periodic.c b/src/core/mainloop/periodic.c new file mode 100644 index 0000000000..041b2d287b --- /dev/null +++ b/src/core/mainloop/periodic.c @@ -0,0 +1,172 @@ +/* Copyright (c) 2015-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file periodic.c + * + * \brief Generic backend for handling periodic events. + * + * The events in this module are used by main.c to track items that need + * to fire once every N seconds, possibly picking a new interval each time + * that they fire. See periodic_events[] in main.c for examples. + */ + +#include "or/or.h" +#include "lib/evloop/compat_libevent.h" +#include "or/config.h" +#include "or/main.h" +#include "or/periodic.h" +#include "lib/evloop/compat_libevent.h" + +/** We disable any interval greater than this number of seconds, on the + * grounds that it is probably an absolute time mistakenly passed in as a + * relative time. + */ +static const int MAX_INTERVAL = 10 * 365 * 86400; + +/** Set the event <b>event</b> to run in <b>next_interval</b> seconds from + * now. */ +static void +periodic_event_set_interval(periodic_event_item_t *event, + time_t next_interval) +{ + tor_assert(next_interval < MAX_INTERVAL); + struct timeval tv; + tv.tv_sec = next_interval; + tv.tv_usec = 0; + mainloop_event_schedule(event->ev, &tv); +} + +/** Wraps dispatches for periodic events, <b>data</b> will be a pointer to the + * event that needs to be called */ +static void +periodic_event_dispatch(mainloop_event_t *ev, void *data) +{ + periodic_event_item_t *event = data; + tor_assert(ev == event->ev); + + if (BUG(!periodic_event_is_enabled(event))) { + return; + } + + time_t now = time(NULL); + update_current_time(now); + const or_options_t *options = get_options(); +// log_debug(LD_GENERAL, "Dispatching %s", event->name); + int r = event->fn(now, options); + int next_interval = 0; + + if (!periodic_event_is_enabled(event)) { + /* The event got disabled from inside its callback; no need to + * reschedule. */ + return; + } + + /* update the last run time if action was taken */ + if (r==0) { + log_err(LD_BUG, "Invalid return value for periodic event from %s.", + event->name); + tor_assert(r != 0); + } else if (r > 0) { + event->last_action_time = now; + /* If the event is meant to happen after ten years, that's likely + * a bug, and somebody gave an absolute time rather than an interval. + */ + tor_assert(r < MAX_INTERVAL); + next_interval = r; + } else { + /* no action was taken, it is likely a precondition failed, + * we should reschedule for next second incase the precondition + * passes then */ + next_interval = 1; + } + +// log_debug(LD_GENERAL, "Scheduling %s for %d seconds", event->name, +// next_interval); + struct timeval tv = { next_interval , 0 }; + mainloop_event_schedule(ev, &tv); +} + +/** Schedules <b>event</b> to run as soon as possible from now. */ +void +periodic_event_reschedule(periodic_event_item_t *event) +{ + /* Don't reschedule a disabled event. */ + if (periodic_event_is_enabled(event)) { + periodic_event_set_interval(event, 1); + } +} + +/** Initializes the libevent backend for a periodic event. */ +void +periodic_event_setup(periodic_event_item_t *event) +{ + if (event->ev) { /* Already setup? This is a bug */ + log_err(LD_BUG, "Initial dispatch should only be done once."); + tor_assert(0); + } + + event->ev = mainloop_event_new(periodic_event_dispatch, + event); + tor_assert(event->ev); +} + +/** Handles initial dispatch for periodic events. It should happen 1 second + * after the events are created to mimic behaviour before #3199's refactor */ +void +periodic_event_launch(periodic_event_item_t *event) +{ + if (! event->ev) { /* Not setup? This is a bug */ + log_err(LD_BUG, "periodic_event_launch without periodic_event_setup"); + tor_assert(0); + } + /* Event already enabled? This is a bug */ + if (periodic_event_is_enabled(event)) { + log_err(LD_BUG, "periodic_event_launch on an already enabled event"); + tor_assert(0); + } + + // Initial dispatch + event->enabled = 1; + periodic_event_dispatch(event->ev, event); +} + +/** Release all storage associated with <b>event</b> */ +void +periodic_event_destroy(periodic_event_item_t *event) +{ + if (!event) + return; + mainloop_event_free(event->ev); + event->last_action_time = 0; +} + +/** Enable the given event which means the event is launched and then the + * event's enabled flag is set. This can be called for an event that is + * already enabled. */ +void +periodic_event_enable(periodic_event_item_t *event) +{ + tor_assert(event); + /* Safely and silently ignore if this event is already enabled. */ + if (periodic_event_is_enabled(event)) { + return; + } + + periodic_event_launch(event); +} + +/** Disable the given event which means the event is destroyed and then the + * event's enabled flag is unset. This can be called for an event that is + * already disabled. */ +void +periodic_event_disable(periodic_event_item_t *event) +{ + tor_assert(event); + /* Safely and silently ignore if this event is already disabled. */ + if (!periodic_event_is_enabled(event)) { + return; + } + mainloop_event_cancel(event->ev); + event->enabled = 0; +} diff --git a/src/core/mainloop/periodic.h b/src/core/mainloop/periodic.h new file mode 100644 index 0000000000..4c8c3c96cc --- /dev/null +++ b/src/core/mainloop/periodic.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2015-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_PERIODIC_H +#define TOR_PERIODIC_H + +#define PERIODIC_EVENT_NO_UPDATE (-1) + +/* Tor roles for which a periodic event item is for. An event can be for + * multiple roles, they can be combined. */ +#define PERIODIC_EVENT_ROLE_CLIENT (1U << 0) +#define PERIODIC_EVENT_ROLE_RELAY (1U << 1) +#define PERIODIC_EVENT_ROLE_BRIDGE (1U << 2) +#define PERIODIC_EVENT_ROLE_DIRAUTH (1U << 3) +#define PERIODIC_EVENT_ROLE_BRIDGEAUTH (1U << 4) +#define PERIODIC_EVENT_ROLE_HS_SERVICE (1U << 5) +#define PERIODIC_EVENT_ROLE_DIRSERVER (1U << 6) + +/* Helper macro to make it a bit less annoying to defined groups of roles that + * are often used. */ + +/* Router that is a Bridge or Relay. */ +#define PERIODIC_EVENT_ROLE_ROUTER \ + (PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_RELAY) +/* Authorities that is both bridge and directory. */ +#define PERIODIC_EVENT_ROLE_AUTHORITIES \ + (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_DIRAUTH) +/* All roles. */ +#define PERIODIC_EVENT_ROLE_ALL \ + (PERIODIC_EVENT_ROLE_AUTHORITIES | PERIODIC_EVENT_ROLE_CLIENT | \ + PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_ROUTER) + +/* + * Event flags which can change the behavior of an event. + */ + +/* Indicate that the event needs the network meaning that if we are in + * DisableNetwork or hibernation mode, the event won't be enabled. This obey + * the net_is_disabled() check. */ +#define PERIODIC_EVENT_FLAG_NEED_NET (1U << 0) + +/** Callback function for a periodic event to take action. The return value +* influences the next time the function will get called. Return +* PERIODIC_EVENT_NO_UPDATE to not update <b>last_action_time</b> and be polled +* again in the next second. If a positive value is returned it will update the +* interval time. */ +typedef int (*periodic_event_helper_t)(time_t now, + const or_options_t *options); + +struct mainloop_event_t; + +/** A single item for the periodic-events-function table. */ +typedef struct periodic_event_item_t { + periodic_event_helper_t fn; /**< The function to run the event */ + time_t last_action_time; /**< The last time the function did something */ + struct mainloop_event_t *ev; /**< Libevent callback we're using to implement + * this */ + const char *name; /**< Name of the function -- for debug */ + + /* Bitmask of roles define above for which this event applies. */ + uint32_t roles; + /* Bitmask of flags which can change the behavior of the event. */ + uint32_t flags; + /* Indicate that this event has been enabled that is scheduled. */ + unsigned int enabled : 1; +} periodic_event_item_t; + +/** events will get their interval from first execution */ +#define PERIODIC_EVENT(fn, r, f) { fn##_callback, 0, NULL, #fn, r, f, 0 } +#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL, 0, 0, 0 } + +/* Return true iff the given event was setup before thus is enabled to be + * scheduled. */ +static inline int +periodic_event_is_enabled(const periodic_event_item_t *item) +{ + return item->enabled; +} + +void periodic_event_launch(periodic_event_item_t *event); +void periodic_event_setup(periodic_event_item_t *event); +void periodic_event_destroy(periodic_event_item_t *event); +void periodic_event_reschedule(periodic_event_item_t *event); +void periodic_event_enable(periodic_event_item_t *event); +void periodic_event_disable(periodic_event_item_t *event); + +#endif /* !defined(TOR_PERIODIC_H) */ + |