aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/channel.c8
-rw-r--r--src/or/channel.h4
-rw-r--r--src/or/channeltls.c2
-rw-r--r--src/or/circuitbuild.c227
-rw-r--r--src/or/circuitbuild.h5
-rw-r--r--src/or/circuitlist.c6
-rw-r--r--src/or/config.c33
-rw-r--r--src/or/connection.c304
-rw-r--r--src/or/connection.h23
-rw-r--r--src/or/connection_or.c4
-rw-r--r--src/or/connection_or.h2
-rw-r--r--src/or/dirserv.c14
-rw-r--r--src/or/dirvote.c10
-rw-r--r--src/or/dirvote.h6
-rw-r--r--src/or/hibernate.c2
-rw-r--r--src/or/main.c21
-rw-r--r--src/or/main.h4
-rw-r--r--src/or/networkstatus.c6
-rw-r--r--src/or/nodelist.c28
-rw-r--r--src/or/onion.c4
-rw-r--r--src/or/or.h10
-rw-r--r--src/or/policies.c4
-rw-r--r--src/or/rendclient.c48
-rw-r--r--src/or/rendservice.c11
-rw-r--r--src/or/rendservice.h2
-rw-r--r--src/or/router.c9
-rw-r--r--src/or/routerlist.c51
-rw-r--r--src/or/routerlist.h3
-rw-r--r--src/or/shared_random.c4
29 files changed, 719 insertions, 136 deletions
diff --git a/src/or/channel.c b/src/or/channel.c
index 87fa721089..6a78b21988 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -838,7 +838,7 @@ channel_free(channel_t *chan)
}
/* Call a free method if there is one */
- if (chan->free) chan->free(chan);
+ if (chan->free_fn) chan->free_fn(chan);
channel_clear_remote_end(chan);
@@ -878,7 +878,7 @@ channel_listener_free(channel_listener_t *chan_l)
tor_assert(!(chan_l->registered));
/* Call a free method if there is one */
- if (chan_l->free) chan_l->free(chan_l);
+ if (chan_l->free_fn) chan_l->free_fn(chan_l);
/*
* We're in CLOSED or ERROR, so the incoming channel queue is already
@@ -916,7 +916,7 @@ channel_force_free(channel_t *chan)
}
/* Call a free method if there is one */
- if (chan->free) chan->free(chan);
+ if (chan->free_fn) chan->free_fn(chan);
channel_clear_remote_end(chan);
@@ -958,7 +958,7 @@ channel_listener_force_free(channel_listener_t *chan_l)
chan_l);
/* Call a free method if there is one */
- if (chan_l->free) chan_l->free(chan_l);
+ if (chan_l->free_fn) chan_l->free_fn(chan_l);
/*
* The incoming list just gets emptied and freed; we request close on
diff --git a/src/or/channel.h b/src/or/channel.h
index 78e1b71014..a711b56d44 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -90,7 +90,7 @@ struct channel_s {
/* Methods implemented by the lower layer */
/** Free a channel */
- void (*free)(channel_t *);
+ void (*free_fn)(channel_t *);
/** Close an open channel */
void (*close)(channel_t *);
/** Describe the transport subclass for this channel */
@@ -273,7 +273,7 @@ struct channel_listener_s {
/* Methods implemented by the lower layer */
/** Free a channel */
- void (*free)(channel_listener_t *);
+ void (*free_fn)(channel_listener_t *);
/** Close an open channel */
void (*close)(channel_listener_t *);
/** Describe the transport subclass for this channel */
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index a62f80ef91..9c2411ede8 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -117,7 +117,7 @@ channel_tls_common_init(channel_tls_t *tlschan)
chan->state = CHANNEL_STATE_OPENING;
chan->close = channel_tls_close_method;
chan->describe_transport = channel_tls_describe_transport_method;
- chan->free = channel_tls_free_method;
+ chan->free_fn = channel_tls_free_method;
chan->get_overhead_estimate = channel_tls_get_overhead_estimate_method;
chan->get_remote_addr = channel_tls_get_remote_addr_method;
chan->get_remote_descr = channel_tls_get_remote_descr_method;
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 14d40150db..12c75530e2 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -58,7 +58,6 @@ static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
static int onion_extend_cpath(origin_circuit_t *circ);
static int count_acceptable_nodes(smartlist_t *routers);
static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
-static int circuits_can_use_ntor(void);
/** This function tries to get a channel to the specified endpoint,
* and then calls command_setup_channel() to give it the right
@@ -365,7 +364,7 @@ circuit_rep_hist_note_result(origin_circuit_t *circ)
} while (hop!=circ->cpath);
}
-/** Return 1 iff at least one node in circ's cpath supports ntor. */
+/** Return 1 iff every node in circ's cpath definitely supports ntor. */
static int
circuit_cpath_supports_ntor(const origin_circuit_t *circ)
{
@@ -373,16 +372,19 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ)
cpath = head = circ->cpath;
do {
- if (cpath->extend_info &&
- !tor_mem_is_zero(
- (const char*)cpath->extend_info->curve25519_onion_key.public_key,
- CURVE25519_PUBKEY_LEN))
- return 1;
+ /* if the extend_info is missing, we can't tell if it supports ntor */
+ if (!cpath->extend_info) {
+ return 0;
+ }
+ /* if the key is blank, it definitely doesn't support ntor */
+ if (!extend_info_supports_ntor(cpath->extend_info)) {
+ return 0;
+ }
cpath = cpath->next;
} while (cpath != head);
- return 0;
+ return 1;
}
/** Pick all the entries in our cpath. Stop and return 0 when we're
@@ -390,41 +392,61 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ)
static int
onion_populate_cpath(origin_circuit_t *circ)
{
- int n_tries = 0;
- const int using_ntor = circuits_can_use_ntor();
+ int r = 0;
-#define MAX_POPULATE_ATTEMPTS 32
+ /* onion_extend_cpath assumes these are non-NULL */
+ tor_assert(circ);
+ tor_assert(circ->build_state);
- while (1) {
- int r = onion_extend_cpath(circ);
+ while (r == 0) {
+ r = onion_extend_cpath(circ);
if (r < 0) {
log_info(LD_CIRC,"Generating cpath hop failed.");
return -1;
}
- if (r == 1) {
- /* This circuit doesn't need/shouldn't be forced to have an ntor hop */
- if (circ->build_state->desired_path_len <= 1 || ! using_ntor)
- return 0;
+ }
- /* This circuit has an ntor hop. great! */
- if (circuit_cpath_supports_ntor(circ))
- return 0;
+ /* The path is complete */
+ tor_assert(r == 1);
- /* No node in the circuit supports ntor. Have we already tried too many
- * times? */
- if (++n_tries >= MAX_POPULATE_ATTEMPTS)
- break;
+ /* Does every node in this path support ntor? */
+ int path_supports_ntor = circuit_cpath_supports_ntor(circ);
- /* Clear the path and retry */
- circuit_clear_cpath(circ);
+ /* We would like every path to support ntor, but we have to allow for some
+ * edge cases. */
+ tor_assert(circuit_get_cpath_len(circ));
+ if (circuit_can_use_tap(circ)) {
+ /* Circuits from clients to intro points, and hidden services to
+ * rend points do not support ntor, because the hidden service protocol
+ * does not include ntor onion keys. This is also true for Tor2web clients
+ * and Single Onion Services. */
+ return 0;
+ }
+
+ if (circuit_get_cpath_len(circ) == 1) {
+ /* Allow for bootstrapping: when we're fetching directly from a fallback,
+ * authority, or bridge, we have no way of knowing its ntor onion key
+ * before we connect to it. So instead, we try connecting, and end up using
+ * CREATE_FAST. */
+ tor_assert(circ->cpath);
+ tor_assert(circ->cpath->extend_info);
+ const node_t *node = node_get_by_id(
+ circ->cpath->extend_info->identity_digest);
+ /* If we don't know the node and its descriptor, we must be bootstrapping.
+ */
+ if (!node || !node_has_descriptor(node)) {
+ return 0;
}
}
- log_warn(LD_CIRC, "I tried for %d times, but I couldn't build a %d-hop "
- "circuit with at least one node that supports ntor.",
- MAX_POPULATE_ATTEMPTS,
- circ->build_state->desired_path_len);
- return -1;
+ if (BUG(!path_supports_ntor)) {
+ /* If we're building a multi-hop path, and it's not one of the HS or
+ * bootstrapping exceptions, and it doesn't support ntor, something has
+ * gone wrong. */
+ return -1;
+ }
+
+ return 0;
}
/** Create and return a new origin circuit. Initialize its purpose and
@@ -757,10 +779,13 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ)
tor_assert(circ->cpath);
tor_assert(circ->cpath->extend_info);
- if (!circ->cpath->extend_info->onion_key)
- return 1; /* our hand is forced: only a create_fast will work. */
+ if (!circuit_has_usable_onion_key(circ)) {
+ /* We don't have ntor, and we don't have or can't use TAP,
+ * so our hand is forced: only a create_fast will work. */
+ return 1;
+ }
if (public_server_mode(options)) {
- /* We're a server, and we know an onion key. We can choose.
+ /* We're a server, and we have a usable onion key. We can choose.
* Prefer to blend our circuit into the other circuits we are
* creating on behalf of others. */
return 0;
@@ -785,30 +810,20 @@ circuit_timeout_want_to_count_circ(origin_circuit_t *circ)
&& circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN;
}
-/** Return true if the ntor handshake is enabled in the configuration, or if
- * it's been set to "auto" in the configuration and it's enabled in the
- * consensus. */
-static int
-circuits_can_use_ntor(void)
-{
- const or_options_t *options = get_options();
- if (options->UseNTorHandshake != -1)
- return options->UseNTorHandshake;
- return networkstatus_get_param(NULL, "UseNTorHandshake", 0, 0, 1);
-}
-
/** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b>
* directly, and set *<b>cell_type_out</b> and *<b>handshake_type_out</b>
- * accordingly. */
+ * accordingly.
+ * Note that TAP handshakes are only used for direct connections:
+ * - from Tor2web to intro points not in the client's consensus, and
+ * - from Single Onions to rend points not in the service's consensus.
+ * This is checked in onion_populate_cpath. */
static void
circuit_pick_create_handshake(uint8_t *cell_type_out,
uint16_t *handshake_type_out,
const extend_info_t *ei)
{
- /* XXXX029 Remove support for deciding to use TAP. */
- if (!tor_mem_is_zero((const char*)ei->curve25519_onion_key.public_key,
- CURVE25519_PUBKEY_LEN) &&
- circuits_can_use_ntor()) {
+ /* XXXX030 Remove support for deciding to use TAP. */
+ if (extend_info_supports_ntor(ei)) {
*cell_type_out = CELL_CREATE2;
*handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR;
return;
@@ -822,7 +837,11 @@ circuit_pick_create_handshake(uint8_t *cell_type_out,
* directly, and set *<b>handshake_type_out</b> accordingly. Decide whether,
* in extending through <b>node</b> to do so, we should use an EXTEND2 or an
* EXTEND cell to do so, and set *<b>cell_type_out</b> and
- * *<b>create_cell_type_out</b> accordingly. */
+ * *<b>create_cell_type_out</b> accordingly.
+ * Note that TAP handshakes are only used for extend handshakes:
+ * - from clients to intro points, and
+ * - from hidden services to rend points.
+ * This is checked in onion_populate_cpath. */
static void
circuit_pick_extend_handshake(uint8_t *cell_type_out,
uint8_t *create_cell_type_out,
@@ -833,11 +852,25 @@ circuit_pick_extend_handshake(uint8_t *cell_type_out,
uint8_t t;
circuit_pick_create_handshake(&t, handshake_type_out, ei);
- /* XXXX029 Remove support for deciding to use TAP. */
- if (node_prev &&
- *handshake_type_out != ONION_HANDSHAKE_TYPE_TAP &&
+ /* XXXX030 Remove support for deciding to use TAP. */
+
+ /* It is an error to extend if there is no previous node. */
+ if (BUG(node_prev == NULL)) {
+ *cell_type_out = RELAY_COMMAND_EXTEND;
+ *create_cell_type_out = CELL_CREATE;
+ return;
+ }
+
+ /* It is an error for a node with a known version to be so old it does not
+ * support ntor. */
+ tor_assert_nonfatal(routerstatus_version_supports_ntor(node_prev->rs, 1));
+
+ /* Assume relays without tor versions or routerstatuses support ntor.
+ * The authorities enforce ntor support, and assuming and failing is better
+ * than allowing a malicious node to perform a protocol downgrade to TAP. */
+ if (*handshake_type_out != ONION_HANDSHAKE_TYPE_TAP &&
(node_has_curve25519_onion_key(node_prev) ||
- (node_prev->rs && node_prev->rs->version_supports_extend2_cells))) {
+ (routerstatus_version_supports_ntor(node_prev->rs, 1)))) {
*cell_type_out = RELAY_COMMAND_EXTEND2;
*create_cell_type_out = CELL_CREATE2;
} else {
@@ -2058,15 +2091,18 @@ count_acceptable_nodes(smartlist_t *nodes)
if (! node->is_running)
// log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i);
continue;
+ /* XXX This clause makes us count incorrectly: if AllowInvalidRouters
+ * allows this node in some places, then we're getting an inaccurate
+ * count. For now, be conservative and don't count it. But later we
+ * should try to be smarter. */
if (! node->is_valid)
// log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
continue;
if (! node_has_descriptor(node))
continue;
- /* XXX This clause makes us count incorrectly: if AllowInvalidRouters
- * allows this node in some places, then we're getting an inaccurate
- * count. For now, be conservative and don't count it. But later we
- * should try to be smarter. */
+ /* The node has a descriptor, so we can just check the ntor key directly */
+ if (!node_has_curve25519_onion_key(node))
+ continue;
++num;
} SMARTLIST_FOREACH_END(node);
@@ -2356,6 +2392,14 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
log_warn(LD_CIRC, "Could not choose valid address for %s",
node->ri ? node->ri->nickname : node->rs->nickname);
+ /* Every node we connect or extend to must support ntor */
+ if (!node_has_curve25519_onion_key(node)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
+ "Attempted to create extend_info for a node that does not support "
+ "ntor: %s", node_describe(node));
+ return NULL;
+ }
+
if (valid_addr && node->ri)
return extend_info_new(node->ri->nickname,
node->identity,
@@ -2441,3 +2485,66 @@ extend_info_addr_is_allowed(const tor_addr_t *addr)
return 0;
}
+/* Does ei have a valid TAP key? */
+int
+extend_info_supports_tap(const extend_info_t* ei)
+{
+ tor_assert(ei);
+ /* Valid TAP keys are not NULL */
+ return ei->onion_key != NULL;
+}
+
+/* Does ei have a valid ntor key? */
+int
+extend_info_supports_ntor(const extend_info_t* ei)
+{
+ tor_assert(ei);
+ /* Valid ntor keys have at least one non-zero byte */
+ return !tor_mem_is_zero(
+ (const char*)ei->curve25519_onion_key.public_key,
+ CURVE25519_PUBKEY_LEN);
+}
+
+/* Is circuit purpose allowed to use the deprecated TAP encryption protocol?
+ * The hidden service protocol still uses TAP for some connections, because
+ * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */
+static int
+circuit_purpose_can_use_tap_impl(uint8_t purpose)
+{
+ return (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
+ purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
+}
+
+/* Is circ allowed to use the deprecated TAP encryption protocol?
+ * The hidden service protocol still uses TAP for some connections, because
+ * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */
+int
+circuit_can_use_tap(const origin_circuit_t *circ)
+{
+ tor_assert(circ);
+ tor_assert(circ->cpath);
+ tor_assert(circ->cpath->extend_info);
+ return (circuit_purpose_can_use_tap_impl(circ->base_.purpose) &&
+ extend_info_supports_tap(circ->cpath->extend_info));
+}
+
+/* Does circ have an onion key which it's allowed to use? */
+int
+circuit_has_usable_onion_key(const origin_circuit_t *circ)
+{
+ tor_assert(circ);
+ tor_assert(circ->cpath);
+ tor_assert(circ->cpath->extend_info);
+ return (extend_info_supports_ntor(circ->cpath->extend_info) ||
+ circuit_can_use_tap(circ));
+}
+
+/* Does ei have an onion key which it would prefer to use?
+ * Currently, we prefer ntor keys*/
+int
+extend_info_has_preferred_onion_key(const extend_info_t* ei)
+{
+ tor_assert(ei);
+ return extend_info_supports_ntor(ei);
+}
+
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 7f5fd511a9..1244601f71 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -54,6 +54,11 @@ extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect);
extend_info_t *extend_info_dup(extend_info_t *info);
void extend_info_free(extend_info_t *info);
int extend_info_addr_is_allowed(const tor_addr_t *addr);
+int extend_info_supports_tap(const extend_info_t* ei);
+int extend_info_supports_ntor(const extend_info_t* ei);
+int circuit_can_use_tap(const origin_circuit_t *circ);
+int circuit_has_usable_onion_key(const origin_circuit_t *circ);
+int extend_info_has_preferred_onion_key(const extend_info_t* ei);
const node_t *build_state_get_exit_node(cpath_build_state_t *state);
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 5c691644a4..3c92baa274 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -1613,7 +1613,8 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
return best;
}
-/** Return the number of hops in circuit's path. */
+/** Return the number of hops in circuit's path. If circ has no entries,
+ * or is NULL, returns 0. */
int
circuit_get_cpath_len(origin_circuit_t *circ)
{
@@ -1629,7 +1630,8 @@ circuit_get_cpath_len(origin_circuit_t *circ)
}
/** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there
- * aren't that many hops in the list. */
+ * aren't that many hops in the list. <b>hopnum</b> starts at 1.
+ * Returns NULL if <b>hopnum</b> is 0 or negative. */
crypt_path_t *
circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum)
{
diff --git a/src/or/config.c b/src/or/config.c
index 10002ff620..9c5514f1da 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -211,6 +211,7 @@ static config_var_t option_vars_[] = {
V(CountPrivateBandwidth, BOOL, "0"),
V(DataDirectory, FILENAME, NULL),
V(DataDirectoryGroupReadable, BOOL, "0"),
+ V(DisableOOSCheck, BOOL, "1"),
V(DisableNetwork, BOOL, "0"),
V(DirAllowPrivateAddresses, BOOL, "0"),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"),
@@ -437,7 +438,7 @@ static config_var_t option_vars_[] = {
V(UseEntryGuardsAsDirGuards, BOOL, "1"),
V(UseGuardFraction, AUTOBOOL, "auto"),
V(UseMicrodescriptors, AUTOBOOL, "auto"),
- V(UseNTorHandshake, AUTOBOOL, "1"),
+ OBSOLETE("UseNTorHandshake"),
V(User, STRING, NULL),
OBSOLETE("UserspaceIOCPBuffers"),
V(AuthDirSharedRandomness, BOOL, "1"),
@@ -612,7 +613,6 @@ static const config_deprecation_t option_deprecation_notes_[] = {
"to accidentally lose your anonymity by leaking DNS information" },
{ "TLSECGroup", "The default is a nice secure choice; the other option "
"is less secure." },
- { "UseNTorHandshake", "The ntor handshake should always be used." },
{ "ControlListenAddress", "Use ControlPort instead." },
{ "DirListenAddress", "Use DirPort instead, possibly with the "
"NoAdvertise sub-option" },
@@ -1374,6 +1374,35 @@ options_act_reversible(const or_options_t *old_options, char **msg)
connection_mark_for_close(conn);
}
});
+
+ if (set_conn_limit) {
+ /*
+ * If we adjusted the conn limit, recompute the OOS threshold too
+ *
+ * How many possible sockets to keep in reserve? If we have lots of
+ * possible sockets, keep this below a limit and set ConnLimit_high_thresh
+ * very close to ConnLimit_, but if ConnLimit_ is low, shrink it in
+ * proportion.
+ *
+ * Somewhat arbitrarily, set socks_in_reserve to 5% of ConnLimit_, but
+ * cap it at 64.
+ */
+ int socks_in_reserve = options->ConnLimit_ / 20;
+ if (socks_in_reserve > 64) socks_in_reserve = 64;
+
+ options->ConnLimit_high_thresh = options->ConnLimit_ - socks_in_reserve;
+ options->ConnLimit_low_thresh = (options->ConnLimit_ / 4) * 3;
+ log_info(LD_GENERAL,
+ "Recomputed OOS thresholds: ConnLimit %d, ConnLimit_ %d, "
+ "ConnLimit_high_thresh %d, ConnLimit_low_thresh %d",
+ options->ConnLimit, options->ConnLimit_,
+ options->ConnLimit_high_thresh,
+ options->ConnLimit_low_thresh);
+
+ /* Give the OOS handler a chance with the new thresholds */
+ connection_check_oos(get_n_open_sockets(), 0);
+ }
+
goto done;
rollback:
diff --git a/src/or/connection.c b/src/or/connection.c
index 68e442df54..5ecd1ad7bf 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -754,9 +754,9 @@ connection_mark_for_close_(connection_t *conn, int line, const char *file)
* For all other cases, use connection_mark_and_flush() instead, which
* checks for or_connection_t properly, instead. See below.
*/
-void
-connection_mark_for_close_internal_(connection_t *conn,
- int line, const char *file)
+MOCK_IMPL(void,
+connection_mark_for_close_internal_, (connection_t *conn,
+ int line, const char *file))
{
assert_connection_ok(conn,0);
tor_assert(line);
@@ -1090,6 +1090,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
int start_reading = 0;
static int global_next_session_group = SESSION_GROUP_FIRST_AUTO;
tor_addr_t addr;
+ int exhaustion = 0;
if (listensockaddr->sa_family == AF_INET ||
listensockaddr->sa_family == AF_INET6) {
@@ -1108,6 +1109,11 @@ connection_listener_new(const struct sockaddr *listensockaddr,
int e = tor_socket_errno(s);
if (ERRNO_IS_RESOURCE_LIMIT(e)) {
warn_too_many_conns();
+ /*
+ * We'll call the OOS handler at the error exit, so set the
+ * exhaustion flag for it.
+ */
+ exhaustion = 1;
} else {
log_warn(LD_NET, "Socket creation failed: %s",
tor_socket_strerror(e));
@@ -1226,6 +1232,11 @@ connection_listener_new(const struct sockaddr *listensockaddr,
int e = tor_socket_errno(s);
if (ERRNO_IS_RESOURCE_LIMIT(e)) {
warn_too_many_conns();
+ /*
+ * We'll call the OOS handler at the error exit, so set the
+ * exhaustion flag for it.
+ */
+ exhaustion = 1;
} else {
log_warn(LD_NET,"Socket creation failed: %s.", strerror(e));
}
@@ -1344,6 +1355,12 @@ connection_listener_new(const struct sockaddr *listensockaddr,
dnsserv_configure_listener(conn);
}
+ /*
+ * Normal exit; call the OOS handler since connection count just changed;
+ * the exhaustion flag will always be zero here though.
+ */
+ connection_check_oos(get_n_open_sockets(), 0);
+
return conn;
err:
@@ -1352,6 +1369,9 @@ connection_listener_new(const struct sockaddr *listensockaddr,
if (conn)
connection_free(conn);
+ /* Call the OOS handler, indicate if we saw an exhaustion-related error */
+ connection_check_oos(get_n_open_sockets(), exhaustion);
+
return NULL;
}
@@ -1442,21 +1462,34 @@ connection_handle_listener_read(connection_t *conn, int new_type)
if (!SOCKET_OK(news)) { /* accept() error */
int e = tor_socket_errno(conn->s);
if (ERRNO_IS_ACCEPT_EAGAIN(e)) {
- return 0; /* they hung up before we could accept(). that's fine. */
+ /*
+ * they hung up before we could accept(). that's fine.
+ *
+ * give the OOS handler a chance to run though
+ */
+ connection_check_oos(get_n_open_sockets(), 0);
+ return 0;
} else if (ERRNO_IS_RESOURCE_LIMIT(e)) {
warn_too_many_conns();
+ /* Exhaustion; tell the OOS handler */
+ connection_check_oos(get_n_open_sockets(), 1);
return 0;
}
/* else there was a real error. */
log_warn(LD_NET,"accept() failed: %s. Closing listener.",
tor_socket_strerror(e));
connection_mark_for_close(conn);
+ /* Tell the OOS handler about this too */
+ connection_check_oos(get_n_open_sockets(), 0);
return -1;
}
log_debug(LD_NET,
"Connection accepted on socket %d (child of fd %d).",
(int)news,(int)conn->s);
+ /* We accepted a new conn; run OOS handler */
+ connection_check_oos(get_n_open_sockets(), 0);
+
if (make_socket_reuseable(news) < 0) {
if (tor_socket_errno(news) == EINVAL) {
/* This can happen on OSX if we get a badly timed shutdown. */
@@ -1661,12 +1694,18 @@ connection_connect_sockaddr,(connection_t *conn,
s = tor_open_socket_nonblocking(protocol_family, SOCK_STREAM, proto);
if (! SOCKET_OK(s)) {
+ /*
+ * Early OOS handler calls; it matters if it's an exhaustion-related
+ * error or not.
+ */
*socket_error = tor_socket_errno(s);
if (ERRNO_IS_RESOURCE_LIMIT(*socket_error)) {
warn_too_many_conns();
+ connection_check_oos(get_n_open_sockets(), 1);
} else {
log_warn(LD_NET,"Error creating network socket: %s",
tor_socket_strerror(*socket_error));
+ connection_check_oos(get_n_open_sockets(), 0);
}
return -1;
}
@@ -1676,6 +1715,13 @@ connection_connect_sockaddr,(connection_t *conn,
tor_socket_strerror(errno));
}
+ /*
+ * We've got the socket open; give the OOS handler a chance to check
+ * against configuured maximum socket number, but tell it no exhaustion
+ * failure.
+ */
+ connection_check_oos(get_n_open_sockets(), 0);
+
if (bindaddr && bind(s, bindaddr, bindaddr_len) < 0) {
*socket_error = tor_socket_errno(s);
log_warn(LD_NET,"Error binding network socket: %s",
@@ -4454,6 +4500,256 @@ connection_reached_eof(connection_t *conn)
}
}
+/** Comparator for the two-orconn case in OOS victim sort */
+static int
+oos_victim_comparator_for_orconns(or_connection_t *a, or_connection_t *b)
+{
+ int a_circs, b_circs;
+ /* Fewer circuits == higher priority for OOS kill, sort earlier */
+
+ a_circs = connection_or_get_num_circuits(a);
+ b_circs = connection_or_get_num_circuits(b);
+
+ if (a_circs < b_circs) return 1;
+ else if (a_circs > b_circs) return -1;
+ else return 0;
+}
+
+/** Sort comparator for OOS victims; better targets sort before worse
+ * ones. */
+static int
+oos_victim_comparator(const void **a_v, const void **b_v)
+{
+ connection_t *a = NULL, *b = NULL;
+
+ /* Get connection pointers out */
+
+ a = (connection_t *)(*a_v);
+ b = (connection_t *)(*b_v);
+
+ tor_assert(a != NULL);
+ tor_assert(b != NULL);
+
+ /*
+ * We always prefer orconns as victims currently; we won't even see
+ * these non-orconn cases, but if we do, sort them after orconns.
+ */
+ if (a->type == CONN_TYPE_OR && b->type == CONN_TYPE_OR) {
+ return oos_victim_comparator_for_orconns(TO_OR_CONN(a), TO_OR_CONN(b));
+ } else {
+ /*
+ * One isn't an orconn; if one is, it goes first. We currently have no
+ * opinions about cases where neither is an orconn.
+ */
+ if (a->type == CONN_TYPE_OR) return -1;
+ else if (b->type == CONN_TYPE_OR) return 1;
+ else return 0;
+ }
+}
+
+/** Pick n victim connections for the OOS handler and return them in a
+ * smartlist.
+ */
+MOCK_IMPL(STATIC smartlist_t *,
+pick_oos_victims, (int n))
+{
+ smartlist_t *eligible = NULL, *victims = NULL;
+ smartlist_t *conns;
+ int conn_counts_by_type[CONN_TYPE_MAX_ + 1], i;
+
+ /*
+ * Big damn assumption (someone improve this someday!):
+ *
+ * Socket exhaustion normally happens on high-volume relays, and so
+ * most of the connections involved are orconns. We should pick victims
+ * by assembling a list of all orconns, and sorting them in order of
+ * how much 'damage' by some metric we'd be doing by dropping them.
+ *
+ * If we move on from orconns, we should probably think about incoming
+ * directory connections next, or exit connections. Things we should
+ * probably never kill are controller connections and listeners.
+ *
+ * This function will count how many connections of different types
+ * exist and log it for purposes of gathering data on typical OOS
+ * situations to guide future improvements.
+ */
+
+ /* First, get the connection array */
+ conns = get_connection_array();
+ /*
+ * Iterate it and pick out eligible connection types, and log some stats
+ * along the way.
+ */
+ eligible = smartlist_new();
+ memset(conn_counts_by_type, 0, sizeof(conn_counts_by_type));
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) {
+ /* Bump the counter */
+ tor_assert(c->type <= CONN_TYPE_MAX_);
+ ++(conn_counts_by_type[c->type]);
+
+ /* Skip anything without a socket we can free */
+ if (!(SOCKET_OK(c->s))) {
+ continue;
+ }
+
+ /* Skip anything we would count as moribund */
+ if (connection_is_moribund(c)) {
+ continue;
+ }
+
+ switch (c->type) {
+ case CONN_TYPE_OR:
+ /* We've got an orconn, it's eligible to be OOSed */
+ smartlist_add(eligible, c);
+ break;
+ default:
+ /* We don't know what to do with it, ignore it */
+ break;
+ }
+ } SMARTLIST_FOREACH_END(c);
+
+ /* Log some stats */
+ if (smartlist_len(conns) > 0) {
+ /* At least one counter must be non-zero */
+ log_info(LD_NET, "Some stats on conn types seen during OOS follow");
+ for (i = CONN_TYPE_MIN_; i <= CONN_TYPE_MAX_; ++i) {
+ /* Did we see any? */
+ if (conn_counts_by_type[i] > 0) {
+ log_info(LD_NET, "%s: %d conns",
+ conn_type_to_string(i),
+ conn_counts_by_type[i]);
+ }
+ }
+ log_info(LD_NET, "Done with OOS conn type stats");
+ }
+
+ /* Did we find more eligible targets than we want to kill? */
+ if (smartlist_len(eligible) > n) {
+ /* Sort the list in order of target preference */
+ smartlist_sort(eligible, oos_victim_comparator);
+ /* Pick first n as victims */
+ victims = smartlist_new();
+ for (i = 0; i < n; ++i) {
+ smartlist_add(victims, smartlist_get(eligible, i));
+ }
+ /* Free the original list */
+ smartlist_free(eligible);
+ } else {
+ /* No, we can just call them all victims */
+ victims = eligible;
+ }
+
+ return victims;
+}
+
+/** Kill a list of connections for the OOS handler. */
+MOCK_IMPL(STATIC void,
+kill_conn_list_for_oos, (smartlist_t *conns))
+{
+ if (!conns) return;
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) {
+ /* Make sure the channel layer gets told about orconns */
+ if (c->type == CONN_TYPE_OR) {
+ connection_or_close_for_error(TO_OR_CONN(c), 1);
+ } else {
+ connection_mark_for_close(c);
+ }
+ } SMARTLIST_FOREACH_END(c);
+
+ log_notice(LD_NET,
+ "OOS handler marked %d connections",
+ smartlist_len(conns));
+}
+
+/** Out-of-Sockets handler; n_socks is the current number of open
+ * sockets, and failed is non-zero if a socket exhaustion related
+ * error immediately preceded this call. This is where to do
+ * circuit-killing heuristics as needed.
+ */
+void
+connection_check_oos(int n_socks, int failed)
+{
+ int target_n_socks = 0, moribund_socks, socks_to_kill;
+ smartlist_t *conns;
+
+ /* Early exit: is OOS checking disabled? */
+ if (get_options()->DisableOOSCheck) {
+ return;
+ }
+
+ /* Sanity-check args */
+ tor_assert(n_socks >= 0);
+
+ /*
+ * Make some log noise; keep it at debug level since this gets a chance
+ * to run on every connection attempt.
+ */
+ log_debug(LD_NET,
+ "Running the OOS handler (%d open sockets, %s)",
+ n_socks, (failed != 0) ? "exhaustion seen" : "no exhaustion");
+
+ /*
+ * Check if we're really handling an OOS condition, and if so decide how
+ * many sockets we want to get down to. Be sure we check if the threshold
+ * is distinct from zero first; it's possible for this to be called a few
+ * times before we've finished reading the config.
+ */
+ if (n_socks >= get_options()->ConnLimit_high_thresh &&
+ get_options()->ConnLimit_high_thresh != 0 &&
+ get_options()->ConnLimit_ != 0) {
+ /* Try to get down to the low threshold */
+ target_n_socks = get_options()->ConnLimit_low_thresh;
+ log_notice(LD_NET,
+ "Current number of sockets %d is greater than configured "
+ "limit %d; OOS handler trying to get down to %d",
+ n_socks, get_options()->ConnLimit_high_thresh,
+ target_n_socks);
+ } else if (failed) {
+ /*
+ * If we're not at the limit but we hit a socket exhaustion error, try to
+ * drop some (but not as aggressively as ConnLimit_low_threshold, which is
+ * 3/4 of ConnLimit_)
+ */
+ target_n_socks = (n_socks * 9) / 10;
+ log_notice(LD_NET,
+ "We saw socket exhaustion at %d open sockets; OOS handler "
+ "trying to get down to %d",
+ n_socks, target_n_socks);
+ }
+
+ if (target_n_socks > 0) {
+ /*
+ * It's an OOS!
+ *
+ * Count moribund sockets; it's be important that anything we decide
+ * to get rid of here but don't immediately close get counted as moribund
+ * on subsequent invocations so we don't try to kill too many things if
+ * connection_check_oos() gets called multiple times.
+ */
+ moribund_socks = connection_count_moribund();
+
+ if (moribund_socks < n_socks - target_n_socks) {
+ socks_to_kill = n_socks - target_n_socks - moribund_socks;
+
+ conns = pick_oos_victims(socks_to_kill);
+ if (conns) {
+ kill_conn_list_for_oos(conns);
+ log_notice(LD_NET,
+ "OOS handler killed %d conns", smartlist_len(conns));
+ smartlist_free(conns);
+ } else {
+ log_notice(LD_NET, "OOS handler failed to pick any victim conns");
+ }
+ } else {
+ log_notice(LD_NET,
+ "Not killing any sockets for OOS because there are %d "
+ "already moribund, and we only want to eliminate %d",
+ moribund_socks, n_socks - target_n_socks);
+ }
+ }
+}
+
/** Log how many bytes are used by buffers of different kinds and sizes. */
void
connection_dump_buffer_mem_stats(int severity)
diff --git a/src/or/connection.h b/src/or/connection.h
index f8e0f73246..d25e002fa4 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -34,8 +34,8 @@ void connection_about_to_close_connection(connection_t *conn);
void connection_close_immediate(connection_t *conn);
void connection_mark_for_close_(connection_t *conn,
int line, const char *file);
-void connection_mark_for_close_internal_(connection_t *conn,
- int line, const char *file);
+MOCK_DECL(void, connection_mark_for_close_internal_,
+ (connection_t *conn, int line, const char *file));
#define connection_mark_for_close(c) \
connection_mark_for_close_((c), __LINE__, SHORT_FILE__)
@@ -247,6 +247,22 @@ void clock_skew_warning(const connection_t *conn, long apparent_skew,
int trusted, log_domain_mask_t domain,
const char *received, const char *source);
+/** Check if a connection is on the way out so the OOS handler doesn't try
+ * to kill more than it needs. */
+static inline int
+connection_is_moribund(connection_t *conn)
+{
+ if (conn != NULL &&
+ (conn->conn_array_index < 0 ||
+ conn->marked_for_close)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void connection_check_oos(int n_socks, int failed);
+
#ifdef CONNECTION_PRIVATE
STATIC void connection_free_(connection_t *conn);
@@ -265,6 +281,9 @@ MOCK_DECL(STATIC int,connection_connect_sockaddr,
const struct sockaddr *bindaddr,
socklen_t bindaddr_len,
int *socket_error));
+MOCK_DECL(STATIC void, kill_conn_list_for_oos, (smartlist_t *conns));
+MOCK_DECL(STATIC smartlist_t *, pick_oos_victims, (int n));
+
#endif
#endif
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 1f0c4bdef5..72d8e13e90 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -394,8 +394,8 @@ connection_or_change_state(or_connection_t *conn, uint8_t state)
* be an or_connection_t field, but it got moved to channel_t and we
* shouldn't maintain two copies. */
-int
-connection_or_get_num_circuits(or_connection_t *conn)
+MOCK_IMPL(int,
+connection_or_get_num_circuits, (or_connection_t *conn))
{
tor_assert(conn);
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index e2ec47a4f2..2e8c6066cc 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -64,7 +64,7 @@ void connection_or_init_conn_from_address(or_connection_t *conn,
int connection_or_client_learned_peer_id(or_connection_t *conn,
const uint8_t *peer_id);
time_t connection_or_client_used(or_connection_t *conn);
-int connection_or_get_num_circuits(or_connection_t *conn);
+MOCK_DECL(int, connection_or_get_num_circuits, (or_connection_t *conn));
void or_handshake_state_free(or_handshake_state_t *state);
void or_handshake_state_record_cell(or_connection_t *conn,
or_handshake_state_t *state,
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 64ebde6fdd..ff50ca4417 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -255,6 +255,20 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
return FP_REJECT;
}
+ /* dirserv_get_status_impl already rejects versions older than 0.2.4.18-rc,
+ * and onion_curve25519_pkey was introduced in 0.2.4.8-alpha.
+ * But just in case a relay doesn't provide or lies about its version, or
+ * doesn't include an ntor key in its descriptor, check that it exists,
+ * and is non-zero (clients check that it's non-zero before using it). */
+ if (!routerinfo_has_curve25519_onion_key(router)) {
+ log_fn(severity, LD_DIR,
+ "Descriptor from router %s is missing an ntor curve25519 onion "
+ "key.", router_describe(router));
+ if (msg)
+ *msg = "Missing ntor curve25519 onion key. Please upgrade!";
+ return FP_REJECT;
+ }
+
if (router->cache_info.signing_key_cert) {
/* This has an ed25519 identity key. */
if (KEYPIN_MISMATCH ==
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 9748f4ae4d..ae869c9064 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -1582,7 +1582,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
const char *chosen_version;
const char *chosen_name = NULL;
int exitsummary_disagreement = 0;
- int is_named = 0, is_unnamed = 0, is_running = 0;
+ int is_named = 0, is_unnamed = 0, is_running = 0, is_valid = 0;
int is_guard = 0, is_exit = 0, is_bad_exit = 0;
int naming_conflict = 0;
int n_listing = 0;
@@ -1733,6 +1733,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
is_running = 1;
else if (!strcmp(fl, "BadExit"))
is_bad_exit = 1;
+ else if (!strcmp(fl, "Valid"))
+ is_valid = 1;
}
}
} SMARTLIST_FOREACH_END(fl);
@@ -1742,6 +1744,12 @@ networkstatus_compute_consensus(smartlist_t *votes,
if (!is_running)
continue;
+ /* Starting with consensus method 24, we don't list servers
+ * that are not valid in a consensus. See Proposal 272 */
+ if (!is_valid &&
+ consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES)
+ continue;
+
/* Pick the version. */
if (smartlist_len(versions)) {
sort_version_list(versions, 0);
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index a1f71ce4bb..06bfe671bd 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -55,7 +55,7 @@
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 23
+#define MAX_SUPPORTED_CONSENSUS_METHOD 24
/** Lowest consensus method where microdesc consensuses omit any entry
* with no microdesc. */
@@ -99,6 +99,10 @@
* value(s). */
#define MIN_METHOD_FOR_SHARED_RANDOM 23
+/** Lowest consensus method where authorities drop all nodes that don't get
+ * the Valid flag. */
+#define MIN_METHOD_FOR_EXCLUDING_INVALID_NODES 24
+
/** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
* get confused with the above macros.) */
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 209aae01cf..7e25306234 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -692,7 +692,7 @@ read_bandwidth_usage(void)
int res;
res = unlink(fname);
- if (res != 0) {
+ if (res != 0 && errno != ENOENT) {
log_warn(LD_FS,
"Failed to unlink %s: %s",
fname, strerror(errno));
diff --git a/src/or/main.c b/src/or/main.c
index 4dbd9a005b..03c2b7ed58 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -381,8 +381,8 @@ connection_in_array(connection_t *conn)
/** Set <b>*array</b> to an array of all connections. <b>*array</b> must not
* be modified.
*/
-smartlist_t *
-get_connection_array(void)
+MOCK_IMPL(smartlist_t *,
+get_connection_array, (void))
{
if (!connection_array)
connection_array = smartlist_new();
@@ -651,6 +651,23 @@ close_closeable_connections(void)
}
}
+/** Count moribund connections for the OOS handler */
+MOCK_IMPL(int,
+connection_count_moribund, (void))
+{
+ int moribund = 0;
+
+ /*
+ * Count things we'll try to kill when close_closeable_connections()
+ * runs next.
+ */
+ SMARTLIST_FOREACH_BEGIN(closeable_connection_lst, connection_t *, conn) {
+ if (SOCKET_OK(conn->s) && connection_is_moribund(conn)) ++moribund;
+ } SMARTLIST_FOREACH_END(conn);
+
+ return moribund;
+}
+
/** Libevent callback: this gets invoked when (connection_t*)<b>conn</b> has
* some data to read. */
static void
diff --git a/src/or/main.h b/src/or/main.h
index 31a22de424..0220ae3c57 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -25,7 +25,7 @@ int connection_in_array(connection_t *conn);
void add_connection_to_closeable_list(connection_t *conn);
int connection_is_on_closeable_list(connection_t *conn);
-smartlist_t *get_connection_array(void);
+MOCK_DECL(smartlist_t *, get_connection_array, (void));
MOCK_DECL(uint64_t,get_bytes_read,(void));
MOCK_DECL(uint64_t,get_bytes_written,(void));
@@ -47,6 +47,8 @@ MOCK_DECL(void,connection_start_writing,(connection_t *conn));
void connection_stop_reading_from_linked_conn(connection_t *conn);
+MOCK_DECL(int, connection_count_moribund, (void));
+
void directory_all_unreachable(time_t now);
void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs);
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index fe4b4562ff..72af505d19 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -2275,6 +2275,12 @@ client_would_use_router(const routerstatus_t *rs, time_t now,
/* We'd drop it immediately for being too old. */
return 0;
}
+ if (!routerstatus_version_supports_ntor(rs, 1)) {
+ /* We'd ignore it because it doesn't support ntor.
+ * If we don't know the version, download the descriptor so we can
+ * check if it supports ntor. */
+ return 0;
+ }
return 1;
}
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 7b64cafd79..070e2e9e0d 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -1173,14 +1173,38 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out)
}
}
+/** Return true iff <b>md</b> has a curve25519 onion key.
+ * Use node_has_curve25519_onion_key() instead of calling this directly. */
+static int
+microdesc_has_curve25519_onion_key(const microdesc_t *md)
+{
+ if (!md) {
+ return 0;
+ }
+
+ if (!md->onion_curve25519_pkey) {
+ return 0;
+ }
+
+ if (tor_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key,
+ CURVE25519_PUBKEY_LEN)) {
+ return 0;
+ }
+
+ return 1;
+}
+
/** Return true iff <b>node</b> has a curve25519 onion key. */
int
node_has_curve25519_onion_key(const node_t *node)
{
+ if (!node)
+ return 0;
+
if (node->ri)
- return node->ri->onion_curve25519_pkey != NULL;
+ return routerinfo_has_curve25519_onion_key(node->ri);
else if (node->md)
- return node->md->onion_curve25519_pkey != NULL;
+ return microdesc_has_curve25519_onion_key(node->md);
else
return 0;
}
diff --git a/src/or/onion.c b/src/or/onion.c
index 5495074a83..8a566af766 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -11,6 +11,7 @@
**/
#include "or.h"
+#include "circuitbuild.h"
#include "circuitlist.h"
#include "config.h"
#include "cpuworker.h"
@@ -438,8 +439,7 @@ onion_skin_create(int type,
r = CREATE_FAST_LEN;
break;
case ONION_HANDSHAKE_TYPE_NTOR:
- if (tor_mem_is_zero((const char*)node->curve25519_onion_key.public_key,
- CURVE25519_PUBKEY_LEN))
+ if (!extend_info_supports_ntor(node))
return -1;
if (onion_skin_ntor_create((const uint8_t*)node->identity_digest,
&node->curve25519_onion_key,
diff --git a/src/or/or.h b/src/or/or.h
index d83a921ae9..5b9b007ac1 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -3701,6 +3701,10 @@ typedef struct {
int ConnLimit; /**< Demanded minimum number of simultaneous connections. */
int ConnLimit_; /**< Maximum allowed number of simultaneous connections. */
+ int ConnLimit_high_thresh; /**< start trying to lower socket usage if we
+ * have this many. */
+ int ConnLimit_low_thresh; /**< try to get down to here after socket
+ * exhaustion. */
int RunAsDaemon; /**< If true, run in the background. (Unix only) */
int FascistFirewall; /**< Whether to prefer ORs reachable on open ports. */
smartlist_t *FirewallPorts; /**< Which ports our firewall allows
@@ -4382,9 +4386,6 @@ typedef struct {
char *TLSECGroup; /**< One of "P256", "P224", or nil for auto */
- /** Autobool: should we use the ntor handshake if we can? */
- int UseNTorHandshake;
-
/** Fraction: */
double PathsNeededToBuildCircuits;
@@ -4456,6 +4457,9 @@ typedef struct {
* participate in the protocol. If on (default), a flag is added to the
* vote indicating participation. */
int AuthDirSharedRandomness;
+
+ /** If 1, we skip all OOS checks. */
+ int DisableOOSCheck;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
diff --git a/src/or/policies.c b/src/or/policies.c
index 07f256f5cc..44a46d2fe2 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -2119,8 +2119,10 @@ exit_policy_is_general_exit_helper(smartlist_t *policy, int port)
if (subnet_status[i] != 0)
continue; /* We already reject some part of this /8 */
tor_addr_from_ipv4h(&addr, i<<24);
- if (tor_addr_is_internal(&addr, 0))
+ if (tor_addr_is_internal(&addr, 0) &&
+ !get_options()->DirAllowPrivateAddresses) {
continue; /* Local or non-routable addresses */
+ }
if (p->policy_type == ADDR_POLICY_ACCEPT) {
if (p->maskbits > 8)
continue; /* Narrower than a /8. */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 3468b07561..263dd3d876 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -1368,40 +1368,20 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
i = crypto_rand_int(smartlist_len(usable_nodes));
intro = smartlist_get(usable_nodes, i);
- /* Do we need to look up the router or is the extend info complete? */
- if (!intro->extend_info->onion_key) {
- const node_t *node;
- extend_info_t *new_extend_info;
- if (tor_digest_is_zero(intro->extend_info->identity_digest))
- node = node_get_by_hex_id(intro->extend_info->nickname);
- else
- node = node_get_by_id(intro->extend_info->identity_digest);
- if (!node) {
- log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
- intro->extend_info->nickname);
- smartlist_del(usable_nodes, i);
- goto again;
- }
-#ifdef ENABLE_TOR2WEB_MODE
- new_extend_info = extend_info_from_node(node, options->Tor2webMode);
-#else
- new_extend_info = extend_info_from_node(node, 0);
-#endif
- if (!new_extend_info) {
- const char *alternate_reason = "";
-#ifdef ENABLE_TOR2WEB_MODE
- alternate_reason = ", or we cannot connect directly to it";
-#endif
- log_info(LD_REND, "We don't have a descriptor for the intro-point relay "
- "'%s'%s; trying another.",
- extend_info_describe(intro->extend_info), alternate_reason);
- smartlist_del(usable_nodes, i);
- goto again;
- } else {
- extend_info_free(intro->extend_info);
- intro->extend_info = new_extend_info;
- }
- tor_assert(intro->extend_info != NULL);
+ if (BUG(!intro->extend_info)) {
+ /* This should never happen, but it isn't fatal, just try another */
+ smartlist_del(usable_nodes, i);
+ goto again;
+ }
+ /* All version 2 HS descriptors come with a TAP onion key.
+ * Clients used to try to get the TAP onion key from the consensus, but this
+ * meant that hidden services could discover which consensus clients have. */
+ if (!extend_info_supports_tap(intro->extend_info)) {
+ log_info(LD_REND, "The HS descriptor is missing a TAP onion key for the "
+ "intro-point relay '%s'; trying another.",
+ safe_str_client(extend_info_describe(intro->extend_info)));
+ smartlist_del(usable_nodes, i);
+ goto again;
}
/* Check if we should refuse to talk to this router. */
if (strict &&
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 4c88f1fa5f..8d3a7d704c 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -508,7 +508,7 @@ rend_config_services(const or_options_t *options, int validate_only)
if (!strcasecmp(line->key, "HiddenServiceDir")) {
if (service) { /* register the one we just finished parsing */
if (validate_only)
- rend_service_free(service);
+ rend_service_free(service);
else
rend_add_service(service);
}
@@ -3896,3 +3896,12 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
return -2;
}
+/* Stub that should be replaced with the #17178 version of the function
+ * when merging. */
+int
+rend_service_allow_direct_connection(const or_options_t *options)
+{
+ (void)options;
+ return 0;
+}
+
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 4966cb0302..1622086a99 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -131,5 +131,7 @@ void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
const char *service_id, int seconds_valid);
void rend_service_desc_has_uploaded(const rend_data_t *rend_data);
+int rend_service_allow_direct_connection(const or_options_t *options);
+
#endif
diff --git a/src/or/router.c b/src/or/router.c
index e9961d4594..8fa5799896 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -452,7 +452,8 @@ init_key_from_file(const char *fname, int generate, int severity,
goto error;
}
} else {
- log_info(LD_GENERAL, "No key found in \"%s\"", fname);
+ tor_log(severity, LD_GENERAL, "No key found in \"%s\"", fname);
+ goto error;
}
return prkey;
case FN_FILE:
@@ -560,7 +561,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out,
fname = get_datadir_fname2("keys",
legacy ? "legacy_signing_key" : "authority_signing_key");
- signing_key = init_key_from_file(fname, 0, LOG_INFO, 0);
+ signing_key = init_key_from_file(fname, 0, LOG_ERR, 0);
if (!signing_key) {
log_warn(LD_DIR, "No version 3 directory key found in %s", fname);
goto done;
@@ -2836,6 +2837,10 @@ router_dump_router_to_string(routerinfo_t *router,
(const char *)router->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
+ } else {
+ /* Authorities will start rejecting relays without ntor keys in 0.2.9 */
+ log_err(LD_BUG, "A relay must have an ntor onion key");
+ goto err;
}
/* Write the exit policy to the end of 's'. */
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 1773f1d05c..74b8d1b1d3 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -2260,10 +2260,16 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
continue;
if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
continue;
- /* Choose a node with an OR address that matches the firewall rules,
- * if we are making a direct connection */
+ /* Don't choose nodes if we are certain they can't do ntor */
+ if (node->rs && !routerstatus_version_supports_ntor(node->rs, 1))
+ continue;
+ if ((node->ri || node->md) && !node_has_curve25519_onion_key(node))
+ continue;
+ /* Choose a node with an OR address that matches the firewall rules */
if (direct_conn && check_reach &&
- !fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, pref_addr))
+ !fascist_firewall_allows_node(node,
+ FIREWALL_OR_CONNECTION,
+ pref_addr))
continue;
smartlist_add(sl, (void *)node);
@@ -5497,6 +5503,45 @@ routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey,
return r;
}
+/* Does ri have a valid ntor onion key?
+ * Valid ntor onion keys exist and have at least one non-zero byte. */
+int
+routerinfo_has_curve25519_onion_key(const routerinfo_t *ri)
+{
+ if (!ri) {
+ return 0;
+ }
+
+ if (!ri->onion_curve25519_pkey) {
+ return 0;
+ }
+
+ if (tor_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key,
+ CURVE25519_PUBKEY_LEN)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Is rs running a tor version known to support ntor?
+ * If allow_unknown_versions is true, return true if the version is unknown.
+ * Otherwise, return false if the version is unknown. */
+int
+routerstatus_version_supports_ntor(const routerstatus_t *rs,
+ int allow_unknown_versions)
+{
+ if (!rs) {
+ return allow_unknown_versions;
+ }
+
+ if (!rs->version_known) {
+ return allow_unknown_versions;
+ }
+
+ return rs->version_supports_extend2_cells;
+}
+
/** Assert that the internal representation of <b>rl</b> is
* self-consistent. */
void
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 72ab6d9bf3..47e5445e57 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -206,6 +206,9 @@ int routerinfo_incompatible_with_extrainfo(const crypto_pk_t *ri,
extrainfo_t *ei,
signed_descriptor_t *sd,
const char **msg);
+int routerinfo_has_curve25519_onion_key(const routerinfo_t *ri);
+int routerstatus_version_supports_ntor(const routerstatus_t *rs,
+ int allow_unknown_versions);
void routerlist_assert_ok(const routerlist_t *rl);
const char *esc_router_info(const routerinfo_t *router);
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 19564f5924..e672a416be 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -578,8 +578,8 @@ commit_is_authoritative(const sr_commit_t *commit,
tor_assert(commit);
tor_assert(voter_key);
- return !memcmp(commit->rsa_identity, voter_key,
- sizeof(commit->rsa_identity));
+ return fast_memeq(commit->rsa_identity, voter_key,
+ sizeof(commit->rsa_identity));
}
/* Decide if the newly received <b>commit</b> should be kept depending on