summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/addressmap.c2
-rw-r--r--src/or/bridges.c27
-rw-r--r--src/or/bridges.h2
-rw-r--r--src/or/channel.c25
-rw-r--r--src/or/channelpadding.c34
-rw-r--r--src/or/channelpadding.h5
-rw-r--r--src/or/channeltls.c27
-rw-r--r--src/or/circuitbuild.c11
-rw-r--r--src/or/circuitlist.c4
-rw-r--r--src/or/circuituse.c6
-rw-r--r--src/or/config.c124
-rw-r--r--src/or/connection.c28
-rw-r--r--src/or/connection.h1
-rw-r--r--src/or/connection_edge.c37
-rw-r--r--src/or/connection_or.c8
-rw-r--r--src/or/control.c211
-rw-r--r--src/or/control.h13
-rw-r--r--src/or/directory.c145
-rw-r--r--src/or/dirserv.c12
-rw-r--r--src/or/entrynodes.c2
-rw-r--r--src/or/geoip.c9
-rw-r--r--src/or/hs_cache.c100
-rw-r--r--src/or/hs_cache.h8
-rw-r--r--src/or/hs_circuitmap.c24
-rw-r--r--src/or/hs_client.c120
-rw-r--r--src/or/hs_client.h31
-rw-r--r--src/or/hs_common.c176
-rw-r--r--src/or/hs_common.h20
-rw-r--r--src/or/hs_descriptor.h7
-rw-r--r--src/or/hs_ident.c16
-rw-r--r--src/or/hs_ident.h8
-rw-r--r--src/or/hs_service.c320
-rw-r--r--src/or/hs_service.h14
-rw-r--r--src/or/main.c4
-rw-r--r--src/or/networkstatus.c87
-rw-r--r--src/or/networkstatus.h5
-rw-r--r--src/or/nodelist.c334
-rw-r--r--src/or/nodelist.h23
-rw-r--r--src/or/or.h15
-rw-r--r--src/or/relay.c6
-rw-r--r--src/or/rendcache.c4
-rw-r--r--src/or/rendservice.c12
-rw-r--r--src/or/router.c15
-rw-r--r--src/or/routerlist.c24
-rw-r--r--src/or/routerlist.h3
-rw-r--r--src/or/routerset.c2
46 files changed, 1398 insertions, 713 deletions
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index c92af38254..f278564e80 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -214,7 +214,7 @@ addressmap_clear_excluded_trackexithosts(const or_options_t *options)
dot--;
if (*dot == '.') dot++;
nodename = tor_strndup(dot, len-5-(dot-target));;
- node = node_get_by_nickname(nodename, 0);
+ node = node_get_by_nickname(nodename, NNF_NO_WARN_UNNAMED);
tor_free(nodename);
if (!node ||
(allow_nodes && !routerset_contains_node(allow_nodes, node)) ||
diff --git a/src/or/bridges.c b/src/or/bridges.c
index 0818fb0812..1eec4e39ec 100644
--- a/src/or/bridges.c
+++ b/src/or/bridges.c
@@ -454,6 +454,9 @@ bridge_add_from_config(bridge_line_t *bridge_line)
b->transport_name = bridge_line->transport_name;
b->fetch_status.schedule = DL_SCHED_BRIDGE;
b->fetch_status.backoff = DL_SCHED_RANDOM_EXPONENTIAL;
+ b->fetch_status.increment_on = DL_SCHED_INCREMENT_ATTEMPT;
+ /* We can't reset the bridge's download status here, because UseBridges
+ * might be 0 now, and it might be changed to 1 much later. */
b->socks_args = bridge_line->socks_args;
if (!bridge_list)
bridge_list = smartlist_new();
@@ -622,6 +625,7 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge)
{
+ /* This resets the download status on first use */
if (!download_status_is_ready(&bridge->fetch_status, now,
IMPOSSIBLE_TO_DOWNLOAD))
continue; /* don't bother, no need to retry yet */
@@ -632,8 +636,13 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
continue;
}
- /* schedule another fetch as if this one will fail, in case it does */
- download_status_failed(&bridge->fetch_status, 0);
+ /* schedule the next attempt
+ * we can't increment after a failure, because sometimes we use the
+ * bridge authority, and sometimes we use the bridge direct */
+ download_status_increment_attempt(
+ &bridge->fetch_status,
+ safe_str_client(fmt_and_decorate_addr(&bridge->addr)),
+ now);
can_use_bridge_authority = !tor_digest_is_zero(bridge->identity) &&
num_bridge_auths;
@@ -787,8 +796,12 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
if (bridge) { /* if we actually want to use this one */
node_t *node;
/* it's here; schedule its re-fetch for a long time from now. */
- if (!from_cache)
+ if (!from_cache) {
+ /* This schedules the re-fetch at a constant interval, which produces
+ * a pattern of bridge traffic. But it's better than trying all
+ * configured briges several times in the first few minutes. */
download_status_reset(&bridge->fetch_status);
+ }
node = node_get_mutable_by_id(ri->cache_info.identity_digest);
tor_assert(node);
@@ -820,10 +833,12 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
* We use this function to decide if we're ready to start building
* circuits through our bridges, or if we need to wait until the
* directory "server/authority" requests finish. */
-int
-any_bridge_descriptors_known(void)
+MOCK_IMPL(int,
+any_bridge_descriptors_known, (void))
{
- tor_assert(get_options()->UseBridges);
+ if (BUG(!get_options()->UseBridges)) {
+ return 0;
+ }
if (!bridge_list)
return 0;
diff --git a/src/or/bridges.h b/src/or/bridges.h
index 3bfc782f9a..19341818f4 100644
--- a/src/or/bridges.h
+++ b/src/or/bridges.h
@@ -45,7 +45,7 @@ void bridge_add_from_config(struct bridge_line_t *bridge_line);
void retry_bridge_descriptor_fetch_directly(const char *digest);
void fetch_bridge_descriptors(const or_options_t *options, time_t now);
void learned_bridge_descriptor(routerinfo_t *ri, int from_cache);
-int any_bridge_descriptors_known(void);
+MOCK_DECL(int, any_bridge_descriptors_known, (void));
const smartlist_t *get_socks_args_by_bridge_addrport(const tor_addr_t *addr,
uint16_t port);
diff --git a/src/or/channel.c b/src/or/channel.c
index 56c54c0c51..44152ff457 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -65,6 +65,8 @@
#include "routerlist.h"
#include "scheduler.h"
#include "compat_time.h"
+#include "networkstatus.h"
+#include "rendservice.h"
/* Global lists of channels */
@@ -2731,12 +2733,25 @@ channel_do_open_actions(channel_t *chan)
/* Disable or reduce padding according to user prefs. */
if (chan->padding_enabled || get_options()->ConnectionPadding == 1) {
if (!get_options()->ConnectionPadding) {
+ /* Disable if torrc disabled */
channelpadding_disable_padding_on_channel(chan);
- }
-
- /* Padding can be forced and/or reduced by clients, regardless of if
- * the channel supports it */
- if (get_options()->ReducedConnectionPadding) {
+ } else if (get_options()->Tor2webMode &&
+ !networkstatus_get_param(NULL,
+ CHANNELPADDING_TOR2WEB_PARAM,
+ CHANNELPADDING_TOR2WEB_DEFAULT, 0, 1)) {
+ /* Disable if we're using tor2web and the consensus disabled padding
+ * for tor2web */
+ channelpadding_disable_padding_on_channel(chan);
+ } else if (rend_service_allow_non_anonymous_connection(get_options()) &&
+ !networkstatus_get_param(NULL,
+ CHANNELPADDING_SOS_PARAM,
+ CHANNELPADDING_SOS_DEFAULT, 0, 1)) {
+ /* Disable if we're using RSOS and the consensus disabled padding
+ * for RSOS*/
+ channelpadding_disable_padding_on_channel(chan);
+ } else if (get_options()->ReducedConnectionPadding) {
+ /* Padding can be forced and/or reduced by clients, regardless of if
+ * the channel supports it */
channelpadding_reduce_padding_on_channel(chan);
}
}
diff --git a/src/or/channelpadding.c b/src/or/channelpadding.c
index bed2489837..ccaf5b4ec8 100644
--- a/src/or/channelpadding.c
+++ b/src/or/channelpadding.c
@@ -21,6 +21,7 @@
#include "router.h"
#include "compat_time.h"
#include <event2/event.h>
+#include "rendservice.h"
STATIC int channelpadding_get_netflow_inactive_timeout_ms(const channel_t *);
STATIC int channelpadding_send_disable_command(channel_t *);
@@ -46,6 +47,10 @@ static int consensus_nf_conntimeout_clients;
static int consensus_nf_pad_before_usage;
/** Should we pad relay-to-relay connections? */
static int consensus_nf_pad_relays;
+/** Should we pad tor2web connections? */
+static int consensus_nf_pad_tor2web;
+/** Should we pad rosos connections? */
+static int consensus_nf_pad_single_onion;
#define TOR_MSEC_PER_SEC 1000
#define TOR_USEC_PER_MSEC 1000
@@ -130,6 +135,16 @@ channelpadding_new_consensus_params(networkstatus_t *ns)
consensus_nf_pad_relays =
networkstatus_get_param(ns, "nf_pad_relays", 0, 0, 1);
+
+ consensus_nf_pad_tor2web =
+ networkstatus_get_param(ns,
+ CHANNELPADDING_TOR2WEB_PARAM,
+ CHANNELPADDING_TOR2WEB_DEFAULT, 0, 1);
+
+ consensus_nf_pad_single_onion =
+ networkstatus_get_param(ns,
+ CHANNELPADDING_SOS_PARAM,
+ CHANNELPADDING_SOS_DEFAULT, 0, 1);
}
/**
@@ -717,6 +732,25 @@ channelpadding_decide_to_pad_channel(channel_t *chan)
return CHANNELPADDING_WONTPAD;
}
+ if (options->Tor2webMode && !consensus_nf_pad_tor2web) {
+ /* If the consensus just changed values, this channel may still
+ * think padding is enabled. Negotiate it off. */
+ if (chan->padding_enabled)
+ channelpadding_disable_padding_on_channel(chan);
+
+ return CHANNELPADDING_WONTPAD;
+ }
+
+ if (rend_service_allow_non_anonymous_connection(options) &&
+ !consensus_nf_pad_single_onion) {
+ /* If the consensus just changed values, this channel may still
+ * think padding is enabled. Negotiate it off. */
+ if (chan->padding_enabled)
+ channelpadding_disable_padding_on_channel(chan);
+
+ return CHANNELPADDING_WONTPAD;
+ }
+
if (!chan->has_queued_writes(chan)) {
int is_client_channel = 0;
diff --git a/src/or/channelpadding.h b/src/or/channelpadding.h
index 2708ee9739..a227e27d5b 100644
--- a/src/or/channelpadding.h
+++ b/src/or/channelpadding.h
@@ -13,6 +13,11 @@
#include "channelpadding_negotiation.h"
+#define CHANNELPADDING_TOR2WEB_PARAM "nf_pad_tor2web"
+#define CHANNELPADDING_TOR2WEB_DEFAULT 1
+#define CHANNELPADDING_SOS_PARAM "nf_pad_single_onion"
+#define CHANNELPADDING_SOS_DEFAULT 1
+
typedef enum {
CHANNELPADDING_WONTPAD,
CHANNELPADDING_PADLATER,
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index 4ccd3b5fbf..fe1e7e91d8 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -1680,6 +1680,8 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
long apparent_skew = 0;
tor_addr_t my_apparent_addr = TOR_ADDR_NULL;
+ int started_here = 0;
+ const char *identity_digest = NULL;
tor_assert(cell);
tor_assert(chan);
@@ -1699,10 +1701,12 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
}
tor_assert(chan->conn->handshake_state &&
chan->conn->handshake_state->received_versions);
+ started_here = connection_or_nonopen_was_started_here(chan->conn);
+ identity_digest = chan->conn->identity_digest;
if (chan->conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) {
tor_assert(chan->conn->link_proto >= 3);
- if (chan->conn->handshake_state->started_here) {
+ if (started_here) {
if (!(chan->conn->handshake_state->authenticated)) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Got a NETINFO cell from server, "
@@ -1813,7 +1817,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
"they will not consider this connection canonical. They "
"think we are at %s, but we think its %s.",
safe_str(descr),
- safe_str(hex_str(chan->conn->identity_digest, DIGEST_LEN)),
+ safe_str(hex_str(identity_digest, DIGEST_LEN)),
safe_str(tor_addr_is_null(&my_apparent_addr) ?
"<none>" : fmt_and_decorate_addr(&my_apparent_addr)),
safe_str(fmt_addr32(me->addr)));
@@ -1823,8 +1827,9 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
/** Warn when we get a netinfo skew with at least this value. */
#define NETINFO_NOTICE_SKEW 3600
if (labs(apparent_skew) > NETINFO_NOTICE_SKEW &&
- router_get_by_id_digest(chan->conn->identity_digest)) {
- int trusted = router_digest_is_trusted_dir(chan->conn->identity_digest);
+ (started_here ||
+ connection_or_digest_is_known_relay(identity_digest))) {
+ int trusted = router_digest_is_trusted_dir(identity_digest);
clock_skew_warning(TO_CONN(chan->conn), apparent_skew, trusted, LD_GENERAL,
"NETINFO cell", "OR");
}
@@ -1857,8 +1862,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
safe_str_client(chan->conn->base_.address),
chan->conn->base_.port,
(int)(chan->conn->link_proto),
- hex_str(TLS_CHAN_TO_BASE(chan)->identity_digest,
- DIGEST_LEN),
+ hex_str(identity_digest, DIGEST_LEN),
tor_addr_is_null(&my_apparent_addr) ?
"<none>" : fmt_and_decorate_addr(&my_apparent_addr));
}
@@ -1929,7 +1933,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
int n_certs, i;
certs_cell_t *cc = NULL;
- int send_netinfo = 0;
+ int send_netinfo = 0, started_here = 0;
memset(x509_certs, 0, sizeof(x509_certs));
memset(ed_certs, 0, sizeof(ed_certs));
@@ -1947,6 +1951,11 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
goto err; \
} while (0)
+ /* Can't use connection_or_nonopen_was_started_here(); its conn->tls
+ * check looks like it breaks
+ * test_link_handshake_recv_certs_ok_server(). */
+ started_here = chan->conn->handshake_state->started_here;
+
if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
ERR("We're not doing a v3 handshake!");
if (chan->conn->link_proto < 3)
@@ -2060,7 +2069,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
/* Note that this warns more loudly about time and validity if we were
* _trying_ to connect to an authority, not necessarily if we _did_ connect
* to one. */
- if (chan->conn->handshake_state->started_here &&
+ if (started_here &&
router_digest_is_trusted_dir(TLS_CHAN_TO_BASE(chan)->identity_digest))
severity = LOG_WARN;
else
@@ -2078,7 +2087,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
if (!checked_rsa_id)
ERR("Invalid certificate chain!");
- if (chan->conn->handshake_state->started_here) {
+ if (started_here) {
/* No more information is needed. */
chan->conn->handshake_state->authenticated = 1;
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index e5c6767d4a..279308afcb 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -290,14 +290,9 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
}
} else { /* ! verbose_names */
- node = node_get_by_id(id);
- if (node && node_is_named(node)) {
- elt = tor_strdup(node_get_nickname(node));
- } else {
- elt = tor_malloc(HEX_DIGEST_LEN+2);
- elt[0] = '$';
- base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
- }
+ elt = tor_malloc(HEX_DIGEST_LEN+2);
+ elt[0] = '$';
+ base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
}
tor_assert(elt);
if (verbose) {
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 774edc90b4..5cc6252325 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -1912,6 +1912,10 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
circuits_pending_close = smartlist_new();
smartlist_add(circuits_pending_close, circ);
+
+ log_info(LD_GENERAL, "Circuit %u marked for close at %s:%d (orig reason: "
+ "%u, new reason: %u)",
+ circ->n_circ_id, file, line, orig_reason, reason);
}
/** Called immediately before freeing a marked circuit <b>circ</b> from
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index c229c84d96..7545dea290 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -2121,7 +2121,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
} else {
/* XXXX Duplicates checks in connection_ap_handshake_attach_circuit:
* refactor into a single function. */
- const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 0);
int opt = conn->chosen_exit_optional;
if (node && !connection_ap_can_use_exit(conn, node)) {
log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
@@ -2204,7 +2204,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
if (conn->chosen_exit_name) {
const node_t *r;
int opt = conn->chosen_exit_optional;
- r = node_get_by_nickname(conn->chosen_exit_name, 1);
+ r = node_get_by_nickname(conn->chosen_exit_name, 0);
if (r && node_has_descriptor(r)) {
/* We might want to connect to an IPv6 bridge for loading
descriptors so we use the preferred address rather than
@@ -2620,7 +2620,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
* open to that exit. See what exit we meant, and whether we can use it.
*/
if (conn->chosen_exit_name) {
- const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 0);
int opt = conn->chosen_exit_optional;
if (!node && !want_onehop) {
/* We ran into this warning when trying to extend a circuit to a
diff --git a/src/or/config.c b/src/or/config.c
index 95f27b0717..4a1361f9f4 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -169,6 +169,8 @@ static config_abbrev_t option_abbrevs_[] = {
{ "BridgeAuthoritativeDirectory", "BridgeAuthoritativeDir", 0, 0},
{ "HashedControlPassword", "__HashedControlSessionPassword", 1, 0},
{ "VirtualAddrNetwork", "VirtualAddrNetworkIPv4", 0, 0},
+ { "ClientDNSRejectInternalAddresses",
+ "TestingClientDNSRejectInternalAddresses", 0, 1, },
{ NULL, NULL, 0, 0},
};
@@ -251,7 +253,7 @@ static config_var_t option_vars_[] = {
V(CircuitsAvailableTimeout, INTERVAL, "0"),
V(CircuitStreamTimeout, INTERVAL, "0"),
V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/
- V(ClientDNSRejectInternalAddresses, BOOL,"1"),
+ V(TestingClientDNSRejectInternalAddresses, BOOL,"1"),
V(ClientOnly, BOOL, "0"),
V(ClientPreferIPv6ORPort, AUTOBOOL, "auto"),
V(ClientPreferIPv6DirPort, AUTOBOOL, "auto"),
@@ -590,7 +592,16 @@ static config_var_t option_vars_[] = {
* blackholed. Clients will try 3 directories simultaneously.
* (Relays never use simultaneous connections.) */
V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"),
- V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "1200, 900, 900, 3600"),
+ /* When a client has any running bridges, check each bridge occasionally,
+ * whether or not that bridge is actually up. */
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL,
+ "10800, 25200, 54000, 111600, 262800"),
+ /* When a client is just starting, or has no running bridges, check each
+ * bridge a few times quickly, and then try again later. These schedules
+ * are much longer than the other schedules, because we try each and every
+ * configured bridge with this schedule. */
+ V(TestingBridgeBootstrapDownloadSchedule, CSV_INTERVAL,
+ "0, 30, 90, 600, 3600, 10800, 25200, 54000, 111600, 262800"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
V(TestingConsensusMaxDownloadTries, UINT, "8"),
@@ -616,7 +627,6 @@ static config_var_t option_vars_[] = {
/** Override default values with these if the user sets the TestingTorNetwork
* option. */
static const config_var_t testing_tor_network_defaults[] = {
- V(ServerDNSAllowBrokenConfig, BOOL, "1"),
V(DirAllowPrivateAddresses, BOOL, "1"),
V(EnforceDistinctSubnets, BOOL, "0"),
V(AssumeReachable, BOOL, "1"),
@@ -629,7 +639,7 @@ static const config_var_t testing_tor_network_defaults[] = {
"0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
V(ClientBootstrapConsensusMaxDownloadTries, UINT, "80"),
V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"),
- V(ClientDNSRejectInternalAddresses, BOOL,"0"), // deprecated in 0.2.9.2-alpha
+ V(TestingClientDNSRejectInternalAddresses, BOOL,"0"),
V(ClientRejectInternalAddresses, BOOL, "0"),
V(CountPrivateBandwidth, BOOL, "1"),
V(ExitPolicyRejectPrivate, BOOL, "0"),
@@ -640,7 +650,6 @@ static const config_var_t testing_tor_network_defaults[] = {
V(TestingV3AuthInitialVotingInterval, INTERVAL, "150 seconds"),
V(TestingV3AuthInitialVoteDelay, INTERVAL, "20 seconds"),
V(TestingV3AuthInitialDistDelay, INTERVAL, "20 seconds"),
- V(TestingV3AuthVotingStartOffset, INTERVAL, "0"),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"),
V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
@@ -652,7 +661,9 @@ static const config_var_t testing_tor_network_defaults[] = {
"15, 20, 30, 60"),
V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
"15, 20, 30, 60"),
- V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"),
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "10, 30, 60"),
+ V(TestingBridgeBootstrapDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
+ "15, 20, 30, 60"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
V(TestingConsensusMaxDownloadTries, UINT, "80"),
@@ -676,8 +687,6 @@ static const config_deprecation_t option_deprecation_notes_[] = {
/* Deprecated since 0.2.9.2-alpha... */
{ "AllowDotExit", "Unrestricted use of the .exit notation can be used for "
"a wide variety of application-level attacks." },
- { "ClientDNSRejectInternalAddresses", "Turning this on makes your client "
- "easier to fingerprint, and may open you to esoteric attacks." },
/* End of options deprecated since 0.2.9.2-alpha. */
/* Deprecated since 0.3.2.0-alpha. */
@@ -769,6 +778,9 @@ static int have_parsed_cmdline = 0;
static char *global_dirfrontpagecontents = NULL;
/** List of port_cfg_t for all configured ports. */
static smartlist_t *configured_ports = NULL;
+/** True iff we're currently validating options, and any calls to
+ * get_options() are likely to be bugs. */
+static int in_option_validation = 0;
/** Return the contents of our frontpage string, or NULL if not configured. */
MOCK_IMPL(const char*,
@@ -782,6 +794,7 @@ MOCK_IMPL(or_options_t *,
get_options_mutable, (void))
{
tor_assert(global_options);
+ tor_assert_nonfatal(! in_option_validation);
return global_options;
}
@@ -1779,9 +1792,13 @@ options_act(const or_options_t *old_options)
}
/* Write our PID to the PID file. If we do not have write permissions we
- * will log a warning */
+ * will log a warning and exit. */
if (options->PidFile && !sandbox_is_active()) {
- write_pidfile(options->PidFile);
+ if (write_pidfile(options->PidFile) < 0) {
+ log_err(LD_CONFIG, "Unable to write PIDFile %s",
+ escaped(options->PidFile));
+ return -1;
+ }
}
/* Register addressmap directives */
@@ -2309,24 +2326,36 @@ options_trial_assign(config_line_t *list, unsigned flags, char **msg)
return r;
}
- if (options_validate(get_options_mutable(), trial_options,
+ setopt_err_t rv;
+ or_options_t *cur_options = get_options_mutable();
+
+ in_option_validation = 1;
+
+ if (options_validate(cur_options, trial_options,
global_default_options, 1, msg) < 0) {
or_options_free(trial_options);
- return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */
+ rv = SETOPT_ERR_PARSE; /*XXX make this a separate return value. */
+ goto done;
}
- if (options_transition_allowed(get_options(), trial_options, msg) < 0) {
+ if (options_transition_allowed(cur_options, trial_options, msg) < 0) {
or_options_free(trial_options);
- return SETOPT_ERR_TRANSITION;
+ rv = SETOPT_ERR_TRANSITION;
+ goto done;
}
+ in_option_validation = 0;
if (set_options(trial_options, msg)<0) {
or_options_free(trial_options);
- return SETOPT_ERR_SETTING;
+ rv = SETOPT_ERR_SETTING;
+ goto done;
}
/* we liked it. put it in place. */
- return SETOPT_OK;
+ rv = SETOPT_OK;
+ done:
+ in_option_validation = 0;
+ return rv;
}
/** Print a usage message for tor. */
@@ -2829,8 +2858,11 @@ static int
options_validate_cb(void *old_options, void *options, void *default_options,
int from_setconf, char **msg)
{
- return options_validate(old_options, options, default_options,
+ in_option_validation = 1;
+ int rv = options_validate(old_options, options, default_options,
from_setconf, msg);
+ in_option_validation = 0;
+ return rv;
}
#define REJECT(arg) \
@@ -2848,8 +2880,10 @@ options_validate_cb(void *old_options, void *options, void *default_options,
* an absolute path that <b>filepath</b> will resolve to.
*
* In case <b>filepath</b> is absolute, do nothing.
+ *
+ * Return 1 if there were relative paths; 0 otherwise.
*/
-static void
+static int
warn_if_option_path_is_relative(const char *option,
char *filepath)
{
@@ -2858,39 +2892,45 @@ warn_if_option_path_is_relative(const char *option,
COMPLAIN("Path for %s (%s) is relative and will resolve to %s."
" Is this what you wanted?", option, filepath, abs_path);
tor_free(abs_path);
+ return 1;
}
+ return 0;
}
/** Scan <b>options</b> for occurances of relative file/directory
* path and log a warning whenever it is found.
+ *
+ * Return 1 if there were relative paths; 0 otherwise.
*/
-static void
+static int
warn_about_relative_paths(or_options_t *options)
{
tor_assert(options);
+ int n = 0;
- warn_if_option_path_is_relative("CookieAuthFile",
- options->CookieAuthFile);
- warn_if_option_path_is_relative("ExtORPortCookieAuthFile",
- options->ExtORPortCookieAuthFile);
- warn_if_option_path_is_relative("DirPortFrontPage",
- options->DirPortFrontPage);
- warn_if_option_path_is_relative("V3BandwidthsFile",
- options->V3BandwidthsFile);
- warn_if_option_path_is_relative("ControlPortWriteToFile",
- options->ControlPortWriteToFile);
- warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile);
- warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File);
- warn_if_option_path_is_relative("Log",options->DebugLogFile);
- warn_if_option_path_is_relative("AccelDir",options->AccelDir);
- warn_if_option_path_is_relative("DataDirectory",options->DataDirectory);
- warn_if_option_path_is_relative("PidFile",options->PidFile);
+ n += warn_if_option_path_is_relative("CookieAuthFile",
+ options->CookieAuthFile);
+ n += warn_if_option_path_is_relative("ExtORPortCookieAuthFile",
+ options->ExtORPortCookieAuthFile);
+ n += warn_if_option_path_is_relative("DirPortFrontPage",
+ options->DirPortFrontPage);
+ n += warn_if_option_path_is_relative("V3BandwidthsFile",
+ options->V3BandwidthsFile);
+ n += warn_if_option_path_is_relative("ControlPortWriteToFile",
+ options->ControlPortWriteToFile);
+ n += warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile);
+ n += warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File);
+ n += warn_if_option_path_is_relative("Log",options->DebugLogFile);
+ n += warn_if_option_path_is_relative("AccelDir",options->AccelDir);
+ n += warn_if_option_path_is_relative("DataDirectory",options->DataDirectory);
+ n += warn_if_option_path_is_relative("PidFile",options->PidFile);
for (config_line_t *hs_line = options->RendConfigLines; hs_line;
hs_line = hs_line->next) {
if (!strcasecmp(hs_line->key, "HiddenServiceDir"))
- warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value);
+ n += warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value);
}
+ return n != 0;
}
/* Validate options related to the scheduler. From the Schedulers list, the
@@ -3049,7 +3089,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
* Always use the value of UseEntryGuards, not UseEntryGuards_option. */
options->UseEntryGuards = options->UseEntryGuards_option;
- warn_about_relative_paths(options);
+ if (warn_about_relative_paths(options) && options->RunAsDaemon) {
+ REJECT("You have specified at least one relative path (see above) "
+ "with the RunAsDaemon option. RunAsDaemon is not compatible "
+ "with relative paths.");
+ }
if (server_mode(options) &&
(!strcmpstart(uname, "Windows 95") ||
@@ -4112,6 +4156,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
CHECK_DEFAULT(TestingServerConsensusDownloadSchedule);
CHECK_DEFAULT(TestingClientConsensusDownloadSchedule);
CHECK_DEFAULT(TestingBridgeDownloadSchedule);
+ CHECK_DEFAULT(TestingBridgeBootstrapDownloadSchedule);
CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest);
CHECK_DEFAULT(TestingDirConnectionMaxStall);
CHECK_DEFAULT(TestingConsensusMaxDownloadTries);
@@ -4123,6 +4168,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
CHECK_DEFAULT(TestingSigningKeySlop);
CHECK_DEFAULT(TestingAuthKeySlop);
CHECK_DEFAULT(TestingLinkKeySlop);
+ CHECK_DEFAULT(TestingClientDNSRejectInternalAddresses);
#undef CHECK_DEFAULT
if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2)
@@ -5253,6 +5299,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
}
newoptions->IncludeUsed = cf_has_include;
+ in_option_validation = 1;
/* Validate newoptions */
if (options_validate(oldoptions, newoptions, newdefaultoptions,
@@ -5265,17 +5312,20 @@ options_init_from_string(const char *cf_defaults, const char *cf,
err = SETOPT_ERR_TRANSITION;
goto err;
}
+ in_option_validation = 0;
if (set_options(newoptions, msg)) {
err = SETOPT_ERR_SETTING;
goto err; /* frees and replaces old options */
}
+
or_options_free(global_default_options);
global_default_options = newdefaultoptions;
return SETOPT_OK;
err:
+ in_option_validation = 0;
or_options_free(newoptions);
or_options_free(newdefaultoptions);
if (*msg) {
diff --git a/src/or/connection.c b/src/or/connection.c
index e2392b52e3..b831d231b0 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -4124,16 +4124,27 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
return ret_conns; \
STMT_END
-/* Return a list of connections that aren't close and matches the given state.
- * The returned list can be empty and must be freed using smartlist_free().
- * The caller does NOT have owernship of the objects in the list so it must
- * not free them nor reference them as they can disapear. */
+/* Return a list of connections that aren't close and matches the given type
+ * and state. The returned list can be empty and must be freed using
+ * smartlist_free(). The caller does NOT have owernship of the objects in the
+ * list so it must not free them nor reference them as they can disappear. */
smartlist_t *
connection_list_by_type_state(int type, int state)
{
CONN_GET_ALL_TEMPLATE(conn, (conn->type == type && conn->state == state));
}
+/* Return a list of connections that aren't close and matches the given type
+ * and purpose. The returned list can be empty and must be freed using
+ * smartlist_free(). The caller does NOT have owernship of the objects in the
+ * list so it must not free them nor reference them as they can disappear. */
+smartlist_t *
+connection_list_by_type_purpose(int type, int purpose)
+{
+ CONN_GET_ALL_TEMPLATE(conn,
+ (conn->type == type && conn->purpose == purpose));
+}
+
/** Return a connection_t * from get_connection_array() that satisfies test on
* var, and that is not marked for close. */
#define CONN_GET_TEMPLATE(var, test) \
@@ -5210,7 +5221,7 @@ clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted,
const char *source)
{
char dbuf[64];
- char *ext_source = NULL;
+ char *ext_source = NULL, *warn = NULL;
format_time_interval(dbuf, sizeof(dbuf), apparent_skew);
if (conn)
tor_asprintf(&ext_source, "%s:%s:%d", source, conn->address, conn->port);
@@ -5224,9 +5235,14 @@ clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted,
apparent_skew > 0 ? "ahead" : "behind", dbuf,
apparent_skew > 0 ? "behind" : "ahead",
(!conn || trusted) ? "" : ", or they are sending us the wrong time");
- if (trusted)
+ if (trusted) {
control_event_general_status(LOG_WARN, "CLOCK_SKEW SKEW=%ld SOURCE=%s",
apparent_skew, ext_source);
+ tor_asprintf(&warn, "Clock skew %ld in %s from %s", apparent_skew,
+ received, source);
+ control_event_bootstrap_problem(warn, "CLOCK_SKEW", conn, 1);
+ }
+ tor_free(warn);
tor_free(ext_source);
}
diff --git a/src/or/connection.h b/src/or/connection.h
index 57b9a4a1df..5b51bb9fae 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -183,6 +183,7 @@ connection_t *connection_get_by_type_state(int type, int state);
connection_t *connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery);
smartlist_t *connection_list_by_type_state(int type, int state);
+smartlist_t *connection_list_by_type_purpose(int type, int purpose);
smartlist_t *connection_dir_list_by_purpose_and_resource(
int purpose,
const char *resource);
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index a9fdeee0ee..d1b8abda82 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -80,6 +80,7 @@
#include "hs_client.h"
#include "hs_circuit.h"
#include "main.h"
+#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
#include "proto_http.h"
@@ -1086,7 +1087,8 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
if (!entry_conn->chosen_exit_optional &&
!entry_conn->chosen_exit_retries)
continue;
- r1 = node_get_by_nickname(entry_conn->chosen_exit_name, 0);
+ r1 = node_get_by_nickname(entry_conn->chosen_exit_name,
+ NNF_NO_WARN_UNNAMED);
r2 = node_get_by_id(info->identity_digest);
if (!r1 || !r2 || r1 != r2)
continue;
@@ -1343,7 +1345,7 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
/* Hang on, did we find an answer saying that this is a reverse lookup for
* an internal address? If so, we should reject it if we're configured to
* do so. */
- if (options->ClientDNSRejectInternalAddresses) {
+ if (options->TestingClientDNSRejectInternalAddresses) {
/* Don't let clients try to do a reverse lookup on 10.0.0.1. */
tor_addr_t addr;
int ok;
@@ -1560,18 +1562,37 @@ connection_ap_handle_onion(entry_connection_t *conn,
if (addresstype == ONION_V2_HOSTNAME) {
tor_assert(edge_conn->rend_data);
rend_client_refetch_v2_renddesc(edge_conn->rend_data);
+ /* Whatever the result of the refetch, we don't go further. */
+ return 0;
} else {
tor_assert(addresstype == ONION_V3_HOSTNAME);
tor_assert(edge_conn->hs_ident);
- hs_client_refetch_hsdesc(&edge_conn->hs_ident->identity_pk);
+ /* Attempt to fetch the hsv3 descriptor. Check the retval to see how it
+ * went and act accordingly. */
+ int ret = hs_client_refetch_hsdesc(&edge_conn->hs_ident->identity_pk);
+ switch (ret) {
+ case HS_CLIENT_FETCH_MISSING_INFO:
+ /* By going to the end, the connection is put in waiting for a circuit
+ * state which means that it will be retried and consider as a pending
+ * connection. */
+ goto end;
+ case HS_CLIENT_FETCH_LAUNCHED:
+ case HS_CLIENT_FETCH_HAVE_DESC:
+ return 0;
+ case HS_CLIENT_FETCH_ERROR:
+ case HS_CLIENT_FETCH_NO_HSDIRS:
+ case HS_CLIENT_FETCH_NOT_ALLOWED:
+ /* Can't proceed further and better close the SOCKS request. */
+ return -1;
+ }
}
- return 0;
}
/* We have the descriptor! So launch a connection to the HS. */
- base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here. Great.");
+ end:
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
/* We'll try to attach it at the next event loop, or whenever
* we call connection_ap_attach_pending() */
connection_ap_mark_as_pending_circuit(conn);
@@ -1694,7 +1715,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
if (s[1] != '\0') {
/* Looks like a real .exit one. */
conn->chosen_exit_name = tor_strdup(s+1);
- node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ node = node_get_by_nickname(conn->chosen_exit_name, 0);
if (exit_source == ADDRMAPSRC_TRACKEXIT) {
/* We 5 tries before it expires the addressmap */
@@ -1715,7 +1736,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
* form that means (foo's address).foo.exit. */
conn->chosen_exit_name = tor_strdup(socks->address);
- node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ node = node_get_by_nickname(conn->chosen_exit_name, 0);
if (node) {
*socks->address = 0;
node_get_address_string(node, socks->address, sizeof(socks->address));
@@ -3819,7 +3840,7 @@ connection_ap_can_use_exit(const entry_connection_t *conn,
*/
if (conn->chosen_exit_name) {
const node_t *chosen_exit =
- node_get_by_nickname(conn->chosen_exit_name, 1);
+ node_get_by_nickname(conn->chosen_exit_name, 0);
if (!chosen_exit || tor_memneq(chosen_exit->identity,
exit_node->identity, DIGEST_LEN)) {
/* doesn't match */
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index d890b58da6..e4fda7ce78 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -727,7 +727,7 @@ connection_or_about_to_close(or_connection_t *or_conn)
control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED,
reason);
if (!authdir_mode_tests_reachability(options))
- control_event_bootstrap_problem(
+ control_event_bootstrap_prob_or(
orconn_end_reason_to_control_string(reason),
reason, or_conn);
}
@@ -1113,7 +1113,7 @@ connection_or_connect_failed(or_connection_t *conn,
{
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, reason);
if (!authdir_mode_tests_reachability(get_options()))
- control_event_bootstrap_problem(msg, reason, conn);
+ control_event_bootstrap_prob_or(msg, reason, conn);
}
/** <b>conn</b> got an error in connection_handle_read_impl() or
@@ -1236,7 +1236,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port),
transport_name, transport_name);
- control_event_bootstrap_problem(
+ control_event_bootstrap_prob_or(
"Can't connect to bridge",
END_OR_CONN_REASON_PT_MISSING,
conn);
@@ -1714,7 +1714,7 @@ connection_or_client_learned_peer_id(or_connection_t *conn,
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
END_OR_CONN_REASON_OR_IDENTITY);
if (!authdir_mode_tests_reachability(options))
- control_event_bootstrap_problem(
+ control_event_bootstrap_prob_or(
"Unexpected identity in router certificate",
END_OR_CONN_REASON_OR_IDENTITY,
conn);
diff --git a/src/or/control.c b/src/or/control.c
index eb6f213520..c815502aa4 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1156,7 +1156,6 @@ static const struct control_event_t control_event_table[] = {
{ EVENT_ERR_MSG, "ERR" },
{ EVENT_NEW_DESC, "NEWDESC" },
{ EVENT_ADDRMAP, "ADDRMAP" },
- { EVENT_AUTHDIR_NEWDESCS, "AUTHDIR_NEWDESCS" },
{ EVENT_DESCCHANGED, "DESCCHANGED" },
{ EVENT_NS, "NS" },
{ EVENT_STATUS_GENERAL, "STATUS_GENERAL" },
@@ -1196,7 +1195,10 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
{
- if (!strcasecmp(ev, "EXTENDED")) {
+ if (!strcasecmp(ev, "EXTENDED") ||
+ !strcasecmp(ev, "AUTHDIR_NEWDESCS")) {
+ log_warn(LD_CONTROL, "The \"%s\" SETEVENTS argument is no longer "
+ "supported.", ev);
continue;
} else {
int i;
@@ -1896,7 +1898,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
(void) control_conn;
if (!strcmpstart(question, "desc/id/")) {
const routerinfo_t *ri = NULL;
- const node_t *node = node_get_by_hex_id(question+strlen("desc/id/"));
+ const node_t *node = node_get_by_hex_id(question+strlen("desc/id/"), 0);
if (node)
ri = node->ri;
if (ri) {
@@ -1915,7 +1917,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
/* XXX Setting 'warn_if_unnamed' here is a bit silly -- the
* warning goes to the user, not to the controller. */
const node_t *node =
- node_get_by_nickname(question+strlen("desc/name/"), 1);
+ node_get_by_nickname(question+strlen("desc/name/"), 0);
if (node)
ri = node->ri;
if (ri) {
@@ -2001,7 +2003,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
return -1;
}
} else if (!strcmpstart(question, "md/id/")) {
- const node_t *node = node_get_by_hex_id(question+strlen("md/id/"));
+ const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0);
const microdesc_t *md = NULL;
if (node) md = node->md;
if (md && md->body) {
@@ -2010,7 +2012,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
} else if (!strcmpstart(question, "md/name/")) {
/* XXX Setting 'warn_if_unnamed' here is a bit silly -- the
* warning goes to the user, not to the controller. */
- const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 1);
+ const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 0);
/* XXXX duplicated code */
const microdesc_t *md = NULL;
if (node) md = node->md;
@@ -2023,7 +2025,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
} else if (!strcmpstart(question, "desc-annotations/id/")) {
const routerinfo_t *ri = NULL;
const node_t *node =
- node_get_by_hex_id(question+strlen("desc-annotations/id/"));
+ node_get_by_hex_id(question+strlen("desc-annotations/id/"), 0);
if (node)
ri = node->ri;
if (ri) {
@@ -2156,7 +2158,7 @@ download_status_to_string(const download_status_t *dl)
if (dl) {
/* Get some substrings of the eventual output ready */
- format_iso_time(tbuf, dl->next_attempt_at);
+ format_iso_time(tbuf, download_status_get_next_attempt_at(dl));
switch (dl->schedule) {
case DL_SCHED_GENERIC:
@@ -3404,7 +3406,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
nodes = smartlist_new();
SMARTLIST_FOREACH_BEGIN(router_nicknames, const char *, n) {
- const node_t *node = node_get_by_nickname(n, 1);
+ const node_t *node = node_get_by_nickname(n, 0);
if (!node) {
connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n);
goto done;
@@ -3631,12 +3633,15 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
int cache = 0; /* eventually, we may switch this to 1 */
const char *cp = memchr(body, '\n', len);
- smartlist_t *args = smartlist_new();
- tor_assert(cp);
+
+ if (cp == NULL) {
+ connection_printf_to_buf(conn, "251 Empty body\r\n");
+ return 0;
+ }
++cp;
char *cmdline = tor_memdup_nulterm(body, cp-body);
-
+ smartlist_t *args = smartlist_new();
smartlist_split_string(args, cmdline, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(args, char *, option) {
@@ -4168,7 +4173,7 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len,
const char *server;
server = arg + strlen(opt_server);
- node = node_get_by_hex_id(server);
+ node = node_get_by_hex_id(server, 0);
if (!node) {
connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
server);
@@ -4229,14 +4234,19 @@ handle_control_hspost(control_connection_t *conn,
const char *body)
{
static const char *opt_server = "SERVER=";
- smartlist_t *args = smartlist_new();
smartlist_t *hs_dirs = NULL;
const char *encoded_desc = body;
size_t encoded_desc_len = len;
char *cp = memchr(body, '\n', len);
+ if (cp == NULL) {
+ connection_printf_to_buf(conn, "251 Empty body\r\n");
+ return 0;
+ }
char *argline = tor_strndup(body, cp-body);
+ smartlist_t *args = smartlist_new();
+
/* If any SERVER= options were specified, try parse the options line */
if (!strcasecmpstart(argline, opt_server)) {
/* encoded_desc begins after a newline character */
@@ -4249,7 +4259,7 @@ handle_control_hspost(control_connection_t *conn,
SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
if (!strcasecmpstart(arg, opt_server)) {
const char *server = arg + strlen(opt_server);
- const node_t *node = node_get_by_hex_id(server);
+ const node_t *node = node_get_by_hex_id(server, 0);
if (!node || !node->rs) {
connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
@@ -5577,15 +5587,20 @@ control_event_stream_bandwidth(edge_connection_t *edge_conn)
{
circuit_t *circ;
origin_circuit_t *ocirc;
+ struct timeval now;
+ char tbuf[ISO_TIME_USEC_LEN+1];
if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
if (!edge_conn->n_read && !edge_conn->n_written)
return 0;
+ tor_gettimeofday(&now);
+ format_iso_time_nospace_usec(tbuf, &now);
send_control_event(EVENT_STREAM_BANDWIDTH_USED,
- "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n",
+ "650 STREAM_BW "U64_FORMAT" %lu %lu %s\r\n",
U64_PRINTF_ARG(edge_conn->base_.global_identifier),
(unsigned long)edge_conn->n_read,
- (unsigned long)edge_conn->n_written);
+ (unsigned long)edge_conn->n_written,
+ tbuf);
circ = circuit_get_by_edge_conn(edge_conn);
if (circ && CIRCUIT_IS_ORIGIN(circ)) {
@@ -5607,6 +5622,8 @@ control_event_stream_bandwidth_used(void)
if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
smartlist_t *conns = get_connection_array();
edge_connection_t *edge_conn;
+ struct timeval now;
+ char tbuf[ISO_TIME_USEC_LEN+1];
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn)
{
@@ -5616,11 +5633,14 @@ control_event_stream_bandwidth_used(void)
if (!edge_conn->n_read && !edge_conn->n_written)
continue;
+ tor_gettimeofday(&now);
+ format_iso_time_nospace_usec(tbuf, &now);
send_control_event(EVENT_STREAM_BANDWIDTH_USED,
- "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n",
+ "650 STREAM_BW "U64_FORMAT" %lu %lu %s\r\n",
U64_PRINTF_ARG(edge_conn->base_.global_identifier),
(unsigned long)edge_conn->n_read,
- (unsigned long)edge_conn->n_written);
+ (unsigned long)edge_conn->n_written,
+ tbuf);
edge_conn->n_written = edge_conn->n_read = 0;
}
@@ -5636,6 +5656,8 @@ int
control_event_circ_bandwidth_used(void)
{
origin_circuit_t *ocirc;
+ struct timeval now;
+ char tbuf[ISO_TIME_USEC_LEN+1];
if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED))
return 0;
@@ -5645,11 +5667,15 @@ control_event_circ_bandwidth_used(void)
ocirc = TO_ORIGIN_CIRCUIT(circ);
if (!ocirc->n_read_circ_bw && !ocirc->n_written_circ_bw)
continue;
+ tor_gettimeofday(&now);
+ format_iso_time_nospace_usec(tbuf, &now);
send_control_event(EVENT_CIRC_BANDWIDTH_USED,
- "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu\r\n",
+ "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu "
+ "TIME=%s\r\n",
ocirc->global_identifier,
(unsigned long)ocirc->n_read_circ_bw,
- (unsigned long)ocirc->n_written_circ_bw);
+ (unsigned long)ocirc->n_written_circ_bw,
+ tbuf);
ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
}
SMARTLIST_FOREACH_END(circ);
@@ -6055,47 +6081,6 @@ control_event_address_mapped(const char *from, const char *to, time_t expires,
return 0;
}
-/** The authoritative dirserver has received a new descriptor that
- * has passed basic syntax checks and is properly self-signed.
- *
- * Notify any interested party of the new descriptor and what has
- * been done with it, and also optionally give an explanation/reason. */
-int
-control_event_or_authdir_new_descriptor(const char *action,
- const char *desc, size_t desclen,
- const char *msg)
-{
- char firstline[1024];
- char *buf;
- size_t totallen;
- char *esc = NULL;
- size_t esclen;
-
- if (!EVENT_IS_INTERESTING(EVENT_AUTHDIR_NEWDESCS))
- return 0;
-
- tor_snprintf(firstline, sizeof(firstline),
- "650+AUTHDIR_NEWDESC=\r\n%s\r\n%s\r\n",
- action,
- msg ? msg : "");
-
- /* Escape the server descriptor properly */
- esclen = write_escaped_data(desc, desclen, &esc);
-
- totallen = strlen(firstline) + esclen + 1;
- buf = tor_malloc(totallen);
- strlcpy(buf, firstline, totallen);
- strlcpy(buf+strlen(firstline), esc, totallen);
- send_control_event_string(EVENT_AUTHDIR_NEWDESCS,
- buf);
- send_control_event_string(EVENT_AUTHDIR_NEWDESCS,
- "650 OK\r\n");
- tor_free(esc);
- tor_free(buf);
-
- return 0;
-}
-
/** Cached liveness for network liveness events and GETINFO
*/
@@ -6785,80 +6770,112 @@ control_event_bootstrap(bootstrap_status_t status, int progress)
}
/** Called when Tor has failed to make bootstrapping progress in a way
- * that indicates a problem. <b>warn</b> gives a hint as to why, and
- * <b>reason</b> provides an "or_conn_end_reason" tag. <b>or_conn</b>
- * is the connection that caused this problem.
+ * that indicates a problem. <b>warn</b> gives a human-readable hint
+ * as to why, and <b>reason</b> provides a controller-facing short
+ * tag. <b>conn</b> is the connection that caused this problem and
+ * can be NULL if a connection cannot be easily identified.
*/
-MOCK_IMPL(void,
-control_event_bootstrap_problem, (const char *warn, int reason,
- or_connection_t *or_conn))
+void
+control_event_bootstrap_problem(const char *warn, const char *reason,
+ const connection_t *conn, int dowarn)
{
int status = bootstrap_percent;
const char *tag = "", *summary = "";
char buf[BOOTSTRAP_MSG_LEN];
const char *recommendation = "ignore";
int severity;
+ char *or_id = NULL, *hostaddr = NULL;
+ or_connection_t *or_conn = NULL;
/* bootstrap_percent must not be in "undefined" state here. */
tor_assert(status >= 0);
- if (or_conn->have_noted_bootstrap_problem)
- return;
-
- or_conn->have_noted_bootstrap_problem = 1;
-
if (bootstrap_percent == 100)
return; /* already bootstrapped; nothing to be done here. */
bootstrap_problems++;
if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
- recommendation = "warn";
-
- if (reason == END_OR_CONN_REASON_NO_ROUTE)
- recommendation = "warn";
-
- /* If we are using bridges and all our OR connections are now
- closed, it means that we totally failed to connect to our
- bridges. Throw a warning. */
- if (get_options()->UseBridges && !any_other_active_or_conns(or_conn))
- recommendation = "warn";
+ dowarn = 1;
if (we_are_hibernating())
- recommendation = "ignore";
+ dowarn = 0;
while (status>=0 && bootstrap_status_to_string(status, &tag, &summary) < 0)
status--; /* find a recognized status string based on current progress */
status = bootstrap_percent; /* set status back to the actual number */
- severity = !strcmp(recommendation, "warn") ? LOG_WARN : LOG_INFO;
+ severity = dowarn ? LOG_WARN : LOG_INFO;
+
+ if (dowarn)
+ recommendation = "warn";
+
+ if (conn && conn->type == CONN_TYPE_OR) {
+ /* XXX TO_OR_CONN can't deal with const */
+ or_conn = TO_OR_CONN((connection_t *)conn);
+ or_id = tor_strdup(hex_str(or_conn->identity_digest, DIGEST_LEN));
+ } else {
+ or_id = tor_strdup("?");
+ }
+
+ if (conn)
+ tor_asprintf(&hostaddr, "%s:%d", conn->address, (int)conn->port);
+ else
+ hostaddr = tor_strdup("?");
log_fn(severity,
LD_CONTROL, "Problem bootstrapping. Stuck at %d%%: %s. (%s; %s; "
- "count %d; recommendation %s; host %s at %s:%d)",
- status, summary, warn,
- orconn_end_reason_to_control_string(reason),
+ "count %d; recommendation %s; host %s at %s)",
+ status, summary, warn, reason,
bootstrap_problems, recommendation,
- hex_str(or_conn->identity_digest, DIGEST_LEN),
- or_conn->base_.address,
- or_conn->base_.port);
+ or_id, hostaddr);
connection_or_report_broken_states(severity, LD_HANDSHAKE);
tor_snprintf(buf, sizeof(buf),
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\" WARNING=\"%s\" REASON=%s "
- "COUNT=%d RECOMMENDATION=%s HOSTID=\"%s\" HOSTADDR=\"%s:%d\"",
- bootstrap_percent, tag, summary, warn,
- orconn_end_reason_to_control_string(reason), bootstrap_problems,
+ "COUNT=%d RECOMMENDATION=%s HOSTID=\"%s\" HOSTADDR=\"%s\"",
+ bootstrap_percent, tag, summary, warn, reason, bootstrap_problems,
recommendation,
- hex_str(or_conn->identity_digest, DIGEST_LEN),
- or_conn->base_.address,
- (int)or_conn->base_.port);
+ or_id, hostaddr);
tor_snprintf(last_sent_bootstrap_message,
sizeof(last_sent_bootstrap_message),
"WARN %s", buf);
control_event_client_status(LOG_WARN, "%s", buf);
+
+ tor_free(hostaddr);
+ tor_free(or_id);
+}
+
+/** Called when Tor has failed to make bootstrapping progress in a way
+ * that indicates a problem. <b>warn</b> gives a hint as to why, and
+ * <b>reason</b> provides an "or_conn_end_reason" tag. <b>or_conn</b>
+ * is the connection that caused this problem.
+ */
+MOCK_IMPL(void,
+control_event_bootstrap_prob_or, (const char *warn, int reason,
+ or_connection_t *or_conn))
+{
+ int dowarn = 0;
+
+ if (or_conn->have_noted_bootstrap_problem)
+ return;
+
+ or_conn->have_noted_bootstrap_problem = 1;
+
+ if (reason == END_OR_CONN_REASON_NO_ROUTE)
+ dowarn = 1;
+
+ /* If we are using bridges and all our OR connections are now
+ closed, it means that we totally failed to connect to our
+ bridges. Throw a warning. */
+ if (get_options()->UseBridges && !any_other_active_or_conns(or_conn))
+ dowarn = 1;
+
+ control_event_bootstrap_problem(warn,
+ orconn_end_reason_to_control_string(reason),
+ TO_CONN(or_conn), dowarn);
}
/** We just generated a new summary of which countries we've seen clients
diff --git a/src/or/control.h b/src/or/control.h
index 41a194bfcb..8003126f3e 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -33,7 +33,6 @@ void connection_control_closed(control_connection_t *conn);
int connection_control_process_inbuf(control_connection_t *conn);
-#define EVENT_AUTHDIR_NEWDESCS 0x000D
#define EVENT_NS 0x000F
int control_event_is_interesting(int event);
@@ -64,10 +63,6 @@ int control_event_descriptors_changed(smartlist_t *routers);
int control_event_address_mapped(const char *from, const char *to,
time_t expires, const char *error,
const int cached);
-int control_event_or_authdir_new_descriptor(const char *action,
- const char *desc,
- size_t desclen,
- const char *msg);
int control_event_my_descriptor_changed(void);
int control_event_network_liveness_update(int liveness);
int control_event_networkstatus_changed(smartlist_t *statuses);
@@ -104,9 +99,11 @@ void enable_control_logging(void);
void monitor_owning_controller_process(const char *process_spec);
int control_event_bootstrap(bootstrap_status_t status, int progress);
-MOCK_DECL(void, control_event_bootstrap_problem,(const char *warn,
+MOCK_DECL(void, control_event_bootstrap_prob_or,(const char *warn,
int reason,
or_connection_t *or_conn));
+void control_event_bootstrap_problem(const char *warn, const char *reason,
+ const connection_t *conn, int dowarn);
void control_event_clients_seen(const char *controller_str);
void control_event_transport_launched(const char *mode,
@@ -169,8 +166,8 @@ void control_free_all(void);
#define EVENT_WARN_MSG 0x000A
#define EVENT_ERR_MSG 0x000B
#define EVENT_ADDRMAP 0x000C
-/* Exposed above */
-// #define EVENT_AUTHDIR_NEWDESCS 0x000D
+/* There was an AUTHDIR_NEWDESCS event, but it no longer exists. We
+ can reclaim 0x000D. */
#define EVENT_DESCCHANGED 0x000E
/* Exposed above */
// #define EVENT_NS 0x000F
diff --git a/src/or/directory.c b/src/or/directory.c
index 007235d108..630524db67 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -3476,17 +3476,31 @@ connection_dir_about_to_close(dir_connection_t *dir_conn)
* <b>status</b> and <b>reason_phrase</b>. Write it to <b>conn</b>.
*/
static void
-write_http_status_line(dir_connection_t *conn, int status,
+write_short_http_response(dir_connection_t *conn, int status,
const char *reason_phrase)
{
- char buf[256];
- if (tor_snprintf(buf, sizeof(buf), "HTTP/1.0 %d %s\r\n\r\n",
- status, reason_phrase ? reason_phrase : "OK") < 0) {
- log_warn(LD_BUG,"status line too long.");
- return;
+ char *buf = NULL;
+ char *datestring = NULL;
+
+ IF_BUG_ONCE(!reason_phrase) { /* bullet-proofing */
+ reason_phrase = "unspecified";
+ }
+
+ if (server_mode(get_options())) {
+ /* include the Date: header, but only if we're a relay or bridge */
+ char datebuf[RFC1123_TIME_LEN+1];
+ format_rfc1123_time(datebuf, time(NULL));
+ tor_asprintf(&datestring, "Date: %s\r\n", datebuf);
}
+
+ tor_asprintf(&buf, "HTTP/1.0 %d %s\r\n%s\r\n",
+ status, reason_phrase, datestring?datestring:"");
+
log_debug(LD_DIRSERV,"Wrote status 'HTTP/1.0 %d %s'", status, reason_phrase);
connection_buf_add(buf, strlen(buf), TO_CONN(conn));
+
+ tor_free(datestring);
+ tor_free(buf);
}
/** Write the header for an HTTP/1.0 response onto <b>conn</b>-\>outbuf,
@@ -3835,7 +3849,7 @@ directory_handle_command_get,(dir_connection_t *conn, const char *headers,
conn->base_.state = DIR_CONN_STATE_SERVER_WRITING;
if (parse_http_url(headers, &url) < 0) {
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
return 0;
}
if ((header = http_get_header(headers, "If-Modified-Since: "))) {
@@ -3896,7 +3910,7 @@ directory_handle_command_get,(dir_connection_t *conn, const char *headers,
}
/* we didn't recognize the url */
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
result = 0;
done:
@@ -3924,7 +3938,7 @@ handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args)
NULL, DIRPORTFRONTPAGE_CACHE_LIFETIME);
connection_buf_add(frontpage, dlen, TO_CONN(conn));
} else {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
}
return 0;
}
@@ -4306,13 +4320,13 @@ handle_get_current_consensus(dir_connection_t *conn,
parsed_consensus_request_t req;
if (parse_consensus_request(&req, args) < 0) {
- write_http_status_line(conn, 404, "Couldn't parse request");
+ write_short_http_response(conn, 404, "Couldn't parse request");
goto done;
}
if (digest_list_contains_best_consensus(req.flav,
req.diff_from_digests)) {
- write_http_status_line(conn, 304, "Not modified");
+ write_short_http_response(conn, 304, "Not modified");
geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED);
goto done;
}
@@ -4327,7 +4341,7 @@ handle_get_current_consensus(dir_connection_t *conn,
}
if (req.diff_only && !cached_consensus) {
- write_http_status_line(conn, 404, "No such diff available");
+ write_short_http_response(conn, 404, "No such diff available");
// XXXX warn_consensus_is_too_old(v, req.flavor, now);
geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
goto done;
@@ -4350,7 +4364,7 @@ handle_get_current_consensus(dir_connection_t *conn,
if (cached_consensus && have_valid_until &&
!networkstatus_valid_until_is_reasonably_live(valid_until, now)) {
- write_http_status_line(conn, 404, "Consensus is too old");
+ write_short_http_response(conn, 404, "Consensus is too old");
warn_consensus_is_too_old(cached_consensus, req.flavor, now);
geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
goto done;
@@ -4358,7 +4372,7 @@ handle_get_current_consensus(dir_connection_t *conn,
if (cached_consensus && req.want_fps &&
!client_likes_consensus(cached_consensus, req.want_fps)) {
- write_http_status_line(conn, 404, "Consensus not signed by sufficient "
+ write_short_http_response(conn, 404, "Consensus not signed by sufficient "
"number of requested authorities");
geoip_note_ns_response(GEOIP_REJECT_NOT_ENOUGH_SIGS);
goto done;
@@ -4384,11 +4398,11 @@ handle_get_current_consensus(dir_connection_t *conn,
&n_expired);
if (!smartlist_len(conn->spool) && !n_expired) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
goto done;
} else if (!smartlist_len(conn->spool)) {
- write_http_status_line(conn, 304, "Not modified");
+ write_short_http_response(conn, 304, "Not modified");
geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED);
goto done;
}
@@ -4397,7 +4411,7 @@ handle_get_current_consensus(dir_connection_t *conn,
log_debug(LD_DIRSERV,
"Client asked for network status lists, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503, "Directory busy, try again later");
geoip_note_ns_response(GEOIP_REJECT_BUSY);
goto done;
}
@@ -4510,7 +4524,7 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
smartlist_free(fps);
}
if (!smartlist_len(dir_items) && !smartlist_len(items)) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto vote_done;
}
@@ -4547,7 +4561,7 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
});
if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503, "Directory busy, try again later");
goto vote_done;
}
write_http_response_header(conn, body_len ? body_len : -1,
@@ -4604,14 +4618,14 @@ handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args)
compress_method != NO_METHOD,
&size_guess, NULL);
if (smartlist_len(conn->spool) == 0) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto done;
}
if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503, "Directory busy, try again later");
goto done;
}
@@ -4703,13 +4717,14 @@ handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args)
if (res < 0 || size_guess == 0 || smartlist_len(conn->spool) == 0) {
if (msg == NULL)
msg = "Not found";
- write_http_status_line(conn, 404, msg);
+ write_short_http_response(conn, 404, msg);
} else {
if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503,
+ "Directory busy, try again later");
dir_conn_clear_spool(conn);
goto done;
}
@@ -4782,18 +4797,18 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
});
smartlist_free(fp_sks);
} else {
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
goto keys_done;
}
if (!smartlist_len(certs)) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto keys_done;
}
SMARTLIST_FOREACH(certs, authority_cert_t *, c,
if (c->cache_info.published_on < if_modified_since)
SMARTLIST_DEL_CURRENT(certs, c));
if (!smartlist_len(certs)) {
- write_http_status_line(conn, 304, "Not modified");
+ write_short_http_response(conn, 304, "Not modified");
goto keys_done;
}
len = 0;
@@ -4803,7 +4818,7 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
if (global_write_bucket_low(TO_CONN(conn),
compress_method != NO_METHOD ? len/2 : len,
2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503, "Directory busy, try again later");
goto keys_done;
}
@@ -4854,19 +4869,19 @@ handle_get_hs_descriptor_v2(dir_connection_t *conn,
connection_buf_add(descp, strlen(descp), TO_CONN(conn));
break;
case 0: /* well-formed but not present */
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
break;
case -1: /* not well-formed */
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
break;
}
} else { /* not well-formed */
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
}
goto done;
} else {
/* Not encrypted! */
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
}
done:
return 0;
@@ -4885,7 +4900,7 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn,
/* Reject unencrypted dir connections */
if (!connection_dir_is_encrypted(conn)) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto done;
}
@@ -4897,7 +4912,7 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn,
retval = hs_cache_lookup_as_dir(HS_VERSION_THREE,
pubkey_str, &desc_str);
if (retval <= 0 || desc_str == NULL) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto done;
}
@@ -4932,7 +4947,7 @@ handle_get_networkstatus_bridges(dir_connection_t *conn,
if (!header ||
tor_memneq(digest,
options->BridgePassword_AuthDigest_, DIGEST256_LEN)) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
tor_free(header);
goto done;
}
@@ -5067,12 +5082,12 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
if (!public_server_mode(options)) {
log_info(LD_DIR, "Rejected dir post request from %s "
"since we're not a public relay.", conn->base_.address);
- write_http_status_line(conn, 503, "Not acting as a public relay");
+ write_short_http_response(conn, 503, "Not acting as a public relay");
goto done;
}
if (parse_http_url(headers, &url) < 0) {
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
return 0;
}
log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url));
@@ -5083,10 +5098,10 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
if (rend_cache_store_v2_desc_as_dir(body) < 0) {
log_warn(LD_REND, "Rejected v2 rend descriptor (body size %d) from %s.",
(int)body_len, conn->base_.address);
- write_http_status_line(conn, 400,
+ write_short_http_response(conn, 400,
"Invalid v2 service descriptor rejected");
} else {
- write_http_status_line(conn, 200, "Service descriptor (v2) stored");
+ write_short_http_response(conn, 200, "Service descriptor (v2) stored");
log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted");
}
goto done;
@@ -5103,14 +5118,14 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
if (code != 200) {
msg = "Invalid HS descriptor. Rejected.";
}
- write_http_status_line(conn, code, msg);
+ write_short_http_response(conn, code, msg);
goto done;
}
if (!authdir_mode(options)) {
/* we just provide cached directories; we don't want to
* receive anything. */
- write_http_status_line(conn, 400, "Nonauthoritative directory does not "
+ write_short_http_response(conn, 400, "Nonauthoritative directory does not "
"accept posted server descriptors");
goto done;
}
@@ -5125,7 +5140,7 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
tor_assert(msg);
if (r == ROUTER_ADDED_SUCCESSFULLY) {
- write_http_status_line(conn, 200, msg);
+ write_short_http_response(conn, 200, msg);
} else if (WRA_WAS_OUTDATED(r)) {
write_http_response_header_impl(conn, -1, NULL, NULL,
"X-Descriptor-Not-New: Yes\r\n", -1);
@@ -5134,7 +5149,7 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
"Rejected router descriptor or extra-info from %s "
"(\"%s\").",
conn->base_.address, msg);
- write_http_status_line(conn, 400, msg);
+ write_short_http_response(conn, 400, msg);
}
goto done;
}
@@ -5144,12 +5159,12 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
const char *msg = "OK";
int status;
if (dirvote_add_vote(body, &msg, &status)) {
- write_http_status_line(conn, status, "Vote stored");
+ write_short_http_response(conn, status, "Vote stored");
} else {
tor_assert(msg);
log_warn(LD_DIRSERV, "Rejected vote from %s (\"%s\").",
conn->base_.address, msg);
- write_http_status_line(conn, status, msg);
+ write_short_http_response(conn, status, msg);
}
goto done;
}
@@ -5158,17 +5173,18 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
!strcmp(url,"/tor/post/consensus-signature")) { /* sigs on consensus. */
const char *msg = NULL;
if (dirvote_add_signatures(body, conn->base_.address, &msg)>=0) {
- write_http_status_line(conn, 200, msg?msg:"Signatures stored");
+ write_short_http_response(conn, 200, msg?msg:"Signatures stored");
} else {
log_warn(LD_DIR, "Unable to store signatures posted by %s: %s",
conn->base_.address, msg?msg:"???");
- write_http_status_line(conn, 400, msg?msg:"Unable to store signatures");
+ write_short_http_response(conn, 400,
+ msg?msg:"Unable to store signatures");
}
goto done;
}
/* we didn't recognize the url */
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
done:
tor_free(url);
@@ -5323,26 +5339,24 @@ connection_dir_finished_connecting(dir_connection_t *conn)
STATIC const smartlist_t *
find_dl_schedule(const download_status_t *dls, const or_options_t *options)
{
- const int dir_server = dir_server_mode(options);
- const int multi_d = networkstatus_consensus_can_use_multiple_directories(
- options);
- const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
- time(NULL));
- const int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(
- options);
switch (dls->schedule) {
case DL_SCHED_GENERIC:
- if (dir_server) {
+ /* Any other directory document */
+ if (dir_server_mode(options)) {
+ /* A directory authority or directory mirror */
return options->TestingServerDownloadSchedule;
} else {
return options->TestingClientDownloadSchedule;
}
case DL_SCHED_CONSENSUS:
- if (!multi_d) {
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
+ /* A public relay */
return options->TestingServerConsensusDownloadSchedule;
} else {
- if (we_are_bootstrapping) {
- if (!use_fallbacks) {
+ /* A client or bridge */
+ if (networkstatus_consensus_is_bootstrapping(time(NULL))) {
+ /* During bootstrapping */
+ if (!networkstatus_consensus_can_use_extra_fallbacks(options)) {
/* A bootstrapping client without extra fallback directories */
return
options->ClientBootstrapConsensusAuthorityOnlyDownloadSchedule;
@@ -5358,11 +5372,20 @@ find_dl_schedule(const download_status_t *dls, const or_options_t *options)
options->ClientBootstrapConsensusFallbackDownloadSchedule;
}
} else {
+ /* A client with a reasonably live consensus, with or without
+ * certificates */
return options->TestingClientConsensusDownloadSchedule;
}
}
case DL_SCHED_BRIDGE:
- return options->TestingBridgeDownloadSchedule;
+ /* A bridge client downloading bridge descriptors */
+ if (options->UseBridges && any_bridge_descriptors_known()) {
+ /* A bridge client with one or more running bridges */
+ return options->TestingBridgeDownloadSchedule;
+ } else {
+ /* A bridge client with no running bridges */
+ return options->TestingBridgeBootstrapDownloadSchedule;
+ }
default:
tor_assert(0);
}
@@ -5683,8 +5706,8 @@ download_status_get_initial_delay_from_now(const download_status_t *dls)
* (We find the zeroth element of the download schedule, and set
* next_attempt_at to be the appropriate offset from 'now'. In most
* cases this means setting it to 'now', so the item will be immediately
- * downloadable; in the case of bridge descriptors, the zeroth element
- * is an hour from now.) */
+ * downloadable; when using authorities with fallbacks, there is a few seconds'
+ * delay.) */
void
download_status_reset(download_status_t *dls)
{
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index b3a0b0d23f..06ac15d587 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -673,9 +673,6 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
ri->nickname, source, (int)ri->cache_info.signed_descriptor_len,
MAX_DESCRIPTOR_UPLOAD_SIZE);
*msg = "Router descriptor was too large.";
- control_event_or_authdir_new_descriptor("REJECTED",
- ri->cache_info.signed_descriptor_body,
- desclen, *msg);
r = ROUTER_AUTHDIR_REJECTS;
goto fail;
}
@@ -694,9 +691,6 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
router_describe(ri), source);
*msg = "Not replacing router descriptor; no information has changed since "
"the last one with this identity.";
- control_event_or_authdir_new_descriptor("DROPPED",
- ri->cache_info.signed_descriptor_body,
- desclen, *msg);
r = ROUTER_IS_ALREADY_KNOWN;
goto fail;
}
@@ -709,9 +703,6 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
if (ed25519_validate_pubkey(pkey) < 0) {
log_warn(LD_DIRSERV, "Received bad key from %s (source %s)",
router_describe(ri), source);
- control_event_or_authdir_new_descriptor("REJECTED",
- ri->cache_info.signed_descriptor_body,
- desclen, *msg);
routerinfo_free(ri);
return ROUTER_AUTHDIR_REJECTS;
}
@@ -754,14 +745,11 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
r = router_add_to_routerlist(ri, msg, 0, 0);
if (!WRA_WAS_ADDED(r)) {
/* unless the routerinfo was fine, just out-of-date */
- if (WRA_WAS_REJECTED(r))
- control_event_or_authdir_new_descriptor("REJECTED", desc, desclen, *msg);
log_info(LD_DIRSERV,
"Did not add descriptor from '%s' (source: %s): %s.",
nickname, source, *msg ? *msg : "(no message)");
} else {
smartlist_t *changed;
- control_event_or_authdir_new_descriptor("ACCEPTED", desc, desclen, *msg);
changed = smartlist_new();
smartlist_add(changed, ri);
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 739ec82484..c104b8a973 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -2385,7 +2385,7 @@ entry_guards_upgrade_waiting_circuits(guard_selection_t *gs,
SMARTLIST_FOREACH_BEGIN(all_circuits, origin_circuit_t *, circ) {
circuit_guard_state_t *state = origin_circuit_get_guard_state(circ);
- if BUG((state == NULL))
+ if (BUG(state == NULL))
continue;
if (state->state == GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD) {
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 65d00b8659..3944b2cf69 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -1665,6 +1665,15 @@ getinfo_helper_geoip(control_connection_t *control_conn,
sa_family_t family;
tor_addr_t addr;
question += strlen("ip-to-country/");
+
+ if (!strcmp(question, "ipv4-available") ||
+ !strcmp(question, "ipv6-available")) {
+ family = !strcmp(question, "ipv4-available") ? AF_INET : AF_INET6;
+ const int available = geoip_is_loaded(family);
+ tor_asprintf(answer, "%d", !! available);
+ return 0;
+ }
+
family = tor_addr_parse(&addr, question);
if (family != AF_INET && family != AF_INET6) {
*errmsg = "Invalid address family";
diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c
index 6962c5ce44..3ebe13fb4d 100644
--- a/src/or/hs_cache.c
+++ b/src/or/hs_cache.c
@@ -20,6 +20,9 @@
#include "hs_cache.h"
+static int cached_client_descriptor_has_expired(time_t now,
+ const hs_cache_client_descriptor_t *cached_desc);
+
/********************** Directory HS cache ******************/
/* Directory descriptor cache. Map indexed by blinded key. */
@@ -234,8 +237,7 @@ cache_clean_v3_as_dir(time_t now, time_t global_cutoff)
/* Logging. */
{
char key_b64[BASE64_DIGEST256_LEN + 1];
- base64_encode(key_b64, sizeof(key_b64), (const char *) key,
- DIGEST256_LEN, 0);
+ digest256_to_base64(key_b64, (const char *) key);
log_info(LD_REND, "Removing v3 descriptor '%s' from HSDir cache",
safe_str_client(key_b64));
}
@@ -357,12 +359,27 @@ store_v3_desc_as_client(hs_cache_client_descriptor_t *desc)
rend_cache_increment_allocation(cache_get_client_entry_size(desc));
}
-/* Query our cache and return the entry or NULL if not found. */
+/* Query our cache and return the entry or NULL if not found or if expired. */
STATIC hs_cache_client_descriptor_t *
lookup_v3_desc_as_client(const uint8_t *key)
{
+ time_t now = approx_time();
+ hs_cache_client_descriptor_t *cached_desc;
+
tor_assert(key);
- return digest256map_get(hs_cache_v3_client, key);
+
+ /* Do the lookup */
+ cached_desc = digest256map_get(hs_cache_v3_client, key);
+ if (!cached_desc) {
+ return NULL;
+ }
+
+ /* Don't return expired entries */
+ if (cached_client_descriptor_has_expired(now, cached_desc)) {
+ return NULL;
+ }
+
+ return cached_desc;
}
/* Parse the encoded descriptor in <b>desc_str</b> using
@@ -389,7 +406,10 @@ cache_client_desc_new(const char *desc_str,
/* All is good: make a cache object for this descriptor */
client_desc = tor_malloc_zero(sizeof(hs_cache_client_descriptor_t));
ed25519_pubkey_copy(&client_desc->key, service_identity_pk);
- client_desc->created_ts = approx_time();
+ /* Set expiration time for this cached descriptor to be the start of the next
+ * time period since that's when clients need to start using the next blinded
+ * pk of the service (and hence will need its next descriptor). */
+ client_desc->expiration_ts = hs_get_start_time_of_next_time_period(0);
client_desc->desc = desc;
client_desc->encoded_desc = tor_strdup(desc_str);
@@ -604,9 +624,8 @@ cache_store_as_client(hs_cache_client_descriptor_t *client_desc)
if (cache_entry != NULL) {
/* If we have an entry in our cache that has a revision counter greater
* than the one we just fetched, discard the one we fetched. */
- if (cache_entry->desc->plaintext_data.revision_counter >
- client_desc->desc->plaintext_data.revision_counter) {
- log_info(LD_REND, "We already have fresher descriptor. Ignoring.");
+ if (BUG(cache_entry->desc->plaintext_data.revision_counter >
+ client_desc->desc->plaintext_data.revision_counter)) {
cache_client_desc_free(client_desc);
goto done;
}
@@ -622,7 +641,30 @@ cache_store_as_client(hs_cache_client_descriptor_t *client_desc)
return 0;
}
-/* Clean the client cache using now as the current time. Return the total size
+/* Return true iff the cached client descriptor at <b>cached_desc</b has
+ * expired. */
+static int
+cached_client_descriptor_has_expired(time_t now,
+ const hs_cache_client_descriptor_t *cached_desc)
+{
+ /* We use the current consensus time to see if we should expire this
+ * descriptor since we use consensus time for all other parts of the protocol
+ * as well (e.g. to build the blinded key and compute time periods). */
+ const networkstatus_t *ns = networkstatus_get_live_consensus(now);
+ /* If we don't have a recent consensus, consider this entry expired since we
+ * will want to fetch a new HS desc when we get a live consensus. */
+ if (!ns) {
+ return 1;
+ }
+
+ if (cached_desc->expiration_ts <= ns->valid_after) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* clean the client cache using now as the current time. Return the total size
* of removed bytes from the cache. */
static size_t
cache_clean_v3_as_client(time_t now)
@@ -636,11 +678,9 @@ cache_clean_v3_as_client(time_t now)
DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
hs_cache_client_descriptor_t *, entry) {
size_t entry_size;
- time_t cutoff = now - rend_cache_max_entry_lifetime();
- /* If the entry has been created _after_ the cutoff, not expired so
- * continue to the next entry in our v3 cache. */
- if (entry->created_ts > cutoff) {
+ /* If the entry has not expired, continue to the next cached entry */
+ if (!cached_client_descriptor_has_expired(now, entry)) {
continue;
}
/* Here, our entry has expired, remove and free. */
@@ -655,8 +695,7 @@ cache_clean_v3_as_client(time_t now)
/* Logging. */
{
char key_b64[BASE64_DIGEST256_LEN + 1];
- base64_encode(key_b64, sizeof(key_b64), (const char *) key,
- DIGEST256_LEN, 0);
+ digest256_to_base64(key_b64, (const char *) key);
log_info(LD_REND, "Removing hidden service v3 descriptor '%s' "
"from client cache",
safe_str_client(key_b64));
@@ -726,6 +765,23 @@ hs_cache_clean_as_client(time_t now)
cache_clean_v3_as_client(now);
}
+/* Purge the client descriptor cache. */
+void
+hs_cache_purge_as_client(void)
+{
+ DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
+ hs_cache_client_descriptor_t *, entry) {
+ size_t entry_size = cache_get_client_entry_size(entry);
+ MAP_DEL_CURRENT(key);
+ cache_client_desc_free(entry);
+ /* Update our OOM. We didn't use the remove() function because we are in
+ * a loop so we have to explicitely decrement. */
+ rend_cache_decrement_allocation(entry_size);
+ } DIGEST256MAP_FOREACH_END;
+
+ log_info(LD_REND, "Hidden service client descriptor cache purged.");
+}
+
/* For a given service identity public key and an introduction authentication
* key, note the given failure in the client intro state cache. */
void
@@ -779,6 +835,20 @@ hs_cache_client_intro_state_clean(time_t now)
} DIGEST256MAP_FOREACH_END;
}
+/* Purge the client introduction state cache. */
+void
+hs_cache_client_intro_state_purge(void)
+{
+ DIGEST256MAP_FOREACH_MODIFY(hs_cache_client_intro_state, key,
+ hs_cache_client_intro_state_t *, cache) {
+ MAP_DEL_CURRENT(key);
+ cache_client_intro_state_free(cache);
+ } DIGEST256MAP_FOREACH_END;
+
+ log_info(LD_REND, "Hidden service client introduction point state "
+ "cache purged.");
+}
+
/**************** Generics *********************************/
/* Do a round of OOM cleanup on all directory caches. Return the amount of
diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h
index 2a4d2dbb2f..a6beaebc10 100644
--- a/src/or/hs_cache.h
+++ b/src/or/hs_cache.h
@@ -84,6 +84,7 @@ hs_cache_lookup_as_client(const ed25519_public_key_t *key);
int hs_cache_store_as_client(const char *desc_str,
const ed25519_public_key_t *identity_pk);
void hs_cache_clean_as_client(time_t now);
+void hs_cache_purge_as_client(void);
/* Client failure cache. */
void hs_cache_client_intro_state_note(const ed25519_public_key_t *service_pk,
@@ -93,6 +94,7 @@ const hs_cache_intro_state_t *hs_cache_client_intro_state_find(
const ed25519_public_key_t *service_pk,
const ed25519_public_key_t *auth_key);
void hs_cache_client_intro_state_clean(time_t now);
+void hs_cache_client_intro_state_purge(void);
#ifdef HS_CACHE_PRIVATE
@@ -101,8 +103,10 @@ typedef struct hs_cache_client_descriptor_t {
/* This object is indexed using the service identity public key */
ed25519_public_key_t key;
- /* When was this entry created. Used to expire entries. */
- time_t created_ts;
+ /* When will this entry expire? We expire cached client descriptors in the
+ * start of the next time period, since that's when clients need to start
+ * using the next blinded key of the service. */
+ time_t expiration_ts;
/* The cached descriptor, this object is the owner. It can't be NULL. A
* cache object without a valid descriptor is not possible. */
diff --git a/src/or/hs_circuitmap.c b/src/or/hs_circuitmap.c
index 09704d796c..63d5c1ba2e 100644
--- a/src/or/hs_circuitmap.c
+++ b/src/or/hs_circuitmap.c
@@ -407,9 +407,20 @@ hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie)
}
/* Public function: Return client-side rendezvous circuit with rendezvous
- * <b>cookie</b>. It will first lookup for the CIRCUIT_PURPOSE_C_REND_READY
- * purpose and then try for CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED and then
- * finally tries for CIRCUIT_PURPOSE_C_ESTABLISH_REND.
+ * <b>cookie</b>. It will look for circuits with the following purposes:
+
+ * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received
+ * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for
+ * INTRODUCE_ACK from intro point.
+ *
+ * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and
+ * introduce circuit acked. Waiting for RENDEZVOUS2 from service.
+ *
+ * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received
+ * RENDEZVOUS2 from service.
+ *
+ * d) CIRCUIT_PURPOSE_C_ESTABLISH_REND: Rend circuit open but not yet
+ * established.
*
* Return NULL if no such circuit is found in the circuitmap. */
origin_circuit_t *
@@ -433,6 +444,13 @@ hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie)
circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
REND_TOKEN_LEN, cookie,
+ CIRCUIT_PURPOSE_C_REND_JOINED);
+ if (circ) {
+ return circ;
+ }
+
+ circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
+ REND_TOKEN_LEN, cookie,
CIRCUIT_PURPOSE_C_ESTABLISH_REND);
return circ;
}
diff --git a/src/or/hs_client.c b/src/or/hs_client.c
index 0e8fc6c24b..77fbf548ed 100644
--- a/src/or/hs_client.c
+++ b/src/or/hs_client.c
@@ -6,6 +6,8 @@
* \brief Implement next generation hidden service client functionality
**/
+#define HS_CLIENT_PRIVATE
+
#include "or.h"
#include "hs_circuit.h"
#include "hs_ident.h"
@@ -29,6 +31,31 @@
#include "connection.h"
#include "hs_ntor.h"
#include "circuitbuild.h"
+#include "networkstatus.h"
+
+/* Cancel all descriptor fetches currently in progress. */
+static void
+cancel_descriptor_fetches(void)
+{
+ smartlist_t *conns =
+ connection_list_by_type_state(CONN_TYPE_DIR, DIR_PURPOSE_FETCH_HSDESC);
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
+ const hs_ident_dir_conn_t *ident = TO_DIR_CONN(conn)->hs_ident;
+ if (BUG(ident == NULL)) {
+ /* A directory connection fetching a service descriptor can't have an
+ * empty hidden service identifier. */
+ continue;
+ }
+ log_debug(LD_REND, "Marking for close a directory connection fetching "
+ "a hidden service descriptor for service %s.",
+ safe_str_client(ed25519_fmt(&ident->identity_pk)));
+ connection_mark_for_close(conn);
+ } SMARTLIST_FOREACH_END(conn);
+
+ /* No ownership of the objects in this list. */
+ smartlist_free(conns);
+ log_info(LD_REND, "Hidden service client descriptor fetches cancelled.");
+}
/* Get all connections that are waiting on a circuit and flag them back to
* waiting for a hidden service descriptor for the given service key
@@ -110,7 +137,7 @@ static int
directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
const routerstatus_t *hsdir)
{
- uint64_t current_time_period = hs_get_time_period_num(approx_time());
+ uint64_t current_time_period = hs_get_time_period_num(0);
ed25519_public_key_t blinded_pubkey;
char base64_blinded_pubkey[ED25519_BASE64_LEN + 1];
hs_ident_dir_conn_t hs_conn_dir_ident;
@@ -125,11 +152,12 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
/* ...and base64 it. */
retval = ed25519_public_to_base64(base64_blinded_pubkey, &blinded_pubkey);
if (BUG(retval < 0)) {
- return -1;
+ return HS_CLIENT_FETCH_ERROR;
}
/* Copy onion pk to a dir_ident so that we attach it to the dir conn */
- ed25519_pubkey_copy(&hs_conn_dir_ident.identity_pk, onion_identity_pk);
+ hs_ident_dir_conn_init(onion_identity_pk, &blinded_pubkey,
+ &hs_conn_dir_ident);
/* Setup directory request */
directory_request_t *req =
@@ -152,17 +180,17 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
memwipe(base64_blinded_pubkey, 0, sizeof(base64_blinded_pubkey));
memwipe(&hs_conn_dir_ident, 0, sizeof(hs_conn_dir_ident));
- return 1;
+ return HS_CLIENT_FETCH_LAUNCHED;
}
/** Return the HSDir we should use to fetch the descriptor of the hidden
* service with identity key <b>onion_identity_pk</b>. */
-static routerstatus_t *
+STATIC routerstatus_t *
pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk)
{
int retval;
char base64_blinded_pubkey[ED25519_BASE64_LEN + 1];
- uint64_t current_time_period = hs_get_time_period_num(approx_time());
+ uint64_t current_time_period = hs_get_time_period_num(0);
smartlist_t *responsible_hsdirs;
ed25519_public_key_t blinded_pubkey;
routerstatus_t *hsdir_rs = NULL;
@@ -181,8 +209,8 @@ pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk)
}
/* Get responsible hsdirs of service for this time period */
- hs_get_responsible_hsdirs(&blinded_pubkey, current_time_period, 0, 1,
- responsible_hsdirs);
+ hs_get_responsible_hsdirs(&blinded_pubkey, current_time_period,
+ 0, 1, responsible_hsdirs);
log_debug(LD_REND, "Found %d responsible HSDirs and about to pick one.",
smartlist_len(responsible_hsdirs));
@@ -208,7 +236,7 @@ fetch_v3_desc(const ed25519_public_key_t *onion_identity_pk)
hsdir_rs = pick_hsdir_v3(onion_identity_pk);
if (!hsdir_rs) {
log_info(LD_REND, "Couldn't pick a v3 hsdir.");
- return 0;
+ return HS_CLIENT_FETCH_NO_HSDIRS;
}
return directory_launch_v3_desc_fetch(onion_identity_pk, hsdir_rs);
@@ -493,7 +521,7 @@ client_rendezvous_circ_has_opened(origin_circuit_t *circ)
* to a newly allocated extend_info_t object fully initialized. Return NULL if
* we can't convert it for which chances are that we are missing or malformed
* link specifiers. */
-static extend_info_t *
+STATIC extend_info_t *
desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip)
{
extend_info_t *ei;
@@ -566,7 +594,7 @@ intro_point_is_usable(const ed25519_public_key_t *service_pk,
/* Using a descriptor desc, return a newly allocated extend_info_t object of a
* randomly picked introduction point from its list. Return NULL if none are
* usable. */
-static extend_info_t *
+STATIC extend_info_t *
client_get_random_intro(const ed25519_public_key_t *service_pk)
{
extend_info_t *ei = NULL, *ei_excluded = NULL;
@@ -618,6 +646,12 @@ client_get_random_intro(const ed25519_public_key_t *service_pk)
/* If this pick is in the ExcludeNodes list, we keep its reference so if
* we ever end up not being able to pick anything else and StrictNodes is
* unset, we'll use it. */
+ if (ei_excluded) {
+ /* If something was already here free it. After the loop is gone we
+ * will examine the last excluded intro point, and that's fine since
+ * that's random anyway */
+ extend_info_free(ei_excluded);
+ }
ei_excluded = ei;
continue;
}
@@ -634,6 +668,7 @@ client_get_random_intro(const ed25519_public_key_t *service_pk)
if (options->StrictNodes) {
log_warn(LD_REND, "Every introduction points are in the ExcludeNodes set "
"and StrictNodes is set. We can't connect.");
+ extend_info_free(ei);
ei = NULL;
}
@@ -719,6 +754,14 @@ handle_introduce_ack_success(origin_circuit_t *intro_circ)
}
assert_circ_anonymity_ok(rend_circ, get_options());
+
+ /* It is possible to get a RENDEZVOUS2 cell before the INTRODUCE_ACK which
+ * means that the circuit will be joined and already transmitting data. In
+ * that case, simply skip the purpose change and close the intro circuit
+ * like it should be. */
+ if (TO_CIRCUIT(rend_circ)->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
+ goto end;
+ }
circuit_change_purpose(TO_CIRCUIT(rend_circ),
CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
/* Set timestamp_dirty, because circuit_expire_building expects it to
@@ -906,7 +949,7 @@ hs_client_decode_descriptor(const char *desc_str,
/* Create subcredential for this HS so that we can decrypt */
{
- uint64_t current_time_period = hs_get_time_period_num(approx_time());
+ uint64_t current_time_period = hs_get_time_period_num(0);
hs_build_blinded_pubkey(service_identity_pk, NULL, 0, current_time_period,
&blinded_pubkey);
hs_get_subcredential(service_identity_pk, &blinded_pubkey, subcredential);
@@ -916,7 +959,10 @@ hs_client_decode_descriptor(const char *desc_str,
ret = hs_desc_decode_descriptor(desc_str, subcredential, desc);
memwipe(subcredential, 0, sizeof(subcredential));
if (ret < 0) {
- log_warn(LD_GENERAL, "Could not parse received descriptor as client");
+ log_warn(LD_GENERAL, "Could not parse received descriptor as client.");
+ if (get_options()->SafeLogging_ == SAFELOG_SCRUB_NONE) {
+ log_warn(LD_GENERAL, "%s", escaped(desc_str));
+ }
goto err;
}
@@ -959,8 +1005,7 @@ hs_client_any_intro_points_usable(const ed25519_public_key_t *service_pk,
/** Launch a connection to a hidden service directory to fetch a hidden
* service descriptor using <b>identity_pk</b> to get the necessary keys.
*
- * On success, 1 is returned. If no hidden service is left to ask, return 0.
- * On error, -1 is returned. (retval is only used by unittests right now) */
+ * A hs_client_fetch_status_t code is returned. */
int
hs_client_refetch_hsdesc(const ed25519_public_key_t *identity_pk)
{
@@ -970,7 +1015,23 @@ hs_client_refetch_hsdesc(const ed25519_public_key_t *identity_pk)
if (!get_options()->FetchHidServDescriptors) {
log_warn(LD_REND, "We received an onion address for a hidden service "
"descriptor but we are configured to not fetch.");
- return 0;
+ return HS_CLIENT_FETCH_NOT_ALLOWED;
+ }
+
+ /* Without a live consensus we can't do any client actions. It is needed to
+ * compute the hashring for a service. */
+ if (!networkstatus_get_live_consensus(approx_time())) {
+ log_info(LD_REND, "Can't fetch descriptor for service %s because we "
+ "are missing a live consensus. Stalling connection.",
+ safe_str_client(ed25519_fmt(identity_pk)));
+ return HS_CLIENT_FETCH_MISSING_INFO;
+ }
+
+ if (!router_have_minimum_dir_info()) {
+ log_info(LD_REND, "Can't fetch descriptor for service %s because we "
+ "dont have enough descriptors. Stalling connection.",
+ safe_str_client(ed25519_fmt(identity_pk)));
+ return HS_CLIENT_FETCH_MISSING_INFO;
}
/* Check if fetching a desc for this HS is useful to us right now */
@@ -979,9 +1040,9 @@ hs_client_refetch_hsdesc(const ed25519_public_key_t *identity_pk)
cached_desc = hs_cache_lookup_as_client(identity_pk);
if (cached_desc && hs_client_any_intro_points_usable(identity_pk,
cached_desc)) {
- log_warn(LD_GENERAL, "We would fetch a v3 hidden service descriptor "
- "but we already have a useable descriprot.");
- return 0;
+ log_info(LD_GENERAL, "We would fetch a v3 hidden service descriptor "
+ "but we already have a usable descriptor.");
+ return HS_CLIENT_FETCH_HAVE_DESC;
}
}
@@ -1272,3 +1333,24 @@ hs_client_free_all(void)
hs_purge_last_hid_serv_requests();
}
+/* Purge all potentially remotely-detectable state held in the hidden
+ * service client code. Called on SIGNAL NEWNYM. */
+void
+hs_client_purge_state(void)
+{
+ /* v2 subsystem. */
+ rend_client_purge_state();
+
+ /* Cancel all descriptor fetches. Do this first so once done we are sure
+ * that our descriptor cache won't modified. */
+ cancel_descriptor_fetches();
+ /* Purge the introduction point state cache. */
+ hs_cache_client_intro_state_purge();
+ /* Purge the descriptor cache. */
+ hs_cache_purge_as_client();
+ /* Purge the last hidden service request cache. */
+ hs_purge_last_hid_serv_requests();
+
+ log_info(LD_REND, "Hidden service client state has been purged.");
+}
+
diff --git a/src/or/hs_client.h b/src/or/hs_client.h
index 86784f52c3..d50d346217 100644
--- a/src/or/hs_client.h
+++ b/src/or/hs_client.h
@@ -13,6 +13,22 @@
#include "hs_descriptor.h"
#include "hs_ident.h"
+/* Status code of a descriptor fetch request. */
+typedef enum {
+ /* Something internally went wrong. */
+ HS_CLIENT_FETCH_ERROR = -1,
+ /* The fetch request has been launched successfully. */
+ HS_CLIENT_FETCH_LAUNCHED = 0,
+ /* We already have a usable descriptor. No fetch. */
+ HS_CLIENT_FETCH_HAVE_DESC = 1,
+ /* No more HSDir available to query. */
+ HS_CLIENT_FETCH_NO_HSDIRS = 2,
+ /* The fetch request is not allowed. */
+ HS_CLIENT_FETCH_NOT_ALLOWED = 3,
+ /* We are missing information to be able to launch a request. */
+ HS_CLIENT_FETCH_MISSING_INFO = 4,
+} hs_client_fetch_status_t;
+
void hs_client_note_connection_attempt_succeeded(
const edge_connection_t *conn);
@@ -46,7 +62,22 @@ extend_info_t *hs_client_get_random_intro_from_edge(
int hs_client_reextend_intro_circuit(origin_circuit_t *circ);
+void hs_client_purge_state(void);
+
void hs_client_free_all(void);
+#ifdef HS_CLIENT_PRIVATE
+
+STATIC routerstatus_t *
+pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk);
+
+STATIC extend_info_t *
+client_get_random_intro(const ed25519_public_key_t *service_pk);
+
+STATIC extend_info_t *
+desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip);
+
+#endif /* HS_CLIENT_PRIVATE */
+
#endif /* TOR_HS_CLIENT_H */
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index 5ea44b97e7..6fe6b82f5e 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -99,42 +99,65 @@ add_unix_port(smartlist_t *ports, rend_service_port_config_t *p)
/* Helper function: The key is a digest that we compare to a node_t object
* current hsdir_index. */
static int
-compare_digest_to_current_hsdir_index(const void *_key, const void **_member)
+compare_digest_to_fetch_hsdir_index(const void *_key, const void **_member)
{
const char *key = _key;
const node_t *node = *_member;
- return tor_memcmp(key, node->hsdir_index->current, DIGEST256_LEN);
+ return tor_memcmp(key, node->hsdir_index->fetch, DIGEST256_LEN);
}
/* Helper function: The key is a digest that we compare to a node_t object
* next hsdir_index. */
static int
-compare_digest_to_next_hsdir_index(const void *_key, const void **_member)
+compare_digest_to_store_first_hsdir_index(const void *_key,
+ const void **_member)
{
const char *key = _key;
const node_t *node = *_member;
- return tor_memcmp(key, node->hsdir_index->next, DIGEST256_LEN);
+ return tor_memcmp(key, node->hsdir_index->store_first, DIGEST256_LEN);
+}
+
+/* Helper function: The key is a digest that we compare to a node_t object
+ * next hsdir_index. */
+static int
+compare_digest_to_store_second_hsdir_index(const void *_key,
+ const void **_member)
+{
+ const char *key = _key;
+ const node_t *node = *_member;
+ return tor_memcmp(key, node->hsdir_index->store_second, DIGEST256_LEN);
}
/* Helper function: Compare two node_t objects current hsdir_index. */
static int
-compare_node_current_hsdir_index(const void **a, const void **b)
+compare_node_fetch_hsdir_index(const void **a, const void **b)
{
const node_t *node1= *a;
const node_t *node2 = *b;
- return tor_memcmp(node1->hsdir_index->current,
- node2->hsdir_index->current,
+ return tor_memcmp(node1->hsdir_index->fetch,
+ node2->hsdir_index->fetch,
DIGEST256_LEN);
}
/* Helper function: Compare two node_t objects next hsdir_index. */
static int
-compare_node_next_hsdir_index(const void **a, const void **b)
+compare_node_store_first_hsdir_index(const void **a, const void **b)
{
const node_t *node1= *a;
const node_t *node2 = *b;
- return tor_memcmp(node1->hsdir_index->next,
- node2->hsdir_index->next,
+ return tor_memcmp(node1->hsdir_index->store_first,
+ node2->hsdir_index->store_first,
+ DIGEST256_LEN);
+}
+
+/* Helper function: Compare two node_t objects next hsdir_index. */
+static int
+compare_node_store_second_hsdir_index(const void **a, const void **b)
+{
+ const node_t *node1= *a;
+ const node_t *node2 = *b;
+ return tor_memcmp(node1->hsdir_index->store_second,
+ node2->hsdir_index->store_second,
DIGEST256_LEN);
}
@@ -211,15 +234,26 @@ get_time_period_length(void)
return (uint64_t) time_period_length;
}
-/** Get the HS time period number at time <b>now</b> */
+/** Get the HS time period number at time <b>now</b>. If <b>now</b> is not set,
+ * we try to get the time ourselves from a live consensus. */
uint64_t
hs_get_time_period_num(time_t now)
{
uint64_t time_period_num;
+ time_t current_time;
+
+ /* If no time is specified, set current time based on consensus time, and
+ * only fall back to system time if that fails. */
+ if (now != 0) {
+ current_time = now;
+ } else {
+ networkstatus_t *ns = networkstatus_get_live_consensus(approx_time());
+ current_time = ns ? ns->valid_after : approx_time();
+ }
/* Start by calculating minutes since the epoch */
uint64_t time_period_length = get_time_period_length();
- uint64_t minutes_since_epoch = now / 60;
+ uint64_t minutes_since_epoch = current_time / 60;
/* Apply the rotation offset as specified by prop224 (section
* [TIME-PERIODS]), so that new time periods synchronize nicely with SRV
@@ -235,14 +269,26 @@ hs_get_time_period_num(time_t now)
}
/** Get the number of the _upcoming_ HS time period, given that the current
- * time is <b>now</b>. */
+ * time is <b>now</b>. If <b>now</b> is not set, we try to get the time from a
+ * live consensus. */
uint64_t
hs_get_next_time_period_num(time_t now)
{
return hs_get_time_period_num(now) + 1;
}
-/* Return the start time of the upcoming time period based on <b>now</b>. */
+/* Get the number of the _previous_ HS time period, given that the current time
+ * is <b>now</b>. If <b>now</b> is not set, we try to get the time from a live
+ * consensus. */
+uint64_t
+hs_get_previous_time_period_num(time_t now)
+{
+ return hs_get_time_period_num(now) - 1;
+}
+
+/* Return the start time of the upcoming time period based on <b>now</b>. If
+ <b>now</b> is not set, we try to get the time ourselves from a live
+ consensus. */
time_t
hs_get_start_time_of_next_time_period(time_t now)
{
@@ -547,7 +593,7 @@ compute_disaster_srv(uint64_t time_period_num, uint8_t *srv_out)
* would have to do it thousands of times in a row, we always cache the
* computer disaster SRV (and its corresponding time period num) in case we
* want to reuse it soon after. We need to cache two SRVs, one for each active
- * time period (in case of overlap mode).
+ * time period.
*/
static uint8_t cached_disaster_srv[2][DIGEST256_LEN];
static uint64_t cached_time_period_nums[2] = {0};
@@ -872,22 +918,31 @@ hs_address_is_valid(const char *address)
uint8_t version;
uint8_t checksum[HS_SERVICE_ADDR_CHECKSUM_LEN_USED];
uint8_t target_checksum[DIGEST256_LEN];
- ed25519_public_key_t key;
+ ed25519_public_key_t service_pubkey;
/* Parse the decoded address into the fields we need. */
- if (hs_parse_address(address, &key, checksum, &version) < 0) {
+ if (hs_parse_address(address, &service_pubkey, checksum, &version) < 0) {
goto invalid;
}
/* Get the checksum it's suppose to be and compare it with what we have
* encoded in the address. */
- build_hs_checksum(&key, version, target_checksum);
+ build_hs_checksum(&service_pubkey, version, target_checksum);
if (tor_memcmp(checksum, target_checksum, sizeof(checksum))) {
log_warn(LD_REND, "Service address %s invalid checksum.",
escaped_safe_str(address));
goto invalid;
}
+ /* Validate that this pubkey does not have a torsion component. We need to do
+ * this on the prop224 client-side so that attackers can't give equivalent
+ * forms of an onion address to users. */
+ if (ed25519_validate_pubkey(&service_pubkey) < 0) {
+ log_warn(LD_REND, "Service address %s has bad pubkey .",
+ escaped_safe_str(address));
+ goto invalid;
+ }
+
/* Valid address. */
return 1;
invalid:
@@ -992,10 +1047,22 @@ hs_build_blinded_keypair(const ed25519_keypair_t *kp,
memwipe(param, 0, sizeof(param));
}
-/* Return true if overlap mode is active given the date in consensus. If
- * consensus is NULL, then we use the latest live consensus we can find. */
+/* Return true if we are currently in the time segment between a new time
+ * period and a new SRV (in the real network that happens between 12:00 and
+ * 00:00 UTC). Here is a diagram showing exactly when this returns true:
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^^^^^^^^^^^^ ^^^^^^^^^^^^ |
+ * | |
+ * +------------------------------------------------------------------+
+ */
MOCK_IMPL(int,
-hs_overlap_mode_is_active, (const networkstatus_t *consensus, time_t now))
+hs_in_period_between_tp_and_srv,(const networkstatus_t *consensus, time_t now))
{
time_t valid_after;
time_t srv_start_time, tp_start_time;
@@ -1007,19 +1074,18 @@ hs_overlap_mode_is_active, (const networkstatus_t *consensus, time_t now))
}
}
- /* We consider to be in overlap mode when we are in the period of time
- * between a fresh SRV and the beginning of the new time period (in the
- * normal network this is between 00:00 (inclusive) and 12:00 UTC
- * (exclusive)) */
+ /* Get start time of next TP and of current SRV protocol run, and check if we
+ * are between them. */
valid_after = consensus->valid_after;
- srv_start_time =sr_state_get_start_time_of_current_protocol_run(valid_after);
+ srv_start_time =
+ sr_state_get_start_time_of_current_protocol_run(valid_after);
tp_start_time = hs_get_start_time_of_next_time_period(srv_start_time);
if (valid_after >= srv_start_time && valid_after < tp_start_time) {
- return 1;
+ return 0;
}
- return 0;
+ return 1;
}
/* Return 1 if any virtual port in ports needs a circuit with good uptime.
@@ -1189,10 +1255,9 @@ hs_get_hsdir_spread_store(void)
}
/** <b>node</b> is an HSDir so make sure that we have assigned an hsdir index.
- * If <b>is_for_next_period</b> is set, also check the next HSDir index field.
* Return 0 if everything is as expected, else return -1. */
static int
-node_has_hsdir_index(const node_t *node, int is_for_next_period)
+node_has_hsdir_index(const node_t *node)
{
tor_assert(node_supports_v3_hsdir(node));
@@ -1204,19 +1269,19 @@ node_has_hsdir_index(const node_t *node, int is_for_next_period)
/* At this point, since the node has a desc, this node must also have an
* hsdir index. If not, something went wrong, so BUG out. */
- if (BUG(node->hsdir_index == NULL) ||
- BUG(tor_mem_is_zero((const char*)node->hsdir_index->current,
+ if (BUG(node->hsdir_index == NULL)) {
+ return 0;
+ }
+ if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->fetch,
DIGEST256_LEN))) {
- log_warn(LD_BUG, "Zero current index (ri: %p, rs: %p, md: %p)",
- node->ri, node->rs, node->md);
return 0;
}
-
- if (is_for_next_period &&
- BUG(tor_mem_is_zero((const char*)node->hsdir_index->next,
+ if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->store_first,
+ DIGEST256_LEN))) {
+ return 0;
+ }
+ if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->store_second,
DIGEST256_LEN))) {
- log_warn(LD_BUG, "Zero next index (ri: %p, rs: %p, md: %p)",
- node->ri, node->rs, node->md);
return 0;
}
@@ -1225,19 +1290,19 @@ node_has_hsdir_index(const node_t *node, int is_for_next_period)
/* For a given blinded key and time period number, get the responsible HSDir
* and put their routerstatus_t object in the responsible_dirs list. If
- * is_next_period is true, the next hsdir_index of the node_t is used. If
- * is_client is true, the spread fetch consensus parameter is used else the
- * spread store is used which is only for upload. This function can't fail but
- * it is possible that the responsible_dirs list contains fewer nodes than
- * expected.
+ * 'use_second_hsdir_index' is true, use the second hsdir_index of the node_t
+ * is used. If 'for_fetching' is true, the spread fetch consensus parameter is
+ * used else the spread store is used which is only for upload. This function
+ * can't fail but it is possible that the responsible_dirs list contains fewer
+ * nodes than expected.
*
* This function goes over the latest consensus routerstatus list and sorts it
* by their node_t hsdir_index then does a binary search to find the closest
* node. All of this makes it a bit CPU intensive so use it wisely. */
void
hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
- uint64_t time_period_num, int is_next_period,
- int is_client, smartlist_t *responsible_dirs)
+ uint64_t time_period_num, int use_second_hsdir_index,
+ int for_fetching, smartlist_t *responsible_dirs)
{
smartlist_t *sorted_nodes;
/* The compare function used for the smartlist bsearch. We have two
@@ -1264,7 +1329,7 @@ hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
node_t *n = node_get_mutable_by_id(rs->identity_digest);
tor_assert(n);
if (node_supports_v3_hsdir(n) && rs->is_hs_dir) {
- if (!node_has_hsdir_index(n, is_next_period)) {
+ if (!node_has_hsdir_index(n)) {
log_info(LD_GENERAL, "Node %s was found without hsdir index.",
node_describe(n));
continue;
@@ -1281,12 +1346,15 @@ hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
/* First thing we have to do is sort all node_t by hsdir_index. The
* is_next_period tells us if we want the current or the next one. Set the
* bsearch compare function also while we are at it. */
- if (is_next_period) {
- smartlist_sort(sorted_nodes, compare_node_next_hsdir_index);
- cmp_fct = compare_digest_to_next_hsdir_index;
+ if (for_fetching) {
+ smartlist_sort(sorted_nodes, compare_node_fetch_hsdir_index);
+ cmp_fct = compare_digest_to_fetch_hsdir_index;
+ } else if (use_second_hsdir_index) {
+ smartlist_sort(sorted_nodes, compare_node_store_second_hsdir_index);
+ cmp_fct = compare_digest_to_store_second_hsdir_index;
} else {
- smartlist_sort(sorted_nodes, compare_node_current_hsdir_index);
- cmp_fct = compare_digest_to_current_hsdir_index;
+ smartlist_sort(sorted_nodes, compare_node_store_first_hsdir_index);
+ cmp_fct = compare_digest_to_store_first_hsdir_index;
}
/* For all replicas, we'll select a set of HSDirs using the consensus
@@ -1297,8 +1365,8 @@ hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
uint8_t hs_index[DIGEST256_LEN] = {0};
/* Number of node to add to the responsible dirs list depends on if we are
* trying to fetch or store. A client always fetches. */
- int n_to_add = (is_client) ? hs_get_hsdir_spread_fetch() :
- hs_get_hsdir_spread_store();
+ int n_to_add = (for_fetching) ? hs_get_hsdir_spread_fetch() :
+ hs_get_hsdir_spread_store();
/* Get the index that we should use to select the node. */
hs_build_hs_index(replica, blinded_pk, time_period_num, hs_index);
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 79d92d915f..5851578fd6 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -142,10 +142,14 @@ typedef struct rend_service_port_config_t {
/* Hidden service directory index used in a node_t which is set once we set
* the consensus. */
typedef struct hsdir_index_t {
- /* The hsdir index for the current time period. */
- uint8_t current[DIGEST256_LEN];
- /* The hsdir index for the next time period. */
- uint8_t next[DIGEST256_LEN];
+ /* HSDir index to use when fetching a descriptor. */
+ uint8_t fetch[DIGEST256_LEN];
+
+ /* HSDir index used by services to store their first and second
+ * descriptor. The first descriptor is chronologically older than the second
+ * one and uses older TP and SRV values. */
+ uint8_t store_first[DIGEST256_LEN];
+ uint8_t store_second[DIGEST256_LEN];
} hsdir_index_t;
void hs_init(void);
@@ -193,13 +197,14 @@ void hs_get_subcredential(const ed25519_public_key_t *identity_pk,
const ed25519_public_key_t *blinded_pk,
uint8_t *subcred_out);
+uint64_t hs_get_previous_time_period_num(time_t now);
uint64_t hs_get_time_period_num(time_t now);
uint64_t hs_get_next_time_period_num(time_t now);
time_t hs_get_start_time_of_next_time_period(time_t now);
link_specifier_t *hs_link_specifier_dup(const link_specifier_t *lspec);
-MOCK_DECL(int, hs_overlap_mode_is_active,
+MOCK_DECL(int, hs_in_period_between_tp_and_srv,
(const networkstatus_t *consensus, time_t now));
uint8_t *hs_get_current_srv(uint64_t time_period_num,
@@ -219,8 +224,9 @@ int32_t hs_get_hsdir_spread_fetch(void);
int32_t hs_get_hsdir_spread_store(void);
void hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
- uint64_t time_period_num, int is_next_period,
- int is_client, smartlist_t *responsible_dirs);
+ uint64_t time_period_num,
+ int use_second_hsdir_index,
+ int for_fetching, smartlist_t *responsible_dirs);
routerstatus_t *hs_pick_hsdir(smartlist_t *responsible_dirs,
const char *req_key_str);
diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h
index 3e82746c35..61e7a28c2e 100644
--- a/src/or/hs_descriptor.h
+++ b/src/or/hs_descriptor.h
@@ -33,8 +33,11 @@ struct link_specifier_t;
* which is 720 minutes or 43200 seconds. */
#define HS_DESC_MAX_LIFETIME (12 * 60 * 60)
/* Lifetime of certificate in the descriptor. This defines the lifetime of the
- * descriptor signing key and the cross certification cert of that key. */
-#define HS_DESC_CERT_LIFETIME (36 * 60 * 60)
+ * descriptor signing key and the cross certification cert of that key. It is
+ * set to 54 hours because a descriptor can be around for 48 hours and because
+ * consensuses are used after the hour, add an extra 6 hours to give some time
+ * for the service to stop using it. */
+#define HS_DESC_CERT_LIFETIME (54 * 60 * 60)
/* Length of the salt needed for the encrypted section of a descriptor. */
#define HS_DESC_ENCRYPTED_SALT_LEN 16
/* Length of the secret input needed for the KDF construction which derives
diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c
index df39285158..b0e4e36a9b 100644
--- a/src/or/hs_ident.c
+++ b/src/or/hs_ident.c
@@ -65,6 +65,22 @@ hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident)
tor_free(ident);
}
+/* Initialized the allocated ident object with identity_pk and blinded_pk.
+ * None of them can be NULL since a valid directory connection identifier must
+ * have all fields set. */
+void
+hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ hs_ident_dir_conn_t *ident)
+{
+ tor_assert(identity_pk);
+ tor_assert(blinded_pk);
+ tor_assert(ident);
+
+ ed25519_pubkey_copy(&ident->identity_pk, identity_pk);
+ ed25519_pubkey_copy(&ident->blinded_pk, blinded_pk);
+}
+
/* Return a newly allocated edge connection identifier. The given public key
* identity_pk is copied into the identifier. */
hs_ident_edge_conn_t *
diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h
index cfcde781d1..101c1cfffd 100644
--- a/src/or/hs_ident.h
+++ b/src/or/hs_ident.h
@@ -96,6 +96,11 @@ typedef struct hs_ident_dir_conn_t {
* in the onion address. */
ed25519_public_key_t identity_pk;
+ /* The blinded public key used to uniquely identify the descriptor that this
+ * directory connection identifier is for. Only used by the service-side code
+ * to fine control descriptor uploads. */
+ ed25519_public_key_t blinded_pk;
+
/* XXX: Client authorization. */
} hs_ident_dir_conn_t;
@@ -120,6 +125,9 @@ hs_ident_circuit_t *hs_ident_circuit_dup(const hs_ident_circuit_t *src);
/* Directory connection identifier API. */
hs_ident_dir_conn_t *hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src);
void hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident);
+void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ hs_ident_dir_conn_t *ident);
/* Edge connection identifier API. */
hs_ident_edge_conn_t *hs_ident_edge_conn_new(
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 706890e34f..05045035f3 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -14,6 +14,7 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
+#include "connection.h"
#include "directory.h"
#include "main.h"
#include "networkstatus.h"
@@ -23,6 +24,7 @@
#include "router.h"
#include "routerkeys.h"
#include "routerlist.h"
+#include "shared_random_state.h"
#include "statefile.h"
#include "hs_circuit.h"
@@ -640,6 +642,41 @@ count_desc_circuit_established(const hs_service_descriptor_t *desc)
return count;
}
+/* For a given service and descriptor of that service, close all active
+ * directory connections. */
+static void
+close_directory_connections(const hs_service_t *service,
+ const hs_service_descriptor_t *desc)
+{
+ unsigned int count = 0;
+ smartlist_t *dir_conns;
+
+ tor_assert(service);
+ tor_assert(desc);
+
+ /* Close pending HS desc upload connections for the blinded key of 'desc'. */
+ dir_conns = connection_list_by_type_purpose(CONN_TYPE_DIR,
+ DIR_PURPOSE_UPLOAD_HSDESC);
+ SMARTLIST_FOREACH_BEGIN(dir_conns, connection_t *, conn) {
+ dir_connection_t *dir_conn = TO_DIR_CONN(conn);
+ if (ed25519_pubkey_eq(&dir_conn->hs_ident->identity_pk,
+ &service->keys.identity_pk) &&
+ ed25519_pubkey_eq(&dir_conn->hs_ident->blinded_pk,
+ &desc->blinded_kp.pubkey)) {
+ connection_mark_for_close(conn);
+ count++;
+ continue;
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ log_info(LD_REND, "Closed %u active service directory connections for "
+ "descriptor %s of service %s",
+ count, safe_str_client(ed25519_fmt(&desc->blinded_kp.pubkey)),
+ safe_str_client(service->onion_address));
+ /* We don't have ownership of the objects in this list. */
+ smartlist_free(dir_conns);
+}
+
/* Close all rendezvous circuits for the given service. */
static void
close_service_rp_circuits(hs_service_t *service)
@@ -769,7 +806,6 @@ move_hs_state(hs_service_t *src_service, hs_service_t *dst_service)
/* Let's do a shallow copy */
dst->intro_circ_retry_started_time = src->intro_circ_retry_started_time;
dst->num_intro_circ_launched = src->num_intro_circ_launched;
- dst->in_overlap_period = src->in_overlap_period;
dst->replay_cache_rend_cookie = src->replay_cache_rend_cookie;
src->replay_cache_rend_cookie = NULL; /* steal pointer reference */
@@ -1366,28 +1402,81 @@ build_service_descriptor(hs_service_t *service, time_t now,
service_descriptor_free(desc);
}
+/* Build both descriptors for the given service that has just booted up.
+ * Because it's a special case, it deserves its special function ;). */
+static void
+build_descriptors_for_new_service(hs_service_t *service, time_t now)
+{
+ uint64_t current_desc_tp, next_desc_tp;
+
+ tor_assert(service);
+ /* These are the conditions for a new service. */
+ tor_assert(!service->desc_current);
+ tor_assert(!service->desc_next);
+
+ /*
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | A B |
+ * +------------------------------------------------------------------+
+ *
+ * Case A: The service boots up before a new time period, the current time
+ * period is thus TP#1 and the next is TP#2 which for both we have access to
+ * their SRVs.
+ *
+ * Case B: The service boots up inside TP#2, we can't use the TP#3 for the
+ * next descriptor because we don't have the SRV#3 so the current should be
+ * TP#1 and next TP#2.
+ */
+
+ if (hs_in_period_between_tp_and_srv(NULL, now)) {
+ /* Case B from the above, inside of the new time period. */
+ current_desc_tp = hs_get_previous_time_period_num(0); /* TP#1 */
+ next_desc_tp = hs_get_time_period_num(0); /* TP#2 */
+ } else {
+ /* Case A from the above, outside of the new time period. */
+ current_desc_tp = hs_get_time_period_num(0); /* TP#1 */
+ next_desc_tp = hs_get_next_time_period_num(0); /* TP#2 */
+ }
+
+ /* Build descriptors. */
+ build_service_descriptor(service, now, current_desc_tp,
+ &service->desc_current);
+ build_service_descriptor(service, now, next_desc_tp,
+ &service->desc_next);
+ log_info(LD_REND, "Hidden service %s has just started. Both descriptors "
+ "built. Now scheduled for upload.",
+ safe_str_client(service->onion_address));
+}
+
/* Build descriptors for each service if needed. There are conditions to build
* a descriptor which are details in the function. */
STATIC void
build_all_descriptors(time_t now)
{
FOR_EACH_SERVICE_BEGIN(service) {
- if (service->desc_current == NULL) {
- /* This means we just booted up because else this descriptor will never
- * be NULL as it should always point to the descriptor that was in
- * desc_next after rotation. */
- build_service_descriptor(service, now, hs_get_time_period_num(now),
- &service->desc_current);
-
- log_info(LD_REND, "Hidden service %s current descriptor successfully "
- "built. Now scheduled for upload.",
- safe_str_client(service->onion_address));
+
+ /* A service booting up will have both descriptors to NULL. No other cases
+ * makes both descriptor non existent. */
+ if (service->desc_current == NULL && service->desc_next == NULL) {
+ build_descriptors_for_new_service(service, now);
+ continue;
}
- /* A next descriptor to NULL indicate that we need to build a fresh one if
- * we are in the overlap period for the _next_ time period since it means
- * we either just booted or we just rotated our descriptors. */
- if (hs_overlap_mode_is_active(NULL, now) && service->desc_next == NULL) {
- build_service_descriptor(service, now, hs_get_next_time_period_num(now),
+
+ /* Reaching this point means we are pass bootup so at runtime. We should
+ * *never* have an empty current descriptor. If the next descriptor is
+ * empty, we'll try to build it for the next time period. This only
+ * happens when we rotate meaning that we are guaranteed to have a new SRV
+ * at that point for the next time period. */
+ tor_assert(service->desc_current);
+
+ if (service->desc_next == NULL) {
+ build_service_descriptor(service, now, hs_get_next_time_period_num(0),
&service->desc_next);
log_info(LD_REND, "Hidden service %s next descriptor successfully "
"built. Now scheduled for upload.",
@@ -1716,53 +1805,81 @@ cleanup_intro_points(hs_service_t *service, time_t now)
} FOR_EACH_DESCRIPTOR_END;
}
-/** We just entered overlap period and we need to rotate our <b>service</b>
- * descriptors */
+/* Set the next rotation time of the descriptors for the given service for the
+ * time now. */
static void
-rotate_service_descriptors(hs_service_t *service)
+set_rotation_time(hs_service_t *service, time_t now)
{
- if (service->desc_current) {
- /* Close all IP circuits for the descriptor. */
- close_intro_circuits(&service->desc_current->intro_points);
- /* We don't need this one anymore, we won't serve any clients coming with
- * this service descriptor. */
- service_descriptor_free(service->desc_current);
+ time_t valid_after;
+ const networkstatus_t *ns = networkstatus_get_live_consensus(now);
+ if (ns) {
+ valid_after = ns->valid_after;
+ } else {
+ valid_after = now;
+ }
+
+ tor_assert(service);
+ service->state.next_rotation_time =
+ sr_state_get_start_time_of_current_protocol_run(valid_after) +
+ sr_state_get_protocol_run_duration();
+
+ {
+ char fmt_time[ISO_TIME_LEN + 1];
+ format_local_iso_time(fmt_time, service->state.next_rotation_time);
+ log_info(LD_REND, "Next descriptor rotation time set to %s for %s",
+ fmt_time, safe_str_client(service->onion_address));
}
- /* The next one become the current one and emptying the next will trigger
- * a descriptor creation for it. */
- service->desc_current = service->desc_next;
- service->desc_next = NULL;
}
-/** Return true if <b>service</b> **just** entered overlap mode. */
-static int
-service_just_entered_overlap_mode(const hs_service_t *service,
- int overlap_mode_is_active)
+/* Return true iff the service should rotate its descriptor. The time now is
+ * only used to fetch the live consensus and if none can be found, this
+ * returns false. */
+static unsigned int
+should_rotate_descriptors(hs_service_t *service, time_t now)
{
- if (overlap_mode_is_active && !service->state.in_overlap_period) {
- return 1;
+ const networkstatus_t *ns;
+
+ tor_assert(service);
+
+ ns = networkstatus_get_live_consensus(now);
+ if (ns == NULL) {
+ goto no_rotation;
+ }
+
+ if (ns->valid_after >= service->state.next_rotation_time) {
+ goto rotation;
}
+ no_rotation:
return 0;
+ rotation:
+ return 1;
}
-/** Return true if <b>service</b> **just** left overlap mode. */
-static int
-service_just_left_overlap_mode(const hs_service_t *service,
- int overlap_mode_is_active)
+/* Rotate the service descriptors of the given service. The current descriptor
+ * will be freed, the next one put in as the current and finally the next
+ * descriptor pointer is NULLified. */
+static void
+rotate_service_descriptors(hs_service_t *service, time_t now)
{
- if (!overlap_mode_is_active && service->state.in_overlap_period) {
- return 1;
+ if (service->desc_current) {
+ /* Close all IP circuits for the descriptor. */
+ close_intro_circuits(&service->desc_current->intro_points);
+ /* We don't need this one anymore, we won't serve any clients coming with
+ * this service descriptor. */
+ service_descriptor_free(service->desc_current);
}
+ /* The next one become the current one and emptying the next will trigger
+ * a descriptor creation for it. */
+ service->desc_current = service->desc_next;
+ service->desc_next = NULL;
- return 0;
+ /* We've just rotated, set the next time for the rotation. */
+ set_rotation_time(service, now);
}
-/* Rotate descriptors for each service if needed. If we are just entering or
- * leaving the overlap period, rotate them that is point the previous
- * descriptor to the current and cleanup the previous one. A non existing
- * current descriptor will trigger a descriptor build for the next time
- * period. */
+/* Rotate descriptors for each service if needed. A non existing current
+ * descriptor will trigger a descriptor build for the next time period. */
STATIC void
rotate_all_descriptors(time_t now)
{
@@ -1770,56 +1887,26 @@ rotate_all_descriptors(time_t now)
* be wise, to rotate service descriptors independently to hide that all
* those descriptors are on the same tor instance */
- int overlap_mode_is_active = hs_overlap_mode_is_active(NULL, now);
-
FOR_EACH_SERVICE_BEGIN(service) {
- int service_entered_overlap = service_just_entered_overlap_mode(service,
- overlap_mode_is_active);
- int service_left_overlap = service_just_left_overlap_mode(service,
- overlap_mode_is_active);
- /* This should not be possible */
- if (BUG(service_entered_overlap && service_left_overlap)) {
- return;
- }
-
- /* No changes in overlap mode: nothing to do here */
- if (!service_entered_overlap && !service_left_overlap) {
- return;
- }
-
- /* To get down there means that some change happened to overlap mode */
- tor_assert(service_entered_overlap || service_left_overlap);
- /* Update the overlap marks on this service */
- if (service_entered_overlap) {
- /* It's the first time the service encounters the overlap period so flag
- * it in order to make sure we don't rotate at next check. */
- service->state.in_overlap_period = 1;
- } else if (service_left_overlap) {
- service->state.in_overlap_period = 0;
+ /* Note for a service booting up: Both descriptors are NULL in that case
+ * so this function might return true if we are in the timeframe for a
+ * rotation leading to basically swapping two NULL pointers which is
+ * harmless. However, the side effect is that triggering a rotation will
+ * update the service state and avoid doing anymore rotations after the
+ * two descriptors have been built. */
+ if (!should_rotate_descriptors(service, now)) {
+ continue;
}
- if (service_entered_overlap) {
- /* We just entered overlap period: recompute all HSDir indices. We need
- * to do this otherwise nodes can get stuck with old HSDir indices until
- * we fetch a new consensus, and we might need to reupload our desc
- * before that. */
- /* XXX find a better place than rotate_all_descriptors() to do this */
- nodelist_recompute_all_hsdir_indices();
- }
+ tor_assert(service->desc_current);
+ tor_assert(service->desc_next);
- /* If we just entered or left overlap mode, rotate our descriptors */
- log_info(LD_REND, "We just %s overlap period. About to rotate %s "
- "descriptors (%p / %p).",
- service_entered_overlap ? "entered" : "left",
- safe_str_client(service->onion_address),
- service->desc_current, service->desc_next);
+ log_info(LD_REND, "Time to rotate our descriptors (%p / %p) for %s",
+ service->desc_current, service->desc_next,
+ safe_str_client(service->onion_address));
- /* If we have a next descriptor lined up, rotate the descriptors so that it
- * becomes current. */
- if (service->desc_next) {
- rotate_service_descriptors(service);
- }
+ rotate_service_descriptors(service, now);
} FOR_EACH_SERVICE_END;
}
@@ -1833,6 +1920,17 @@ run_housekeeping_event(time_t now)
* simply moving things around or removing uneeded elements. */
FOR_EACH_SERVICE_BEGIN(service) {
+
+ /* If the service is starting off, set the rotation time. We can't do that
+ * at configure time because the get_options() needs to be set for setting
+ * that time that uses the voting interval. */
+ if (service->state.next_rotation_time == 0) {
+ /* Set the next rotation time of the descriptors. If it's Oct 25th
+ * 23:47:00, the next rotation time is when the next SRV is computed
+ * which is at Oct 26th 00:00:00 that is in 13 minutes. */
+ set_rotation_time(service, now);
+ }
+
/* Cleanup invalid intro points from the service descriptor. */
cleanup_intro_points(service, now);
@@ -2075,7 +2173,9 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
}
/* Setup the connection identifier. */
- ed25519_pubkey_copy(&ident.identity_pk, &service->keys.identity_pk);
+ hs_ident_dir_conn_init(&service->keys.identity_pk, &desc->blinded_kp.pubkey,
+ &ident);
+
/* This is our resource when uploading which is used to construct the URL
* with the version number: "/tor/hs/<version>/publish". */
tor_snprintf(version_str, sizeof(version_str), "%u",
@@ -2102,8 +2202,8 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
/* Logging so we know where it was sent. */
{
int is_next_desc = (service->desc_next == desc);
- const uint8_t *index = (is_next_desc) ? hsdir->hsdir_index->next :
- hsdir->hsdir_index->current;
+ const uint8_t *index = (is_next_desc) ? hsdir->hsdir_index->store_second:
+ hsdir->hsdir_index->store_first;
log_info(LD_REND, "Service %s %s descriptor of revision %" PRIu64
" initiated upload request to %s with index %s",
safe_str_client(service->onion_address),
@@ -2318,19 +2418,26 @@ set_descriptor_revision_counter(hs_descriptor_t *hs_desc)
* if PublishHidServDescriptors is false. */
STATIC void
upload_descriptor_to_all(const hs_service_t *service,
- hs_service_descriptor_t *desc, int for_next_period)
+ hs_service_descriptor_t *desc)
{
smartlist_t *responsible_dirs = NULL;
tor_assert(service);
tor_assert(desc);
+ /* We'll first cancel any directory request that are ongoing for this
+ * descriptor. It is possible that we can trigger multiple uploads in a
+ * short time frame which can lead to a race where the second upload arrives
+ * before the first one leading to a 400 malformed descriptor response from
+ * the directory. Closing all pending requests avoids that. */
+ close_directory_connections(service, desc);
+
/* Get our list of responsible HSDir. */
responsible_dirs = smartlist_new();
/* The parameter 0 means that we aren't a client so tell the function to use
* the spread store consensus paremeter. */
hs_get_responsible_hsdirs(&desc->blinded_kp.pubkey, desc->time_period_num,
- for_next_period, 0, responsible_dirs);
+ service->desc_next == desc, 0, responsible_dirs);
/** Clear list of previous hsdirs since we are about to upload to a new
* list. Let's keep it up to date. */
@@ -2477,8 +2584,6 @@ run_upload_descriptor_event(time_t now)
/* Run v3+ check. */
FOR_EACH_SERVICE_BEGIN(service) {
FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
- int for_next_period = 0;
-
/* If we were asked to re-examine the hash ring, and it changed, then
schedule an upload */
if (consider_republishing_hs_descriptors &&
@@ -2504,13 +2609,7 @@ run_upload_descriptor_event(time_t now)
* accurate because all circuits have been established. */
build_desc_intro_points(service, desc, now);
- /* If the service is in the overlap period and this descriptor is the
- * next one, it has to be uploaded for the next time period meaning
- * we'll use the next node_t hsdir_index to pick the HSDirs. */
- if (desc == service->desc_next) {
- for_next_period = 1;
- }
- upload_descriptor_to_all(service, desc, for_next_period);
+ upload_descriptor_to_all(service, desc);
} FOR_EACH_DESCRIPTOR_END;
} FOR_EACH_SERVICE_END;
@@ -2924,8 +3023,14 @@ hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
void
hs_service_dir_info_changed(void)
{
- log_info(LD_REND, "New dirinfo arrived: consider reuploading descriptor");
- consider_republishing_hs_descriptors = 1;
+ if (hs_service_get_num_services() > 0) {
+ /* New directory information usually goes every consensus so rate limit
+ * every 30 minutes to not be too conservative. */
+ static struct ratelim_t dir_info_changed_ratelim = RATELIM_INIT(30 * 60);
+ log_fn_ratelim(&dir_info_changed_ratelim, LOG_INFO, LD_REND,
+ "New dirinfo arrived: consider reuploading descriptor");
+ consider_republishing_hs_descriptors = 1;
+ }
}
/* Called when we get an INTRODUCE2 cell on the circ. Respond to the cell and
@@ -3091,6 +3196,7 @@ hs_service_new(const or_options_t *options)
/* Allocate the CLIENT_PK replay cache in service state. */
service->state.replay_cache_rend_cookie =
replaycache_new(REND_REPLAY_TIME_INTERVAL, REND_REPLAY_TIME_INTERVAL);
+
return service;
}
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index 317b9d795d..248df27e10 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -196,16 +196,16 @@ typedef struct hs_service_state_t {
* should never go over MAX_INTRO_CIRCS_PER_PERIOD. */
unsigned int num_intro_circ_launched;
- /* Indicate that the service has entered the overlap period. We use this
- * flag to check for descriptor rotation. */
- unsigned int in_overlap_period : 1;
-
/* Replay cache tracking the REND_COOKIE found in INTRODUCE2 cell to detect
* repeats. Clients may send INTRODUCE1 cells for the same rendezvous point
* through two or more different introduction points; when they do, this
* keeps us from launching multiple simultaneous attempts to connect to the
* same rend point. */
replaycache_t *replay_cache_rend_cookie;
+
+ /* When is the next time we should rotate our descriptors. This is has to be
+ * done at the start time of the next SRV protocol run. */
+ time_t next_rotation_time;
} hs_service_state_t;
/* Representation of a service running on this tor instance. */
@@ -229,8 +229,7 @@ typedef struct hs_service_t {
/* Current descriptor. */
hs_service_descriptor_t *desc_current;
- /* Next descriptor that we need for the overlap period for which we have to
- * keep two sets of opened introduction point circuits. */
+ /* Next descriptor. */
hs_service_descriptor_t *desc_next;
/* XXX: Credential (client auth.) #20700. */
@@ -338,8 +337,7 @@ STATIC int
write_address_to_file(const hs_service_t *service, const char *fname_);
STATIC void upload_descriptor_to_all(const hs_service_t *service,
- hs_service_descriptor_t *desc,
- int for_next_period);
+ hs_service_descriptor_t *desc);
STATIC void service_desc_schedule_upload(hs_service_descriptor_t *desc,
time_t now,
diff --git a/src/or/main.c b/src/or/main.c
index cd726776c4..117857e543 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -81,6 +81,7 @@
#include "hibernate.h"
#include "hs_cache.h"
#include "hs_circuitmap.h"
+#include "hs_client.h"
#include "keypin.h"
#include "main.h"
#include "microdesc.h"
@@ -1144,7 +1145,7 @@ signewnym_impl(time_t now)
circuit_mark_all_dirty_circs_as_unusable();
addressmap_clear_transient();
- rend_client_purge_state();
+ hs_client_purge_state();
time_of_last_signewnym = now;
signewnym_is_pending = 0;
@@ -3784,7 +3785,6 @@ tor_main(int argc, char *argv[])
case CMD_KEY_EXPIRATION:
init_keys();
result = log_cert_expiration();
- result = 0;
break;
case CMD_LIST_FINGERPRINT:
result = do_list_fingerprint();
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index ee6d0f3cc1..00d9222ef1 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -67,12 +67,6 @@
#include "torcert.h"
#include "channelpadding.h"
-/** Map from lowercase nickname to identity digest of named server, if any. */
-static strmap_t *named_server_map = NULL;
-/** Map from lowercase nickname to (void*)1 for all names that are listed
- * as unnamed for some server in the consensus. */
-static strmap_t *unnamed_server_map = NULL;
-
/** Most recently received and validated v3 "ns"-flavored consensus network
* status. */
STATIC networkstatus_t *current_ns_consensus = NULL;
@@ -143,7 +137,6 @@ static int have_warned_about_old_version = 0;
* listed by the authorities. */
static int have_warned_about_new_version = 0;
-static void routerstatus_list_update_named_server_map(void);
static void update_consensus_bootstrap_multiple_downloads(
time_t now,
const or_options_t *options);
@@ -251,13 +244,6 @@ router_reload_consensus_networkstatus(void)
}
}
- if (!networkstatus_get_latest_consensus()) {
- if (!named_server_map)
- named_server_map = strmap_new();
- if (!unnamed_server_map)
- unnamed_server_map = strmap_new();
- }
-
update_certificate_downloads(time(NULL));
routers_update_all_from_networkstatus(time(NULL), 3);
@@ -795,41 +781,6 @@ router_get_consensus_status_by_id(const char *digest)
return router_get_mutable_consensus_status_by_id(digest);
}
-/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
- * the corresponding routerstatus_t, or NULL if none exists. Warn the
- * user if <b>warn_if_unnamed</b> is set, and they have specified a router by
- * nickname, but the Named flag isn't set for that router. */
-const routerstatus_t *
-router_get_consensus_status_by_nickname(const char *nickname,
- int warn_if_unnamed)
-{
- const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
- if (node)
- return node->rs;
- else
- return NULL;
-}
-
-/** Return the identity digest that's mapped to officially by
- * <b>nickname</b>. */
-const char *
-networkstatus_get_router_digest_by_nickname(const char *nickname)
-{
- if (!named_server_map)
- return NULL;
- return strmap_get_lc(named_server_map, nickname);
-}
-
-/** Return true iff <b>nickname</b> is disallowed from being the nickname
- * of any server. */
-int
-networkstatus_nickname_is_unnamed(const char *nickname)
-{
- if (!unnamed_server_map)
- return 0;
- return strmap_get_lc(unnamed_server_map, nickname) != NULL;
-}
-
/** How frequently do directory authorities re-download fresh networkstatus
* documents? */
#define AUTHORITY_NS_CACHE_INTERVAL (10*60)
@@ -1994,7 +1945,6 @@ networkstatus_set_current_consensus(const char *consensus,
update_consensus_networkstatus_fetch_time(now);
dirvote_recalculate_timing(options, now);
- routerstatus_list_update_named_server_map();
/* Update ewma and adjust policy if needed; first cache the old value */
old_ewma_enabled = cell_ewma_enabled();
@@ -2048,14 +1998,16 @@ networkstatus_set_current_consensus(const char *consensus,
char tbuf[ISO_TIME_LEN+1];
char dbuf[64];
long delta = now - c->valid_after;
+ char *flavormsg = NULL;
format_iso_time(tbuf, c->valid_after);
format_time_interval(dbuf, sizeof(dbuf), delta);
log_warn(LD_GENERAL, "Our clock is %s behind the time published in the "
"consensus network status document (%s UTC). Tor needs an "
"accurate clock to work correctly. Please check your time and "
"date settings!", dbuf, tbuf);
- control_event_general_status(LOG_WARN,
- "CLOCK_SKEW MIN_SKEW=%ld SOURCE=CONSENSUS", delta);
+ tor_asprintf(&flavormsg, "%s flavor consensus", flavor);
+ clock_skew_warning(NULL, delta, 1, LD_GENERAL, flavormsg, "CONSENSUS");
+ tor_free(flavormsg);
}
router_dir_info_changed();
@@ -2163,31 +2115,6 @@ routers_update_all_from_networkstatus(time_t now, int dir_version)
}
}
-/** Update our view of the list of named servers from the most recently
- * retrieved networkstatus consensus. */
-static void
-routerstatus_list_update_named_server_map(void)
-{
- networkstatus_t *ns = networkstatus_get_latest_consensus();
- if (!ns)
- return;
-
- strmap_free(named_server_map, tor_free_);
- named_server_map = strmap_new();
- strmap_free(unnamed_server_map, NULL);
- unnamed_server_map = strmap_new();
- smartlist_t *rslist = ns->routerstatus_list;
- SMARTLIST_FOREACH_BEGIN(rslist, const routerstatus_t *, rs) {
- if (rs->is_named) {
- strmap_set_lc(named_server_map, rs->nickname,
- tor_memdup(rs->identity_digest, DIGEST_LEN));
- }
- if (rs->is_unnamed) {
- strmap_set_lc(unnamed_server_map, rs->nickname, (void*)1);
- }
- } SMARTLIST_FOREACH_END(rs);
-}
-
/** Given a list <b>routers</b> of routerinfo_t *, update each status field
* according to our current consensus networkstatus. May re-order
* <b>routers</b>. */
@@ -2565,7 +2492,8 @@ getinfo_helper_networkstatus(control_connection_t *conn,
}
status = router_get_consensus_status_by_id(d);
} else if (!strcmpstart(question, "ns/name/")) {
- status = router_get_consensus_status_by_nickname(question+8, 0);
+ const node_t *n = node_get_by_nickname(question+8, 0);
+ status = n ? n->rs : NULL;
} else if (!strcmpstart(question, "ns/purpose/")) {
*answer = networkstatus_getinfo_by_purpose(question+11, time(NULL));
return *answer ? 0 : -1;
@@ -2672,8 +2600,5 @@ networkstatus_free_all(void)
}
tor_free(waiting->body);
}
-
- strmap_free(named_server_map, tor_free_);
- strmap_free(unnamed_server_map, NULL);
}
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index 7abd47b0a8..89ff7eaba6 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -62,11 +62,6 @@ const routerstatus_t *router_get_consensus_status_by_descriptor_digest(
MOCK_DECL(routerstatus_t *,
router_get_mutable_consensus_status_by_descriptor_digest,
(networkstatus_t *consensus, const char *digest));
-const routerstatus_t *router_get_consensus_status_by_nickname(
- const char *nickname,
- int warn_if_unnamed);
-const char *networkstatus_get_router_digest_by_nickname(const char *nickname);
-int networkstatus_nickname_is_unnamed(const char *nickname);
int we_want_to_fetch_flavor(const or_options_t *options, int flavor);
int we_want_to_fetch_unknown_auth_certs(const or_options_t *options);
void networkstatus_consensus_download_failed(int status_code,
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 03b315e682..df735a9d24 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -38,6 +38,8 @@
* used for authorities and fallback directories.)
*/
+#define NODELIST_PRIVATE
+
#include "or.h"
#include "address.h"
#include "config.h"
@@ -92,7 +94,14 @@ typedef struct nodelist_t {
smartlist_t *nodes;
/* Hash table to map from node ID digest to node. */
HT_HEAD(nodelist_map, node_t) nodes_by_id;
-
+ /* Hash table to map from node Ed25519 ID to node.
+ *
+ * Whenever a node's routerinfo or microdescriptor is about to change,
+ * you should remove it from this map with node_remove_from_ed25519_map().
+ * Whenever a node's routerinfo or microdescriptor has just chaned,
+ * you should add it to this map with node_add_to_ed25519_map().
+ */
+ HT_HEAD(nodelist_ed_map, node_t) nodes_by_ed_id;
} nodelist_t;
static inline unsigned int
@@ -111,6 +120,23 @@ HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq)
HT_GENERATE2(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq,
0.6, tor_reallocarray_, tor_free_)
+static inline unsigned int
+node_ed_id_hash(const node_t *node)
+{
+ return (unsigned) siphash24g(node->ed25519_id.pubkey, ED25519_PUBKEY_LEN);
+}
+
+static inline unsigned int
+node_ed_id_eq(const node_t *node1, const node_t *node2)
+{
+ return ed25519_pubkey_eq(&node1->ed25519_id, &node2->ed25519_id);
+}
+
+HT_PROTOTYPE(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash,
+ node_ed_id_eq)
+HT_GENERATE2(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash,
+ node_ed_id_eq, 0.6, tor_reallocarray_, tor_free_)
+
/** The global nodelist. */
static nodelist_t *the_nodelist=NULL;
@@ -121,6 +147,7 @@ init_nodelist(void)
if (PREDICT_UNLIKELY(the_nodelist == NULL)) {
the_nodelist = tor_malloc_zero(sizeof(nodelist_t));
HT_INIT(nodelist_map, &the_nodelist->nodes_by_id);
+ HT_INIT(nodelist_ed_map, &the_nodelist->nodes_by_ed_id);
the_nodelist->nodes = smartlist_new();
}
}
@@ -138,6 +165,21 @@ node_get_mutable_by_id(const char *identity_digest)
return node;
}
+/** As node_get_by_ed25519_id, but returns a non-const pointer */
+node_t *
+node_get_mutable_by_ed25519_id(const ed25519_public_key_t *ed_id)
+{
+ node_t search, *node;
+ if (PREDICT_UNLIKELY(the_nodelist == NULL))
+ return NULL;
+ if (BUG(ed_id == NULL) || BUG(ed25519_public_key_is_zero(ed_id)))
+ return NULL;
+
+ memcpy(&search.ed25519_id, ed_id, sizeof(search.ed25519_id));
+ node = HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, &search);
+ return node;
+}
+
/** Return the node_t whose identity is <b>identity_digest</b>, or NULL
* if no such node exists. */
MOCK_IMPL(const node_t *,
@@ -146,6 +188,14 @@ node_get_by_id,(const char *identity_digest))
return node_get_mutable_by_id(identity_digest);
}
+/** Return the node_t whose ed25519 identity is <b>ed_id</b>, or NULL
+ * if no such node exists. */
+MOCK_IMPL(const node_t *,
+node_get_by_ed25519_id,(const ed25519_public_key_t *ed_id))
+{
+ return node_get_mutable_by_ed25519_id(ed_id);
+}
+
/** Internal: return the node_t whose identity_digest is
* <b>identity_digest</b>. If none exists, create a new one, add it to the
* nodelist, and return it.
@@ -173,68 +223,150 @@ node_get_or_create(const char *identity_digest)
return node;
}
+/** Remove <b>node</b> from the ed25519 map (if it present), and
+ * set its ed25519_id field to zero. */
+static int
+node_remove_from_ed25519_map(node_t *node)
+{
+ tor_assert(the_nodelist);
+ tor_assert(node);
+
+ if (ed25519_public_key_is_zero(&node->ed25519_id)) {
+ return 0;
+ }
+
+ int rv = 0;
+ node_t *search =
+ HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ if (BUG(search != node)) {
+ goto clear_and_return;
+ }
+
+ search = HT_REMOVE(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ tor_assert(search == node);
+ rv = 1;
+
+ clear_and_return:
+ memset(&node->ed25519_id, 0, sizeof(node->ed25519_id));
+ return rv;
+}
+
+/** If <b>node</b> has an ed25519 id, and it is not already in the ed25519 id
+ * map, set its ed25519_id field, and add it to the ed25519 map.
+ */
+static int
+node_add_to_ed25519_map(node_t *node)
+{
+ tor_assert(the_nodelist);
+ tor_assert(node);
+
+ if (! ed25519_public_key_is_zero(&node->ed25519_id)) {
+ return 0;
+ }
+
+ const ed25519_public_key_t *key = node_get_ed25519_id(node);
+ if (!key) {
+ return 0;
+ }
+
+ node_t *old;
+ memcpy(&node->ed25519_id, key, sizeof(node->ed25519_id));
+ old = HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ if (BUG(old)) {
+ /* XXXX order matters here, and this may mean that authorities aren't
+ * pinning. */
+ if (old != node)
+ memset(&node->ed25519_id, 0, sizeof(node->ed25519_id));
+ return 0;
+ }
+
+ HT_INSERT(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ return 1;
+}
+
/* For a given <b>node</b> for the consensus <b>ns</b>, set the hsdir index
* for the node, both current and next if possible. This can only fails if the
* node_t ed25519 identity key can't be found which would be a bug. */
-static void
+STATIC void
node_set_hsdir_index(node_t *node, const networkstatus_t *ns)
{
time_t now = approx_time();
const ed25519_public_key_t *node_identity_pk;
- uint8_t *next_hsdir_index_srv = NULL, *current_hsdir_index_srv = NULL;
+ uint8_t *fetch_srv = NULL, *store_first_srv = NULL, *store_second_srv = NULL;
uint64_t next_time_period_num, current_time_period_num;
+ uint64_t fetch_tp, store_first_tp, store_second_tp;
tor_assert(node);
tor_assert(ns);
if (!networkstatus_is_live(ns, now)) {
- log_info(LD_GENERAL, "Not setting hsdir index with a non-live consensus.");
+ static struct ratelim_t live_consensus_ratelim = RATELIM_INIT(30 * 60);
+ log_fn_ratelim(&live_consensus_ratelim, LOG_INFO, LD_GENERAL,
+ "Not setting hsdir index with a non-live consensus.");
goto done;
}
node_identity_pk = node_get_ed25519_id(node);
if (node_identity_pk == NULL) {
log_debug(LD_GENERAL, "ed25519 identity public key not found when "
- "trying to build the hsdir indexes for node %s",
+ "trying to build the hsdir indexes for node %s",
node_describe(node));
goto done;
}
- /* Get the current and next time period number, we might use them both. */
- current_time_period_num = hs_get_time_period_num(now);
- next_time_period_num = hs_get_next_time_period_num(now);
-
- if (hs_overlap_mode_is_active(ns, now)) {
- /* We are in overlap mode, this means that our consensus has just cycled
- * from current SRV to previous SRV so for the _next_ upcoming time
- * period, we have to use the current SRV and use the previous SRV for the
- * current time period. If the current or previous SRV can't be found, the
- * disaster one is returned. */
- next_hsdir_index_srv = hs_get_current_srv(next_time_period_num, ns);
- /* The following can be confusing so again, in overlap mode, we use our
- * previous SRV for our _current_ hsdir index. */
- current_hsdir_index_srv = hs_get_previous_srv(current_time_period_num, ns);
+ /* Get the current and next time period number. */
+ current_time_period_num = hs_get_time_period_num(0);
+ next_time_period_num = hs_get_next_time_period_num(0);
+
+ /* We always use the current time period for fetching descs */
+ fetch_tp = current_time_period_num;
+
+ /* Now extract the needed SRVs and time periods for building hsdir indices */
+ if (hs_in_period_between_tp_and_srv(ns, now)) {
+ fetch_srv = hs_get_current_srv(fetch_tp, ns);
+
+ store_first_tp = hs_get_previous_time_period_num(0);
+ store_second_tp = current_time_period_num;
+ } else {
+ fetch_srv = hs_get_previous_srv(fetch_tp, ns);
+
+ store_first_tp = current_time_period_num;
+ store_second_tp = next_time_period_num;
+ }
+
+ /* We always use the old SRV for storing the first descriptor and the latest
+ * SRV for storing the second descriptor */
+ store_first_srv = hs_get_previous_srv(store_first_tp, ns);
+ store_second_srv = hs_get_current_srv(store_second_tp, ns);
+
+ /* Build the fetch index. */
+ hs_build_hsdir_index(node_identity_pk, fetch_srv, fetch_tp,
+ node->hsdir_index->fetch);
+
+ /* If we are in the time segment between SRV#N and TP#N, the fetch index is
+ the same as the first store index */
+ if (!hs_in_period_between_tp_and_srv(ns, now)) {
+ memcpy(node->hsdir_index->store_first, node->hsdir_index->fetch,
+ sizeof(node->hsdir_index->store_first));
} else {
- /* If NOT in overlap mode, we only need to compute the current hsdir index
- * for the ongoing time period and thus the current SRV. If it can't be
- * found, the disaster one is returned. */
- current_hsdir_index_srv = hs_get_current_srv(current_time_period_num, ns);
- }
-
- /* Build the current hsdir index. */
- hs_build_hsdir_index(node_identity_pk, current_hsdir_index_srv,
- current_time_period_num, node->hsdir_index->current);
- if (next_hsdir_index_srv) {
- /* Build the next hsdir index if we have a next SRV that we can use. */
- hs_build_hsdir_index(node_identity_pk, next_hsdir_index_srv,
- next_time_period_num, node->hsdir_index->next);
+ hs_build_hsdir_index(node_identity_pk, store_first_srv, store_first_tp,
+ node->hsdir_index->store_first);
+ }
+
+ /* If we are in the time segment between TP#N and SRV#N+1, the fetch index is
+ the same as the second store index */
+ if (hs_in_period_between_tp_and_srv(ns, now)) {
+ memcpy(node->hsdir_index->store_second, node->hsdir_index->fetch,
+ sizeof(node->hsdir_index->store_second));
} else {
- memset(node->hsdir_index->next, 0, sizeof(node->hsdir_index->next));
+ hs_build_hsdir_index(node_identity_pk, store_second_srv, store_second_tp,
+ node->hsdir_index->store_second);
}
done:
- tor_free(current_hsdir_index_srv);
- tor_free(next_hsdir_index_srv);
+ tor_free(fetch_srv);
+ tor_free(store_first_srv);
+ tor_free(store_second_srv);
return;
}
@@ -283,6 +415,8 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
id_digest = ri->cache_info.identity_digest;
node = node_get_or_create(id_digest);
+ node_remove_from_ed25519_map(node);
+
if (node->ri) {
if (!routers_have_same_or_addrs(node->ri, ri)) {
node_addrs_changed(node);
@@ -296,6 +430,8 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
}
node->ri = ri;
+ node_add_to_ed25519_map(node);
+
if (node->country == -1)
node_set_country(node);
@@ -339,8 +475,10 @@ nodelist_add_microdesc(microdesc_t *md)
return NULL;
node = node_get_mutable_by_id(rs->identity_digest);
if (node) {
+ node_remove_from_ed25519_map(node);
if (node->md)
node->md->held_by_nodes--;
+
node->md = md;
md->held_by_nodes++;
/* Setting the HSDir index requires the ed25519 identity key which can
@@ -349,7 +487,9 @@ nodelist_add_microdesc(microdesc_t *md)
if (rs->supports_v3_hsdir) {
node_set_hsdir_index(node, ns);
}
+ node_add_to_ed25519_map(node);
}
+
return node;
}
@@ -377,12 +517,14 @@ nodelist_set_consensus(networkstatus_t *ns)
if (ns->flavor == FLAV_MICRODESC) {
if (node->md == NULL ||
tor_memneq(node->md->digest,rs->descriptor_digest,DIGEST256_LEN)) {
+ node_remove_from_ed25519_map(node);
if (node->md)
node->md->held_by_nodes--;
node->md = microdesc_cache_lookup_by_digest256(NULL,
rs->descriptor_digest);
if (node->md)
node->md->held_by_nodes++;
+ node_add_to_ed25519_map(node);
}
}
@@ -447,6 +589,9 @@ nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md)
if (node && node->md == md) {
node->md = NULL;
md->held_by_nodes--;
+ if (! node_get_ed25519_id(node)) {
+ node_remove_from_ed25519_map(node);
+ }
}
}
@@ -475,6 +620,7 @@ nodelist_drop_node(node_t *node, int remove_from_ht)
tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node);
tor_assert(tmp == node);
}
+ node_remove_from_ed25519_map(node);
idx = node->nodelist_idx;
tor_assert(idx >= 0);
@@ -558,6 +704,7 @@ nodelist_free_all(void)
return;
HT_CLEAR(nodelist_map, &the_nodelist->nodes_by_id);
+ HT_CLEAR(nodelist_ed_map, &the_nodelist->nodes_by_ed_id);
SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
node->nodelist_idx = -1;
node_free(node);
@@ -622,9 +769,27 @@ nodelist_assert_ok(void)
tor_assert(node_sl_idx == node->nodelist_idx);
} SMARTLIST_FOREACH_END(node);
+ /* Every node listed with an ed25519 identity should be listed by that
+ * identity.
+ */
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ if (!ed25519_public_key_is_zero(&node->ed25519_id)) {
+ tor_assert(node == node_get_by_ed25519_id(&node->ed25519_id));
+ }
+ } SMARTLIST_FOREACH_END(node);
+
+ node_t **idx;
+ HT_FOREACH(idx, nodelist_ed_map, &the_nodelist->nodes_by_ed_id) {
+ node_t *node = *idx;
+ tor_assert(node == node_get_by_ed25519_id(&node->ed25519_id));
+ }
+
tor_assert((long)smartlist_len(the_nodelist->nodes) ==
(long)HT_SIZE(&the_nodelist->nodes_by_id));
+ tor_assert((long)smartlist_len(the_nodelist->nodes) >=
+ (long)HT_SIZE(&the_nodelist->nodes_by_ed_id));
+
digestmap_free(dm, NULL);
}
@@ -641,28 +806,23 @@ nodelist_get_list,(void))
/** Given a hex-encoded nickname of the format DIGEST, $DIGEST, $DIGEST=name,
* or $DIGEST~name, return the node with the matching identity digest and
* nickname (if any). Return NULL if no such node exists, or if <b>hex_id</b>
- * is not well-formed. */
+ * is not well-formed. DOCDOC flags */
const node_t *
-node_get_by_hex_id(const char *hex_id)
+node_get_by_hex_id(const char *hex_id, unsigned flags)
{
char digest_buf[DIGEST_LEN];
char nn_buf[MAX_NICKNAME_LEN+1];
char nn_char='\0';
+ (void) flags; // XXXX
+
if (hex_digest_nickname_decode(hex_id, digest_buf, &nn_char, nn_buf)==0) {
const node_t *node = node_get_by_id(digest_buf);
if (!node)
return NULL;
- if (nn_char) {
- const char *real_name = node_get_nickname(node);
- if (!real_name || strcasecmp(real_name, nn_buf))
- return NULL;
- if (nn_char == '=') {
- const char *named_id =
- networkstatus_get_router_digest_by_nickname(nn_buf);
- if (!named_id || tor_memneq(named_id, digest_buf, DIGEST_LEN))
- return NULL;
- }
+ if (nn_char == '=') {
+ /* "=" indicates a Named relay, but there aren't any of those now. */
+ return NULL;
}
return node;
}
@@ -671,42 +831,27 @@ node_get_by_hex_id(const char *hex_id)
}
/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
- * the corresponding node_t, or NULL if none exists. Warn the user if
- * <b>warn_if_unnamed</b> is set, and they have specified a router by
- * nickname, but the Named flag isn't set for that router. */
+ * the corresponding node_t, or NULL if none exists. Warn the user if they
+ * have specified a router by nickname, unless the NNF_NO_WARN_UNNAMED bit is
+ * set in <b>flags</b>. */
MOCK_IMPL(const node_t *,
-node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
+node_get_by_nickname,(const char *nickname, unsigned flags))
{
+ const int warn_if_unnamed = !(flags & NNF_NO_WARN_UNNAMED);
+
if (!the_nodelist)
return NULL;
/* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */
{
const node_t *node;
- if ((node = node_get_by_hex_id(nickname)) != NULL)
+ if ((node = node_get_by_hex_id(nickname, flags)) != NULL)
return node;
}
if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME))
return NULL;
- /* Okay, so if we get here, the nickname is just a nickname. Is there
- * a binding for it in the consensus? */
- {
- const char *named_id =
- networkstatus_get_router_digest_by_nickname(nickname);
- if (named_id)
- return node_get_by_id(named_id);
- }
-
- /* Is it marked as owned-by-someone-else? */
- if (networkstatus_nickname_is_unnamed(nickname)) {
- log_info(LD_GENERAL, "The name %s is listed as Unnamed: there is some "
- "router that holds it, but not one listed in the current "
- "consensus.", escaped(nickname));
- return NULL;
- }
-
/* Okay, so the name is not canonical for anybody. */
{
smartlist_t *matches = smartlist_new();
@@ -758,22 +903,34 @@ node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
const ed25519_public_key_t *
node_get_ed25519_id(const node_t *node)
{
+ const ed25519_public_key_t *ri_pk = NULL;
+ const ed25519_public_key_t *md_pk = NULL;
if (node->ri) {
if (node->ri->cache_info.signing_key_cert) {
- const ed25519_public_key_t *pk =
- &node->ri->cache_info.signing_key_cert->signing_key;
- if (BUG(ed25519_public_key_is_zero(pk)))
- goto try_the_md;
- return pk;
+ ri_pk = &node->ri->cache_info.signing_key_cert->signing_key;
+ if (BUG(ed25519_public_key_is_zero(ri_pk)))
+ ri_pk = NULL;
}
}
- try_the_md:
+
if (node->md) {
if (node->md->ed25519_identity_pkey) {
- return node->md->ed25519_identity_pkey;
+ md_pk = node->md->ed25519_identity_pkey;
}
}
- return NULL;
+
+ if (ri_pk && md_pk) {
+ if (ed25519_pubkey_eq(ri_pk, md_pk)) {
+ return ri_pk;
+ } else {
+ log_warn(LD_GENERAL, "Inconsistent ed25519 identities in the nodelist");
+ return NULL;
+ }
+ } else if (ri_pk) {
+ return ri_pk;
+ } else {
+ return md_pk;
+ }
}
/** Return true iff this node's Ed25519 identity matches <b>id</b>.
@@ -906,21 +1063,6 @@ node_get_nickname(const node_t *node)
return NULL;
}
-/** Return true iff the nickname of <b>node</b> is canonical, based on the
- * latest consensus. */
-int
-node_is_named(const node_t *node)
-{
- const char *named_id;
- const char *nickname = node_get_nickname(node);
- if (!nickname)
- return 0;
- named_id = networkstatus_get_router_digest_by_nickname(nickname);
- if (!named_id)
- return 0;
- return tor_memeq(named_id, node->identity, DIGEST_LEN);
-}
-
/** Return true iff <b>node</b> appears to be a directory authority or
* directory cache */
int
@@ -968,13 +1110,12 @@ node_get_verbose_nickname(const node_t *node,
char *verbose_name_out)
{
const char *nickname = node_get_nickname(node);
- int is_named = node_is_named(node);
verbose_name_out[0] = '$';
base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, node->identity,
DIGEST_LEN);
if (!nickname)
return;
- verbose_name_out[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
+ verbose_name_out[1+HEX_DIGEST_LEN] = '~';
strlcpy(verbose_name_out+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1);
}
@@ -1529,8 +1670,7 @@ node_nickname_matches(const node_t *node, const char *nickname)
return 1;
return hex_digest_nickname_matches(nickname,
node->identity,
- n,
- node_is_named(node));
+ n);
}
/** Return true iff <b>node</b> is named by some nickname in <b>lst</b>. */
@@ -1632,7 +1772,7 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) {
const node_t *node2;
const smartlist_t *family2;
- if (!(node2 = node_get_by_nickname(name, 0)))
+ if (!(node2 = node_get_by_nickname(name, NNF_NO_WARN_UNNAMED)))
continue;
if (!(family2 = node_get_declared_family(node2)))
continue;
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 9676263f75..427e449ad9 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -18,7 +18,14 @@
node_t *node_get_mutable_by_id(const char *identity_digest);
MOCK_DECL(const node_t *, node_get_by_id, (const char *identity_digest));
-const node_t *node_get_by_hex_id(const char *identity_digest);
+node_t *node_get_mutable_by_ed25519_id(const ed25519_public_key_t *ed_id);
+MOCK_DECL(const node_t *, node_get_by_ed25519_id,
+ (const ed25519_public_key_t *ed_id));
+
+#define NNF_NO_WARN_UNNAMED (1u<<0)
+
+const node_t *node_get_by_hex_id(const char *identity_digest,
+ unsigned flags);
node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out);
node_t *nodelist_add_microdesc(microdesc_t *md);
void nodelist_set_consensus(networkstatus_t *ns);
@@ -34,12 +41,11 @@ void nodelist_free_all(void);
void nodelist_assert_ok(void);
MOCK_DECL(const node_t *, node_get_by_nickname,
- (const char *nickname, int warn_if_unnamed));
+ (const char *nickname, unsigned flags));
void node_get_verbose_nickname(const node_t *node,
char *verbose_name_out);
void node_get_verbose_nickname_by_id(const char *id_digest,
char *verbose_name_out);
-int node_is_named(const node_t *node);
int node_is_dir(const node_t *node);
int node_has_descriptor(const node_t *node);
int node_get_purpose(const node_t *node);
@@ -136,5 +142,16 @@ void router_dir_info_changed(void);
const char *get_dir_info_status_string(void);
int count_loading_descriptors_progress(void);
+#ifdef NODELIST_PRIVATE
+
+#ifdef TOR_UNIT_TESTS
+
+STATIC void
+node_set_hsdir_index(node_t *node, const networkstatus_t *ns);
+
+#endif /* TOR_UNIT_TESTS */
+
+#endif /* NODELIST_PRIVATE */
+
#endif
diff --git a/src/or/or.h b/src/or/or.h
index fbeaf70ab1..72fdea17ea 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2459,6 +2459,8 @@ typedef struct node_t {
/** Used to look up the node_t by its identity digest. */
HT_ENTRY(node_t) ht_ent;
+ /** Used to look up the node_t by its ed25519 identity digest. */
+ HT_ENTRY(node_t) ed_ht_ent;
/** Position of the node within the list of nodes */
int nodelist_idx;
@@ -2466,6 +2468,13 @@ typedef struct node_t {
* identity may exist at a time. */
char identity[DIGEST_LEN];
+ /** The ed25519 identity of this node_t. This field is nonzero iff we
+ * currently have an ed25519 identity for this node in either md or ri,
+ * _and_ this node has been inserted to the ed25519-to-node map in the
+ * nodelist.
+ */
+ ed25519_public_key_t ed25519_id;
+
microdesc_t *md;
routerinfo_t *ri;
routerstatus_t *rs;
@@ -4203,7 +4212,7 @@ typedef struct {
/** If true, do not believe anybody who tells us that a domain resolves
* to an internal address, or that an internal address has a PTR mapping.
* Helps avoid some cross-site attacks. */
- int ClientDNSRejectInternalAddresses;
+ int TestingClientDNSRejectInternalAddresses;
/** If true, do not accept any requests to connect to internal addresses
* over randomly chosen exits. */
@@ -4331,6 +4340,10 @@ typedef struct {
* altered on testing networks. */
smartlist_t *TestingBridgeDownloadSchedule;
+ /** Schedule for when clients should download bridge descriptors when they
+ * have no running bridges. Only altered on testing networks. */
+ smartlist_t *TestingBridgeBootstrapDownloadSchedule;
+
/** When directory clients have only a few descriptors to request, they
* batch them until they have more, or until this amount of time has
* passed. Only altered on testing networks. */
diff --git a/src/or/relay.c b/src/or/relay.c
index ba9c09b843..c00afc8e72 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -930,7 +930,7 @@ connection_ap_process_end_not_open(
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return 0;
}
- if (get_options()->ClientDNSRejectInternalAddresses &&
+ if (get_options()->TestingClientDNSRejectInternalAddresses &&
tor_addr_is_internal(&addr, 0)) {
log_info(LD_APP,"Address '%s' resolved to internal. Closing,",
safe_str(conn->socks_request->address));
@@ -1347,7 +1347,7 @@ connection_edge_process_resolved_cell(edge_connection_t *conn,
goto done;
}
- if (get_options()->ClientDNSRejectInternalAddresses) {
+ if (get_options()->TestingClientDNSRejectInternalAddresses) {
int orig_len = smartlist_len(resolved_addresses);
SMARTLIST_FOREACH_BEGIN(resolved_addresses, address_ttl_t *, addr) {
if (addr->hostname == NULL && tor_addr_is_internal(&addr->addr, 0)) {
@@ -1440,7 +1440,7 @@ connection_edge_process_relay_cell_not_open(
if (tor_addr_family(&addr) != AF_UNSPEC) {
const sa_family_t family = tor_addr_family(&addr);
if (tor_addr_is_null(&addr) ||
- (get_options()->ClientDNSRejectInternalAddresses &&
+ (get_options()->TestingClientDNSRejectInternalAddresses &&
tor_addr_is_internal(&addr, 0))) {
log_info(LD_APP, "...but it claims the IP address was %s. Closing.",
fmt_addr(&addr));
diff --git a/src/or/rendcache.c b/src/or/rendcache.c
index 792f6a1302..b98b2bccfa 100644
--- a/src/or/rendcache.c
+++ b/src/or/rendcache.c
@@ -303,7 +303,7 @@ void
rend_cache_purge(void)
{
if (rend_cache) {
- log_info(LD_REND, "Purging HS descriptor cache");
+ log_info(LD_REND, "Purging HS v2 descriptor cache");
strmap_free(rend_cache, rend_cache_entry_free_);
}
rend_cache = strmap_new();
@@ -315,7 +315,7 @@ void
rend_cache_failure_purge(void)
{
if (rend_cache_failure) {
- log_info(LD_REND, "Purging HS failure cache");
+ log_info(LD_REND, "Purging HS v2 failure cache");
strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
}
rend_cache_failure = strmap_new();
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 9edb7cc4b7..01147c0072 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -558,7 +558,10 @@ rend_service_prune_list_impl_(void)
* matching surviving configured service. If not, close the circuit. */
while ((ocirc = circuit_get_next_service_intro_circ(ocirc))) {
int keep_it = 0;
- tor_assert(ocirc->rend_data);
+ if (ocirc->rend_data == NULL) {
+ /* This is a v3 circuit, ignore it. */
+ continue;
+ }
SMARTLIST_FOREACH_BEGIN(surviving_services, const rend_service_t *, s) {
if (rend_circuit_pk_digest_eq(ocirc, (uint8_t *) s->pk_digest)) {
/* Keep this circuit as we have a matching configured service. */
@@ -915,8 +918,8 @@ rend_service_del_ephemeral(const char *service_id)
(circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
- tor_assert(oc->rend_data);
- if (!rend_circuit_pk_digest_eq(oc, (uint8_t *) s->pk_digest)) {
+ if (oc->rend_data == NULL ||
+ !rend_circuit_pk_digest_eq(oc, (uint8_t *) s->pk_digest)) {
continue;
}
log_debug(LD_REND, "Closing intro point %s for service %s.",
@@ -2119,7 +2122,7 @@ find_rp_for_intro(const rend_intro_cell_t *intro,
if (intro->version == 0 || intro->version == 1) {
rp_nickname = (const char *)(intro->u.v0_v1.rp);
- node = node_get_by_nickname(rp_nickname, 0);
+ node = node_get_by_nickname(rp_nickname, NNF_NO_WARN_UNNAMED);
if (!node) {
if (err_msg_out) {
tor_asprintf(&err_msg,
@@ -4260,7 +4263,6 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
tor_assert(circ->rend_data);
log_debug(LD_REND,"beginning to hunt for addr/port");
- /* XXX: This is version 2 specific (only one supported). */
rend_pk_digest = (char *) rend_data_get_pk_digest(circ->rend_data, NULL);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
rend_pk_digest, REND_SERVICE_ID_LEN);
diff --git a/src/or/router.c b/src/or/router.c
index c8e78659c0..d2d3d12f5b 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -2287,7 +2287,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
if (!strcasecmp(name, options->Nickname))
continue; /* Don't list ourself, that's redundant */
else
- member = node_get_by_nickname(name, 1);
+ member = node_get_by_nickname(name, 0);
if (!member) {
int is_legal = is_legal_nickname_or_hexdigest(name);
if (!smartlist_contains_string(warned_nonexistent_family, name) &&
@@ -3497,7 +3497,7 @@ router_get_description(char *buf, const routerinfo_t *ri)
return "<null>";
return format_node_description(buf,
ri->cache_info.identity_digest,
- router_is_named(ri),
+ 0,
ri->nickname,
NULL,
ri->addr);
@@ -3607,7 +3607,7 @@ routerstatus_describe(const routerstatus_t *rs)
return routerstatus_get_description(buf, rs);
}
-/** Return a human-readable description of the extend_info_t <b>ri</b>.
+/** Return a human-readable description of the extend_info_t <b>ei</b>.
*
* This function is not thread-safe. Each call to this function invalidates
* previous values returned by this function.
@@ -3623,21 +3623,16 @@ extend_info_describe(const extend_info_t *ei)
* verbose representation of the identity of <b>router</b>. The format is:
* A dollar sign.
* The upper-case hexadecimal encoding of the SHA1 hash of router's identity.
- * A "=" if the router is named; a "~" if it is not.
+ * A "=" if the router is named (no longer implemented); a "~" if it is not.
* The router's nickname.
**/
void
router_get_verbose_nickname(char *buf, const routerinfo_t *router)
{
- const char *good_digest = networkstatus_get_router_digest_by_nickname(
- router->nickname);
- int is_named = good_digest && tor_memeq(good_digest,
- router->cache_info.identity_digest,
- DIGEST_LEN);
buf[0] = '$';
base16_encode(buf+1, HEX_DIGEST_LEN+1, router->cache_info.identity_digest,
DIGEST_LEN);
- buf[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
+ buf[1+HEX_DIGEST_LEN] = '~';
strlcpy(buf+1+HEX_DIGEST_LEN+1, router->nickname, MAX_NICKNAME_LEN+1);
}
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index da2dff600e..9111d93c3c 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -2934,7 +2934,7 @@ hex_digest_nickname_decode(const char *hexdigest,
* <b>hexdigest</b> is malformed, or it doesn't match. */
int
hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest,
- const char *nickname, int is_named)
+ const char *nickname)
{
char digest[DIGEST_LEN];
char nn_char='\0';
@@ -2943,30 +2943,20 @@ hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest,
if (hex_digest_nickname_decode(hexdigest, digest, &nn_char, nn_buf) == -1)
return 0;
- if (nn_char == '=' || nn_char == '~') {
- if (!nickname)
+ if (nn_char == '=') {
+ return 0;
+ }
+
+ if (nn_char == '~') {
+ if (!nickname) // XXX This seems wrong. -NM
return 0;
if (strcasecmp(nn_buf, nickname))
return 0;
- if (nn_char == '=' && !is_named)
- return 0;
}
return tor_memeq(digest, identity_digest, DIGEST_LEN);
}
-/** Return true iff <b>router</b> is listed as named in the current
- * consensus. */
-int
-router_is_named(const routerinfo_t *router)
-{
- const char *digest =
- networkstatus_get_router_digest_by_nickname(router->nickname);
-
- return (digest &&
- tor_memeq(digest, router->cache_info.identity_digest, DIGEST_LEN));
-}
-
/** Return true iff <b>digest</b> is the digest of the identity key of a
* trusted directory matching at least one bit of <b>type</b>. If <b>type</b>
* is zero (NO_DIRINFO), or ALL_DIRINFO, any authority is okay. */
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index e0ed4e623a..27c8d88f05 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -80,7 +80,6 @@ const node_t *router_choose_random_node(smartlist_t *excludedsmartlist,
struct routerset_t *excludedset,
router_crn_flags_t flags);
-int router_is_named(const routerinfo_t *router);
int router_digest_is_trusted_dir_type(const char *digest,
dirinfo_type_t type);
#define router_digest_is_trusted_dir(d) \
@@ -228,7 +227,7 @@ int hex_digest_nickname_decode(const char *hexdigest,
char *nickname_out);
int hex_digest_nickname_matches(const char *hexdigest,
const char *identity_digest,
- const char *nickname, int is_named);
+ const char *nickname);
#ifdef ROUTERLIST_PRIVATE
STATIC int choose_array_element_by_weight(const uint64_t *entries,
diff --git a/src/or/routerset.c b/src/or/routerset.c
index 4906c6a51d..54e26ef943 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -362,7 +362,7 @@ routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
/* No routers are specified by type; all are given by name or digest.
* we can do a lookup in O(len(routerset)). */
SMARTLIST_FOREACH(routerset->list, const char *, name, {
- const node_t *node = node_get_by_nickname(name, 1);
+ const node_t *node = node_get_by_nickname(name, 0);
if (node) {
if (!running_only || node->is_running)
if (!routerset_contains_node(excludeset, node))