diff options
Diffstat (limited to 'src/core/mainloop/connection.c')
-rw-r--r-- | src/core/mainloop/connection.c | 947 |
1 files changed, 657 insertions, 290 deletions
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index 21d4332758..c827af7a9a 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,9 +65,9 @@ * 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 "app/config/resolve_addr.h" #include "core/mainloop/connection.h" #include "core/mainloop/mainloop.h" #include "core/mainloop/netstatus.h" @@ -82,18 +82,25 @@ #include "core/or/policies.h" #include "core/or/reasons.h" #include "core/or/relay.h" +#include "core/or/status.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" #include "feature/hs/hs_common.h" #include "feature/hs/hs_ident.h" +#include "feature/hs/hs_metrics.h" +#include "feature/metrics/metrics.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerlist.h" #include "feature/relay/dns.h" @@ -101,10 +108,14 @@ #include "feature/relay/routermode.h" #include "feature/rend/rendclient.h" #include "feature/rend/rendcommon.h" +#include "feature/stats/connstats.h" #include "feature/stats/rephist.h" +#include "feature/stats/bwhist.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/crypto_format.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" @@ -209,12 +220,17 @@ static smartlist_t *outgoing_addrs = NULL; case CONN_TYPE_AP_TRANS_LISTENER: \ case CONN_TYPE_AP_NATD_LISTENER: \ case CONN_TYPE_AP_DNS_LISTENER: \ - case CONN_TYPE_AP_HTTP_CONNECT_LISTENER + case CONN_TYPE_AP_HTTP_CONNECT_LISTENER: \ + case CONN_TYPE_METRICS_LISTENER /**************************************************************/ -/** Convert a connection_t* to an listener_connection_t*; assert if the cast - * is invalid. */ +/** + * Cast a `connection_t *` to a `listener_connection_t *`. + * + * Exit with an assertion failure if the input is not a + * `listener_connection_t`. + **/ listener_connection_t * TO_LISTENER_CONN(connection_t *c) { @@ -222,6 +238,18 @@ TO_LISTENER_CONN(connection_t *c) return DOWNCAST(listener_connection_t, c); } +/** + * Cast a `const connection_t *` to a `const listener_connection_t *`. + * + * Exit with an assertion failure if the input is not a + * `listener_connection_t`. + **/ +const listener_connection_t * +CONST_TO_LISTENER_CONN(const connection_t *c) +{ + return TO_LISTENER_CONN((connection_t *)c); +} + size_t connection_get_inbuf_len(connection_t *conn) { @@ -258,6 +286,8 @@ conn_type_to_string(int type) case CONN_TYPE_EXT_OR: return "Extended OR"; case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener"; case CONN_TYPE_AP_HTTP_CONNECT_LISTENER: return "HTTP tunnel listener"; + case CONN_TYPE_METRICS_LISTENER: return "Metrics listener"; + case CONN_TYPE_METRICS: return "Metrics"; default: log_warn(LD_BUG, "unknown connection type %d", type); tor_snprintf(buf, sizeof(buf), "unknown [%d]", type); @@ -345,13 +375,187 @@ conn_state_to_string(int type, int state) break; } + if (state == 0) { + return "uninitialized"; + } + log_warn(LD_BUG, "unknown connection state %d (type %d)", state, type); tor_snprintf(buf, sizeof(buf), "unknown state [%d] on unknown [%s] connection", state, conn_type_to_string(type)); + tor_assert_nonfatal_unreached_once(); return buf; } +/** + * Helper: describe the peer or address of connection @a conn in a + * human-readable manner. + * + * Returns a pointer to a static buffer; future calls to + * connection_describe_peer_internal() will invalidate this buffer. + * + * If <b>include_preposition</b> is true, include a preposition before the + * peer address. + * + * Nobody should parse the output of this function; it can and will change in + * future versions of tor. + **/ +static const char * +connection_describe_peer_internal(const connection_t *conn, + bool include_preposition) +{ + IF_BUG_ONCE(!conn) { + return "null peer"; + } + + static char peer_buf[256]; + const tor_addr_t *addr = &conn->addr; + const char *address = NULL; + const char *prep; + bool scrub = false; + char extra_buf[128]; + extra_buf[0] = 0; + + /* First, figure out the preposition to use */ + switch (conn->type) { + CASE_ANY_LISTENER_TYPE: + prep = "on"; + break; + case CONN_TYPE_EXIT: + prep = "to"; + break; + case CONN_TYPE_CONTROL: + case CONN_TYPE_AP: + case CONN_TYPE_EXT_OR: + prep = "from"; + break; + default: + prep = "with"; + break; + } + + /* Now figure out the address. */ + if (conn->socket_family == AF_UNIX) { + /* For unix sockets, we always use the `address` string. */ + address = conn->address ? conn->address : "unix socket"; + } else if (conn->type == CONN_TYPE_OR) { + /* For OR connections, we have a lot to do. */ + const or_connection_t *or_conn = CONST_TO_OR_CONN(conn); + /* We report the IDs we're talking to... */ + if (fast_digest_is_zero(or_conn->identity_digest)) { + // This could be a client, so scrub it. No identity to report. + scrub = true; + } else { + const ed25519_public_key_t *ed_id = + connection_or_get_alleged_ed25519_id(or_conn); + char ed_id_buf[ED25519_BASE64_LEN+1]; + char rsa_id_buf[HEX_DIGEST_LEN+1]; + if (ed_id) { + ed25519_public_to_base64(ed_id_buf, ed_id); + } else { + strlcpy(ed_id_buf, "<none>", sizeof(ed_id_buf)); + } + base16_encode(rsa_id_buf, sizeof(rsa_id_buf), + or_conn->identity_digest, DIGEST_LEN); + tor_snprintf(extra_buf, sizeof(extra_buf), + " ID=%s RSA_ID=%s", ed_id_buf, rsa_id_buf); + } + if (! scrub && (! tor_addr_eq(addr, &or_conn->canonical_orport.addr) || + conn->port != or_conn->canonical_orport.port)) { + /* We report canonical address, if it's different */ + char canonical_addr_buf[TOR_ADDR_BUF_LEN]; + if (tor_addr_to_str(canonical_addr_buf, &or_conn->canonical_orport.addr, + sizeof(canonical_addr_buf), 1)) { + tor_snprintf(extra_buf+strlen(extra_buf), + sizeof(extra_buf)-strlen(extra_buf), + " canonical_addr=%s:%"PRIu16, + canonical_addr_buf, + or_conn->canonical_orport.port); + } + } + } else if (conn->type == CONN_TYPE_EXIT) { + scrub = true; /* This is a client's request; scrub it with SafeLogging. */ + if (tor_addr_is_null(addr)) { + address = conn->address; + strlcpy(extra_buf, " (DNS lookup pending)", sizeof(extra_buf)); + } + } + + char addr_buf[TOR_ADDR_BUF_LEN]; + if (address == NULL) { + if (tor_addr_family(addr) == 0) { + address = "<unset>"; + } else { + address = tor_addr_to_str(addr_buf, addr, sizeof(addr_buf), 1); + if (!address) { + address = "<can't format!>"; + tor_assert_nonfatal_unreached_once(); + } + } + } + + char portbuf[7]; + portbuf[0]=0; + if (scrub && get_options()->SafeLogging_ != SAFELOG_SCRUB_NONE) { + address = "[scrubbed]"; + } else { + /* Only set the port if we're not scrubbing the address. */ + if (conn->port != 0) { + tor_snprintf(portbuf, sizeof(portbuf), ":%d", conn->port); + } + } + + const char *sp = include_preposition ? " " : ""; + if (! include_preposition) + prep = ""; + + tor_snprintf(peer_buf, sizeof(peer_buf), + "%s%s%s%s%s", prep, sp, address, portbuf, extra_buf); + return peer_buf; +} + +/** + * Describe the peer or address of connection @a conn in a + * human-readable manner. + * + * Returns a pointer to a static buffer; future calls to + * connection_describe_peer() or connection_describe() will invalidate this + * buffer. + * + * Nobody should parse the output of this function; it can and will change in + * future versions of tor. + **/ +const char * +connection_describe_peer(const connection_t *conn) +{ + return connection_describe_peer_internal(conn, false); +} + +/** + * Describe a connection for logging purposes. + * + * Returns a pointer to a static buffer; future calls to connection_describe() + * will invalidate this buffer. + * + * Nobody should parse the output of this function; it can and will change in + * future versions of tor. + **/ +const char * +connection_describe(const connection_t *conn) +{ + IF_BUG_ONCE(!conn) { + return "null connection"; + } + static char desc_buf[256]; + const char *peer = connection_describe_peer_internal(conn, true); + tor_snprintf(desc_buf, sizeof(desc_buf), + "%s connection (%s) %s", + conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state), + peer); + return desc_buf; +} + /** Allocate and return a new dir_connection_t, initialized as by * connection_init(). */ dir_connection_t * @@ -377,6 +581,7 @@ or_connection_new(int type, int socket_family) tor_assert(type == CONN_TYPE_OR || type == CONN_TYPE_EXT_OR); connection_init(now, TO_CONN(or_conn), type, socket_family); + tor_addr_make_unspec(&or_conn->canonical_orport.addr); connection_or_set_canonical(or_conn, 0); if (type == CONN_TYPE_EXT_OR) { @@ -641,7 +846,7 @@ connection_free_minimal(connection_t *conn) } } - tor_free(conn->address); + tor_str_wipe_and_free(conn->address); if (connection_speaks_cells(conn)) { or_connection_t *or_conn = TO_OR_CONN(conn); @@ -661,7 +866,7 @@ connection_free_minimal(connection_t *conn) } or_handshake_state_free(or_conn->handshake_state); or_conn->handshake_state = NULL; - tor_free(or_conn->nickname); + tor_str_wipe_and_free(or_conn->nickname); if (or_conn->chan) { /* Owww, this shouldn't happen, but... */ channel_t *base_chan = TLS_CHAN_TO_BASE(or_conn->chan); @@ -681,8 +886,8 @@ connection_free_minimal(connection_t *conn) } if (conn->type == CONN_TYPE_AP) { entry_connection_t *entry_conn = TO_ENTRY_CONN(conn); - tor_free(entry_conn->chosen_exit_name); - tor_free(entry_conn->original_dest_address); + tor_str_wipe_and_free(entry_conn->chosen_exit_name); + tor_str_wipe_and_free(entry_conn->original_dest_address); if (entry_conn->socks_request) socks_request_free(entry_conn->socks_request); if (entry_conn->pending_optimistic_data) { @@ -700,6 +905,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)); @@ -719,11 +925,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); @@ -850,11 +1052,11 @@ connection_close_immediate(connection_t *conn) tor_fragile_assert(); return; } - if (conn->outbuf_flushlen) { - log_info(LD_NET,"fd %d, type %s, state %s, %d bytes on outbuf.", + if (connection_get_outbuf_len(conn)) { + log_info(LD_NET,"fd %d, type %s, state %s, %"TOR_PRIuSZ" bytes on outbuf.", (int)conn->s, conn_type_to_string(conn->type), conn_state_to_string(conn->type, conn->state), - (int)conn->outbuf_flushlen); + buf_datalen(conn->outbuf)); } connection_unregister_events(conn); @@ -870,7 +1072,6 @@ connection_close_immediate(connection_t *conn) conn->linked_conn_is_closed = 1; if (conn->outbuf) buf_clear(conn->outbuf); - conn->outbuf_flushlen = 0; } /** Mark <b>conn</b> to be closed next time we loop through @@ -1199,7 +1400,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) */ @@ -1470,6 +1671,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.", @@ -1503,10 +1718,11 @@ connection_listener_new(const struct sockaddr *listensockaddr, } } + /* Force IPv4 and IPv6 traffic on for non-SOCKSPorts. + * Forcing options on isn't a good idea, see #32994 and #33607. */ if (type != CONN_TYPE_AP_LISTENER) { lis_conn->entry_cfg.ipv4_traffic = 1; lis_conn->entry_cfg.ipv6_traffic = 1; - lis_conn->entry_cfg.prefer_ipv6 = 0; } if (connection_add(conn) < 0) { /* no space, forget it */ @@ -1532,13 +1748,8 @@ connection_listener_new(const struct sockaddr *listensockaddr, */ connection_check_oos(get_n_open_sockets(), 0); - if (conn->socket_family == AF_UNIX) { - log_notice(LD_NET, "Opened %s on %s", - conn_type_to_string(type), conn->address); - } else { - log_notice(LD_NET, "Opened %s on %s", - conn_type_to_string(type), fmt_addrport(&addr, gotPort)); - } + log_notice(LD_NET, "Opened %s", connection_describe(conn)); + return conn; err: @@ -1648,7 +1859,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."); @@ -1819,6 +2030,10 @@ connection_handle_listener_read(connection_t *conn, int new_type) log_notice(LD_CONTROL, "New control connection opened from %s.", fmt_and_decorate_addr(&addr)); } + if (new_type == CONN_TYPE_METRICS) { + log_info(LD_CONTROL, "New metrics connection opened from %s.", + fmt_and_decorate_addr(&addr)); + } } else if (conn->socket_family == AF_UNIX && conn->type != CONN_TYPE_AP) { tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER); @@ -1846,6 +2061,9 @@ connection_handle_listener_read(connection_t *conn, int new_type) connection_mark_for_close(newconn); return 0; } + + note_connection(true /* inbound */, conn->socket_family); + return 0; } @@ -1871,7 +2089,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); @@ -1884,11 +2102,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; @@ -2012,6 +2235,8 @@ connection_connect_sockaddr,(connection_t *conn, } } + note_connection(false /* outbound */, conn->socket_family); + /* it succeeded. we're connected. */ log_fn(inprogress ? LOG_DEBUG : LOG_INFO, LD_NET, "Connection to socket %s (sock "TOR_SOCKET_T_FORMAT").", @@ -2045,22 +2270,13 @@ connection_connect_log_client_use_ip_version(const connection_t *conn) return; } - const int must_ipv4 = !fascist_firewall_use_ipv6(options); + const int must_ipv4 = !reachable_addr_use_ipv6(options); const int must_ipv6 = (options->ClientUseIPv4 == 0); const int pref_ipv6 = (conn->type == CONN_TYPE_OR - ? fascist_firewall_prefer_ipv6_orport(options) - : fascist_firewall_prefer_ipv6_dirport(options)); + ? reachable_addr_prefer_ipv6_orport(options) + : reachable_addr_prefer_ipv6_dirport(options)); tor_addr_t real_addr; - tor_addr_make_null(&real_addr, AF_UNSPEC); - - /* OR conns keep the original address in real_addr, as addr gets overwritten - * with the descriptor address */ - if (conn->type == CONN_TYPE_OR) { - const or_connection_t *or_conn = TO_OR_CONN((connection_t *)conn); - tor_addr_copy(&real_addr, &or_conn->real_addr); - } else if (conn->type == CONN_TYPE_DIR) { - tor_addr_copy(&real_addr, &conn->addr); - } + tor_addr_copy(&real_addr, &conn->addr); /* Check if we broke a mandatory address family restriction */ if ((must_ipv4 && tor_addr_family(&real_addr) == AF_INET6) @@ -2083,18 +2299,23 @@ connection_connect_log_client_use_ip_version(const connection_t *conn) return; } + if (reachable_addr_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)) { log_info(LD_NET, "Outgoing connection to %s doesn't satisfy " "ClientPreferIPv6%sPort %d, with ClientUseIPv4 %d, and " - "fascist_firewall_use_ipv6 %d (ClientUseIPv6 %d and UseBridges " + "reachable_addr_use_ipv6 %d (ClientUseIPv6 %d and UseBridges " "%d).", fmt_addr(&real_addr), conn->type == CONN_TYPE_OR ? "OR" : "Dir", conn->type == CONN_TYPE_OR ? options->ClientPreferIPv6ORPort : options->ClientPreferIPv6DirPort, - options->ClientUseIPv4, fascist_firewall_use_ipv6(options), + options->ClientUseIPv4, reachable_addr_use_ipv6(options), options->ClientUseIPv6, options->UseBridges); } } @@ -2128,9 +2349,9 @@ conn_get_outbound_address(sa_family_t family, ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT] [fam_index]; } else if (!tor_addr_is_null( - &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY] [fam_index])) { - ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY] [fam_index]; } } else { // All non-exit connections @@ -2139,9 +2360,9 @@ conn_get_outbound_address(sa_family_t family, ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_OR] [fam_index]; } else if (!tor_addr_is_null( - &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY] [fam_index])) { - ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY] [fam_index]; } } @@ -2261,9 +2482,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; @@ -2296,7 +2520,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; } @@ -2305,165 +2533,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) */ + + buf[0] = 5; /* version */ + + /* We have to use SOCKS5 authentication, if we have a + Socks5ProxyUsername or if we want to pass arguments to our + pluggable transport proxy: */ + if ((options->Socks5ProxyUsername) || + (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; + } + + connection_buf_add((char *)buf, 2 + buf[1], conn); + return 0; +} - case PROXY_SOCKS5: { - unsigned char buf[4]; /* fields: vers, num methods, method list */ +/** 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); - /* Send a SOCKS5 greeting (connect request must wait) */ + if (buf == NULL) { + ret = -1; + goto done; + } - buf[0] = 5; /* version */ + 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; - /* 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; - } + 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); - connection_buf_add((char *)buf, 2 + buf[1], 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 @@ -2494,8 +2802,8 @@ connection_read_https_proxy_response(connection_t *conn) if (parse_http_response(headers, &status_code, &date_header, NULL, &reason) < 0) { log_warn(LD_NET, - "Unparseable headers from proxy (connecting to '%s'). Closing.", - conn->address); + "Unparseable headers from proxy (%s). Closing.", + connection_describe(conn)); tor_free(headers); return -1; } @@ -2504,8 +2812,8 @@ connection_read_https_proxy_response(connection_t *conn) if (status_code == 200) { log_info(LD_NET, - "HTTPS connect to '%s' successful! (200 %s) Starting TLS.", - conn->address, escaped(reason)); + "HTTPS connect for %s successful! (200 %s) Starting TLS.", + connection_describe(conn), escaped(reason)); tor_free(reason); return 1; } @@ -2721,16 +3029,16 @@ connection_read_proxy_handshake(connection_t *conn) if (ret < 0) { if (reason) { - log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d (%s)", - conn->address, conn->port, escaped(reason)); + log_warn(LD_NET, "Proxy Client: unable to connect %s (%s)", + connection_describe(conn), escaped(reason)); tor_free(reason); } else { - log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d", - conn->address, conn->port); + log_warn(LD_NET, "Proxy Client: unable to connect %s", + connection_describe(conn)); } } else if (ret == 1) { - log_info(LD_NET, "Proxy Client: connection to %s:%d successful", - conn->address, conn->port); + log_info(LD_NET, "Proxy Client: %s successful", + connection_describe(conn)); } return ret; @@ -2841,7 +3149,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); @@ -2887,10 +3195,10 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) smartlist_t *replacements = smartlist_new(); const or_options_t *options = get_options(); int retval = 0; - const uint16_t old_or_port = router_get_advertised_or_port(options); + const uint16_t old_or_port = routerconf_find_or_port(options, AF_INET); const uint16_t old_or_port_ipv6 = - router_get_advertised_or_port_by_af(options,AF_INET6); - const uint16_t old_dir_port = router_get_advertised_dir_port(options, 0); + routerconf_find_or_port(options,AF_INET6); + const uint16_t old_dir_port = routerconf_find_dir_port(options, 0); SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { if (connection_is_listener(conn) && !conn->marked_for_close) @@ -2905,6 +3213,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; @@ -2916,8 +3228,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", + connection_describe(old_conn)); continue; + } connection_close_immediate(old_conn); connection_mark_for_close(old_conn); @@ -2938,12 +3253,13 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) smartlist_add(new_conns, new_conn); - log_notice(LD_NET, "Closed no-longer-configured %s on %s:%d " - "(replaced by %s:%d)", - conn_type_to_string(old_conn->type), old_conn->address, - old_conn->port, new_conn->address, new_conn->port); + char *old_desc = tor_strdup(connection_describe(old_conn)); + log_notice(LD_NET, "Closed no-longer-configured %s " + "(replaced by %s)", + old_desc, connection_describe(new_conn)); + tor_free(old_desc); } 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. */ @@ -2959,10 +3275,9 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) SMARTLIST_FOREACH(replacements, listener_replacement_t *, r, tor_free(r)); smartlist_free(replacements); - if (old_or_port != router_get_advertised_or_port(options) || - old_or_port_ipv6 != router_get_advertised_or_port_by_af(options, - AF_INET6) || - old_dir_port != router_get_advertised_dir_port(options, 0)) { + if (old_or_port != routerconf_find_or_port(options, AF_INET) || + old_or_port_ipv6 != routerconf_find_or_port(options, AF_INET6) || + old_dir_port != routerconf_find_dir_port(options, 0)) { /* Our chosen ORPort or DirPort is not what it used to be: the * descriptor we had (if any) should be regenerated. (We won't * automatically notice this because of changes in the option, @@ -3030,7 +3345,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) @@ -3140,12 +3455,12 @@ connection_bucket_write_limit(connection_t *conn, time_t now) { int base = RELAY_PAYLOAD_SIZE; int priority = conn->type != CONN_TYPE_DIR; - size_t conn_bucket = conn->outbuf_flushlen; + size_t conn_bucket = buf_datalen(conn->outbuf); size_t global_bucket_val = token_bucket_rw_get_write(&global_bucket); if (!connection_is_rate_limited(conn)) { /* be willing to write to local conns even if our buckets are empty */ - return conn->outbuf_flushlen; + return conn_bucket; } if (connection_speaks_cells(conn)) { @@ -3166,14 +3481,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>. + * + * If we are a directory authority, always answer dir requests thus true is + * always returned. * - * There are a lot of parameters we could use here: + * 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. @@ -3185,39 +3500,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 */ - } - - 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 true; /* we're already hitting our limits, no more please */ } - return 0; + return false; } /** When did we last tell the accounting subsystem about transmitted @@ -3234,23 +3550,33 @@ record_num_bytes_transferred_impl(connection_t *conn, /* Count bytes of answering direct and tunneled directory requests */ if (conn->type == CONN_TYPE_DIR && conn->purpose == DIR_PURPOSE_SERVER) { if (num_read > 0) - rep_hist_note_dir_bytes_read(num_read, now); + bwhist_note_dir_bytes_read(num_read, now); if (num_written > 0) - rep_hist_note_dir_bytes_written(num_written, now); + bwhist_note_dir_bytes_written(num_written, now); } + /* Linked connections and internal IPs aren't counted for statistics or + * accounting: + * - counting linked connections would double-count BEGINDIR bytes, because + * they are sent as Dir bytes on the linked connection, and OR bytes on + * the OR connection; + * - relays and clients don't connect to internal IPs, unless specifically + * configured to do so. If they are configured that way, we don't count + * internal bytes. + */ if (!connection_is_rate_limited(conn)) - return; /* local IPs are free */ + return; + const bool is_ipv6 = (conn->socket_family == AF_INET6); if (conn->type == CONN_TYPE_OR) - rep_hist_note_or_conn_bytes(conn->global_identifier, num_read, - num_written, now); + conn_stats_note_or_conn_bytes(conn->global_identifier, num_read, + num_written, now, is_ipv6); if (num_read > 0) { - rep_hist_note_bytes_read(num_read, now); + bwhist_note_bytes_read(num_read, now, is_ipv6); } if (num_written > 0) { - rep_hist_note_bytes_written(num_written, now); + bwhist_note_bytes_written(num_written, now, is_ipv6); } if (conn->type == CONN_TYPE_EXIT) rep_hist_note_exit_bytes(conn->port, num_written, num_read); @@ -3576,6 +3902,8 @@ connection_handle_read_impl(connection_t *conn) return connection_handle_listener_read(conn, CONN_TYPE_DIR); case CONN_TYPE_CONTROL_LISTENER: return connection_handle_listener_read(conn, CONN_TYPE_CONTROL); + case CONN_TYPE_METRICS_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_METRICS); case CONN_TYPE_AP_DNS_LISTENER: /* This should never happen; eventdns.c handles the reads here. */ tor_fragile_assert(); @@ -3696,6 +4024,12 @@ connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read, at_most = connection_bucket_read_limit(conn, approx_time()); } + /* Do not allow inbuf to grow past BUF_MAX_LEN. */ + const ssize_t maximum = BUF_MAX_LEN - buf_datalen(conn->inbuf); + if (at_most > maximum) { + at_most = maximum; + } + slack_in_buf = buf_slack(conn->inbuf); again: if ((size_t)at_most > slack_in_buf && slack_in_buf >= 1024) { @@ -3733,17 +4067,14 @@ connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read, switch (result) { case TOR_TLS_CLOSE: case TOR_TLS_ERROR_IO: - log_debug(LD_NET,"TLS connection closed %son read. Closing. " - "(Nickname %s, address %s)", - result == TOR_TLS_CLOSE ? "cleanly " : "", - or_conn->nickname ? or_conn->nickname : "not set", - conn->address); + log_debug(LD_NET,"TLS %s closed %son read. Closing.", + connection_describe(conn), + result == TOR_TLS_CLOSE ? "cleanly " : ""); return result; CASE_TOR_TLS_ERROR_ANY_NONIO: - log_debug(LD_NET,"tls error [%s]. breaking (nickname %s, address %s).", + log_debug(LD_NET,"tls error [%s] from %s. Breaking.", tor_tls_err_to_string(result), - or_conn->nickname ? or_conn->nickname : "not set", - conn->address); + connection_describe(conn)); return result; case TOR_TLS_WANTWRITE: connection_start_writing(conn); @@ -3784,12 +4115,7 @@ connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read, result, (long)n_read, (long)n_written); } else if (conn->linked) { if (conn->linked_conn) { - result = buf_move_to_buf(conn->inbuf, conn->linked_conn->outbuf, - &conn->linked_conn->outbuf_flushlen); - if (BUG(result<0)) { - log_warn(LD_BUG, "reading from linked connection buffer failed."); - return -1; - } + result = (int) buf_move_all(conn->inbuf, conn->linked_conn->outbuf); } else { result = 0; } @@ -3824,6 +4150,14 @@ connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read, /* change *max_to_read */ *max_to_read = at_most - n_read; + /* Onion service application connection. Note read bytes for metrics. */ + if (CONN_IS_EDGE(conn) && TO_EDGE_CONN(conn)->hs_ident) { + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + hs_metrics_app_read_bytes(&edge_conn->hs_ident->identity_pk, + edge_conn->hs_ident->orig_virtual_port, + n_read); + } + /* Update edge_conn->n_read */ if (conn->type == CONN_TYPE_AP) { edge_connection_t *edge_conn = TO_EDGE_CONN(conn); @@ -3893,12 +4227,11 @@ connection_fetch_from_buf_http(connection_t *conn, body_out, body_used, max_bodylen, force_complete); } -/** Return conn-\>outbuf_flushlen: how many bytes conn wants to flush - * from its outbuf. */ +/** Return true if this connection has data to flush. */ int connection_wants_to_flush(connection_t *conn) { - return conn->outbuf_flushlen > 0; + return connection_get_outbuf_len(conn) > 0; } /** Are there too many bytes on edge connection <b>conn</b>'s outbuf to @@ -3908,7 +4241,7 @@ connection_wants_to_flush(connection_t *conn) int connection_outbuf_too_full(connection_t *conn) { - return (conn->outbuf_flushlen > 10*CELL_PAYLOAD_SIZE); + return connection_get_outbuf_len(conn) > 10*CELL_PAYLOAD_SIZE; } /** @@ -3943,9 +4276,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. @@ -4034,7 +4367,7 @@ connection_handle_write_impl(connection_t *conn, int force) return -1; } - max_to_write = force ? (ssize_t)conn->outbuf_flushlen + max_to_write = force ? (ssize_t)buf_datalen(conn->outbuf) : connection_bucket_write_limit(conn, now); if (connection_speaks_cells(conn) && @@ -4066,7 +4399,7 @@ connection_handle_write_impl(connection_t *conn, int force) /* else open, or closing */ initial_size = buf_datalen(conn->outbuf); result = buf_flush_to_tls(conn->outbuf, or_conn->tls, - max_to_write, &conn->outbuf_flushlen); + max_to_write); if (result >= 0) update_send_buffer_size(conn->s); @@ -4080,6 +4413,7 @@ connection_handle_write_impl(connection_t *conn, int force) switch (result) { CASE_TOR_TLS_ERROR_ANY: case TOR_TLS_CLOSE: + or_conn->tls_error = result; log_info(LD_NET, result != TOR_TLS_CLOSE ? "tls error. breaking.":"TLS connection closed on flush"); /* Don't flush; connection is dead. */ @@ -4131,7 +4465,7 @@ connection_handle_write_impl(connection_t *conn, int force) } else { CONN_LOG_PROTECT(conn, result = buf_flush_to_socket(conn->outbuf, conn->s, - max_to_write, &conn->outbuf_flushlen)); + max_to_write)); if (result < 0) { if (CONN_IS_EDGE(conn)) connection_edge_end_errno(TO_EDGE_CONN(conn)); @@ -4287,10 +4621,10 @@ connection_write_to_buf_failed(connection_t *conn) /** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: * * Called when an attempt to add bytes on <b>conn</b>'s outbuf has succeeded: - * record the number of bytes added. + * start writing if appropriate. */ static void -connection_write_to_buf_commit(connection_t *conn, size_t len) +connection_write_to_buf_commit(connection_t *conn) { /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING * state, we don't want to try to write it right away, since @@ -4299,7 +4633,6 @@ connection_write_to_buf_commit(connection_t *conn, size_t len) if (conn->write_event) { connection_start_writing(conn); } - conn->outbuf_flushlen += len; } /** Append <b>len</b> bytes of <b>string</b> onto <b>conn</b>'s @@ -4322,25 +4655,37 @@ connection_write_to_buf_impl_,(const char *string, size_t len, if (!connection_may_write_to_buf(conn)) return; - size_t written; - if (zlib) { - size_t old_datalen = buf_datalen(conn->outbuf); dir_connection_t *dir_conn = TO_DIR_CONN(conn); int done = zlib < 0; CONN_LOG_PROTECT(conn, r = buf_add_compress(conn->outbuf, dir_conn->compress_state, string, len, done)); - written = buf_datalen(conn->outbuf) - old_datalen; } else { CONN_LOG_PROTECT(conn, r = buf_add(conn->outbuf, string, len)); - written = len; } if (r < 0) { connection_write_to_buf_failed(conn); return; } - connection_write_to_buf_commit(conn, written); + connection_write_to_buf_commit(conn); +} + +/** + * 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 @@ -4368,7 +4713,7 @@ connection_buf_add_buf(connection_t *conn, buf_t *buf) return; buf_move_all(conn->outbuf, buf); - connection_write_to_buf_commit(conn, len); + connection_write_to_buf_commit(conn); } #define CONN_GET_ALL_TEMPLATE(var, test) \ @@ -4457,6 +4802,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. @@ -4568,7 +4923,7 @@ any_other_active_or_conns(const or_connection_t *this_conn) connection_t *conn = connection_get_another_active_or_conn(this_conn); if (conn != NULL) { log_debug(LD_DIR, "%s: Found an OR connection: %s", - __func__, conn->address); + __func__, connection_describe(conn)); return 1; } @@ -4589,6 +4944,7 @@ connection_is_listener(connection_t *conn) conn->type == CONN_TYPE_AP_NATD_LISTENER || conn->type == CONN_TYPE_AP_HTTP_CONNECT_LISTENER || conn->type == CONN_TYPE_DIR_LISTENER || + conn->type == CONN_TYPE_METRICS_LISTENER || conn->type == CONN_TYPE_CONTROL_LISTENER) return 1; return 0; @@ -4718,7 +5074,7 @@ client_check_address_changed(tor_socket_t sock) smartlist_clear(outgoing_addrs); smartlist_add(outgoing_addrs, tor_memdup(&out_addr, sizeof(tor_addr_t))); /* We'll need to resolve ourselves again. */ - reset_last_resolved_addr(); + resolved_addr_reset_last(AF_INET); /* Okay, now change our keys. */ ip_address_changed(1); } @@ -4772,6 +5128,8 @@ connection_process_inbuf(connection_t *conn, int package_partial) return connection_dir_process_inbuf(TO_DIR_CONN(conn)); case CONN_TYPE_CONTROL: return connection_control_process_inbuf(TO_CONTROL_CONN(conn)); + case CONN_TYPE_METRICS: + return metrics_connection_process_inbuf(conn); default: log_err(LD_BUG,"got unexpected conn type %d.", conn->type); tor_fragile_assert(); @@ -4830,6 +5188,8 @@ connection_finished_flushing(connection_t *conn) return connection_dir_finished_flushing(TO_DIR_CONN(conn)); case CONN_TYPE_CONTROL: return connection_control_finished_flushing(TO_CONTROL_CONN(conn)); + case CONN_TYPE_METRICS: + return metrics_connection_finished_flushing(conn); default: log_err(LD_BUG,"got unexpected conn type %d.", conn->type); tor_fragile_assert(); @@ -4837,10 +5197,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 @@ -4885,6 +5245,8 @@ connection_reached_eof(connection_t *conn) return connection_dir_reached_eof(TO_DIR_CONN(conn)); case CONN_TYPE_CONTROL: return connection_control_reached_eof(TO_CONTROL_CONN(conn)); + case CONN_TYPE_METRICS: + return metrics_connection_reached_eof(conn); default: log_err(LD_BUG,"got unexpected conn type %d.", conn->type); tor_fragile_assert(); @@ -5246,18 +5608,6 @@ assert_connection_ok(connection_t *conn, time_t now) if (conn->linked) tor_assert(!SOCKET_OK(conn->s)); - if (conn->outbuf_flushlen > 0) { - /* With optimistic data, we may have queued data in - * EXIT_CONN_STATE_RESOLVING while the conn is not yet marked to writing. - * */ - tor_assert((conn->type == CONN_TYPE_EXIT && - conn->state == EXIT_CONN_STATE_RESOLVING) || - connection_is_writing(conn) || - conn->write_blocked_on_bw || - (CONN_IS_EDGE(conn) && - TO_EDGE_CONN(conn)->edge_blocked_on_circ)); - } - if (conn->hold_open_until_flushed) tor_assert(conn->marked_for_close); @@ -5299,7 +5649,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,23 +5697,29 @@ assert_connection_ok(connection_t *conn, time_t now) tor_assert(conn->state >= CONTROL_CONN_STATE_MIN_); tor_assert(conn->state <= CONTROL_CONN_STATE_MAX_); break; + case CONN_TYPE_METRICS: + /* No state. */ + break; default: tor_assert(0); } } /** Fills <b>addr</b> and <b>port</b> with the details of the global - * proxy server we are using. - * <b>conn</b> contains the connection we are using the proxy for. + * 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 @@ -5379,6 +5735,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; } @@ -5400,6 +5757,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); @@ -5415,11 +5779,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.", @@ -5435,6 +5801,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); |