aboutsummaryrefslogtreecommitdiff
path: root/src/core/mainloop/connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/mainloop/connection.c')
-rw-r--r--src/core/mainloop/connection.c947
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);