diff options
author | cypherpunks <cypherpunks@torproject.org> | 2020-03-24 05:51:12 +0000 |
---|---|---|
committer | cypherpunks <cypherpunks@torproject.org> | 2020-03-24 05:51:12 +0000 |
commit | d3ded1cc1c4a1bcd158c1705c4c26425ed538be9 (patch) | |
tree | 28949e1980b8090ce0e90218921beffd46c877a5 /src/core/mainloop | |
parent | 84fe1c891bc77a2363a119f3c7dc834127bcacc7 (diff) | |
parent | 29420ab396cffb4b3c062b76fc859cc01f2206b7 (diff) | |
download | tor-d3ded1cc1c4a1bcd158c1705c4c26425ed538be9.tar.gz tor-d3ded1cc1c4a1bcd158c1705c4c26425ed538be9.zip |
Merge branch 'maint-0.4.3'
Diffstat (limited to 'src/core/mainloop')
-rw-r--r-- | src/core/mainloop/.may_include | 24 | ||||
-rw-r--r-- | src/core/mainloop/connection.c | 517 | ||||
-rw-r--r-- | src/core/mainloop/connection.h | 205 | ||||
-rw-r--r-- | src/core/mainloop/core_mainloop.md | 10 | ||||
-rw-r--r-- | src/core/mainloop/cpuworker.c | 13 | ||||
-rw-r--r-- | src/core/mainloop/cpuworker.h | 6 | ||||
-rw-r--r-- | src/core/mainloop/include.am | 22 | ||||
-rw-r--r-- | src/core/mainloop/mainloop.c | 874 | ||||
-rw-r--r-- | src/core/mainloop/mainloop.h | 25 | ||||
-rw-r--r-- | src/core/mainloop/mainloop_pubsub.c | 179 | ||||
-rw-r--r-- | src/core/mainloop/mainloop_pubsub.h | 61 | ||||
-rw-r--r-- | src/core/mainloop/mainloop_state.inc | 19 | ||||
-rw-r--r-- | src/core/mainloop/mainloop_state_st.h | 23 | ||||
-rw-r--r-- | src/core/mainloop/mainloop_sys.c | 89 | ||||
-rw-r--r-- | src/core/mainloop/mainloop_sys.h | 17 | ||||
-rw-r--r-- | src/core/mainloop/netstatus.c | 144 | ||||
-rw-r--r-- | src/core/mainloop/netstatus.h | 23 | ||||
-rw-r--r-- | src/core/mainloop/periodic.c | 223 | ||||
-rw-r--r-- | src/core/mainloop/periodic.h | 37 |
19 files changed, 1570 insertions, 941 deletions
diff --git a/src/core/mainloop/.may_include b/src/core/mainloop/.may_include new file mode 100644 index 0000000000..8e01cf910e --- /dev/null +++ b/src/core/mainloop/.may_include @@ -0,0 +1,24 @@ +!advisory + +orconfig.h + +lib/conf/*.h +lib/container/*.h +lib/dispatch/*.h +lib/evloop/*.h +lib/pubsub/*.h +lib/subsys/*.h +lib/buf/*.h +lib/crypt_ops/*.h +lib/err/*.h +lib/tls/*.h +lib/net/*.h +lib/evloop/*.h +lib/geoip/*.h +lib/sandbox/*.h +lib/smartlist_core/*.h +lib/compress/*.h +lib/log/*.h + +core/mainloop/*.h +core/mainloop/*.inc
\ No newline at end of file diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index 218873ae66..708fb13cdb 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -57,7 +57,7 @@ #define CONNECTION_PRIVATE #include "core/or/or.h" #include "feature/client/bridges.h" -#include "lib/container/buffers.h" +#include "lib/buf/buffers.h" #include "lib/tls/buffers_tls.h" #include "lib/err/backtrace.h" @@ -65,8 +65,7 @@ * 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 +#define CHANNEL_OBJECT_PRIVATE #include "app/config/config.h" #include "core/mainloop/connection.h" #include "core/mainloop/mainloop.h" @@ -82,13 +81,17 @@ #include "core/or/policies.h" #include "core/or/reasons.h" #include "core/or/relay.h" +#include "core/or/crypt_path.h" +#include "core/proto/proto_haproxy.h" #include "core/proto/proto_http.h" #include "core/proto/proto_socks.h" #include "feature/client/dnsserv.h" #include "feature/client/entrynodes.h" #include "feature/client/transports.h" #include "feature/control/control.h" +#include "feature/control/control_events.h" #include "feature/dirauth/authmode.h" +#include "feature/dirauth/dirauth_config.h" #include "feature/dircache/dirserv.h" #include "feature/dircommon/directory.h" #include "feature/hibernate/hibernate.h" @@ -105,6 +108,7 @@ #include "lib/crypt_ops/crypto_util.h" #include "lib/geoip/geoip.h" +#include "lib/cc/ctassert.h" #include "lib/sandbox/sandbox.h" #include "lib/net/buffers_net.h" #include "lib/tls/tortls.h" @@ -696,6 +700,7 @@ connection_free_minimal(connection_t *conn) control_connection_t *control_conn = TO_CONTROL_CONN(conn); tor_free(control_conn->safecookie_client_hash); tor_free(control_conn->incoming_cmd); + tor_free(control_conn->current_cmd); if (control_conn->ephemeral_onion_services) { SMARTLIST_FOREACH(control_conn->ephemeral_onion_services, char *, cp, { memwipe(cp, 0, strlen(cp)); @@ -715,11 +720,7 @@ connection_free_minimal(connection_t *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); - } + dir_conn_clear_spool(dir_conn); rend_data_free(dir_conn->rend_data); hs_ident_dir_conn_free(dir_conn->hs_ident); @@ -1195,7 +1196,7 @@ make_win32_socket_exclusive(tor_socket_t sock) return -1; } return 0; -#else /* !(defined(SO_EXCLUSIVEADDRUSE)) */ +#else /* !defined(SO_EXCLUSIVEADDRUSE) */ (void) sock; return 0; #endif /* defined(SO_EXCLUSIVEADDRUSE) */ @@ -1466,6 +1467,20 @@ connection_listener_new(const struct sockaddr *listensockaddr, tor_socket_strerror(tor_socket_errno(s))); goto err; } + +#ifndef __APPLE__ + /* This code was introduced to help debug #28229. */ + int value; + socklen_t len = sizeof(value); + + if (!getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, &value, &len)) { + if (value == 0) { + log_err(LD_NET, "Could not listen on %s - " + "getsockopt(.,SO_ACCEPTCONN,.) yields 0.", address); + goto err; + } + } +#endif /* !defined(__APPLE__) */ #endif /* defined(HAVE_SYS_UN_H) */ } else { log_err(LD_BUG, "Got unexpected address family %d.", @@ -1502,7 +1517,7 @@ connection_listener_new(const struct sockaddr *listensockaddr, 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; + lis_conn->entry_cfg.prefer_ipv6 = 1; } if (connection_add(conn) < 0) { /* no space, forget it */ @@ -1644,7 +1659,7 @@ check_sockaddr(const struct sockaddr *sa, int len, int level) len,(int)sizeof(struct sockaddr_in6)); ok = 0; } - if (tor_mem_is_zero((void*)sin6->sin6_addr.s6_addr, 16) || + if (fast_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."); @@ -1867,7 +1882,7 @@ connection_init_accepted_conn(connection_t *conn, /* 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); + connection_or_event_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); @@ -1880,11 +1895,16 @@ connection_init_accepted_conn(connection_t *conn, TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch(); TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->base_.type; + /* Any incoming connection on an entry port counts as user activity. */ + note_user_activity(approx_time()); + 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; + TO_ENTRY_CONN(conn)->socks_request->socks_use_extended_errors = + listener->entry_cfg.extended_socks5_codes; break; case CONN_TYPE_AP_TRANS_LISTENER: TO_ENTRY_CONN(conn)->is_transparent_ap = 1; @@ -2079,6 +2099,11 @@ connection_connect_log_client_use_ip_version(const connection_t *conn) return; } + if (fascist_firewall_use_ipv6(options)) { + log_info(LD_NET, "Our outgoing connection is using IPv%d.", + tor_addr_family(&real_addr) == AF_INET6 ? 6 : 4); + } + /* 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)) { @@ -2257,9 +2282,12 @@ connection_proxy_state_to_string(int state) "PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929", "PROXY_SOCKS5_WANT_AUTH_RFC1929_OK", "PROXY_SOCKS5_WANT_CONNECT_OK", + "PROXY_HAPROXY_WAIT_FOR_FLUSH", "PROXY_CONNECTED", }; + CTASSERT(ARRAY_LENGTH(states) == PROXY_CONNECTED+1); + if (state < PROXY_NONE || state > PROXY_CONNECTED) return unknown; @@ -2292,7 +2320,11 @@ conn_get_proxy_type(const connection_t *conn) return PROXY_SOCKS4; else if (options->Socks5Proxy) return PROXY_SOCKS5; - else + else if (options->TCPProxy) { + /* The only supported protocol in TCPProxy is haproxy. */ + tor_assert(options->TCPProxyProtocol == TCP_PROXY_PROTOCOL_HAPROXY); + return PROXY_HAPROXY; + } else return PROXY_NONE; } @@ -2301,165 +2333,245 @@ conn_get_proxy_type(const connection_t *conn) 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. +/** Write a proxy request of https to conn for conn->addr:conn->port, + * authenticating with the auth details given in the configuration + * (if available). * * 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) +static int +connection_https_proxy_connect(connection_t *conn) { - const or_options_t *options; + tor_assert(conn); + const or_options_t *options = get_options(); + 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; + + return 0; +} + +/** Write a proxy request of socks4 to conn for conn->addr:conn->port. + * + * Returns -1 if conn->addr is incompatible with the proxy protocol, and + * 0 otherwise. + */ +static int +connection_socks4_proxy_connect(connection_t *conn) +{ tor_assert(conn); - options = get_options(); + unsigned char *buf; + uint16_t portn; + uint32_t ip4addr; + size_t buf_size = 0; + char *socks_args_string = NULL; - 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"); - } + /* Send a SOCKS4 connect request */ - 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)); - } + if (tor_addr_family(&conn->addr) != AF_INET) { + log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6"); + return -1; + } - connection_buf_add(buf, strlen(buf), conn); - conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK; - break; + { /* 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 (conn_get_proxy_type(conn) == 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); } + } - case PROXY_SOCKS4: { - unsigned char *buf; - uint16_t portn; - uint32_t ip4addr; - size_t buf_size = 0; - char *socks_args_string = NULL; + { /* Figure out the buffer size we need for the SOCKS message: */ - /* Send a SOCKS4 connect request */ + buf_size = SOCKS4_STANDARD_BUFFER_SIZE; - if (tor_addr_family(&conn->addr) != AF_INET) { - log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6"); - return -1; - } + /* 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); + } - { /* 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': */ + buf = tor_malloc_zero(buf_size); - if (conn_get_proxy_type(conn) == 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); - } - } + ip4addr = tor_addr_to_ipv4n(&conn->addr); + portn = htons(conn->port); - { /* Figure out the buffer size we need for the SOCKS message: */ + 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 */ + } - buf_size = SOCKS4_STANDARD_BUFFER_SIZE; + connection_buf_add((char *)buf, buf_size, conn); + tor_free(buf); - /* 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); - } + conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK; + return 0; +} - 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 */ - } +/** Write a proxy request of socks5 to conn for conn->addr:conn->port, + * authenticating with the auth details given in the configuration + * (if available). + * + * Returns -1 if conn->addr is incompatible with the proxy protocol, and + * 0 otherwise. + */ +static int +connection_socks5_proxy_connect(connection_t *conn) +{ + tor_assert(conn); - connection_buf_add((char *)buf, buf_size, conn); - tor_free(buf); + const or_options_t *options = get_options(); + unsigned char buf[4]; /* fields: vers, num methods, method list */ - conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK; - break; - } + /* Send a SOCKS5 greeting (connect request must wait) */ - case PROXY_SOCKS5: { - unsigned char buf[4]; /* fields: vers, num methods, method list */ + buf[0] = 5; /* version */ - /* Send a SOCKS5 greeting (connect request must wait) */ + /* 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) || + (conn_get_proxy_type(conn) == 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; + } - buf[0] = 5; /* version */ + connection_buf_add((char *)buf, 2 + buf[1], conn); + return 0; +} - /* 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) || - (conn_get_proxy_type(conn) == 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; - } +/** Write a proxy request of haproxy to conn for conn->addr:conn->port. + * + * Returns -1 if conn->addr is incompatible with the proxy protocol, and + * 0 otherwise. + */ +static int +connection_haproxy_proxy_connect(connection_t *conn) +{ + int ret = 0; + tor_addr_port_t *addr_port = tor_addr_port_new(&conn->addr, conn->port); + char *buf = haproxy_format_proxy_header_line(addr_port); + + if (buf == NULL) { + ret = -1; + goto done; + } + + connection_buf_add(buf, strlen(buf), conn); + /* In haproxy, we don't have to wait for the response, but we wait for ack. + * So we can set the state to be PROXY_HAPROXY_WAIT_FOR_FLUSH. */ + conn->proxy_state = PROXY_HAPROXY_WAIT_FOR_FLUSH; - connection_buf_add((char *)buf, 2 + buf[1], conn); + ret = 0; + done: + tor_free(buf); + tor_free(addr_port); + return ret; +} + +/** Write a proxy request of <b>type</b> (socks4, socks5, https, haproxy) + * 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) +{ + int ret = 0; + + tor_assert(conn); + + switch (type) { + case PROXY_CONNECT: + ret = connection_https_proxy_connect(conn); + break; + + case PROXY_SOCKS4: + ret = connection_socks4_proxy_connect(conn); + break; + + case PROXY_SOCKS5: + ret = connection_socks5_proxy_connect(conn); + break; + + case PROXY_HAPROXY: + ret = connection_haproxy_proxy_connect(conn); break; - } default: log_err(LD_BUG, "Invalid proxy protocol, %d", type); tor_fragile_assert(); - return -1; + ret = -1; + break; } - log_debug(LD_NET, "set state %s", - connection_proxy_state_to_string(conn->proxy_state)); + if (ret == 0) { + log_debug(LD_NET, "set state %s", + connection_proxy_state_to_string(conn->proxy_state)); + } - return 0; + return ret; } /** Read conn's inbuf. If the http response from the proxy is all @@ -2837,7 +2949,7 @@ retry_listener_ports(smartlist_t *old_conns, SMARTLIST_DEL_CURRENT(old_conns, conn); break; } -#endif +#endif /* defined(ENABLE_LISTENER_REBIND) */ } } SMARTLIST_FOREACH_END(wanted); @@ -2901,6 +3013,10 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) retval = -1; #ifdef ENABLE_LISTENER_REBIND + if (smartlist_len(replacements)) + log_debug(LD_NET, "%d replacements - starting rebinding loop.", + smartlist_len(replacements)); + SMARTLIST_FOREACH_BEGIN(replacements, listener_replacement_t *, r) { int addr_in_use = 0; int skip = 0; @@ -2912,8 +3028,11 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) connection_listener_new_for_port(r->new_port, &skip, &addr_in_use); connection_t *old_conn = r->old_conn; - if (skip) + if (skip) { + log_debug(LD_NET, "Skipping creating new listener for %s:%d", + old_conn->address, old_conn->port); continue; + } connection_close_immediate(old_conn); connection_mark_for_close(old_conn); @@ -2932,7 +3051,7 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) conn_type_to_string(old_conn->type), old_conn->address, old_conn->port, new_conn->address, new_conn->port); } SMARTLIST_FOREACH_END(r); -#endif +#endif /* defined(ENABLE_LISTENER_REBIND) */ /* Any members that were still in 'listeners' don't correspond to * any configured port. Kill 'em. */ @@ -3019,7 +3138,7 @@ connection_mark_all_noncontrol_connections(void) * 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) +connection_is_rate_limited(const connection_t *conn) { const or_options_t *options = get_options(); if (conn->linked) @@ -3154,14 +3273,14 @@ connection_bucket_write_limit(connection_t *conn, time_t now) global_bucket_val, conn_bucket); } -/** Return 1 if the global write buckets are low enough that we +/** Return true iff 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). + * out to <b>conn</b>. * - * There are a lot of parameters we could use here: + * If we are a directory authority, always answer dir requests thus true is + * always returned. + * + * Note: 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. @@ -3173,39 +3292,40 @@ connection_bucket_write_limit(connection_t *conn, time_t now) * 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) +bool +connection_dir_is_global_write_low(const connection_t *conn, size_t attempt) { 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 */ + + /* Special case for authorities (directory only). */ + if (authdir_mode_v3(get_options())) { + /* Are we configured to possibly reject requests under load? */ + if (!dirauth_should_reject_requests_under_load()) { + /* Answer request no matter what. */ + return false; + } + /* Always answer requests from a known relay which includes the other + * authorities. The following looks up the addresses for relays that we + * have their descriptor _and_ any configured trusted directories. */ + if (nodelist_probably_contains_address(&conn->addr)) { + return false; + } + } if (!connection_is_rate_limited(conn)) - return 0; /* local conns don't get limited */ + return false; /* local conns don't get limited */ if (smaller_bucket < attempt) - return 1; /* not enough space no matter the priority */ + return true; /* not enough space. */ { 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 */ + return true; /* 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; + return false; } /** When did we last tell the accounting subsystem about transmitted @@ -3937,9 +4057,9 @@ update_send_buffer_size(tor_socket_t sock) &isb, sizeof(isb), &bytesReturned, NULL, NULL)) { setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&isb, sizeof(isb)); } -#else +#else /* !defined(_WIN32) */ (void) sock; -#endif +#endif /* defined(_WIN32) */ } /** Try to flush more bytes onto <b>conn</b>-\>s. @@ -4337,6 +4457,23 @@ connection_write_to_buf_impl_,(const char *string, size_t len, connection_write_to_buf_commit(conn, written); } +/** + * Write a <b>string</b> (of size <b>len</b> to directory connection + * <b>dir_conn</b>. Apply compression if connection is configured to use + * it and finalize it if <b>done</b> is true. + */ +void +connection_dir_buf_add(const char *string, size_t len, + dir_connection_t *dir_conn, int done) +{ + if (dir_conn->compress_state != NULL) { + connection_buf_add_compress(string, len, dir_conn, done); + return; + } + + connection_buf_add(string, len, TO_CONN(dir_conn)); +} + void connection_buf_add_compress(const char *string, size_t len, dir_connection_t *conn, int done) @@ -4451,6 +4588,16 @@ 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 is not an internally linked + * connection, and is not marked for close. + **/ +MOCK_IMPL(connection_t *, +connection_get_by_type_nonlinked,(int type)) +{ + CONN_GET_TEMPLATE(conn, conn->type == type && !conn->linked); +} + /** 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. @@ -4831,10 +4978,10 @@ connection_finished_flushing(connection_t *conn) } } -/** Called when our attempt to connect() to another server has just - * succeeded. +/** Called when our attempt to connect() to a server has just succeeded. * - * This function just passes conn to the connection-specific + * This function checks if the interface address has changed (clients only), + * and then passes conn to the connection-specific * connection_*_finished_connecting() function. */ static int @@ -5293,7 +5440,7 @@ assert_connection_ok(connection_t *conn, time_t now) 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); + cpath_assert_layer_ok(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer); } } } @@ -5347,17 +5494,20 @@ assert_connection_ok(connection_t *conn, time_t now) } /** 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. + * proxy server we are using. Store a 1 to the int pointed to by + * <b>is_put_out</b> if the connection is using a pluggable + * transport; store 0 otherwise. <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) + int *is_pt_out, const connection_t *conn) { const or_options_t *options = get_options(); + *is_pt_out = 0; /* 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 @@ -5373,6 +5523,7 @@ get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, tor_addr_copy(addr, &transport->addr); *port = transport->port; *proxy_type = transport->socks_version; + *is_pt_out = 1; return 0; } @@ -5394,6 +5545,13 @@ get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, *port = options->Socks5ProxyPort; *proxy_type = PROXY_SOCKS5; return 0; + } else if (options->TCPProxy) { + tor_addr_copy(addr, &options->TCPProxyAddr); + *port = options->TCPProxyPort; + /* The only supported protocol in TCPProxy is haproxy. */ + tor_assert(options->TCPProxyProtocol == TCP_PROXY_PROTOCOL_HAPROXY); + *proxy_type = PROXY_HAPROXY; + return 0; } tor_addr_make_unspec(addr); @@ -5409,11 +5567,13 @@ log_failed_proxy_connection(connection_t *conn) { tor_addr_t proxy_addr; uint16_t proxy_port; - int proxy_type; + int proxy_type, is_pt; - if (get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, conn) != 0) + if (get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, &is_pt, + conn) != 0) return; /* if we have no proxy set up, leave this function. */ + (void)is_pt; 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.", @@ -5429,6 +5589,7 @@ proxy_type_to_string(int proxy_type) case PROXY_CONNECT: return "HTTP"; case PROXY_SOCKS4: return "SOCKS4"; case PROXY_SOCKS5: return "SOCKS5"; + case PROXY_HAPROXY: return "HAPROXY"; case PROXY_PLUGGABLE: return "pluggable transports SOCKS"; case PROXY_NONE: return "NULL"; default: tor_assert(0); diff --git a/src/core/mainloop/connection.h b/src/core/mainloop/connection.h index 8ecdd6f06f..bcd3d590a5 100644 --- a/src/core/mainloop/connection.h +++ b/src/core/mainloop/connection.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -12,7 +12,25 @@ #ifndef TOR_CONNECTION_H #define TOR_CONNECTION_H -listener_connection_t *TO_LISTENER_CONN(connection_t *); +#include "lib/smartlist_core/smartlist_core.h" +#include "lib/log/log.h" + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +struct listener_connection_t; +struct connection_t; +struct dir_connection_t; +struct or_connection_t; +struct edge_connection_t; +struct entry_connection_t; +struct control_connection_t; +struct port_cfg_t; +struct tor_addr_t; +struct or_options_t; + +struct listener_connection_t *TO_LISTENER_CONN(struct connection_t *); struct buf_t; @@ -56,7 +74,7 @@ struct buf_t; #define CONN_TYPE_MAX_ 19 /* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in - * connection_t. */ + * struct connection_t. */ /* Proxy client handshake states */ /* We use a proxy but we haven't even connected to it yet. */ @@ -75,8 +93,10 @@ struct buf_t; #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 an HAPROXY proxy and we just sent the proxy header. */ +#define PROXY_HAPROXY_WAIT_FOR_FLUSH 8 /* We use a proxy and we CONNECTed successfully!. */ -#define PROXY_CONNECTED 8 +#define PROXY_CONNECTED 9 /** State for any listener connection. */ #define LISTENER_STATE_READY 0 @@ -88,34 +108,36 @@ struct buf_t; */ typedef struct { - connection_t *old_conn; /* Old listener connection to be replaced */ - const port_cfg_t *new_port; /* New port configuration */ + struct connection_t *old_conn; /* Old listener connection to be replaced */ + const struct port_cfg_t *new_port; /* New port configuration */ } listener_replacement_t; 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)); +struct dir_connection_t *dir_connection_new(int socket_family); +struct or_connection_t *or_connection_new(int type, int socket_family); +struct edge_connection_t *edge_connection_new(int type, int socket_family); +struct entry_connection_t *entry_connection_new(int type, int socket_family); +struct control_connection_t *control_connection_new(int socket_family); +struct listener_connection_t *listener_connection_new(int type, + int socket_family); +struct connection_t *connection_new(int type, int socket_family); +int connection_init_accepted_conn(struct connection_t *conn, + const struct listener_connection_t *listener); +void connection_link_connections(struct connection_t *conn_a, + struct connection_t *conn_b); +MOCK_DECL(void,connection_free_,(struct connection_t *conn)); #define connection_free(conn) \ - FREE_AND_NULL(connection_t, connection_free_, (conn)) + FREE_AND_NULL(struct 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, +void connection_about_to_close_connection(struct connection_t *conn); +void connection_close_immediate(struct connection_t *conn); +void connection_mark_for_close_(struct connection_t *conn, int line, const char *file); MOCK_DECL(void, connection_mark_for_close_internal_, - (connection_t *conn, int line, const char *file)); + (struct connection_t *conn, int line, const char *file)); #define connection_mark_for_close(c) \ connection_mark_for_close_((c), __LINE__, SHORT_FILE__) @@ -130,11 +152,11 @@ MOCK_DECL(void, connection_mark_for_close_internal_, * 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. + * checks for struct or_connection_t properly, instead. See below. */ #define connection_mark_and_flush_internal_(c,line,file) \ do { \ - connection_t *tmp_conn__ = (c); \ + struct connection_t *tmp_conn__ = (c); \ connection_mark_for_close_internal_(tmp_conn__, (line), (file)); \ tmp_conn__->hold_open_until_flushed = 1; \ } while (0) @@ -147,7 +169,7 @@ MOCK_DECL(void, connection_mark_for_close_internal_, */ #define connection_mark_and_flush_(c,line,file) \ do { \ - connection_t *tmp_conn_ = (c); \ + struct 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" \ @@ -164,13 +186,13 @@ MOCK_DECL(void, connection_mark_for_close_internal_, void connection_expire_held_open(void); -int connection_connect(connection_t *conn, const char *address, - const tor_addr_t *addr, +int connection_connect(struct connection_t *conn, const char *address, + const struct 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 connection_connect_unix(struct connection_t *conn, const char *socket_path, int *socket_error); #endif /* defined(HAVE_SYS_UN_H) */ @@ -183,75 +205,86 @@ int connection_connect_unix(connection_t *conn, const char *socket_path, 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 connection_proxy_connect(struct connection_t *conn, int type); +int connection_read_proxy_handshake(struct connection_t *conn); +void log_failed_proxy_connection(struct connection_t *conn); +int get_proxy_addrport(struct tor_addr_t *addr, uint16_t *port, + int *proxy_type, + int *is_pt_out, const struct connection_t *conn); -int retry_all_listeners(smartlist_t *new_conns, +int retry_all_listeners(struct 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); +ssize_t connection_bucket_write_limit(struct connection_t *conn, time_t now); +bool connection_dir_is_global_write_low(const struct connection_t *conn, + size_t attempt); void connection_bucket_init(void); -void connection_bucket_adjust(const or_options_t *options); +void connection_bucket_adjust(const struct 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, +void connection_read_bw_exhausted(struct connection_t *conn, + bool is_global_bw); +void connection_write_bw_exhausted(struct connection_t *conn, + bool is_global_bw); +void connection_consider_empty_read_buckets(struct connection_t *conn); +void connection_consider_empty_write_buckets(struct connection_t *conn); + +int connection_handle_read(struct connection_t *conn); + +int connection_buf_get_bytes(char *string, size_t len, + struct connection_t *conn); +int connection_buf_get_line(struct connection_t *conn, char *data, + size_t *data_len); +int connection_fetch_from_buf_http(struct 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); +int connection_wants_to_flush(struct connection_t *conn); +int connection_outbuf_too_full(struct connection_t *conn); +int connection_handle_write(struct connection_t *conn, int force); +int connection_flush(struct connection_t *conn); MOCK_DECL(void, connection_write_to_buf_impl_, - (const char *string, size_t len, connection_t *conn, int zlib)); + (const char *string, size_t len, struct connection_t *conn, + int zlib)); /* DOCDOC connection_write_to_buf */ static void connection_buf_add(const char *string, size_t len, - connection_t *conn); + struct connection_t *conn); +void connection_dir_buf_add(const char *string, size_t len, + struct dir_connection_t *dir_conn, int done); static inline void -connection_buf_add(const char *string, size_t len, connection_t *conn) +connection_buf_add(const char *string, size_t len, struct 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, + struct dir_connection_t *conn, int done); +void connection_buf_add_buf(struct connection_t *conn, struct buf_t *buf); + +size_t connection_get_inbuf_len(struct connection_t *conn); +size_t connection_get_outbuf_len(struct connection_t *conn); +struct connection_t *connection_get_by_global_id(uint64_t id); + +struct connection_t *connection_get_by_type(int type); +MOCK_DECL(struct connection_t *,connection_get_by_type_nonlinked,(int type)); +MOCK_DECL(struct connection_t *,connection_get_by_type_addr_port_purpose, + (int type, + const struct tor_addr_t *addr, + uint16_t port, int purpose)); +struct connection_t *connection_get_by_type_state(int type, int state); +struct 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( +struct smartlist_t *connection_list_by_type_state(int type, int state); +struct smartlist_t *connection_list_by_type_purpose(int type, int purpose); +struct smartlist_t *connection_dir_list_by_purpose_and_resource( int purpose, const char *resource); -smartlist_t *connection_dir_list_by_purpose_resource_and_state( +struct smartlist_t *connection_dir_list_by_purpose_resource_and_state( int purpose, const char *resource, int state); @@ -270,7 +303,7 @@ connection_dir_count_by_purpose_and_resource( int purpose, const char *resource) { - smartlist_t *conns = connection_dir_list_by_purpose_and_resource( + struct smartlist_t *conns = connection_dir_list_by_purpose_and_resource( purpose, resource); CONN_LEN_AND_FREE_TEMPLATE(conns); @@ -284,7 +317,7 @@ connection_dir_count_by_purpose_resource_and_state( const char *resource, int state) { - smartlist_t *conns = + struct smartlist_t *conns = connection_dir_list_by_purpose_resource_and_state( purpose, resource, @@ -294,26 +327,26 @@ connection_dir_count_by_purpose_resource_and_state( #undef CONN_LEN_AND_FREE_TEMPLATE -int any_other_active_or_conns(const or_connection_t *this_conn); +int any_other_active_or_conns(const struct 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); +int connection_is_listener(struct connection_t *conn); +int connection_state_is_open(struct connection_t *conn); +int connection_state_is_connecting(struct 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 assert_connection_ok(struct connection_t *conn, time_t now); +int connection_or_nonopen_was_started_here(struct 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, + (const struct 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); +int connection_is_moribund(struct connection_t *conn); void connection_check_oos(int n_socks, int failed); /** Execute the statement <b>stmt</b>, which may log events concerning the @@ -335,18 +368,18 @@ void connection_check_oos(int n_socks, int failed); STMT_END #ifdef CONNECTION_PRIVATE -STATIC void connection_free_minimal(connection_t *conn); +STATIC void connection_free_minimal(struct connection_t *conn); /* Used only by connection.c and test*.c */ MOCK_DECL(STATIC int,connection_connect_sockaddr, - (connection_t *conn, + (struct 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)); +MOCK_DECL(STATIC void, kill_conn_list_for_oos, (struct smartlist_t *conns)); +MOCK_DECL(STATIC struct smartlist_t *, pick_oos_victims, (int n)); #endif /* defined(CONNECTION_PRIVATE) */ diff --git a/src/core/mainloop/core_mainloop.md b/src/core/mainloop/core_mainloop.md new file mode 100644 index 0000000000..fee8a8179c --- /dev/null +++ b/src/core/mainloop/core_mainloop.md @@ -0,0 +1,10 @@ +@dir /core/mainloop +@brief core/mainloop: Non-onion-routing mainloop functionality + +This module uses the event-loop code of \refdir{lib/evloop} to implement an +asynchronous connection-oriented protocol handler. + +The layering here is imperfect: the code here was split from \refdir{core/or} +without refactoring how the two modules call one another. Probably many +functions should be moved and refactored. + diff --git a/src/core/mainloop/cpuworker.c b/src/core/mainloop/cpuworker.c index e704d55642..abd48f886c 100644 --- a/src/core/mainloop/cpuworker.c +++ b/src/core/mainloop/cpuworker.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -34,11 +34,10 @@ #include "core/crypto/onion_crypto.h" #include "core/or/or_circuit_st.h" -#include "lib/intmath/weakrng.h" static void queue_pending_tasks(void); -typedef struct worker_state_s { +typedef struct worker_state_t { int generation; server_onion_keys_t *onion_keys; } worker_state_t; @@ -74,8 +73,6 @@ worker_state_free_void(void *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; @@ -109,7 +106,6 @@ cpu_init(void) /* 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 @@ -164,7 +160,7 @@ typedef struct cpuworker_reply_t { uint8_t rend_auth_material[DIGEST_LEN]; } cpuworker_reply_t; -typedef struct cpuworker_job_u { +typedef struct cpuworker_job_u_t { or_circuit_t *circ; union { cpuworker_request_t request; @@ -235,9 +231,10 @@ should_time_request(uint16_t onionskin_type) * 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 crypto_fast_rng_one_in_n(get_thread_fast_rng(), 128); } /** Return an estimate of how many microseconds we will need for a single diff --git a/src/core/mainloop/cpuworker.h b/src/core/mainloop/cpuworker.h index 77e2c42508..7e71961750 100644 --- a/src/core/mainloop/cpuworker.h +++ b/src/core/mainloop/cpuworker.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,10 +14,10 @@ void cpu_init(void); void cpuworkers_rotate_keyinfo(void); -struct workqueue_entry_s; +struct workqueue_entry_t; enum workqueue_reply_t; enum workqueue_priority_t; -MOCK_DECL(struct workqueue_entry_s *, cpuworker_queue_work, ( +MOCK_DECL(struct workqueue_entry_t *, cpuworker_queue_work, ( enum workqueue_priority_t priority, enum workqueue_reply_t (*fn)(void *, void *), void (*reply_fn)(void *), diff --git a/src/core/mainloop/include.am b/src/core/mainloop/include.am new file mode 100644 index 0000000000..63643127f3 --- /dev/null +++ b/src/core/mainloop/include.am @@ -0,0 +1,22 @@ + +# ADD_C_FILE: INSERT SOURCES HERE. +LIBTOR_APP_A_SOURCES += \ + src/core/mainloop/connection.c \ + src/core/mainloop/cpuworker.c \ + src/core/mainloop/mainloop.c \ + src/core/mainloop/mainloop_pubsub.c \ + src/core/mainloop/mainloop_sys.c \ + src/core/mainloop/netstatus.c \ + src/core/mainloop/periodic.c + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/core/mainloop/connection.h \ + src/core/mainloop/cpuworker.h \ + src/core/mainloop/mainloop.h \ + src/core/mainloop/mainloop_pubsub.h \ + src/core/mainloop/mainloop_state.inc \ + src/core/mainloop/mainloop_state_st.h \ + src/core/mainloop/mainloop_sys.h \ + src/core/mainloop/netstatus.h \ + src/core/mainloop/periodic.h diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c index f0aa37e8da..260de181e5 100644 --- a/src/core/mainloop/mainloop.c +++ b/src/core/mainloop/mainloop.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -71,12 +71,13 @@ #include "feature/client/bridges.h" #include "feature/client/dnsserv.h" #include "feature/client/entrynodes.h" +#include "feature/client/proxymode.h" #include "feature/client/transports.h" #include "feature/control/control.h" +#include "feature/control/control_events.h" #include "feature/dirauth/authmode.h" -#include "feature/dirauth/reachability.h" #include "feature/dircache/consdiffmgr.h" -#include "feature/dircache/dirserv.h" +#include "feature/dirclient/dirclient_modes.h" #include "feature/dircommon/directory.h" #include "feature/hibernate/hibernate.h" #include "feature/hs/hs_cache.h" @@ -95,7 +96,7 @@ #include "feature/stats/geoip_stats.h" #include "feature/stats/predict_ports.h" #include "feature/stats/rephist.h" -#include "lib/container/buffers.h" +#include "lib/buf/buffers.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/err/backtrace.h" #include "lib/tls/buffers_tls.h" @@ -105,9 +106,6 @@ #include <event2/event.h> -#include "feature/dirauth/dirvote.h" -#include "feature/dirauth/authmode.h" - #include "core/or/cell_st.h" #include "core/or/entry_connection_st.h" #include "feature/nodelist/networkstatus_st.h" @@ -200,12 +198,10 @@ static int can_complete_circuits = 0; #define LAZY_DESCRIPTOR_RETRY_INTERVAL (60) static int conn_close_if_marked(int i); -static int run_main_loop_until_done(void); static void connection_start_reading_from_linked_conn(connection_t *conn); static int connection_should_read_from_linked_conn(connection_t *conn); 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 void shutdown_did_not_work_callback(evutil_socket_t fd, short event, void *arg) ATTR_NORETURN; @@ -759,7 +755,7 @@ tor_shutdown_event_loop_for_restart_cb( tor_event_free(tor_shutdown_event_loop_for_restart_event); tor_shutdown_event_loop_and_exit(0); } -#endif +#endif /* defined(ENABLE_RESTART_DEBUGGING) */ /** * After finishing the current callback (if any), shut down the main loop, @@ -1137,14 +1133,14 @@ directory_info_has_arrived(time_t now, int from_cache, int suppress_logs) if (!router_have_minimum_dir_info()) { int quiet = suppress_logs || from_cache || - directory_too_idle_to_fetch_descriptors(options, now); + dirclient_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)) { + if (dirclient_fetches_from_authorities(options)) { update_all_descriptor_downloads(now); } @@ -1357,123 +1353,101 @@ get_signewnym_epoch(void) static int periodic_events_initialized = 0; /* Declare all the timer callback functions... */ +#ifndef COCCI #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(prune_old_routers); 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); +CALLBACK(control_per_second_events); +CALLBACK(second_elapsed); #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), +#define CALLBACK(name, r, f) \ + PERIODIC_EVENT(name, PERIODIC_EVENT_ROLE_ ## r, f) +#define FL(name) (PERIODIC_EVENT_FLAG_ ## name) +#endif /* !defined(COCCI) */ + +STATIC periodic_event_item_t mainloop_periodic_events[] = { + + /* Everyone needs to run these. They need to have very long timeouts for + * that to be safe. */ + CALLBACK(add_entropy, ALL, 0), + CALLBACK(heartbeat, ALL, 0), + CALLBACK(reset_padding_counts, ALL, 0), + + /* This is a legacy catch-all callback that runs once per second if + * we are online and active. */ + CALLBACK(second_elapsed, NET_PARTICIPANT, + FL(RUN_ON_DISABLE)), + + /* XXXX Do we have a reason to do this on a callback? Does it do any good at + * all? For now, if we're dormant, we can let our listeners decay. */ + CALLBACK(retry_listeners, NET_PARTICIPANT, FL(NEED_NET)), + + /* We need to do these if we're participating in the Tor network. */ + CALLBACK(check_expired_networkstatus, NET_PARTICIPANT, 0), + CALLBACK(fetch_networkstatus, NET_PARTICIPANT, 0), + CALLBACK(launch_descriptor_fetches, NET_PARTICIPANT, FL(NEED_NET)), + CALLBACK(rotate_x509_certificate, NET_PARTICIPANT, 0), + CALLBACK(check_network_participation, NET_PARTICIPANT, 0), + + /* We need to do these if we're participating in the Tor network, and + * immediately before we stop. */ + CALLBACK(clean_caches, NET_PARTICIPANT, FL(RUN_ON_DISABLE)), + CALLBACK(save_state, NET_PARTICIPANT, FL(RUN_ON_DISABLE)), + CALLBACK(write_stats_file, NET_PARTICIPANT, FL(RUN_ON_DISABLE)), + CALLBACK(prune_old_routers, NET_PARTICIPANT, FL(RUN_ON_DISABLE)), /* Hidden Service service only. */ - CALLBACK(hs_service, PERIODIC_EVENT_ROLE_HS_SERVICE, - PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(hs_service, HS_SERVICE, FL(NEED_NET)), // XXXX break this down more /* Bridge only. */ - CALLBACK(record_bridge_stats, PERIODIC_EVENT_ROLE_BRIDGE, 0), + CALLBACK(record_bridge_stats, 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), + /* XXXX this could be restricted to CLIENT+NET_PARTICIPANT */ + CALLBACK(rend_cache_failure_clean, NET_PARTICIPANT, FL(RUN_ON_DISABLE)), /* Directory server only. */ - CALLBACK(clean_consdiffmgr, PERIODIC_EVENT_ROLE_DIRSERVER, 0), + CALLBACK(clean_consdiffmgr, DIRSERVER, 0), + + /* Controller with per-second events only. */ + CALLBACK(control_per_second_events, CONTROLEV, 0), END_OF_PERIODIC_EVENTS }; +#ifndef COCCI #undef CALLBACK +#undef FL +#endif /* 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; +static periodic_event_item_t *prune_old_routers_event=NULL; /** Reset all the periodic events so we'll do all our actions again as if we * just started up. @@ -1483,24 +1457,7 @@ static periodic_event_item_t *save_state_event=NULL; 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; + periodic_events_reset_all(); } /** Return a bitmask of the roles this tor instance is configured for using @@ -1510,7 +1467,7 @@ get_my_roles(const or_options_t *options) { tor_assert(options); - int roles = 0; + int roles = PERIODIC_EVENT_ROLE_ALL; int is_bridge = options->BridgeRelay; int is_relay = server_mode(options); int is_dirauth = authdir_mode_v3(options); @@ -1518,6 +1475,8 @@ get_my_roles(const or_options_t *options) int is_hidden_service = !!hs_service_get_num_services() || !!rend_num_services(); int is_dirserver = dir_server_mode(options); + int sending_control_events = control_any_per_second_event_enabled(); + /* We also consider tor to have the role of a client if the ControlPort is * set because a lot of things can be done over the control port which * requires tor to have basic functionnalities. */ @@ -1525,6 +1484,9 @@ get_my_roles(const or_options_t *options) options->ControlPort_set || options->OwningControllerFD != UINT64_MAX; + int is_net_participant = is_participating_on_network() || + is_relay || is_hidden_service; + if (is_bridge) roles |= PERIODIC_EVENT_ROLE_BRIDGE; if (is_client) roles |= PERIODIC_EVENT_ROLE_CLIENT; if (is_relay) roles |= PERIODIC_EVENT_ROLE_RELAY; @@ -1532,6 +1494,8 @@ get_my_roles(const or_options_t *options) 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; + if (is_net_participant) roles |= PERIODIC_EVENT_ROLE_NET_PARTICIPANT; + if (sending_control_events) roles |= PERIODIC_EVENT_ROLE_CONTROLEV; return roles; } @@ -1556,9 +1520,9 @@ initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data) 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 +/** Set up all the members of mainloop_periodic_events[], and configure them + * all to be launched from a callback. */ +void initialize_periodic_events(void) { if (periodic_events_initialized) @@ -1566,39 +1530,60 @@ initialize_periodic_events(void) 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]); + for (int i = 0; mainloop_periodic_events[i].name; ++i) { + periodic_events_register(&mainloop_periodic_events[i]); } + /* Set up all periodic events. We'll launch them by roles. */ + +#ifndef COCCI #define NAMED_CALLBACK(name) \ - STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END + STMT_BEGIN name ## _event = periodic_events_find( #name ); STMT_END +#endif - NAMED_CALLBACK(check_descriptor); - NAMED_CALLBACK(dirvote); + NAMED_CALLBACK(prune_old_routers); 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_disconnect_all(); + fetch_networkstatus_event = NULL; + launch_descriptor_fetches_event = NULL; + check_dns_honesty_event = NULL; + save_state_event = NULL; + prune_old_routers_event = NULL; periodic_events_initialized = 0; } +static mainloop_event_t *rescan_periodic_events_ev = NULL; + +/** Callback: rescan the periodic event list. */ +static void +rescan_periodic_events_cb(mainloop_event_t *event, void *arg) +{ + (void)event; + (void)arg; + rescan_periodic_events(get_options()); +} + +/** + * Schedule an event that will rescan which periodic events should run. + **/ +MOCK_IMPL(void, +schedule_rescan_periodic_events,(void)) +{ + if (!rescan_periodic_events_ev) { + rescan_periodic_events_ev = + mainloop_event_new(rescan_periodic_events_cb, NULL); + } + mainloop_event_activate(rescan_periodic_events_ev); +} + /** 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 @@ -1606,36 +1591,7 @@ 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]; - - int enable = !!(item->roles & roles); - - /* Handle the event flags. */ - if (net_is_disabled() && - (item->flags & PERIODIC_EVENT_FLAG_NEED_NET)) { - enable = 0; - } - - /* Enable the event if needed. It is safe to enable an event that was - * already enabled. Same goes for disabling it. */ - if (enable) { - 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); - } - } + periodic_events_rescan_by_roles(get_my_roles(options), net_is_disabled()); } /* We just got new options globally set, see if we need to enabled or disable @@ -1643,26 +1599,7 @@ rescan_periodic_events(const or_options_t *options) 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); - } + rescan_periodic_events(options); } /** @@ -1708,40 +1645,41 @@ mainloop_schedule_postloop_cleanup(void) 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; +/** Event to run 'scheduled_shutdown_cb' */ +static mainloop_event_t *scheduled_shutdown_ev=NULL; + +/** Callback: run a scheduled shutdown */ +static void +scheduled_shutdown_cb(mainloop_event_t *ev, void *arg) +{ + (void)ev; + (void)arg; + log_notice(LD_GENERAL, "Clean shutdown finished. Exiting."); + tor_shutdown_event_loop_and_exit(0); +} + +/** Schedule the mainloop to exit after <b>delay_sec</b> seconds. */ +void +mainloop_schedule_shutdown(int delay_sec) +{ + const struct timeval delay_tv = { delay_sec, 0 }; + if (! scheduled_shutdown_ev) { + scheduled_shutdown_ev = mainloop_event_new(scheduled_shutdown_cb, NULL); } + mainloop_event_schedule(scheduled_shutdown_ev, &delay_tv); } /** Perform regular maintenance tasks. This function gets run once per - * second by second_elapsed_callback(). + * second. */ -static void -run_scheduled_events(time_t now) +static int +second_elapsed_callback(time_t now, const or_options_t *options) { - 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. + /* 0. See if our bandwidth limits are exhausted and we should hibernate + * + * Note: we have redundant mechanisms to handle the case where it's + * time to wake up from hibernation; or where we have a scheduled + * shutdown and it's time to run it, but this will also handle those. */ consider_hibernation(now); @@ -1751,10 +1689,13 @@ run_scheduled_events(time_t now) 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. */ + // TODO: client + // Also, schedule this rather than probing 1x / sec fetch_bridge_descriptors(options, now); } if (accounting_is_enabled(options)) { + // TODO: refactor or rewrite? accounting_run_housekeeping(now); } @@ -1765,6 +1706,7 @@ run_scheduled_events(time_t now) */ /* (If our circuit build timeout can ever become lower than a second (which * it can't, currently), we should do this more often.) */ + // TODO: All expire stuff can become NET_PARTICIPANT, RUN_ON_DISABLE circuit_expire_building(); circuit_expire_waiting_for_better_guard(); @@ -1798,80 +1740,8 @@ run_scheduled_events(time_t now) 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; + /* Run again in a second. */ + return 1; } /** @@ -1947,100 +1817,53 @@ add_entropy_callback(time_t now, const or_options_t *options) 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) +/** Periodic callback: if there has been no network usage in a while, + * enter a dormant state. */ +STATIC int +check_network_participation_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); + /* If we're a server, we can't become dormant. */ + if (server_mode(options)) { + goto found_activity; } - 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); -} + /* If we're running an onion service, we can't become dormant. */ + /* XXXX this would be nice to change, so that we can be dormant with a + * service. */ + if (hs_service_get_num_services() || rend_num_services()) { + goto found_activity; + } -/** - * 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."); + /* If we have any currently open entry streams other than "linked" + * connections used for directory requests, those count as user activity. + */ + if (options->DormantTimeoutDisabledByIdleStreams) { + if (connection_get_by_type_nonlinked(CONN_TYPE_AP) != NULL) { + goto found_activity; } } -#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; -} + /* XXXX Make this configurable? */ +/** How often do we check whether we have had network activity? */ +#define CHECK_PARTICIPATION_INTERVAL (5*60) -/** - * 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; + /* Become dormant if there has been no user activity in a long time. + * (The funny checks below are in order to prevent overflow.) */ + time_t time_since_last_activity = 0; + if (get_last_user_activity_time() < now) + time_since_last_activity = now - get_last_user_activity_time(); + if (time_since_last_activity >= options->DormantClientTimeout) { + log_notice(LD_GENERAL, "No user activity in a long time: becoming" + " dormant."); + set_network_participation(false); + rescan_periodic_events(options); } - 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); -} + return CHECK_PARTICIPATION_INTERVAL; -/** 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); - } + found_activity: + note_user_activity(now); + return CHECK_PARTICIPATION_INTERVAL; } /** @@ -2053,11 +1876,9 @@ 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) && + /* Use reasonably live consensuses until they are no longer reasonably live. + */ + if (ns && !networkstatus_consensus_reasonably_live(ns, now) && router_have_minimum_dir_info()) { router_dir_info_changed(); } @@ -2143,17 +1964,6 @@ write_stats_file_callback(time_t now, const or_options_t *options) 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) { @@ -2228,87 +2038,24 @@ rend_cache_failure_clean_callback(time_t now, const or_options_t *options) } /** - * Periodic callback: If we're a server and initializing dns failed, retry. + * Periodic callback: prune routerlist of old information about Tor network. */ static int -retry_dns_callback(time_t now, const or_options_t *options) +prune_old_routers_callback(time_t now, const or_options_t *options) { +#define ROUTERLIST_PRUNING_INTERVAL (60*60) // 1 hour. (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. */ + log_debug(LD_GENERAL, "Pruning routerlist..."); 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; + return ROUTERLIST_PRUNING_INTERVAL; } /** @@ -2322,7 +2069,7 @@ fetch_networkstatus_callback(time_t now, const or_options_t *options) * documents? */ const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping( now); - const int prefer_mirrors = !directory_fetches_from_authorities( + const int prefer_mirrors = !dirclient_fetches_from_authorities( get_options()); int networkstatus_dl_check_interval = 60; /* check more often when testing, or when bootstrapping from mirrors @@ -2353,109 +2100,6 @@ retry_listeners_callback(time_t now, const or_options_t *options) 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; /** @@ -2522,36 +2166,19 @@ hs_service_callback(time_t now, const or_options_t *options) 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. +/* + * Periodic callback: Send once-per-second events to the controller(s). + * This is called every second. */ -void -reschedule_per_second_timer(void) +static int +control_per_second_events_callback(time_t now, const or_options_t *options) { - 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); - } + (void) options; + (void) now; - const bool run_per_second_events = - control_any_per_second_event_enabled() || ! net_is_completely_disabled(); + control_per_second_events(); - if (run_per_second_events) { - periodic_timer_launch(second_timer, &one_second); - } else { - periodic_timer_disable(second_timer); - } + return 1; } /** Last time that update_current_time was called. */ @@ -2581,6 +2208,17 @@ update_current_time(time_t now) memcpy(&last_updated, ¤t_second_last_changed, sizeof(last_updated)); monotime_coarse_get(¤t_second_last_changed); + /** How much clock jumping means that we should adjust our idea of when + * to go dormant? */ +#define NUM_JUMPED_SECONDS_BEFORE_NETSTATUS_UPDATE 20 + + /* Don't go dormant early or late just because we jumped in time. */ + if (ABS(seconds_elapsed) >= NUM_JUMPED_SECONDS_BEFORE_NETSTATUS_UPDATE) { + if (is_participating_on_network()) { + netstatus_note_clock_jumped(seconds_elapsed); + } + } + /** How much clock jumping do we tolerate? */ #define NUM_JUMPED_SECONDS_BEFORE_WARN 100 @@ -2590,6 +2228,7 @@ update_current_time(time_t now) 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 = @@ -2619,31 +2258,6 @@ update_current_time(time_t 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; @@ -2659,18 +2273,23 @@ systemd_watchdog_callback(periodic_timer_t *timer, void *arg) #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. */ +/** Called when our IP address seems to have changed. <b>on_client_conn</b> + * should be true if: + * - we detected a change in our interface address, using an outbound + * connection, and therefore + * - our client TLS keys need to be rotated. + * Otherwise, it should be false, and: + * - we detected a change in our published address + * (using some other method), and therefore + * - the published addresses in our descriptor need to change. + */ void -ip_address_changed(int at_interface) +ip_address_changed(int on_client_conn) { 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 (on_client_conn) { if (! server) { /* Okay, change our keys. */ if (init_keys_client() < 0) @@ -2682,15 +2301,14 @@ ip_address_changed(int at_interface) reset_bandwidth_test(); reset_uptime(); router_reset_reachability(); + /* All relays include their IP addresses as their ORPort addresses in + * their descriptor. + * Exit relays also incorporate interface addresses in their exit + * policies, when ExitPolicyRejectLocalInterfaces is set. */ + mark_my_descriptor_dirty("IP address changed"); } } - /* 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(); } @@ -2701,8 +2319,7 @@ dns_servers_relaunch_checks(void) { if (server_mode(get_options())) { dns_reset_correctness_checks(); - if (periodic_events_initialized) { - tor_assert(check_dns_honesty_event); + if (check_dns_honesty_event) { periodic_event_reschedule(check_dns_honesty_event); } } @@ -2712,8 +2329,6 @@ dns_servers_relaunch_checks(void) void initialize_mainloop_events(void) { - initialize_periodic_events(); - if (!schedule_active_linked_connections_event) { schedule_active_linked_connections_event = mainloop_event_postloop_new(schedule_active_linked_connections_cb, NULL); @@ -2731,11 +2346,16 @@ do_main_loop(void) /* initialize the periodic events first, so that code that depends on the * events being present does not assert. */ - initialize_periodic_events(); + tor_assert(periodic_events_initialized); initialize_mainloop_events(); - /* set up once-a-second callback. */ - reschedule_per_second_timer(); + periodic_events_connect_all(); + + 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); #ifdef HAVE_SYSTEMD_209 uint64_t watchdog_delay; @@ -2758,10 +2378,6 @@ do_main_loop(void) } } #endif /* defined(HAVE_SYSTEMD_209) */ - - main_loop_should_exit = 0; - main_loop_exit_value = 0; - #ifdef ENABLE_RESTART_DEBUGGING { static int first_time = 1; @@ -2785,7 +2401,7 @@ do_main_loop(void) event_add(tor_shutdown_event_loop_for_restart_event, &restart_after); } } -#endif +#endif /* defined(ENABLE_RESTART_DEBUGGING) */ return run_main_loop_until_done(); } @@ -2887,10 +2503,14 @@ run_main_loop_once(void) * * Shadow won't invoke this function, so don't fill it up with things. */ -static int +STATIC int run_main_loop_until_done(void) { int loop_result = 1; + + main_loop_should_exit = 0; + main_loop_exit_value = 0; + do { loop_result = run_main_loop_once(); } while (loop_result == 1); @@ -2921,7 +2541,6 @@ tor_mainloop_free_all(void) 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); @@ -2929,6 +2548,8 @@ tor_mainloop_free_all(void) mainloop_event_free(schedule_active_linked_connections_event); mainloop_event_free(postloop_cleanup_ev); mainloop_event_free(handle_deferred_signewnym_ev); + mainloop_event_free(scheduled_shutdown_ev); + mainloop_event_free(rescan_periodic_events_ev); #ifdef HAVE_SYSTEMD_209 periodic_timer_free(systemd_watchdog_timer); @@ -2948,7 +2569,6 @@ tor_mainloop_free_all(void) 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, diff --git a/src/core/mainloop/mainloop.h b/src/core/mainloop/mainloop.h index c5669fc4e0..1ddfec2162 100644 --- a/src/core/mainloop/mainloop.h +++ b/src/core/mainloop/mainloop.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -56,15 +56,14 @@ 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 ip_address_changed(int on_client_conn); 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); +MOCK_DECL(void, schedule_rescan_periodic_events,(void)); void update_current_time(time_t now); @@ -81,34 +80,38 @@ 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); void do_signewnym(time_t); time_t get_last_signewnym_time(void); +void mainloop_schedule_shutdown(int delay_sec); + void tor_init_connection_lists(void); void initialize_mainloop_events(void); +void initialize_periodic_events(void); void tor_mainloop_free_all(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 MAINLOOP_PRIVATE +STATIC int run_main_loop_until_done(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 *); +STATIC int check_network_participation_callback(time_t now, + const or_options_t *options); + #ifdef TOR_UNIT_TESTS extern smartlist_t *connection_array; /* We need the periodic_event_item_t definition. */ #include "core/mainloop/periodic.h" -extern periodic_event_item_t periodic_events[]; -#endif -#endif /* defined(MAIN_PRIVATE) */ +extern periodic_event_item_t mainloop_periodic_events[]; +#endif /* defined(TOR_UNIT_TESTS) */ +#endif /* defined(MAINLOOP_PRIVATE) */ -#endif +#endif /* !defined(TOR_MAINLOOP_H) */ diff --git a/src/core/mainloop/mainloop_pubsub.c b/src/core/mainloop/mainloop_pubsub.c new file mode 100644 index 0000000000..0e982d4c40 --- /dev/null +++ b/src/core/mainloop/mainloop_pubsub.c @@ -0,0 +1,179 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file mainloop_pubsub.c + * @brief Connect the publish-subscribe code to the main-loop. + * + * This module is responsible for instantiating all the channels used by the + * publish-subscribe code, and making sure that each one's messages are + * processed when appropriate. + **/ + +#include "orconfig.h" + +#include "core/or/or.h" +#include "core/mainloop/mainloop.h" +#include "core/mainloop/mainloop_pubsub.h" + +#include "lib/container/smartlist.h" +#include "lib/dispatch/dispatch.h" +#include "lib/dispatch/dispatch_naming.h" +#include "lib/evloop/compat_libevent.h" +#include "lib/pubsub/pubsub.h" +#include "lib/pubsub/pubsub_build.h" + +/** + * Dispatcher to use for delivering messages. + **/ +static dispatch_t *the_dispatcher = NULL; +static pubsub_items_t *the_pubsub_items = NULL; +/** + * A list of mainloop_event_t, indexed by channel ID, to flush the messages + * on a channel. + **/ +static smartlist_t *alert_events = NULL; + +/** + * Mainloop event callback: flush all the messages in a channel. + * + * The channel is encoded as a pointer, and passed via arg. + **/ +static void +flush_channel_event(mainloop_event_t *ev, void *arg) +{ + (void)ev; + if (!the_dispatcher) + return; + + channel_id_t chan = (channel_id_t)(uintptr_t)(arg); + dispatch_flush(the_dispatcher, chan, INT_MAX); +} + +/** + * Construct our global pubsub object from <b>builder</b>. Return 0 on + * success, -1 on failure. */ +int +tor_mainloop_connect_pubsub(struct pubsub_builder_t *builder) +{ + int rv = -1; + tor_mainloop_disconnect_pubsub(); + + the_dispatcher = pubsub_builder_finalize(builder, &the_pubsub_items); + if (! the_dispatcher) + goto err; + + rv = 0; + goto done; + err: + tor_mainloop_disconnect_pubsub(); + done: + return rv; +} + +/** + * Install libevent events for all of the pubsub channels. + * + * Invoke this after tor_mainloop_connect_pubsub, and after libevent has been + * initialized. + */ +void +tor_mainloop_connect_pubsub_events(void) +{ + tor_assert(the_dispatcher); + tor_assert(! alert_events); + + const size_t num_channels = get_num_channel_ids(); + alert_events = smartlist_new(); + for (size_t i = 0; i < num_channels; ++i) { + smartlist_add(alert_events, + mainloop_event_postloop_new(flush_channel_event, + (void*)(uintptr_t)(i))); + } +} + +/** + * Dispatch alertfn callback: do nothing. Implements DELIV_NEVER. + **/ +static void +alertfn_never(dispatch_t *d, channel_id_t chan, void *arg) +{ + (void)d; + (void)chan; + (void)arg; +} + +/** + * Dispatch alertfn callback: activate a mainloop event. Implements + * DELIV_PROMPT. + **/ +static void +alertfn_prompt(dispatch_t *d, channel_id_t chan, void *arg) +{ + (void)d; + (void)chan; + mainloop_event_t *event = arg; + mainloop_event_activate(event); +} + +/** + * Dispatch alertfn callback: flush all messages right now. Implements + * DELIV_IMMEDIATE. + **/ +static void +alertfn_immediate(dispatch_t *d, channel_id_t chan, void *arg) +{ + (void) arg; + dispatch_flush(d, chan, INT_MAX); +} + +/** + * Set the strategy to be used for delivering messages on the named channel. + * + * This function needs to be called once globally for each channel, to + * set up how messages are delivered. + **/ +int +tor_mainloop_set_delivery_strategy(const char *msg_channel_name, + deliv_strategy_t strategy) +{ + channel_id_t chan = get_channel_id(msg_channel_name); + if (BUG(chan == ERROR_ID) || + BUG(chan >= smartlist_len(alert_events))) + return -1; + + switch (strategy) { + case DELIV_NEVER: + dispatch_set_alert_fn(the_dispatcher, chan, alertfn_never, NULL); + break; + case DELIV_PROMPT: + dispatch_set_alert_fn(the_dispatcher, chan, alertfn_prompt, + smartlist_get(alert_events, chan)); + break; + case DELIV_IMMEDIATE: + dispatch_set_alert_fn(the_dispatcher, chan, alertfn_immediate, NULL); + break; + } + return 0; +} + +/** + * Remove all pubsub dispatchers and events from the mainloop. + **/ +void +tor_mainloop_disconnect_pubsub(void) +{ + if (the_pubsub_items) { + pubsub_items_clear_bindings(the_pubsub_items); + pubsub_items_free(the_pubsub_items); + } + if (alert_events) { + SMARTLIST_FOREACH(alert_events, mainloop_event_t *, ev, + mainloop_event_free(ev)); + smartlist_free(alert_events); + } + dispatch_free(the_dispatcher); +} diff --git a/src/core/mainloop/mainloop_pubsub.h b/src/core/mainloop/mainloop_pubsub.h new file mode 100644 index 0000000000..3698fd8d03 --- /dev/null +++ b/src/core/mainloop/mainloop_pubsub.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file mainloop_pubsub.h + * @brief Header for mainloop_pubsub.c + **/ + +#ifndef TOR_MAINLOOP_PUBSUB_H +#define TOR_MAINLOOP_PUBSUB_H + +struct pubsub_builder_t; + +/** + * Describe when and how messages are delivered on message channel. + * + * Every message channel must be associated with one of these strategies. + **/ +typedef enum { + /** + * Never deliver messages automatically. + * + * If a message channel uses this strategy, then no matter now many + * messages are published on it, they are not delivered until something + * manually calls dispatch_flush() for that channel + **/ + DELIV_NEVER=0, + /** + * Deliver messages promptly, via the event loop. + * + * If a message channel uses this strategy, then publishing a messages + * that channel activates an event that causes messages to be handled + * later in the mainloop. The messages will be processed at some point + * very soon, delaying only for pending IO events and the like. + * + * Generally this is the best choice for a delivery strategy, since + * it avoids stack explosion. + **/ + DELIV_PROMPT, + /** + * Deliver messages immediately, skipping the event loop. + * + * Every event on this channel is flushed immediately after it is queued, + * using the stack. + * + * This delivery type should be used with caution, since it can cause + * unexpected call chains, resource starvation, and the like. + **/ + DELIV_IMMEDIATE, +} deliv_strategy_t; + +int tor_mainloop_connect_pubsub(struct pubsub_builder_t *builder); +void tor_mainloop_connect_pubsub_events(void); +int tor_mainloop_set_delivery_strategy(const char *msg_channel_name, + deliv_strategy_t strategy); +void tor_mainloop_disconnect_pubsub(void); + +#endif /* !defined(TOR_MAINLOOP_PUBSUB_H) */ diff --git a/src/core/mainloop/mainloop_state.inc b/src/core/mainloop/mainloop_state.inc new file mode 100644 index 0000000000..34a37caaa2 --- /dev/null +++ b/src/core/mainloop/mainloop_state.inc @@ -0,0 +1,19 @@ + +/** + * @file mainloop_state.inc + * @brief Declare configuration options for the crypto_ops module. + **/ + +/** Holds state for the mainloop, corresponding to part of the state + * file in Tor's DataDirectory. */ +BEGIN_CONF_STRUCT(mainloop_state_t) + +/** Number of minutes since the last user-initiated request (as defined by + * the dormant net-status system.) Set to zero if we are dormant. */ +CONF_VAR(MinutesSinceUserActivity, POSINT, 0, NULL) + +/** True if we were dormant when we last wrote the file; false if we + * weren't. "auto" on initial startup. */ +CONF_VAR(Dormant, AUTOBOOL, 0, "auto") + +END_CONF_STRUCT(mainloop_state_t) diff --git a/src/core/mainloop/mainloop_state_st.h b/src/core/mainloop/mainloop_state_st.h new file mode 100644 index 0000000000..5649b536f9 --- /dev/null +++ b/src/core/mainloop/mainloop_state_st.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file mainloop_state_st.h + * @brief Declare a state structure for mainloop-relevant fields + **/ + +#ifndef TOR_CORE_MAINLOOP_MAINLOOP_STATE_ST_H +#define TOR_CORE_MAINLOOP_MAINLOOP_STATE_ST_H + +#include "lib/conf/confdecl.h" + +#define CONF_CONTEXT STRUCT +#include "core/mainloop/mainloop_state.inc" +#undef CONF_CONTEXT + +typedef struct mainloop_state_t mainloop_state_t; + +#endif /* !defined(TOR_CORE_MAINLOOP_MAINLOOP_STATE_ST_H) */ diff --git a/src/core/mainloop/mainloop_sys.c b/src/core/mainloop/mainloop_sys.c new file mode 100644 index 0000000000..4b78c90b96 --- /dev/null +++ b/src/core/mainloop/mainloop_sys.c @@ -0,0 +1,89 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file mainloop_sys.c + * @brief Declare the "mainloop" subsystem. + **/ + +#include "core/or/or.h" +#include "core/mainloop/mainloop_sys.h" +#include "core/mainloop/mainloop.h" +#include "core/mainloop/mainloop_state_st.h" +#include "core/mainloop/netstatus.h" +#include "lib/conf/conftypes.h" +#include "lib/conf/confdecl.h" + +#include "lib/subsys/subsys.h" + +static int +subsys_mainloop_initialize(void) +{ + initialize_periodic_events(); + return 0; +} + +static void +subsys_mainloop_shutdown(void) +{ + tor_mainloop_free_all(); +} + +/** Declare a list of state variables for mainloop state. */ +#define CONF_CONTEXT TABLE +#include "core/mainloop/mainloop_state.inc" +#undef CONF_CONTEXT + +/** Magic number for mainloop state objects */ +#define MAINLOOP_STATE_MAGIC 0x59455449 + +/** + * Format object for mainloop state. + **/ +static config_format_t mainloop_state_fmt = { + .size = sizeof(mainloop_state_t), + .magic = { "mainloop_state", + MAINLOOP_STATE_MAGIC, + offsetof(mainloop_state_t, magic) + }, + .vars = mainloop_state_t_vars, +}; + +/** + */ +static int +mainloop_set_state(void *arg) +{ + const mainloop_state_t *state = arg; + tor_assert(state->magic == MAINLOOP_STATE_MAGIC); + + netstatus_load_from_state(state, approx_time()); + + return 0; +} + +static int +mainloop_flush_state(void *arg) +{ + mainloop_state_t *state = arg; + tor_assert(state->magic == MAINLOOP_STATE_MAGIC); + + netstatus_flush_to_state(state, approx_time()); + + return 0; +} + +const struct subsys_fns_t sys_mainloop = { + .name = "mainloop", + .supported = true, + .level = 5, + .initialize = subsys_mainloop_initialize, + .shutdown = subsys_mainloop_shutdown, + + .state_format = &mainloop_state_fmt, + .set_state = mainloop_set_state, + .flush_state = mainloop_flush_state, +}; diff --git a/src/core/mainloop/mainloop_sys.h b/src/core/mainloop/mainloop_sys.h new file mode 100644 index 0000000000..b3ade33cd1 --- /dev/null +++ b/src/core/mainloop/mainloop_sys.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file mainloop_sys.h + * @brief Header for mainloop_sys.c + **/ + +#ifndef MAINLOOP_SYS_H +#define MAINLOOP_SYS_H + +extern const struct subsys_fns_t sys_mainloop; + +#endif /* !defined(MAINLOOP_SYS_H) */ diff --git a/src/core/mainloop/netstatus.c b/src/core/mainloop/netstatus.c index 1444ca5db2..61a3469eaa 100644 --- a/src/core/mainloop/netstatus.c +++ b/src/core/mainloop/netstatus.c @@ -1,14 +1,23 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file netstatus.c + * @brief Track whether the network is disabled, dormant, etc. + **/ + #include "core/or/or.h" #include "core/mainloop/netstatus.h" +#include "core/mainloop/mainloop.h" +#include "core/mainloop/mainloop_state_st.h" #include "app/config/config.h" #include "feature/hibernate/hibernate.h" +#include "app/config/or_state_st.h" + /** Return true iff our network is in some sense disabled or shutting down: * either we're hibernating, entering hibernation, or the network is turned * off with DisableNetwork. */ @@ -26,3 +35,136 @@ net_is_completely_disabled(void) { return get_options()->DisableNetwork || we_are_fully_hibernating(); } + +/** + * The time at which we've last seen "user activity" -- that is, any activity + * that should keep us as a participant on the network. + * + * This is not actually the true time. We will adjust this forward if + * our clock jumps, or if Tor is shut down for a while, so that the time + * since our last activity remains as it was before the jump or shutdown. + */ +static time_t last_user_activity_seen = 0; + +/** + * True iff we are currently a "network participant" -- that is, we + * are building circuits, fetching directory information, and so on. + **/ +static bool participating_on_network = false; + +/** + * Record the fact that we have seen "user activity" at the time now. Move + * "last activity seen" time forwards, but never backwards. + * + * If we were previously not participating on the network, set our + * participation status to true, and launch periodic events as appropriate. + **/ +void +note_user_activity(time_t now) +{ + last_user_activity_seen = MAX(now, last_user_activity_seen); + + if (! participating_on_network) { + log_notice(LD_GENERAL, "Tor is no longer dormant."); + set_network_participation(true); + schedule_rescan_periodic_events(); + } +} + +/** + * Change the time at which "user activitiy" was last seen to <b>now</b>. + * + * Unlike note_user_actity, this function sets the time without checking + * whether it is in the past, and without causing any rescan of periodic events + * or change in participation status. + */ +void +reset_user_activity(time_t now) +{ + last_user_activity_seen = now; +} + +/** + * Return the most recent time at which we recorded "user activity". + **/ +time_t +get_last_user_activity_time(void) +{ + return last_user_activity_seen; +} + +/** + * Set the field that remembers whether we are currently participating on the + * network. Does not schedule or un-schedule periodic events. + **/ +void +set_network_participation(bool participation) +{ + participating_on_network = participation; +} + +/** + * Return true iff we are currently participating on the network. + **/ +bool +is_participating_on_network(void) +{ + return participating_on_network; +} + +/** + * Update 'state' with the last time at which we were active on the network. + **/ +void +netstatus_flush_to_state(mainloop_state_t *state, time_t now) +{ + state->Dormant = ! participating_on_network; + if (participating_on_network) { + time_t sec_since_activity = MAX(0, now - last_user_activity_seen); + state->MinutesSinceUserActivity = (int)(sec_since_activity / 60); + } else { + state->MinutesSinceUserActivity = 0; + } +} + +/** + * Update our current view of network participation from an or_state_t object. + **/ +void +netstatus_load_from_state(const mainloop_state_t *state, time_t now) +{ + time_t last_activity; + if (state->Dormant == -1) { // Initial setup. + if (get_options()->DormantOnFirstStartup) { + last_activity = 0; + participating_on_network = false; + } else { + // Start up as active, treat activity as happening now. + last_activity = now; + participating_on_network = true; + } + } else if (state->Dormant) { + last_activity = 0; + participating_on_network = false; + } else { + last_activity = now - 60 * state->MinutesSinceUserActivity; + participating_on_network = true; + } + if (get_options()->DormantCanceledByStartup) { + last_activity = now; + participating_on_network = true; + } + reset_user_activity(last_activity); +} + +/** + * Adjust the time at which the user was last active by <b>seconds_diff</b> + * in response to a clock jump. + */ +void +netstatus_note_clock_jumped(time_t seconds_diff) +{ + time_t last_active = get_last_user_activity_time(); + if (last_active) + reset_user_activity(last_active + seconds_diff); +} diff --git a/src/core/mainloop/netstatus.h b/src/core/mainloop/netstatus.h index ae8547b30d..5f54e54553 100644 --- a/src/core/mainloop/netstatus.h +++ b/src/core/mainloop/netstatus.h @@ -1,13 +1,32 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file netstatus.h + * @brief Header for netstatus.c + **/ + #ifndef TOR_NETSTATUS_H #define TOR_NETSTATUS_H int net_is_disabled(void); int net_is_completely_disabled(void); -#endif +void note_user_activity(time_t now); +void reset_user_activity(time_t now); +time_t get_last_user_activity_time(void); + +void set_network_participation(bool participation); +bool is_participating_on_network(void); + +struct mainloop_state_t; + +void netstatus_flush_to_state(struct mainloop_state_t *state, time_t now); +void netstatus_load_from_state(const struct mainloop_state_t *state, + time_t now); +void netstatus_note_clock_jumped(time_t seconds_diff); + +#endif /* !defined(TOR_NETSTATUS_H) */ diff --git a/src/core/mainloop/periodic.c b/src/core/mainloop/periodic.c index 2651bbbc89..b5fd8fab61 100644 --- a/src/core/mainloop/periodic.c +++ b/src/core/mainloop/periodic.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2019, The Tor Project, Inc. */ +/* Copyright (c) 2015-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -6,9 +6,22 @@ * * \brief Generic backend for handling periodic events. * - * The events in this module are used by main.c to track items that need + * The events in this module are used 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. + * that they fire. See periodic_events[] in mainloop.c for examples. + * + * This module manages a global list of periodic_event_item_t objects, + * each corresponding to a single event. To register an event, pass it to + * periodic_events_register() when initializing your subsystem. + * + * Registering an event makes the periodic event subsystem know about it, but + * doesn't cause the event to get created immediately. Before the event can + * be started, periodic_event_connect_all() must be called by mainloop.c to + * connect all the events to Libevent. + * + * We expect that periodic_event_item_t objects will be statically allocated; + * we set them up and tear them down here, but we don't take ownership of + * them. */ #include "core/or/or.h" @@ -16,7 +29,6 @@ #include "app/config/config.h" #include "core/mainloop/mainloop.h" #include "core/mainloop/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 @@ -24,6 +36,12 @@ */ static const int MAX_INTERVAL = 10 * 365 * 86400; +/** + * Global list of periodic events that have been registered with + * <b>periodic_event_register</b>. + **/ +static smartlist_t *the_periodic_events = NULL; + /** Set the event <b>event</b> to run in <b>next_interval</b> seconds from * now. */ static void @@ -45,10 +63,6 @@ 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(); @@ -57,7 +71,7 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data) int next_interval = 0; if (!periodic_event_is_enabled(event)) { - /* The event got disabled from inside its callback; no need to + /* The event got disabled from inside its callback, or before: no need to * reschedule. */ return; } @@ -91,15 +105,16 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data) void periodic_event_reschedule(periodic_event_item_t *event) { - /* Don't reschedule a disabled event. */ - if (periodic_event_is_enabled(event)) { + /* Don't reschedule a disabled or uninitialized event. */ + if (event->ev && periodic_event_is_enabled(event)) { periodic_event_set_interval(event, 1); } } -/** Initializes the libevent backend for a periodic event. */ +/** Connects a periodic event to the Libevent backend. Does not launch the + * event immediately. */ void -periodic_event_setup(periodic_event_item_t *event) +periodic_event_connect(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."); @@ -117,7 +132,7 @@ 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"); + log_err(LD_BUG, "periodic_event_launch without periodic_event_connect"); tor_assert(0); } /* Event already enabled? This is a bug */ @@ -131,9 +146,9 @@ periodic_event_launch(periodic_event_item_t *event) periodic_event_dispatch(event->ev, event); } -/** Release all storage associated with <b>event</b> */ -void -periodic_event_destroy(periodic_event_item_t *event) +/** Disconnect and unregister the periodic event in <b>event</b> */ +static void +periodic_event_disconnect(periodic_event_item_t *event) { if (!event) return; @@ -177,3 +192,177 @@ periodic_event_disable(periodic_event_item_t *event) mainloop_event_cancel(event->ev); event->enabled = 0; } + +/** + * Disable an event, then schedule it to run once. + * Do nothing if the event was already disabled. + */ +void +periodic_event_schedule_and_disable(periodic_event_item_t *event) +{ + tor_assert(event); + if (!periodic_event_is_enabled(event)) + return; + + periodic_event_disable(event); + + mainloop_event_activate(event->ev); +} + +/** + * Add <b>item</b> to the list of periodic events. + * + * Note that <b>item</b> should be statically allocated: we do not + * take ownership of it. + **/ +void +periodic_events_register(periodic_event_item_t *item) +{ + if (!the_periodic_events) + the_periodic_events = smartlist_new(); + + if (BUG(smartlist_contains(the_periodic_events, item))) + return; + + smartlist_add(the_periodic_events, item); +} + +/** + * Make all registered periodic events connect to the libevent backend. + */ +void +periodic_events_connect_all(void) +{ + if (! the_periodic_events) + return; + + SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) { + if (item->ev) + continue; + periodic_event_connect(item); + } SMARTLIST_FOREACH_END(item); +} + +/** + * Reset all the registered 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 +periodic_events_reset_all(void) +{ + if (! the_periodic_events) + return; + + SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) { + if (!item->ev) + continue; + + periodic_event_reschedule(item); + } SMARTLIST_FOREACH_END(item); +} + +/** + * Return the registered periodic event whose name is <b>name</b>. + * Return NULL if no such event is found. + */ +periodic_event_item_t * +periodic_events_find(const char *name) +{ + if (! the_periodic_events) + return NULL; + + SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) { + if (strcmp(name, item->name) == 0) + return item; + } SMARTLIST_FOREACH_END(item); + return NULL; +} + +/** + * Start or stop registered periodic events, depending on our current set of + * roles. + * + * Invoked when our list of roles, or the net_disabled flag has changed. + **/ +void +periodic_events_rescan_by_roles(int roles, bool net_disabled) +{ + if (! the_periodic_events) + return; + + SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) { + if (!item->ev) + continue; + + int enable = !!(item->roles & roles); + + /* Handle the event flags. */ + if (net_disabled && + (item->flags & PERIODIC_EVENT_FLAG_NEED_NET)) { + enable = 0; + } + + /* Enable the event if needed. It is safe to enable an event that was + * already enabled. Same goes for disabling it. */ + if (enable) { + 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); + if (item->flags & PERIODIC_EVENT_FLAG_RUN_ON_DISABLE) { + periodic_event_schedule_and_disable(item); + } else { + periodic_event_disable(item); + } + } + } SMARTLIST_FOREACH_END(item); +} + +/** + * Invoked at shutdown: disconnect and unregister all periodic events. + * + * Does not free the periodic_event_item_t object themselves, because we do + * not own them. + */ +void +periodic_events_disconnect_all(void) +{ + if (! the_periodic_events) + return; + + SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) { + periodic_event_disconnect(item); + } SMARTLIST_FOREACH_END(item); + + smartlist_free(the_periodic_events); +} + +#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]. + * + * We use this to answer the question, "how many seconds is it from now until + * next" in periodic timer callbacks. Don't use it for other purposes + **/ +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; + } +} diff --git a/src/core/mainloop/periodic.h b/src/core/mainloop/periodic.h index e49fafd174..de556a6bdb 100644 --- a/src/core/mainloop/periodic.h +++ b/src/core/mainloop/periodic.h @@ -1,6 +1,11 @@ -/* Copyright (c) 2015-2019, The Tor Project, Inc. */ +/* Copyright (c) 2015-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file periodic.h + * @brief Header for periodic.c + **/ + #ifndef TOR_PERIODIC_H #define TOR_PERIODIC_H @@ -15,6 +20,10 @@ #define PERIODIC_EVENT_ROLE_BRIDGEAUTH (1U << 4) #define PERIODIC_EVENT_ROLE_HS_SERVICE (1U << 5) #define PERIODIC_EVENT_ROLE_DIRSERVER (1U << 6) +#define PERIODIC_EVENT_ROLE_CONTROLEV (1U << 7) + +#define PERIODIC_EVENT_ROLE_NET_PARTICIPANT (1U << 8) +#define PERIODIC_EVENT_ROLE_ALL (1U << 9) /* Helper macro to make it a bit less annoying to defined groups of roles that * are often used. */ @@ -25,10 +34,6 @@ /* 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. @@ -39,6 +44,11 @@ * the net_is_disabled() check. */ #define PERIODIC_EVENT_FLAG_NEED_NET (1U << 0) +/* Indicate that if the event is enabled, it needs to be run once before + * it becomes disabled. + */ +#define PERIODIC_EVENT_FLAG_RUN_ON_DISABLE (1U << 1) + /** 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 @@ -66,8 +76,10 @@ typedef struct periodic_event_item_t { } periodic_event_item_t; /** events will get their interval from first execution */ +#ifndef COCCI #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 } +#endif /* Return true iff the given event was setup before thus is enabled to be * scheduled. */ @@ -78,11 +90,20 @@ periodic_event_is_enabled(const periodic_event_item_t *item) } 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_connect(periodic_event_item_t *event); +//void periodic_event_disconnect(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); +void periodic_event_schedule_and_disable(periodic_event_item_t *event); -#endif /* !defined(TOR_PERIODIC_H) */ +void periodic_events_register(periodic_event_item_t *item); +void periodic_events_connect_all(void); +void periodic_events_reset_all(void); +periodic_event_item_t *periodic_events_find(const char *name); +void periodic_events_rescan_by_roles(int roles, bool net_disabled); +void periodic_events_disconnect_all(void); + +int safe_timer_diff(time_t now, time_t next); +#endif /* !defined(TOR_PERIODIC_H) */ |