aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/addressmap.c7
-rw-r--r--src/or/bridges.c10
-rw-r--r--src/or/bridges.h13
-rw-r--r--src/or/channel.c23
-rw-r--r--src/or/channel.h3
-rw-r--r--src/or/channelpadding.c2
-rw-r--r--src/or/channeltls.c5
-rw-r--r--src/or/circpathbias.c64
-rw-r--r--src/or/circpathbias.h1
-rw-r--r--src/or/circuitbuild.c238
-rw-r--r--src/or/circuitbuild.h12
-rw-r--r--src/or/circuitlist.c52
-rw-r--r--src/or/circuitmux.c678
-rw-r--r--src/or/circuitmux_ewma.c196
-rw-r--r--src/or/circuitmux_ewma.h14
-rw-r--r--src/or/circuitstats.c4
-rw-r--r--src/or/circuituse.c43
-rw-r--r--src/or/circuituse.h2
-rw-r--r--src/or/command.c16
-rw-r--r--src/or/config.c255
-rw-r--r--src/or/config.h3
-rw-r--r--src/or/confparse.c80
-rw-r--r--src/or/confparse.h6
-rw-r--r--src/or/connection.c625
-rw-r--r--src/or/connection.h22
-rw-r--r--src/or/connection_edge.c295
-rw-r--r--src/or/connection_edge.h23
-rw-r--r--src/or/connection_or.c33
-rw-r--r--src/or/conscache.c1
-rw-r--r--src/or/consdiffmgr.c33
-rw-r--r--src/or/control.c246
-rw-r--r--src/or/control.h13
-rw-r--r--src/or/cpuworker.c29
-rw-r--r--src/or/dirauth/dircollate.c (renamed from src/or/dircollate.c)38
-rw-r--r--src/or/dirauth/dircollate.h (renamed from src/or/dircollate.h)0
-rw-r--r--src/or/dirauth/dirvote.c (renamed from src/or/dirvote.c)1177
-rw-r--r--src/or/dirauth/dirvote.h (renamed from src/or/dirvote.h)249
-rw-r--r--src/or/dirauth/mode.h38
-rw-r--r--src/or/dirauth/shared_random.c (renamed from src/or/shared_random.c)174
-rw-r--r--src/or/dirauth/shared_random.h (renamed from src/or/shared_random.h)39
-rw-r--r--src/or/dirauth/shared_random_state.c (renamed from src/or/shared_random_state.c)88
-rw-r--r--src/or/dirauth/shared_random_state.h (renamed from src/or/shared_random_state.h)5
-rw-r--r--src/or/directory.c130
-rw-r--r--src/or/directory.h4
-rw-r--r--src/or/dirserv.c675
-rw-r--r--src/or/dirserv.h18
-rw-r--r--src/or/dns.c1
-rw-r--r--src/or/dos.c1
-rw-r--r--src/or/entrynodes.c30
-rw-r--r--src/or/entrynodes.h1
-rw-r--r--src/or/ext_orport.c4
-rw-r--r--src/or/fallback_dirs.inc581
-rw-r--r--src/or/geoip.c8
-rw-r--r--src/or/hibernate.c105
-rw-r--r--src/or/hibernate.h2
-rw-r--r--src/or/hs_cache.c1
-rw-r--r--src/or/hs_cache.h1
-rw-r--r--src/or/hs_cell.c3
-rw-r--r--src/or/hs_circuit.c21
-rw-r--r--src/or/hs_circuit.h1
-rw-r--r--src/or/hs_client.c42
-rw-r--r--src/or/hs_common.c58
-rw-r--r--src/or/hs_common.h13
-rw-r--r--src/or/hs_control.c4
-rw-r--r--src/or/hs_descriptor.c11
-rw-r--r--src/or/hs_descriptor.h1
-rw-r--r--src/or/hs_ident.c1
-rw-r--r--src/or/hs_ident.h1
-rw-r--r--src/or/hs_intropoint.c4
-rw-r--r--src/or/hs_ntor.c1
-rw-r--r--src/or/hs_service.c61
-rw-r--r--src/or/hs_service.h4
-rw-r--r--src/or/hs_stats.c6
-rw-r--r--src/or/include.am55
-rw-r--r--src/or/keypin.c13
-rw-r--r--src/or/main.c1044
-rw-r--r--src/or/main.h25
-rw-r--r--src/or/networkstatus.c195
-rw-r--r--src/or/networkstatus.h9
-rw-r--r--src/or/nodelist.c257
-rw-r--r--src/or/nodelist.h5
-rw-r--r--src/or/ntmain.c5
-rw-r--r--src/or/onion.c15
-rw-r--r--src/or/onion_fast.c2
-rw-r--r--src/or/onion_ntor.c2
-rw-r--r--src/or/onion_tap.c2
-rw-r--r--src/or/or.h189
-rw-r--r--src/or/parsecommon.c5
-rw-r--r--src/or/parsecommon.h2
-rw-r--r--src/or/periodic.c75
-rw-r--r--src/or/periodic.h59
-rw-r--r--src/or/policies.c124
-rw-r--r--src/or/policies.h14
-rw-r--r--src/or/proto_socks.c1
-rw-r--r--src/or/protover.c19
-rw-r--r--src/or/protover.h2
-rw-r--r--src/or/relay.c362
-rw-r--r--src/or/relay.h8
-rw-r--r--src/or/relay_crypto.c327
-rw-r--r--src/or/relay_crypto.h31
-rw-r--r--src/or/rendclient.c10
-rw-r--r--src/or/rendcommon.c14
-rw-r--r--src/or/rendmid.c6
-rw-r--r--src/or/rendservice.c13
-rw-r--r--src/or/rephist.c10
-rw-r--r--src/or/rephist.h5
-rw-r--r--src/or/router.c391
-rw-r--r--src/or/router.h33
-rw-r--r--src/or/routerkeys.c1
-rw-r--r--src/or/routerlist.c8
-rw-r--r--src/or/routerparse.c98
-rw-r--r--src/or/scheduler.c25
-rw-r--r--src/or/scheduler.h4
-rw-r--r--src/or/scheduler_kist.c6
-rw-r--r--src/or/scheduler_vanilla.c4
-rw-r--r--src/or/shared_random_client.c259
-rw-r--r--src/or/shared_random_client.h47
-rw-r--r--src/or/statefile.c13
-rw-r--r--src/or/statefile.h1
-rw-r--r--src/or/status.c7
-rw-r--r--src/or/torcert.c2
-rw-r--r--src/or/transports.c188
-rw-r--r--src/or/transports.h5
-rw-r--r--src/or/voting_schedule.c192
-rw-r--r--src/or/voting_schedule.h65
125 files changed, 6114 insertions, 4789 deletions
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index 96ce275578..7f861e4d24 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -21,9 +21,10 @@
#include "config.h"
#include "connection_edge.h"
#include "control.h"
+#include "crypto_rand.h"
#include "dns.h"
-#include "routerset.h"
#include "nodelist.h"
+#include "routerset.h"
/** A client-side struct to remember requests to rewrite addresses
* to new addresses. These structs are stored in the hash table
@@ -959,9 +960,11 @@ addressmap_get_virtual_address(int type)
char tmp[TOR_ADDR_BUF_LEN];
tor_addr_to_str(tmp, &addr, sizeof(tmp), 0);
if (strmap_get(addressmap, tmp)) {
+ // LCOV_EXCL_START
log_warn(LD_BUG, "%s wasn't in the addressmap, but %s was.",
buf, tmp);
continue;
+ // LCOV_EXCL_STOP
}
return tor_strdup(buf);
@@ -970,8 +973,10 @@ addressmap_get_virtual_address(int type)
log_warn(LD_CONFIG, "Ran out of virtual addresses!");
return NULL;
} else {
+ // LCOV_EXCL_START
log_warn(LD_BUG, "Called with unsupported address type (%d)", type);
return NULL;
+ // LCOV_EXCL_STOP
}
}
diff --git a/src/or/bridges.c b/src/or/bridges.c
index 29d00f37ba..699e030e6c 100644
--- a/src/or/bridges.c
+++ b/src/or/bridges.c
@@ -11,6 +11,8 @@
* Bridges are fixed entry nodes, used for censorship circumvention.
**/
+#define TOR_BRIDGES_PRIVATE
+
#include "or.h"
#include "bridges.h"
#include "circuitbuild.h"
@@ -93,7 +95,7 @@ sweep_bridge_list(void)
}
/** Initialize the bridge list to empty, creating it if needed. */
-static void
+STATIC void
clear_bridge_list(void)
{
if (!bridge_list)
@@ -156,7 +158,7 @@ bridge_get_addr_port(const bridge_info_t *bridge)
* bridge with no known digest whose address matches any of the
* tor_addr_port_t's in <b>orports</b>, return that bridge. Else return
* NULL. */
-static bridge_info_t *
+STATIC bridge_info_t *
get_configured_bridge_by_orports_digest(const char *digest,
const smartlist_t *orports)
{
@@ -350,7 +352,7 @@ bridge_has_digest(const bridge_info_t *bridge, const char *digest)
* existing bridge with the same address and port, and warn the user as
* appropriate.
*/
-static void
+STATIC void
bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port,
const char *digest, const char *transport_name)
{
@@ -471,7 +473,7 @@ bridge_add_from_config(bridge_line_t *bridge_line)
}
/** If <b>digest</b> is one of our known bridges, return it. */
-bridge_info_t *
+STATIC bridge_info_t *
find_bridge_by_digest(const char *digest)
{
if (! bridge_list)
diff --git a/src/or/bridges.h b/src/or/bridges.h
index 54a6250259..3108eb555d 100644
--- a/src/or/bridges.h
+++ b/src/or/bridges.h
@@ -20,7 +20,6 @@ typedef struct bridge_info_t bridge_info_t;
void mark_bridge_list(void);
void sweep_bridge_list(void);
const smartlist_t *bridge_list_get(void);
-bridge_info_t *find_bridge_by_digest(const char *digest);
const uint8_t *bridge_get_rsa_id_digest(const bridge_info_t *bridge);
const tor_addr_port_t * bridge_get_addr_port(const bridge_info_t *bridge);
bridge_info_t *get_configured_bridge_by_addr_port_digest(
@@ -65,5 +64,17 @@ MOCK_DECL(download_status_t *, get_bridge_dl_status_by_id,
void bridges_free_all(void);
+#ifdef TOR_BRIDGES_PRIVATE
+STATIC void clear_bridge_list(void);
+STATIC bridge_info_t *find_bridge_by_digest(const char *digest);
+STATIC bridge_info_t *get_configured_bridge_by_orports_digest(
+ const char *digest,
+ const smartlist_t *orports);
+STATIC void bridge_resolve_conflicts(const tor_addr_t *addr,
+ uint16_t port,
+ const char *digest,
+ const char *transport_name);
+#endif /* defined(TOR_BRIDGES_PRIVATE) */
+
#endif /* !defined(TOR_BRIDGES_H) */
diff --git a/src/or/channel.c b/src/or/channel.c
index a4740dd752..c30e508018 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -69,6 +69,7 @@
#include "circuitmux.h"
#include "entrynodes.h"
#include "geoip.h"
+#include "main.h"
#include "nodelist.h"
#include "relay.h"
#include "rephist.h"
@@ -404,6 +405,7 @@ channel_register(channel_t *chan)
/* Put it in the finished list, creating it if necessary */
if (!finished_channels) finished_channels = smartlist_new();
smartlist_add(finished_channels, chan);
+ mainloop_schedule_postloop_cleanup();
} else {
/* Put it in the active list, creating it if necessary */
if (!active_channels) active_channels = smartlist_new();
@@ -1548,6 +1550,7 @@ channel_change_state_(channel_t *chan, channel_state_t to_state)
if (active_channels) smartlist_remove(active_channels, chan);
if (!finished_channels) finished_channels = smartlist_new();
smartlist_add(finished_channels, chan);
+ mainloop_schedule_postloop_cleanup();
}
/* Need to put on active list? */
else if (!was_active && is_active) {
@@ -1666,6 +1669,7 @@ channel_listener_change_state(channel_listener_t *chan_l,
if (active_listeners) smartlist_remove(active_listeners, chan_l);
if (!finished_listeners) finished_listeners = smartlist_new();
smartlist_add(finished_listeners, chan_l);
+ mainloop_schedule_postloop_cleanup();
}
/* Need to put on active list? */
else if (!was_active && is_active) {
@@ -2109,21 +2113,6 @@ channel_listener_dumpstats(int severity)
}
/**
- * Set the cmux policy on all active channels.
- */
-void
-channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol)
-{
- if (!active_channels) return;
-
- SMARTLIST_FOREACH_BEGIN(active_channels, channel_t *, curr) {
- if (curr->cmux) {
- circuitmux_set_policy(curr->cmux, pol);
- }
- } SMARTLIST_FOREACH_END(curr);
-}
-
-/**
* Clean up channels.
*
* This gets called periodically from run_scheduled_events() in main.c;
@@ -2402,7 +2391,7 @@ channel_get_for_extend(const char *rsa_id_digest,
{
channel_t *chan, *best = NULL;
int n_inprogress_goodaddr = 0, n_old = 0;
- int n_noncanonical = 0, n_possible = 0;
+ int n_noncanonical = 0;
tor_assert(msg_out);
tor_assert(launch_out);
@@ -2465,8 +2454,6 @@ channel_get_for_extend(const char *rsa_id_digest,
continue;
}
- ++n_possible;
-
if (!best) {
best = chan; /* If we have no 'best' so far, this one is good enough. */
continue;
diff --git a/src/or/channel.h b/src/or/channel.h
index 0af5aed414..6cf8cd7f72 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -422,9 +422,6 @@ void channel_free_all(void);
void channel_dumpstats(int severity);
void channel_listener_dumpstats(int severity);
-/* Set the cmux policy on all active channels */
-void channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol);
-
#ifdef TOR_CHANNEL_INTERNAL_
#ifdef CHANNEL_PRIVATE_
diff --git a/src/or/channelpadding.c b/src/or/channelpadding.c
index ba432e716b..3dbdd019ab 100644
--- a/src/or/channelpadding.c
+++ b/src/or/channelpadding.c
@@ -16,11 +16,11 @@
#include "networkstatus.h"
#include "connection.h"
#include "connection_or.h"
+#include "crypto_rand.h"
#include "main.h"
#include "rephist.h"
#include "router.h"
#include "compat_time.h"
-#include <event2/event.h>
#include "rendservice.h"
STATIC int32_t channelpadding_get_netflow_inactive_timeout_ms(
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index 9000703b01..54d94f6109 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -160,9 +160,8 @@ channel_tls_common_init(channel_tls_t *tlschan)
chan->write_var_cell = channel_tls_write_var_cell_method;
chan->cmux = circuitmux_alloc();
- if (cell_ewma_enabled()) {
- circuitmux_set_policy(chan->cmux, &ewma_policy);
- }
+ /* We only have one policy for now so always set it to EWMA. */
+ circuitmux_set_policy(chan->cmux, &ewma_policy);
}
/**
diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c
index c1c1ca31be..3cdd16261a 100644
--- a/src/or/circpathbias.c
+++ b/src/or/circpathbias.c
@@ -30,6 +30,7 @@
#include "circuitstats.h"
#include "connection_edge.h"
#include "config.h"
+#include "crypto_rand.h"
#include "entrynodes.h"
#include "networkstatus.h"
#include "relay.h"
@@ -892,6 +893,7 @@ pathbias_check_probe_response(circuit_t *circ, const cell_t *cell)
/* Check nonce */
if (ipv4_host == ocirc->pathbias_probe_nonce) {
pathbias_mark_use_success(ocirc);
+ circuit_read_valid_data(ocirc, rh.length);
circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
log_info(LD_CIRC,
"Got valid path bias probe back for circ %d, stream %d.",
@@ -913,6 +915,68 @@ pathbias_check_probe_response(circuit_t *circ, const cell_t *cell)
}
/**
+ * Check if a cell is counts as valid data for a circuit,
+ * and if so, count it as valid.
+ */
+void
+pathbias_count_valid_cells(circuit_t *circ, const cell_t *cell)
+{
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ relay_header_t rh;
+
+ relay_header_unpack(&rh, cell->payload);
+
+ /* Check to see if this is a cell from a previous connection,
+ * or is a request to close the circuit. */
+ switch (rh.command) {
+ case RELAY_COMMAND_TRUNCATED:
+ /* Truncated cells can arrive on path bias circs. When they do,
+ * just process them. This closes the circ, but it was junk anyway.
+ * No reason to wait for the probe. */
+ circuit_read_valid_data(ocirc, rh.length);
+ circuit_truncated(TO_ORIGIN_CIRCUIT(circ),
+ get_uint8(cell->payload + RELAY_HEADER_SIZE));
+
+ break;
+
+ case RELAY_COMMAND_END:
+ if (connection_half_edge_is_valid_end(ocirc->half_streams,
+ rh.stream_id)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
+ }
+ break;
+
+ case RELAY_COMMAND_DATA:
+ if (connection_half_edge_is_valid_data(ocirc->half_streams,
+ rh.stream_id)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
+ }
+ break;
+
+ case RELAY_COMMAND_SENDME:
+ if (connection_half_edge_is_valid_sendme(ocirc->half_streams,
+ rh.stream_id)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
+ }
+ break;
+
+ case RELAY_COMMAND_CONNECTED:
+ if (connection_half_edge_is_valid_connected(ocirc->half_streams,
+ rh.stream_id)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
+ }
+ break;
+
+ case RELAY_COMMAND_RESOLVED:
+ if (connection_half_edge_is_valid_resolved(ocirc->half_streams,
+ rh.stream_id)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
+ }
+ break;
+ }
+}
+
+/**
* Check if a circuit was used and/or closed successfully.
*
* If we attempted to use the circuit to carry a stream but failed
diff --git a/src/or/circpathbias.h b/src/or/circpathbias.h
index c9e572d2ae..689b2a6207 100644
--- a/src/or/circpathbias.h
+++ b/src/or/circpathbias.h
@@ -20,6 +20,7 @@ void pathbias_count_build_success(origin_circuit_t *circ);
int pathbias_count_build_attempt(origin_circuit_t *circ);
int pathbias_check_close(origin_circuit_t *circ, int reason);
int pathbias_check_probe_response(circuit_t *circ, const cell_t *cell);
+void pathbias_count_valid_cells(circuit_t *circ, const cell_t *cell);
void pathbias_count_use_attempt(origin_circuit_t *circ);
void pathbias_mark_use_success(origin_circuit_t *circ);
void pathbias_mark_use_rollback(origin_circuit_t *circ);
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 75307c367f..8f17f27868 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -43,7 +43,7 @@
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
-#include "crypto.h"
+#include "crypto_rand.h"
#include "directory.h"
#include "entrynodes.h"
#include "hs_ntor.h"
@@ -56,6 +56,7 @@
#include "onion_fast.h"
#include "policies.h"
#include "relay.h"
+#include "relay_crypto.h"
#include "rendcommon.h"
#include "rephist.h"
#include "router.h"
@@ -71,10 +72,7 @@ static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
static int circuit_deliver_create_cell(circuit_t *circ,
const create_cell_t *create_cell,
int relayed);
-static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit,
- int is_hs_v3_rp_circuit);
static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
-static int onion_extend_cpath(origin_circuit_t *circ);
STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
static int circuit_send_first_onion_skin(origin_circuit_t *circ);
static int circuit_build_no_more_hops(origin_circuit_t *circ);
@@ -1055,7 +1053,7 @@ circuit_build_no_more_hops(origin_circuit_t *circ)
clear_broken_connection_map(1);
if (server_mode(options) && !check_whether_orport_reachable(options)) {
inform_testing_reachability();
- consider_testing_reachability(1, 1);
+ router_do_reachability_checks(1, 1);
}
}
@@ -1132,19 +1130,29 @@ circuit_send_intermediate_onion_skin(origin_circuit_t *circ,
return 0;
}
-/** Our clock just jumped by <b>seconds_elapsed</b>. Assume
- * something has also gone wrong with our network: notify the user,
- * and abandon all not-yet-used circuits. */
+/** Our clock just jumped by <b>seconds_elapsed</b>. If <b>was_idle</b> is
+ * true, then the monotonic time matches; otherwise it doesn't. Assume
+ * something has also gone wrong with our network: notify the user, and
+ * abandon all not-yet-used circuits. */
void
-circuit_note_clock_jumped(int seconds_elapsed)
+circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle)
{
int severity = server_mode(get_options()) ? LOG_WARN : LOG_NOTICE;
- tor_log(severity, LD_GENERAL, "Your system clock just jumped %d seconds %s; "
- "assuming established circuits no longer work.",
- seconds_elapsed >=0 ? seconds_elapsed : -seconds_elapsed,
- seconds_elapsed >=0 ? "forward" : "backward");
- control_event_general_status(LOG_WARN, "CLOCK_JUMPED TIME=%d",
- seconds_elapsed);
+ if (was_idle) {
+ tor_log(severity, LD_GENERAL, "Tor has been idle for "I64_FORMAT
+ " seconds; assuming established circuits no longer work.",
+ I64_PRINTF_ARG(seconds_elapsed));
+ } else {
+ tor_log(severity, LD_GENERAL,
+ "Your system clock just jumped "I64_FORMAT" seconds %s; "
+ "assuming established circuits no longer work.",
+ I64_PRINTF_ARG(
+ seconds_elapsed >=0 ? seconds_elapsed : -seconds_elapsed),
+ seconds_elapsed >=0 ? "forward" : "backward");
+ }
+ control_event_general_status(LOG_WARN, "CLOCK_JUMPED TIME="I64_FORMAT
+ " IDLE=%d",
+ I64_PRINTF_ARG(seconds_elapsed), was_idle?1:0);
/* so we log when it works again */
note_that_we_maybe_cant_complete_circuits();
control_event_client_status(severity, "CIRCUIT_NOT_ESTABLISHED REASON=%s",
@@ -1337,69 +1345,10 @@ circuit_init_cpath_crypto(crypt_path_t *cpath,
const char *key_data, size_t key_data_len,
int reverse, int is_hs_v3)
{
- crypto_digest_t *tmp_digest;
- crypto_cipher_t *tmp_crypto;
- size_t digest_len = 0;
- size_t cipher_key_len = 0;
tor_assert(cpath);
- tor_assert(key_data);
- tor_assert(!(cpath->f_crypto || cpath->b_crypto ||
- cpath->f_digest || cpath->b_digest));
-
- /* Basic key size validation */
- if (is_hs_v3 && BUG(key_data_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
- return -1;
- } else if (!is_hs_v3 && BUG(key_data_len != CPATH_KEY_MATERIAL_LEN)) {
- return -1;
- }
-
- /* If we are using this cpath for next gen onion services use SHA3-256,
- otherwise use good ol' SHA1 */
- if (is_hs_v3) {
- digest_len = DIGEST256_LEN;
- cipher_key_len = CIPHER256_KEY_LEN;
- cpath->f_digest = crypto_digest256_new(DIGEST_SHA3_256);
- cpath->b_digest = crypto_digest256_new(DIGEST_SHA3_256);
- } else {
- digest_len = DIGEST_LEN;
- cipher_key_len = CIPHER_KEY_LEN;
- cpath->f_digest = crypto_digest_new();
- cpath->b_digest = crypto_digest_new();
- }
-
- tor_assert(digest_len != 0);
- tor_assert(cipher_key_len != 0);
- const int cipher_key_bits = (int) cipher_key_len * 8;
-
- crypto_digest_add_bytes(cpath->f_digest, key_data, digest_len);
- crypto_digest_add_bytes(cpath->b_digest, key_data+digest_len, digest_len);
-
- cpath->f_crypto = crypto_cipher_new_with_bits(key_data+(2*digest_len),
- cipher_key_bits);
- if (!cpath->f_crypto) {
- log_warn(LD_BUG,"Forward cipher initialization failed.");
- return -1;
- }
-
- cpath->b_crypto = crypto_cipher_new_with_bits(
- key_data+(2*digest_len)+cipher_key_len,
- cipher_key_bits);
- if (!cpath->b_crypto) {
- log_warn(LD_BUG,"Backward cipher initialization failed.");
- return -1;
- }
-
- if (reverse) {
- tmp_digest = cpath->f_digest;
- cpath->f_digest = cpath->b_digest;
- cpath->b_digest = tmp_digest;
- tmp_crypto = cpath->f_crypto;
- cpath->f_crypto = cpath->b_crypto;
- cpath->b_crypto = tmp_crypto;
- }
-
- return 0;
+ return relay_crypto_init(&cpath->crypto, key_data, key_data_len, reverse,
+ is_hs_v3);
}
/** A "created" cell <b>reply</b> came back to us on circuit <b>circ</b>.
@@ -1470,13 +1419,12 @@ circuit_finish_handshake(origin_circuit_t *circ,
* just give up: force circ to close, and return 0.
*/
int
-circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason)
+circuit_truncated(origin_circuit_t *circ, int reason)
{
// crypt_path_t *victim;
// connection_t *stream;
tor_assert(circ);
- tor_assert(layer);
/* XXX Since we don't send truncates currently, getting a truncated
* means that a connection broke or an extend failed. For now,
@@ -1522,7 +1470,6 @@ onionskin_answer(or_circuit_t *circ,
const uint8_t *rend_circ_nonce)
{
cell_t cell;
- crypt_path_t *tmp_cpath;
tor_assert(keys_len == CPATH_KEY_MATERIAL_LEN);
@@ -1533,25 +1480,15 @@ onionskin_answer(or_circuit_t *circ,
}
cell.circ_id = circ->p_circ_id;
- tmp_cpath = tor_malloc_zero(sizeof(crypt_path_t));
- tmp_cpath->magic = CRYPT_PATH_MAGIC;
-
circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.",
(unsigned int)get_uint32(keys),
(unsigned int)get_uint32(keys+20));
- if (circuit_init_cpath_crypto(tmp_cpath, keys, keys_len, 0, 0)<0) {
+ if (relay_crypto_init(&circ->crypto, keys, keys_len, 0, 0)<0) {
log_warn(LD_BUG,"Circuit initialization failed");
- tor_free(tmp_cpath);
return -1;
}
- circ->n_digest = tmp_cpath->f_digest;
- circ->n_crypto = tmp_cpath->f_crypto;
- circ->p_digest = tmp_cpath->b_digest;
- circ->p_crypto = tmp_cpath->b_crypto;
- tmp_cpath->magic = 0;
- tor_free(tmp_cpath);
memcpy(circ->rend_circ_nonce, rend_circ_nonce, DIGEST_LEN);
@@ -1612,7 +1549,7 @@ onionskin_answer(or_circuit_t *circ,
* rend_service_launch_establish_intro())
*
* - We are a router testing its own reachabiity
- * (CIRCUIT_PURPOSE_TESTING, via consider_testing_reachability())
+ * (CIRCUIT_PURPOSE_TESTING, via router_do_reachability_checks())
*
* onion_pick_cpath_exit() bypasses us (by not calling
* new_route_len()) in the one-hop tunnel case, so we don't need to
@@ -2353,7 +2290,7 @@ warn_if_last_router_excluded(origin_circuit_t *circ,
* be used as an HS v3 rendezvous point.
*
* Return 0 if ok, -1 if circuit should be closed. */
-static int
+STATIC int
onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
int is_hs_v3_rp_circuit)
{
@@ -2524,12 +2461,71 @@ cpath_get_n_hops(crypt_path_t **head_ptr)
#endif /* defined(TOR_UNIT_TESTS) */
/**
+ * Build the exclude list for vanguard circuits.
+ *
+ * For vanguard circuits we exclude all the already chosen nodes (including the
+ * exit) from being middle hops to prevent the creation of A - B - A subpaths.
+ * We also allow the 4th hop to be the same as the guard node so as to not leak
+ * guard information to RP/IP/HSDirs.
+ *
+ * For vanguard circuits, we don't apply any subnet or family restrictions.
+ * This is to avoid impossible-to-build circuit paths, or just situations where
+ * our earlier guards prevent us from using most of our later ones.
+ *
+ * The alternative is building the circuit in reverse. Reverse calls to
+ * onion_extend_cpath() (ie: select outer hops first) would then have the
+ * property that you don't gain information about inner hops by observing
+ * outer ones. See https://trac.torproject.org/projects/tor/ticket/24487
+ * for this.
+ *
+ * (Note further that we still exclude the exit to prevent A - B - A
+ * at the end of the path. */
+static smartlist_t *
+build_vanguard_middle_exclude_list(uint8_t purpose,
+ cpath_build_state_t *state,
+ crypt_path_t *head,
+ int cur_len)
+{
+ smartlist_t *excluded;
+ const node_t *r;
+ crypt_path_t *cpath;
+ int i;
+
+ (void) purpose;
+
+ excluded = smartlist_new();
+
+ /* Add the exit to the exclude list (note that the exit/last hop is always
+ * chosen first in circuit_establish_circuit()). */
+ if ((r = build_state_get_exit_node(state))) {
+ smartlist_add(excluded, (node_t*)r);
+ }
+
+ /* If we are picking the 4th hop, allow that node to be the guard too.
+ * This prevents us from avoiding the Guard for those hops, which
+ * gives the adversary information about our guard if they control
+ * the RP, IP, or HSDIR. We don't do this check based on purpose
+ * because we also want to allow HS_VANGUARDS pre-build circuits
+ * to use the guard for that last hop.
+ */
+ if (cur_len == DEFAULT_ROUTE_LEN+1) {
+ /* Skip the first hop for the exclude list below */
+ head = head->next;
+ cur_len--;
+ }
+
+ for (i = 0, cpath = head; cpath && i < cur_len; ++i, cpath=cpath->next) {
+ if ((r = node_get_by_id(cpath->extend_info->identity_digest))) {
+ smartlist_add(excluded, (node_t*)r);
+ }
+ }
+
+ return excluded;
+}
+
+/**
* Build a list of nodes to exclude from the choice of this middle
* hop, based on already chosen nodes.
- *
- * XXX: At present, this function does not exclude any nodes from
- * the vanguard circuits. See
- * https://trac.torproject.org/projects/tor/ticket/24487
*/
static smartlist_t *
build_middle_exclude_list(uint8_t purpose,
@@ -2542,32 +2538,21 @@ build_middle_exclude_list(uint8_t purpose,
crypt_path_t *cpath;
int i;
+ /** Vanguard circuits have their own path selection rules */
+ if (circuit_should_use_vanguards(purpose)) {
+ return build_vanguard_middle_exclude_list(purpose, state, head, cur_len);
+ }
+
excluded = smartlist_new();
- /* Add the exit to the exclude list (note that the exit/last hop is always
- * chosen first in circuit_establish_circuit()). */
+ /* For non-vanguard circuits, add the exit and its family to the exclude list
+ * (note that the exit/last hop is always chosen first in
+ * circuit_establish_circuit()). */
if ((r = build_state_get_exit_node(state))) {
nodelist_add_node_and_family(excluded, r);
}
- /* XXX: We don't apply any other previously selected node restrictions for
- * vanguards, and allow nodes to be reused for those hop positions in the
- * same circuit. This is because after many rotations, you get to learn
- * inner guard nodes through the nodes that are not selected for outer
- * hops.
- *
- * The alternative is building the circuit in reverse. Reverse calls to
- * onion_extend_cpath() (ie: select outer hops first) would then have the
- * property that you don't gain information about inner hops by observing
- * outer ones. See https://trac.torproject.org/projects/tor/ticket/24487
- * for this.
- *
- * (Note further that we can and do still exclude the exit in the block
- * above, because it is chosen first in circuit_establish_circuit()..) */
- if (circuit_should_use_vanguards(purpose)) {
- return excluded;
- }
-
+ /* also exclude all other already chosen nodes and their family */
for (i = 0, cpath = head; cpath && i < cur_len; ++i, cpath=cpath->next) {
if ((r = node_get_by_id(cpath->extend_info->identity_digest))) {
nodelist_add_node_and_family(excluded, r);
@@ -2667,7 +2652,9 @@ choose_good_middle_server(uint8_t purpose,
/** If a hidden service circuit wants a specific middle node, pin it. */
if (middle_node_must_be_vanguard(options, purpose, cur_len)) {
log_debug(LD_GENERAL, "Picking a sticky node (cur_len = %d)", cur_len);
- return pick_vanguard_middle_node(options, flags, cur_len, excluded);
+ choice = pick_vanguard_middle_node(options, flags, cur_len, excluded);
+ smartlist_free(excluded);
+ return choice;
}
choice = router_choose_random_node(excluded, options->ExcludeNodes, flags);
@@ -2707,7 +2694,7 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state,
/* This request is for an entry server to use for a regular circuit,
* and we use entry guard nodes. Just return one of the guard nodes. */
tor_assert(guard_state_out);
- return guards_choose_guard(state, guard_state_out);
+ return guards_choose_guard(state, purpose, guard_state_out);
}
excluded = smartlist_new();
@@ -2750,7 +2737,7 @@ onion_next_hop_in_cpath(crypt_path_t *cpath)
* Return 1 if the path is complete, 0 if we successfully added a hop,
* and -1 on error.
*/
-static int
+STATIC int
onion_extend_cpath(origin_circuit_t *circ)
{
uint8_t purpose = circ->base_.purpose;
@@ -2872,14 +2859,13 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
return NULL;
}
- /* Choose a preferred address first, but fall back to an allowed address.
- * choose_address returns 1 on success, but get_prim_orport returns 0. */
+ /* Choose a preferred address first, but fall back to an allowed address. */
if (for_direct_connect)
- valid_addr = fascist_firewall_choose_address_node(node,
- FIREWALL_OR_CONNECTION,
- 0, &ap);
- else
- valid_addr = !node_get_prim_orport(node, &ap);
+ fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0, &ap);
+ else {
+ node_get_prim_orport(node, &ap);
+ }
+ valid_addr = tor_addr_port_is_valid_ap(&ap, 0);
if (valid_addr)
log_debug(LD_CIRC, "using %s for %s",
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index bea31ad0dd..e3d30c1104 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -29,7 +29,7 @@ void circuit_n_chan_done(channel_t *chan, int status,
int inform_testing_reachability(void);
int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ);
int circuit_send_next_onion_skin(origin_circuit_t *circ);
-void circuit_note_clock_jumped(int seconds_elapsed);
+void circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle);
int circuit_extend(cell_t *cell, circuit_t *circ);
int circuit_init_cpath_crypto(crypt_path_t *cpath,
const char *key_data, size_t key_data_len,
@@ -37,8 +37,7 @@ int circuit_init_cpath_crypto(crypt_path_t *cpath,
struct created_cell_t;
int circuit_finish_handshake(origin_circuit_t *circ,
const struct created_cell_t *created_cell);
-int circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer,
- int reason);
+int circuit_truncated(origin_circuit_t *circ, int reason);
int onionskin_answer(or_circuit_t *circ,
const struct created_cell_t *created_cell,
const char *keys, size_t keys_len,
@@ -83,6 +82,13 @@ STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan);
STATIC int new_route_len(uint8_t purpose, extend_info_t *exit_ei,
smartlist_t *nodes);
MOCK_DECL(STATIC int, count_acceptable_nodes, (smartlist_t *nodes));
+
+STATIC int onion_extend_cpath(origin_circuit_t *circ);
+
+STATIC int
+onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
+ int is_hs_v3_rp_circuit);
+
#if defined(ENABLE_TOR2WEB_MODE) || defined(TOR_UNIT_TESTS)
STATIC const node_t *pick_tor2web_rendezvous_node(router_crn_flags_t flags,
const or_options_t *options);
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 78d8a66023..fdb039ef0d 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -66,6 +66,8 @@
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "entrynodes.h"
#include "main.h"
#include "hs_circuit.h"
@@ -77,6 +79,7 @@
#include "onion_fast.h"
#include "policies.h"
#include "relay.h"
+#include "relay_crypto.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rephist.h"
@@ -407,9 +410,6 @@ circuit_set_p_circid_chan(or_circuit_t *or_circ, circid_t id,
circuit_set_circid_chan_helper(circ, CELL_DIRECTION_IN, id, chan);
if (chan) {
- tor_assert(bool_eq(or_circ->p_chan_cells.n,
- or_circ->next_active_on_p_chan));
-
chan->timestamp_last_had_circuits = approx_time();
}
@@ -432,8 +432,6 @@ circuit_set_n_circid_chan(circuit_t *circ, circid_t id,
circuit_set_circid_chan_helper(circ, CELL_DIRECTION_OUT, id, chan);
if (chan) {
- tor_assert(bool_eq(circ->n_chan_cells.n, circ->next_active_on_n_chan));
-
chan->timestamp_last_had_circuits = approx_time();
}
@@ -1044,6 +1042,14 @@ circuit_free_(circuit_t *circ)
circuit_remove_from_origin_circuit_list(ocirc);
+ if (ocirc->half_streams) {
+ SMARTLIST_FOREACH_BEGIN(ocirc->half_streams, half_edge_t *,
+ half_conn) {
+ half_edge_free(half_conn);
+ } SMARTLIST_FOREACH_END(half_conn);
+ smartlist_free(ocirc->half_streams);
+ }
+
if (ocirc->build_state) {
extend_info_free(ocirc->build_state->chosen_exit);
circuit_free_cpath_node(ocirc->build_state->pending_final_cpath);
@@ -1088,10 +1094,7 @@ circuit_free_(circuit_t *circ)
should_free = (ocirc->workqueue_entry == NULL);
- crypto_cipher_free(ocirc->p_crypto);
- crypto_digest_free(ocirc->p_digest);
- crypto_cipher_free(ocirc->n_crypto);
- crypto_digest_free(ocirc->n_digest);
+ relay_crypto_clear(&ocirc->crypto);
if (ocirc->rend_splice) {
or_circuit_t *other = ocirc->rend_splice;
@@ -1231,10 +1234,7 @@ circuit_free_cpath_node(crypt_path_t *victim)
if (!victim)
return;
- crypto_cipher_free(victim->f_crypto);
- crypto_cipher_free(victim->b_crypto);
- crypto_digest_free(victim->f_digest);
- crypto_digest_free(victim->b_digest);
+ relay_crypto_clear(&victim->crypto);
onion_handshake_state_release(&victim->handshake_state);
crypto_dh_free(victim->rend_dh_handshake_state);
extend_info_free(victim->extend_info);
@@ -2136,6 +2136,7 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
circuits_pending_close = smartlist_new();
smartlist_add(circuits_pending_close, circ);
+ mainloop_schedule_postloop_cleanup();
log_info(LD_GENERAL, "Circuit %u (id: %" PRIu32 ") marked for close at "
"%s:%d (orig reason: %d, new reason: %d)",
@@ -2382,6 +2383,20 @@ n_cells_in_circ_queues(const circuit_t *c)
return n;
}
+/** Return the number of bytes allocated for <b>c</c>'s half-open streams. */
+static size_t
+circuit_alloc_in_half_streams(const circuit_t *c)
+{
+ if (! CIRCUIT_IS_ORIGIN(c)) {
+ return 0;
+ }
+ const origin_circuit_t *ocirc = CONST_TO_ORIGIN_CIRCUIT(c);
+ if (ocirc->half_streams)
+ return smartlist_len(ocirc->half_streams) * sizeof(half_edge_t);
+ else
+ return 0;
+}
+
/**
* Return the age of the oldest cell queued on <b>c</b>, in timestamp units.
* Return 0 if there are no cells queued on c. Requires that <b>now</b> be
@@ -2616,6 +2631,7 @@ circuits_handle_oom(size_t current_allocation)
/* Now, kill the circuit. */
n = n_cells_in_circ_queues(circ);
+ const size_t half_stream_alloc = circuit_alloc_in_half_streams(circ);
if (! circ->marked_for_close) {
circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
}
@@ -2625,6 +2641,7 @@ circuits_handle_oom(size_t current_allocation)
++n_circuits_killed;
mem_recovered += n * packed_cell_mem_cost();
+ mem_recovered += half_stream_alloc;
mem_recovered += freed;
if (mem_recovered >= mem_to_recover)
@@ -2655,8 +2672,7 @@ assert_cpath_layer_ok(const crypt_path_t *cp)
switch (cp->state)
{
case CPATH_STATE_OPEN:
- tor_assert(cp->f_crypto);
- tor_assert(cp->b_crypto);
+ relay_crypto_assert_ok(&cp->crypto);
/* fall through */
case CPATH_STATE_CLOSED:
/*XXXX Assert that there's no handshake_state either. */
@@ -2746,10 +2762,7 @@ assert_circuit_ok,(const circuit_t *c))
c->state == CIRCUIT_STATE_GUARD_WAIT) {
tor_assert(!c->n_chan_create_cell);
if (or_circ) {
- tor_assert(or_circ->n_crypto);
- tor_assert(or_circ->p_crypto);
- tor_assert(or_circ->n_digest);
- tor_assert(or_circ->p_digest);
+ relay_crypto_assert_ok(&or_circ->crypto);
}
}
if (c->state == CIRCUIT_STATE_CHAN_WAIT && !c->marked_for_close) {
@@ -2773,4 +2786,3 @@ assert_circuit_ok,(const circuit_t *c))
tor_assert(!or_circ || !or_circ->rend_splice);
}
}
-
diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c
index fe3d8f1332..f9f5faa057 100644
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@ -114,13 +114,6 @@ struct circuitmux_s {
*/
chanid_circid_muxinfo_map_t *chanid_circid_map;
- /*
- * Double-linked ring of circuits with queued cells waiting for room to
- * free up on this connection's outbuf. Every time we pull cells from
- * a circuit, we advance this pointer to the next circuit in the ring.
- */
- struct circuit_t *active_circuits_head, *active_circuits_tail;
-
/** List of queued destroy cells */
destroy_cell_queue_t destroy_cell_queue;
/** Boolean: True iff the last cell to circuitmux_get_first_active_circuit
@@ -177,17 +170,6 @@ struct chanid_circid_muxinfo_t {
};
/*
- * Internal-use #defines
- */
-
-#ifdef CMUX_PARANOIA
-#define circuitmux_assert_okay_paranoid(cmux) \
- circuitmux_assert_okay(cmux)
-#else
-#define circuitmux_assert_okay_paranoid(cmux)
-#endif /* defined(CMUX_PARANOIA) */
-
-/*
* Static function declarations
*/
@@ -199,21 +181,9 @@ chanid_circid_entry_hash(chanid_circid_muxinfo_t *a);
static chanid_circid_muxinfo_t *
circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ);
static void
-circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ,
- cell_direction_t direction);
+circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ);
static void
-circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ,
- cell_direction_t direction);
-static inline void
-circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ,
- cell_direction_t direction);
-static inline circuit_t **
-circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ);
-static inline circuit_t **
-circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ);
-static void circuitmux_assert_okay_pass_one(circuitmux_t *cmux);
-static void circuitmux_assert_okay_pass_two(circuitmux_t *cmux);
-static void circuitmux_assert_okay_pass_three(circuitmux_t *cmux);
+circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ);
/* Static global variables */
@@ -223,119 +193,6 @@ static int64_t global_destroy_ctr = 0;
/* Function definitions */
/**
- * Linked list helpers
- */
-
-/**
- * Move an active circuit to the tail of the cmux's active circuits list;
- * used by circuitmux_notify_xmit_cells().
- */
-
-static inline void
-circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ,
- cell_direction_t direction)
-{
- circuit_t **next_p = NULL, **prev_p = NULL;
- circuit_t **next_prev = NULL, **prev_next = NULL;
- circuit_t **tail_next = NULL;
- or_circuit_t *or_circ = NULL;
-
- tor_assert(cmux);
- tor_assert(circ);
-
- circuitmux_assert_okay_paranoid(cmux);
-
- /* Figure out our next_p and prev_p for this cmux/direction */
- if (direction) {
- if (direction == CELL_DIRECTION_OUT) {
- tor_assert(circ->n_mux == cmux);
- next_p = &(circ->next_active_on_n_chan);
- prev_p = &(circ->prev_active_on_n_chan);
- } else {
- or_circ = TO_OR_CIRCUIT(circ);
- tor_assert(or_circ->p_mux == cmux);
- next_p = &(or_circ->next_active_on_p_chan);
- prev_p = &(or_circ->prev_active_on_p_chan);
- }
- } else {
- if (circ->n_mux == cmux) {
- next_p = &(circ->next_active_on_n_chan);
- prev_p = &(circ->prev_active_on_n_chan);
- } else {
- or_circ = TO_OR_CIRCUIT(circ);
- tor_assert(or_circ->p_mux == cmux);
- next_p = &(or_circ->next_active_on_p_chan);
- prev_p = &(or_circ->prev_active_on_p_chan);
- }
- }
- tor_assert(next_p);
- tor_assert(prev_p);
-
- /* Check if this really is an active circuit */
- if ((*next_p == NULL && *prev_p == NULL) &&
- !(circ == cmux->active_circuits_head ||
- circ == cmux->active_circuits_tail)) {
- /* Not active, no-op */
- return;
- }
-
- /* Check if this is already the tail */
- if (circ == cmux->active_circuits_tail) return;
-
- /* Okay, we have to move it; figure out next_prev and prev_next */
- if (*next_p) next_prev = circuitmux_prev_active_circ_p(cmux, *next_p);
- if (*prev_p) prev_next = circuitmux_next_active_circ_p(cmux, *prev_p);
- /* Adjust the previous node's next pointer, if any */
- if (prev_next) *prev_next = *next_p;
- /* Otherwise, we were the head */
- else cmux->active_circuits_head = *next_p;
- /* Adjust the next node's previous pointer, if any */
- if (next_prev) *next_prev = *prev_p;
- /* We're out of the list; now re-attach at the tail */
- /* Adjust our next and prev pointers */
- *next_p = NULL;
- *prev_p = cmux->active_circuits_tail;
- /* Set the next pointer of the tail, or the head if none */
- if (cmux->active_circuits_tail) {
- tail_next = circuitmux_next_active_circ_p(cmux,
- cmux->active_circuits_tail);
- *tail_next = circ;
- } else {
- cmux->active_circuits_head = circ;
- }
- /* Set the tail to this circuit */
- cmux->active_circuits_tail = circ;
-
- circuitmux_assert_okay_paranoid(cmux);
-}
-
-static inline circuit_t **
-circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
-{
- tor_assert(cmux);
- tor_assert(circ);
-
- if (circ->n_mux == cmux) return &(circ->next_active_on_n_chan);
- else {
- tor_assert(TO_OR_CIRCUIT(circ)->p_mux == cmux);
- return &(TO_OR_CIRCUIT(circ)->next_active_on_p_chan);
- }
-}
-
-static inline circuit_t **
-circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
-{
- tor_assert(cmux);
- tor_assert(circ);
-
- if (circ->n_mux == cmux) return &(circ->prev_active_on_n_chan);
- else {
- tor_assert(TO_OR_CIRCUIT(circ)->p_mux == cmux);
- return &(TO_OR_CIRCUIT(circ)->prev_active_on_p_chan);
- }
-}
-
-/**
* Helper for chanid_circid_cell_count_map_t hash table: compare the channel
* ID and circuit ID for a and b, and return less than, equal to, or greater
* than zero appropriately.
@@ -406,11 +263,6 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux, smartlist_t *detached_out)
circuit_t *circ = NULL;
tor_assert(cmux);
- /*
- * Don't circuitmux_assert_okay_paranoid() here; this gets called when
- * channels are being freed and have already been unregistered, so
- * the channel ID lookups it does will fail.
- */
i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map);
while (i) {
@@ -435,7 +287,7 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux, smartlist_t *detached_out)
*/
if (to_remove->muxinfo.cell_count > 0) {
- circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_OUT);
+ circuitmux_make_circuit_inactive(cmux, circ);
}
/* Clear n_mux */
@@ -450,7 +302,7 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux, smartlist_t *detached_out)
*/
if (to_remove->muxinfo.cell_count > 0) {
- circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_IN);
+ circuitmux_make_circuit_inactive(cmux, circ);
}
/*
@@ -606,9 +458,7 @@ circuitmux_clear_policy(circuitmux_t *cmux)
tor_assert(cmux);
/* Internally, this is just setting policy to NULL */
- if (cmux->policy) {
- circuitmux_set_policy(cmux, NULL);
- }
+ circuitmux_set_policy(cmux, NULL);
}
/**
@@ -944,7 +794,6 @@ circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ,
tor_assert(circ);
tor_assert(direction == CELL_DIRECTION_IN ||
direction == CELL_DIRECTION_OUT);
- circuitmux_assert_okay_paranoid(cmux);
/*
* Figure out which channel we're using, and get the circuit's current
@@ -1002,10 +851,10 @@ circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ,
*/
if (hashent->muxinfo.cell_count > 0 && cell_count == 0) {
--(cmux->n_active_circuits);
- circuitmux_make_circuit_inactive(cmux, circ, direction);
+ circuitmux_make_circuit_inactive(cmux, circ);
} else if (hashent->muxinfo.cell_count == 0 && cell_count > 0) {
++(cmux->n_active_circuits);
- circuitmux_make_circuit_active(cmux, circ, direction);
+ circuitmux_make_circuit_active(cmux, circ);
}
cmux->n_cells -= hashent->muxinfo.cell_count;
cmux->n_cells += cell_count;
@@ -1033,7 +882,7 @@ circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ,
hashent->muxinfo.cell_count = cell_count;
hashent->muxinfo.direction = direction;
/* Allocate policy specific circuit data if we need it */
- if (cmux->policy && cmux->policy->alloc_circ_data) {
+ if (cmux->policy->alloc_circ_data) {
/* Assert that we have the means to free policy-specific data */
tor_assert(cmux->policy->free_circ_data);
/* Allocate it */
@@ -1053,25 +902,14 @@ circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ,
if (direction == CELL_DIRECTION_OUT) circ->n_mux = cmux;
else TO_OR_CIRCUIT(circ)->p_mux = cmux;
- /* Make sure the next/prev pointers are NULL */
- if (direction == CELL_DIRECTION_OUT) {
- circ->next_active_on_n_chan = NULL;
- circ->prev_active_on_n_chan = NULL;
- } else {
- TO_OR_CIRCUIT(circ)->next_active_on_p_chan = NULL;
- TO_OR_CIRCUIT(circ)->prev_active_on_p_chan = NULL;
- }
-
/* Update counters */
++(cmux->n_circuits);
if (cell_count > 0) {
++(cmux->n_active_circuits);
- circuitmux_make_circuit_active(cmux, circ, direction);
+ circuitmux_make_circuit_active(cmux, circ);
}
cmux->n_cells += cell_count;
}
-
- circuitmux_assert_okay_paranoid(cmux);
}
/**
@@ -1095,7 +933,6 @@ circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ))
tor_assert(cmux);
tor_assert(cmux->chanid_circid_map);
tor_assert(circ);
- circuitmux_assert_okay_paranoid(cmux);
/* See if we have it for n_chan/n_circ_id */
if (circ->n_chan) {
@@ -1133,7 +970,7 @@ circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ))
if (hashent->muxinfo.cell_count > 0) {
--(cmux->n_active_circuits);
/* This does policy notifies, so comes before freeing policy data */
- circuitmux_make_circuit_inactive(cmux, circ, last_searched_direction);
+ circuitmux_make_circuit_inactive(cmux, circ);
}
cmux->n_cells -= hashent->muxinfo.cell_count;
@@ -1162,8 +999,6 @@ circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ))
/* Free the hash entry */
tor_free(hashent);
}
-
- circuitmux_assert_okay_paranoid(cmux);
}
/**
@@ -1172,94 +1007,22 @@ circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ))
*/
static void
-circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ,
- cell_direction_t direction)
+circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ)
{
- circuit_t **next_active = NULL, **prev_active = NULL, **next_prev = NULL;
- circuitmux_t *circuit_cmux = NULL;
- chanid_circid_muxinfo_t *hashent = NULL;
- channel_t *chan = NULL;
- circid_t circ_id;
- int already_active;
-
tor_assert(cmux);
+ tor_assert(cmux->policy);
tor_assert(circ);
- tor_assert(direction == CELL_DIRECTION_OUT ||
- direction == CELL_DIRECTION_IN);
- /*
- * Don't circuitmux_assert_okay_paranoid(cmux) here because the cell count
- * already got changed and we have to update the list for it to be consistent
- * again.
- */
-
- /* Get the right set of active list links for this direction */
- if (direction == CELL_DIRECTION_OUT) {
- next_active = &(circ->next_active_on_n_chan);
- prev_active = &(circ->prev_active_on_n_chan);
- circuit_cmux = circ->n_mux;
- chan = circ->n_chan;
- circ_id = circ->n_circ_id;
- } else {
- next_active = &(TO_OR_CIRCUIT(circ)->next_active_on_p_chan);
- prev_active = &(TO_OR_CIRCUIT(circ)->prev_active_on_p_chan);
- circuit_cmux = TO_OR_CIRCUIT(circ)->p_mux;
- chan = TO_OR_CIRCUIT(circ)->p_chan;
- circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;
- }
-
- /* Assert that it is attached to this mux and a channel */
- tor_assert(cmux == circuit_cmux);
- tor_assert(chan != NULL);
-
- /*
- * Check if the circuit really was inactive; if it's active, at least one
- * of the next_active and prev_active pointers will not be NULL, or this
- * circuit will be either the head or tail of the list for this cmux.
- */
- already_active = (*prev_active != NULL || *next_active != NULL ||
- cmux->active_circuits_head == circ ||
- cmux->active_circuits_tail == circ);
-
- /* If we're already active, log a warning and finish */
- if (already_active) {
- log_warn(LD_CIRC,
- "Circuit %u on channel " U64_FORMAT " was already active",
- (unsigned)circ_id, U64_PRINTF_ARG(chan->global_identifier));
- return;
- }
-
- /*
- * This is going at the head of the list; if the old head is not NULL,
- * then its prev pointer should point to this.
- */
- *next_active = cmux->active_circuits_head; /* Next is old head */
- *prev_active = NULL; /* Prev is NULL (this will be the head) */
- if (cmux->active_circuits_head) {
- /* The list had an old head; update its prev pointer */
- next_prev =
- circuitmux_prev_active_circ_p(cmux, cmux->active_circuits_head);
- tor_assert(next_prev);
- *next_prev = circ;
- } else {
- /* The list was empty; this becomes the tail as well */
- cmux->active_circuits_tail = circ;
- }
- /* This becomes the new head of the list */
- cmux->active_circuits_head = circ;
/* Policy-specific notification */
- if (cmux->policy &&
- cmux->policy->notify_circ_active) {
+ if (cmux->policy->notify_circ_active) {
/* Okay, we need to check the circuit for policy data now */
- hashent = circuitmux_find_map_entry(cmux, circ);
+ chanid_circid_muxinfo_t *hashent = circuitmux_find_map_entry(cmux, circ);
/* We should have found something */
tor_assert(hashent);
/* Notify */
cmux->policy->notify_circ_active(cmux, cmux->policy_data,
circ, hashent->muxinfo.policy_data);
}
-
- circuitmux_assert_okay_paranoid(cmux);
}
/**
@@ -1268,112 +1031,22 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ,
*/
static void
-circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ,
- cell_direction_t direction)
+circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ)
{
- circuit_t **next_active = NULL, **prev_active = NULL;
- circuit_t **next_prev = NULL, **prev_next = NULL;
- circuitmux_t *circuit_cmux = NULL;
- chanid_circid_muxinfo_t *hashent = NULL;
- channel_t *chan = NULL;
- circid_t circ_id;
- int already_inactive;
-
tor_assert(cmux);
+ tor_assert(cmux->policy);
tor_assert(circ);
- tor_assert(direction == CELL_DIRECTION_OUT ||
- direction == CELL_DIRECTION_IN);
- /*
- * Don't circuitmux_assert_okay_paranoid(cmux) here because the cell count
- * already got changed and we have to update the list for it to be consistent
- * again.
- */
-
- /* Get the right set of active list links for this direction */
- if (direction == CELL_DIRECTION_OUT) {
- next_active = &(circ->next_active_on_n_chan);
- prev_active = &(circ->prev_active_on_n_chan);
- circuit_cmux = circ->n_mux;
- chan = circ->n_chan;
- circ_id = circ->n_circ_id;
- } else {
- next_active = &(TO_OR_CIRCUIT(circ)->next_active_on_p_chan);
- prev_active = &(TO_OR_CIRCUIT(circ)->prev_active_on_p_chan);
- circuit_cmux = TO_OR_CIRCUIT(circ)->p_mux;
- chan = TO_OR_CIRCUIT(circ)->p_chan;
- circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;
- }
-
- /* Assert that it is attached to this mux and a channel */
- tor_assert(cmux == circuit_cmux);
- tor_assert(chan != NULL);
-
- /*
- * Check if the circuit really was active; if it's inactive, the
- * next_active and prev_active pointers will be NULL and this circuit
- * will not be the head or tail of the list for this cmux.
- */
- already_inactive = (*prev_active == NULL && *next_active == NULL &&
- cmux->active_circuits_head != circ &&
- cmux->active_circuits_tail != circ);
-
- /* If we're already inactive, log a warning and finish */
- if (already_inactive) {
- log_warn(LD_CIRC,
- "Circuit %d on channel " U64_FORMAT " was already inactive",
- (unsigned)circ_id, U64_PRINTF_ARG(chan->global_identifier));
- return;
- }
-
- /* Remove from the list; first get next_prev and prev_next */
- if (*next_active) {
- /*
- * If there's a next circuit, its previous circuit becomes this
- * circuit's previous circuit.
- */
- next_prev = circuitmux_prev_active_circ_p(cmux, *next_active);
- } else {
- /* Else, the tail becomes this circuit's previous circuit */
- next_prev = &(cmux->active_circuits_tail);
- }
-
- /* Got next_prev, now prev_next */
- if (*prev_active) {
- /*
- * If there's a previous circuit, its next circuit becomes this circuit's
- * next circuit.
- */
- prev_next = circuitmux_next_active_circ_p(cmux, *prev_active);
- } else {
- /* Else, the head becomes this circuit's next circuit */
- prev_next = &(cmux->active_circuits_head);
- }
-
- /* Assert that we got sensible values for the next/prev pointers */
- tor_assert(next_prev != NULL);
- tor_assert(prev_next != NULL);
-
- /* Update the next/prev pointers - this removes circ from the list */
- *next_prev = *prev_active;
- *prev_next = *next_active;
-
- /* Now null out prev_active/next_active */
- *prev_active = NULL;
- *next_active = NULL;
/* Policy-specific notification */
- if (cmux->policy &&
- cmux->policy->notify_circ_inactive) {
+ if (cmux->policy->notify_circ_inactive) {
/* Okay, we need to check the circuit for policy data now */
- hashent = circuitmux_find_map_entry(cmux, circ);
+ chanid_circid_muxinfo_t *hashent = circuitmux_find_map_entry(cmux, circ);
/* We should have found something */
tor_assert(hashent);
/* Notify */
cmux->policy->notify_circ_inactive(cmux, cmux->policy_data,
circ, hashent->muxinfo.policy_data);
}
-
- circuitmux_assert_okay_paranoid(cmux);
}
/**
@@ -1400,8 +1073,6 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ,
tor_assert(cmux);
tor_assert(circ);
- circuitmux_assert_okay_paranoid(cmux);
-
/* Search for this circuit's entry */
hashent = circuitmux_find_map_entry(cmux, circ);
/* Assert that we found one */
@@ -1412,7 +1083,7 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ,
cmux->n_cells += n_cells;
/* Do we need to notify a cmux policy? */
- if (cmux->policy && cmux->policy->notify_set_n_cells) {
+ if (cmux->policy->notify_set_n_cells) {
/* Call notify_set_n_cells */
cmux->policy->notify_set_n_cells(cmux,
cmux->policy_data,
@@ -1428,21 +1099,15 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ,
if (hashent->muxinfo.cell_count > 0 && n_cells == 0) {
--(cmux->n_active_circuits);
hashent->muxinfo.cell_count = n_cells;
- circuitmux_make_circuit_inactive(cmux, circ, hashent->muxinfo.direction);
+ circuitmux_make_circuit_inactive(cmux, circ);
/* Is the old cell count == 0 and the new cell count > 0 ? */
} else if (hashent->muxinfo.cell_count == 0 && n_cells > 0) {
++(cmux->n_active_circuits);
hashent->muxinfo.cell_count = n_cells;
- circuitmux_make_circuit_active(cmux, circ, hashent->muxinfo.direction);
+ circuitmux_make_circuit_active(cmux, circ);
} else {
- /*
- * Update the entry cell count like this so we can put a
- * circuitmux_assert_okay_paranoid inside make_circuit_(in)active() too.
- */
hashent->muxinfo.cell_count = n_cells;
}
-
- circuitmux_assert_okay_paranoid(cmux);
}
/*
@@ -1468,6 +1133,9 @@ circuitmux_get_first_active_circuit(circuitmux_t *cmux,
circuit_t *circ = NULL;
tor_assert(cmux);
+ tor_assert(cmux->policy);
+ /* This callback is mandatory. */
+ tor_assert(cmux->policy->pick_active_circuit);
tor_assert(destroy_queue_out);
*destroy_queue_out = NULL;
@@ -1486,14 +1154,7 @@ circuitmux_get_first_active_circuit(circuitmux_t *cmux,
/* We also must have a cell available for this to be the case */
tor_assert(cmux->n_cells > 0);
/* Do we have a policy-provided circuit selector? */
- if (cmux->policy && cmux->policy->pick_active_circuit) {
- circ = cmux->policy->pick_active_circuit(cmux, cmux->policy_data);
- }
- /* Fall back on the head of the active circuits list */
- if (!circ) {
- tor_assert(cmux->active_circuits_head);
- circ = cmux->active_circuits_head;
- }
+ circ = cmux->policy->pick_active_circuit(cmux, cmux->policy_data);
cmux->last_cell_was_destroy = 0;
} else {
tor_assert(cmux->n_cells == 0);
@@ -1517,7 +1178,6 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ,
tor_assert(cmux);
tor_assert(circ);
- circuitmux_assert_okay_paranoid(cmux);
if (n_cells == 0) return;
@@ -1544,17 +1204,11 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ,
/* Adjust the mux cell counter */
cmux->n_cells -= n_cells;
- /* If we aren't making it inactive later, move it to the tail of the list */
- if (!becomes_inactive) {
- circuitmux_move_active_circ_to_tail(cmux, circ,
- hashent->muxinfo.direction);
- }
-
/*
* We call notify_xmit_cells() before making the circuit inactive if needed,
* so the policy can always count on this coming in on an active circuit.
*/
- if (cmux->policy && cmux->policy->notify_xmit_cells) {
+ if (cmux->policy->notify_xmit_cells) {
cmux->policy->notify_xmit_cells(cmux, cmux->policy_data, circ,
hashent->muxinfo.policy_data,
n_cells);
@@ -1566,10 +1220,8 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ,
*/
if (becomes_inactive) {
--(cmux->n_active_circuits);
- circuitmux_make_circuit_inactive(cmux, circ, hashent->muxinfo.direction);
+ circuitmux_make_circuit_inactive(cmux, circ);
}
-
- circuitmux_assert_okay_paranoid(cmux);
}
/**
@@ -1592,282 +1244,6 @@ circuitmux_notify_xmit_destroy(circuitmux_t *cmux)
I64_PRINTF_ARG(global_destroy_ctr));
}
-/*
- * Circuitmux consistency checking assertions
- */
-
-/**
- * Check that circuitmux data structures are consistent and fail with an
- * assert if not.
- */
-
-void
-circuitmux_assert_okay(circuitmux_t *cmux)
-{
- tor_assert(cmux);
-
- /*
- * Pass 1: iterate the hash table; for each entry:
- * a) Check that the circuit has this cmux for n_mux or p_mux
- * b) If the cell_count is > 0, set the mark bit; otherwise clear it
- * c) Also check activeness (cell_count > 0 should be active)
- * d) Count the number of circuits, active circuits and queued cells
- * and at the end check that they match the counters in the cmux.
- *
- * Pass 2: iterate the active circuits list; for each entry,
- * make sure the circuit is attached to this mux and appears
- * in the hash table. Make sure the mark bit is 1, and clear
- * it in the hash table entry. Consistency-check the linked
- * list pointers.
- *
- * Pass 3: iterate the hash table again; assert if any active circuits
- * (mark bit set to 1) are discovered that weren't cleared in pass 2
- * (don't appear in the linked list).
- */
-
- circuitmux_assert_okay_pass_one(cmux);
- circuitmux_assert_okay_pass_two(cmux);
- circuitmux_assert_okay_pass_three(cmux);
-}
-
-/**
- * Do the first pass of circuitmux_assert_okay(); see the comment in that
- * function.
- */
-
-static void
-circuitmux_assert_okay_pass_one(circuitmux_t *cmux)
-{
- chanid_circid_muxinfo_t **i = NULL;
- uint64_t chan_id;
- channel_t *chan;
- circid_t circ_id;
- circuit_t *circ;
- or_circuit_t *or_circ;
- circuit_t **next_p, **prev_p;
- unsigned int n_circuits, n_active_circuits, n_cells;
-
- tor_assert(cmux);
- tor_assert(cmux->chanid_circid_map);
-
- /* Reset the counters */
- n_circuits = n_active_circuits = n_cells = 0;
- /* Start iterating the hash table */
- i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map);
- while (i) {
- /* Assert that the hash table entry isn't null */
- tor_assert(*i);
-
- /* Get the channel and circuit id */
- chan_id = (*i)->chan_id;
- circ_id = (*i)->circ_id;
-
- /* Find the channel and circuit, assert that they exist */
- chan = channel_find_by_global_id(chan_id);
- tor_assert(chan);
- circ = circuit_get_by_circid_channel_even_if_marked(circ_id, chan);
- tor_assert(circ);
-
- /* Assert that we know which direction this is going */
- tor_assert((*i)->muxinfo.direction == CELL_DIRECTION_OUT ||
- (*i)->muxinfo.direction == CELL_DIRECTION_IN);
-
- if ((*i)->muxinfo.direction == CELL_DIRECTION_OUT) {
- /* We should be n_mux on this circuit */
- tor_assert(cmux == circ->n_mux);
- tor_assert(chan == circ->n_chan);
- /* Get next and prev for next test */
- next_p = &(circ->next_active_on_n_chan);
- prev_p = &(circ->prev_active_on_n_chan);
- } else {
- /* This should be an or_circuit_t and we should be p_mux */
- or_circ = TO_OR_CIRCUIT(circ);
- tor_assert(cmux == or_circ->p_mux);
- tor_assert(chan == or_circ->p_chan);
- /* Get next and prev for next test */
- next_p = &(or_circ->next_active_on_p_chan);
- prev_p = &(or_circ->prev_active_on_p_chan);
- }
-
- /*
- * Should this circuit be active? I.e., does the mux know about > 0
- * cells on it?
- */
- const int circ_is_active = ((*i)->muxinfo.cell_count > 0);
-
- /* It should be in the linked list iff it's active */
- if (circ_is_active) {
- /* Either we have a next link or we are the tail */
- tor_assert(*next_p || (circ == cmux->active_circuits_tail));
- /* Either we have a prev link or we are the head */
- tor_assert(*prev_p || (circ == cmux->active_circuits_head));
- /* Increment the active circuits counter */
- ++n_active_circuits;
- } else {
- /* Shouldn't be in list, so no next or prev link */
- tor_assert(!(*next_p));
- tor_assert(!(*prev_p));
- /* And can't be head or tail */
- tor_assert(circ != cmux->active_circuits_head);
- tor_assert(circ != cmux->active_circuits_tail);
- }
-
- /* Increment the circuits counter */
- ++n_circuits;
- /* Adjust the cell counter */
- n_cells += (*i)->muxinfo.cell_count;
-
- /* Set the mark bit to circ_is_active */
- (*i)->muxinfo.mark = circ_is_active;
-
- /* Advance to the next entry */
- i = HT_NEXT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i);
- }
-
- /* Now check the counters */
- tor_assert(n_cells == cmux->n_cells);
- tor_assert(n_circuits == cmux->n_circuits);
- tor_assert(n_active_circuits == cmux->n_active_circuits);
-}
-
-/**
- * Do the second pass of circuitmux_assert_okay(); see the comment in that
- * function.
- */
-
-static void
-circuitmux_assert_okay_pass_two(circuitmux_t *cmux)
-{
- circuit_t *curr_circ, *prev_circ = NULL, *next_circ;
- or_circuit_t *curr_or_circ;
- uint64_t curr_chan_id;
- circid_t curr_circ_id;
- circuit_t **next_p, **prev_p;
- channel_t *chan;
- unsigned int n_active_circuits = 0;
- chanid_circid_muxinfo_t search, *hashent = NULL;
-
- tor_assert(cmux);
- tor_assert(cmux->chanid_circid_map);
-
- /*
- * Walk the linked list of active circuits in cmux; keep track of the
- * previous circuit seen for consistency checking purposes. Count them
- * to make sure the number in the linked list matches
- * cmux->n_active_circuits.
- */
- curr_circ = cmux->active_circuits_head;
- while (curr_circ) {
- /* Reset some things */
- chan = NULL;
- curr_or_circ = NULL;
- next_circ = NULL;
- next_p = prev_p = NULL;
- cell_direction_t direction;
-
- /* Figure out if this is n_mux or p_mux */
- if (cmux == curr_circ->n_mux) {
- /* Get next_p and prev_p */
- next_p = &(curr_circ->next_active_on_n_chan);
- prev_p = &(curr_circ->prev_active_on_n_chan);
- /* Get the channel */
- chan = curr_circ->n_chan;
- /* Get the circuit id */
- curr_circ_id = curr_circ->n_circ_id;
- /* Remember the direction */
- direction = CELL_DIRECTION_OUT;
- } else {
- /* We must be p_mux and this must be an or_circuit_t */
- curr_or_circ = TO_OR_CIRCUIT(curr_circ);
- tor_assert(cmux == curr_or_circ->p_mux);
- /* Get next_p and prev_p */
- next_p = &(curr_or_circ->next_active_on_p_chan);
- prev_p = &(curr_or_circ->prev_active_on_p_chan);
- /* Get the channel */
- chan = curr_or_circ->p_chan;
- /* Get the circuit id */
- curr_circ_id = curr_or_circ->p_circ_id;
- /* Remember the direction */
- direction = CELL_DIRECTION_IN;
- }
-
- /* Assert that we got a channel and get the channel ID */
- tor_assert(chan);
- curr_chan_id = chan->global_identifier;
-
- /* Assert that prev_p points to last circuit we saw */
- tor_assert(*prev_p == prev_circ);
- /* If that's NULL, assert that we are the head */
- if (!(*prev_p)) tor_assert(curr_circ == cmux->active_circuits_head);
-
- /* Get the next circuit */
- next_circ = *next_p;
- /* If it's NULL, assert that we are the tail */
- if (!(*next_p)) tor_assert(curr_circ == cmux->active_circuits_tail);
-
- /* Now find the hash table entry for this circuit */
- search.chan_id = curr_chan_id;
- search.circ_id = curr_circ_id;
- hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map,
- &search);
-
- /* Assert that we have one */
- tor_assert(hashent);
-
- /* Assert that the direction matches */
- tor_assert(direction == hashent->muxinfo.direction);
-
- /* Assert that the hash entry got marked in pass one */
- tor_assert(hashent->muxinfo.mark);
-
- /* Clear the mark */
- hashent->muxinfo.mark = 0;
-
- /* Increment the counter */
- ++n_active_circuits;
-
- /* Advance to the next active circuit and update prev_circ */
- prev_circ = curr_circ;
- curr_circ = next_circ;
- }
-
- /* Assert that the counter matches the cmux */
- tor_assert(n_active_circuits == cmux->n_active_circuits);
-}
-
-/**
- * Do the third pass of circuitmux_assert_okay(); see the comment in that
- * function.
- */
-
-static void
-circuitmux_assert_okay_pass_three(circuitmux_t *cmux)
-{
- chanid_circid_muxinfo_t **i = NULL;
-
- tor_assert(cmux);
- tor_assert(cmux->chanid_circid_map);
-
- /* Start iterating the hash table */
- i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map);
-
- /* Advance through each entry */
- while (i) {
- /* Assert that it isn't null */
- tor_assert(*i);
-
- /*
- * Assert that this entry is not marked - i.e., that either we didn't
- * think it should be active in pass one or we saw it in the active
- * circuits linked list.
- */
- tor_assert(!((*i)->muxinfo.mark));
-
- /* Advance to the next entry */
- i = HT_NEXT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i);
- }
-}
-
/*DOCDOC */
void
circuitmux_append_destroy_cell(channel_t *chan,
diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c
index fde2d22a89..e5d5a14581 100644
--- a/src/or/circuitmux_ewma.c
+++ b/src/or/circuitmux_ewma.c
@@ -28,7 +28,7 @@
*
**/
-#define TOR_CIRCUITMUX_EWMA_C_
+#define CIRCUITMUX_EWMA_PRIVATE
#include "orconfig.h"
@@ -37,6 +37,7 @@
#include "or.h"
#include "circuitmux.h"
#include "circuitmux_ewma.h"
+#include "crypto_rand.h"
#include "networkstatus.h"
/*** EWMA parameter #defines ***/
@@ -169,8 +170,6 @@ TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol)
static void add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma);
static int compare_cell_ewma_counts(const void *p1, const void *p2);
-static unsigned cell_ewma_tick_from_timeval(const struct timeval *now,
- double *remainder_out);
static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma);
static inline double get_scale_factor(unsigned from_tick, unsigned to_tick);
static cell_ewma_t * pop_first_cell_ewma(ewma_policy_data_t *pol);
@@ -223,8 +222,6 @@ ewma_cmp_cmux(circuitmux_t *cmux_1, circuitmux_policy_data_t *pol_data_1,
* has value ewma_scale_factor ** N.)
*/
static double ewma_scale_factor = 0.1;
-/* DOCDOC ewma_enabled */
-static int ewma_enabled = 0;
/*** EWMA circuitmux_policy_t method table ***/
@@ -241,8 +238,26 @@ circuitmux_policy_t ewma_policy = {
/*.cmp_cmux =*/ ewma_cmp_cmux
};
+/** Have we initialized the ewma tick-counting logic? */
+static int ewma_ticks_initialized = 0;
+/** At what monotime_coarse_t did the current tick begin? */
+static monotime_coarse_t start_of_current_tick;
+/** What is the number of the current tick? */
+static unsigned current_tick_num;
+
/*** EWMA method implementations using the below EWMA helper functions ***/
+/** Compute and return the current cell_ewma tick. */
+static inline unsigned int
+cell_ewma_get_tick(void)
+{
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
+ int32_t msec_diff = monotime_coarse_diff_msec32(&start_of_current_tick,
+ &now);
+ return current_tick_num + msec_diff / (1000*EWMA_TICK_LEN);
+}
+
/**
* Allocate an ewma_policy_data_t and upcast it to a circuitmux_policy_data_t;
* this is called when setting the policy on a circuitmux_t to ewma_policy.
@@ -416,8 +431,6 @@ ewma_notify_xmit_cells(circuitmux_t *cmux,
ewma_policy_circ_data_t *cdata = NULL;
unsigned int tick;
double fractional_tick, ewma_increment;
- /* The current (hi-res) time */
- struct timeval now_hires;
cell_ewma_t *cell_ewma, *tmp;
tor_assert(cmux);
@@ -430,8 +443,7 @@ ewma_notify_xmit_cells(circuitmux_t *cmux,
cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data);
/* Rescale the EWMAs if needed */
- tor_gettimeofday_cached(&now_hires);
- tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick);
+ tick = cell_ewma_get_current_tick_and_fraction(&fractional_tick);
if (tick != pol->active_circuit_pqueue_last_recalibrated) {
scale_active_circuits(pol, tick);
@@ -592,79 +604,122 @@ cell_ewma_to_circuit(cell_ewma_t *ewma)
rescale.
*/
-/** Given a timeval <b>now</b>, compute the cell_ewma tick in which it occurs
- * and the fraction of the tick that has elapsed between the start of the tick
- * and <b>now</b>. Return the former and store the latter in
- * *<b>remainder_out</b>.
+/**
+ * Initialize the system that tells which ewma tick we are in.
+ */
+STATIC void
+cell_ewma_initialize_ticks(void)
+{
+ if (ewma_ticks_initialized)
+ return;
+ monotime_coarse_get(&start_of_current_tick);
+ crypto_rand((char*)&current_tick_num, sizeof(current_tick_num));
+ ewma_ticks_initialized = 1;
+}
+
+/** Compute the current cell_ewma tick and the fraction of the tick that has
+ * elapsed between the start of the tick and the current time. Return the
+ * former and store the latter in *<b>remainder_out</b>.
*
* These tick values are not meant to be shared between Tor instances, or used
* for other purposes. */
-
-static unsigned
-cell_ewma_tick_from_timeval(const struct timeval *now,
- double *remainder_out)
+STATIC unsigned
+cell_ewma_get_current_tick_and_fraction(double *remainder_out)
{
- unsigned res = (unsigned) (now->tv_sec / EWMA_TICK_LEN);
- /* rem */
- double rem = (now->tv_sec % EWMA_TICK_LEN) +
- ((double)(now->tv_usec)) / 1.0e6;
- *remainder_out = rem / EWMA_TICK_LEN;
- return res;
+ if (BUG(!ewma_ticks_initialized)) {
+ cell_ewma_initialize_ticks(); // LCOV_EXCL_LINE
+ }
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
+ int32_t msec_diff = monotime_coarse_diff_msec32(&start_of_current_tick,
+ &now);
+ if (msec_diff > (1000*EWMA_TICK_LEN)) {
+ unsigned ticks_difference = msec_diff / (1000*EWMA_TICK_LEN);
+ monotime_coarse_add_msec(&start_of_current_tick,
+ &start_of_current_tick,
+ ticks_difference * 1000 * EWMA_TICK_LEN);
+ current_tick_num += ticks_difference;
+ msec_diff %= 1000*EWMA_TICK_LEN;
+ }
+ *remainder_out = ((double)msec_diff) / (1.0e3 * EWMA_TICK_LEN);
+ return current_tick_num;
}
-/** Tell the caller whether ewma_enabled is set */
-int
-cell_ewma_enabled(void)
+/* Default value for the CircuitPriorityHalflifeMsec consensus parameter in
+ * msec. */
+#define CMUX_PRIORITY_HALFLIFE_MSEC_DEFAULT 30000
+/* Minimum and maximum value for the CircuitPriorityHalflifeMsec consensus
+ * parameter. */
+#define CMUX_PRIORITY_HALFLIFE_MSEC_MIN 1
+#define CMUX_PRIORITY_HALFLIFE_MSEC_MAX INT32_MAX
+
+/* Return the value of the circuit priority halflife from the options if
+ * available or else from the consensus (in that order). If none can be found,
+ * a default value is returned.
+ *
+ * The source_msg points to a string describing from where the value was
+ * picked so it can be used for logging. */
+static double
+get_circuit_priority_halflife(const or_options_t *options,
+ const networkstatus_t *consensus,
+ const char **source_msg)
{
- return ewma_enabled;
-}
+ int32_t halflife_ms;
+ double halflife;
+ /* Compute the default value now. We might need it. */
+ double halflife_default =
+ ((double) CMUX_PRIORITY_HALFLIFE_MSEC_DEFAULT) / 1000.0;
-/** Compute and return the current cell_ewma tick. */
-unsigned int
-cell_ewma_get_tick(void)
-{
- return ((unsigned)approx_time() / EWMA_TICK_LEN);
+ /* Try to get it from configuration file first. */
+ if (options && options->CircuitPriorityHalflife >= -EPSILON) {
+ halflife = options->CircuitPriorityHalflife;
+ *source_msg = "CircuitPriorityHalflife in configuration";
+ goto end;
+ }
+
+ /* Try to get the msec value from the consensus. */
+ halflife_ms = networkstatus_get_param(consensus,
+ "CircuitPriorityHalflifeMsec",
+ CMUX_PRIORITY_HALFLIFE_MSEC_DEFAULT,
+ CMUX_PRIORITY_HALFLIFE_MSEC_MIN,
+ CMUX_PRIORITY_HALFLIFE_MSEC_MAX);
+ halflife = ((double) halflife_ms) / 1000.0;
+ *source_msg = "CircuitPriorityHalflifeMsec in consensus";
+
+ end:
+ /* We should never go below the EPSILON else we would consider it disabled
+ * and we can't have that. */
+ if (halflife < EPSILON) {
+ log_warn(LD_CONFIG, "CircuitPriorityHalflife is too small (%f). "
+ "Adjusting to the smallest value allowed: %f.",
+ halflife, halflife_default);
+ halflife = halflife_default;
+ }
+ return halflife;
}
/** Adjust the global cell scale factor based on <b>options</b> */
void
-cell_ewma_set_scale_factor(const or_options_t *options,
- const networkstatus_t *consensus)
+cmux_ewma_set_options(const or_options_t *options,
+ const networkstatus_t *consensus)
{
- int32_t halflife_ms;
double halflife;
const char *source;
- if (options && options->CircuitPriorityHalflife >= -EPSILON) {
- halflife = options->CircuitPriorityHalflife;
- source = "CircuitPriorityHalflife in configuration";
- } else if (consensus && (halflife_ms = networkstatus_get_param(
- consensus, "CircuitPriorityHalflifeMsec",
- -1, -1, INT32_MAX)) >= 0) {
- halflife = ((double)halflife_ms)/1000.0;
- source = "CircuitPriorityHalflifeMsec in consensus";
- } else {
- halflife = EWMA_DEFAULT_HALFLIFE;
- source = "Default value";
- }
- if (halflife <= EPSILON) {
- /* The cell EWMA algorithm is disabled. */
- ewma_scale_factor = 0.1;
- ewma_enabled = 0;
- log_info(LD_OR,
- "Disabled cell_ewma algorithm because of value in %s",
- source);
- } else {
- /* convert halflife into halflife-per-tick. */
- halflife /= EWMA_TICK_LEN;
- /* compute per-tick scale factor. */
- ewma_scale_factor = exp( LOG_ONEHALF / halflife );
- ewma_enabled = 1;
- log_info(LD_OR,
- "Enabled cell_ewma algorithm because of value in %s; "
- "scale factor is %f per %d seconds",
- source, ewma_scale_factor, EWMA_TICK_LEN);
- }
+ cell_ewma_initialize_ticks();
+
+ /* Both options and consensus can be NULL. This assures us to either get a
+ * valid configured value or the default one. */
+ halflife = get_circuit_priority_halflife(options, consensus, &source);
+
+ /* convert halflife into halflife-per-tick. */
+ halflife /= EWMA_TICK_LEN;
+ /* compute per-tick scale factor. */
+ ewma_scale_factor = exp( LOG_ONEHALF / halflife );
+ log_info(LD_OR,
+ "Enabled cell_ewma algorithm because of value in %s; "
+ "scale factor is %f per %d seconds",
+ source, ewma_scale_factor, EWMA_TICK_LEN);
}
/** Return the multiplier necessary to convert the value of a cell sent in
@@ -763,3 +818,12 @@ pop_first_cell_ewma(ewma_policy_data_t *pol)
offsetof(cell_ewma_t, heap_index));
}
+/**
+ * Drop all resources held by circuitmux_ewma.c, and deinitialize the
+ * module. */
+void
+circuitmux_ewma_free_all(void)
+{
+ ewma_ticks_initialized = 0;
+}
+
diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h
index 8f4e57865e..f0c4c36095 100644
--- a/src/or/circuitmux_ewma.h
+++ b/src/or/circuitmux_ewma.h
@@ -12,13 +12,19 @@
#include "or.h"
#include "circuitmux.h"
+/* The public EWMA policy callbacks object. */
extern circuitmux_policy_t ewma_policy;
/* Externally visible EWMA functions */
-int cell_ewma_enabled(void);
-unsigned int cell_ewma_get_tick(void);
-void cell_ewma_set_scale_factor(const or_options_t *options,
- const networkstatus_t *consensus);
+void cmux_ewma_set_options(const or_options_t *options,
+ const networkstatus_t *consensus);
+
+void circuitmux_ewma_free_all(void);
+
+#ifdef CIRCUITMUX_EWMA_PRIVATE
+STATIC unsigned cell_ewma_get_current_tick_and_fraction(double *remainder_out);
+STATIC void cell_ewma_initialize_ticks(void);
+#endif
#endif /* !defined(TOR_CIRCUITMUX_EWMA_H) */
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index f06c2e5e38..94f75c590f 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -31,10 +31,12 @@
#include "config.h"
#include "confparse.h"
#include "control.h"
+#include "crypto_rand.h"
#include "main.h"
#include "networkstatus.h"
#include "rendclient.h"
#include "rendservice.h"
+#include "router.h"
#include "statefile.h"
#include "circuitlist.h"
#include "circuituse.h"
@@ -125,7 +127,7 @@ circuit_build_times_disabled_(const or_options_t *options,
ignore_consensus ? 0 : networkstatus_get_param(NULL, "cbtdisabled",
0, 0, 1);
int config_disabled = !options->LearnCircuitBuildTimeout;
- int dirauth_disabled = options->AuthoritativeDir;
+ int dirauth_disabled = authdir_mode(options);
int state_disabled = did_last_state_file_write_fail() ? 1 : 0;
/* LearnCircuitBuildTimeout and Tor2web/Single Onion Services are
* incompatible in two ways:
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index dc62e4d09c..8e007ce920 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -55,7 +55,6 @@
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
-#include "config.h"
static void circuit_expire_old_circuits_clientside(void);
static void circuit_increment_failure_count(void);
@@ -1632,7 +1631,7 @@ circuit_testing_opened(origin_circuit_t *circ)
router_perform_bandwidth_test(NUM_PARALLEL_TESTING_CIRCS, time(NULL));
have_performed_bandwidth_test = 1;
} else
- consider_testing_reachability(1, 0);
+ router_do_reachability_checks(1, 0);
}
/** A testing circuit has failed to build. Take whatever stats we want. */
@@ -2607,7 +2606,7 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ,
log_debug(LD_APP|LD_CIRC, "attaching new conn to circ. n_circ_id %u.",
(unsigned)circ->base_.n_circ_id);
/* reset it, so we can measure circ timeouts */
- ENTRY_TO_CONN(apconn)->timestamp_lastread = time(NULL);
+ ENTRY_TO_CONN(apconn)->timestamp_last_read_allowed = time(NULL);
ENTRY_TO_EDGE_CONN(apconn)->next_stream = circ->p_streams;
ENTRY_TO_EDGE_CONN(apconn)->on_circuit = TO_CIRCUIT(circ);
/* assert_connection_ok(conn, time(NULL)); */
@@ -3107,3 +3106,41 @@ mark_circuit_unusable_for_new_conns(origin_circuit_t *circ)
circ->unusable_for_new_conns = 1;
}
+/**
+ * Add relay_body_len and RELAY_PAYLOAD_SIZE-relay_body_len to
+ * the valid delivered written fields and the overhead field,
+ * respectively.
+ */
+void
+circuit_sent_valid_data(origin_circuit_t *circ, uint16_t relay_body_len)
+{
+ if (!circ) return;
+
+ tor_assert_nonfatal(relay_body_len <= RELAY_PAYLOAD_SIZE);
+
+ circ->n_delivered_written_circ_bw =
+ tor_add_u32_nowrap(circ->n_delivered_written_circ_bw, relay_body_len);
+ circ->n_overhead_written_circ_bw =
+ tor_add_u32_nowrap(circ->n_overhead_written_circ_bw,
+ RELAY_PAYLOAD_SIZE-relay_body_len);
+}
+
+/**
+ * Add relay_body_len and RELAY_PAYLOAD_SIZE-relay_body_len to
+ * the valid delivered read field and the overhead field,
+ * respectively.
+ */
+void
+circuit_read_valid_data(origin_circuit_t *circ, uint16_t relay_body_len)
+{
+ if (!circ) return;
+
+ tor_assert_nonfatal(relay_body_len <= RELAY_PAYLOAD_SIZE);
+
+ circ->n_delivered_read_circ_bw =
+ tor_add_u32_nowrap(circ->n_delivered_read_circ_bw, relay_body_len);
+ circ->n_overhead_read_circ_bw =
+ tor_add_u32_nowrap(circ->n_overhead_read_circ_bw,
+ RELAY_PAYLOAD_SIZE-relay_body_len);
+}
+
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index 71c818b978..6458bd6908 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -65,6 +65,8 @@ void mark_circuit_unusable_for_new_conns(origin_circuit_t *circ);
int circuit_purpose_is_hidden_service(uint8_t);
int circuit_should_use_vanguards(uint8_t);
+void circuit_sent_valid_data(origin_circuit_t *circ, uint16_t relay_body_len);
+void circuit_read_valid_data(origin_circuit_t *circ, uint16_t relay_body_len);
#ifdef TOR_UNIT_TESTS
/* Used only by circuituse.c and test_circuituse.c */
diff --git a/src/or/command.c b/src/or/command.c
index 7280be1396..39950f41bf 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -46,6 +46,7 @@
#include "config.h"
#include "control.h"
#include "cpuworker.h"
+#include "crypto_util.h"
#include "dos.h"
#include "hibernate.h"
#include "nodelist.h"
@@ -339,7 +340,9 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
return;
}
- if (connection_or_digest_is_known_relay(chan->identity_digest)) {
+ if (!channel_is_client(chan)) {
+ /* remember create types we've seen, but don't remember them from
+ * clients, to be extra conservative about client statistics. */
rep_hist_note_circuit_handshake_requested(create_cell->handshake_type);
}
@@ -493,6 +496,17 @@ command_process_relay_cell(cell_t *cell, channel_t *chan)
/* if we're a relay and treating connections with recent local
* traffic better, then this is one of them. */
channel_timestamp_client(chan);
+
+ /* Count all circuit bytes here for control port accuracy. We want
+ * to count even invalid/dropped relay cells, hence counting
+ * before the recognized check and the connection_edge_process_relay
+ * cell checks.
+ */
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+
+ /* Count the payload bytes only. We don't care about cell headers */
+ ocirc->n_read_circ_bw = tor_add_u32_nowrap(ocirc->n_read_circ_bw,
+ CELL_PAYLOAD_SIZE);
}
if (!CIRCUIT_IS_ORIGIN(circ) &&
diff --git a/src/or/config.c b/src/or/config.c
index 58080c65e3..53c19e3900 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -1,3 +1,4 @@
+
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
@@ -78,8 +79,9 @@
#include "control.h"
#include "confparse.h"
#include "cpuworker.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "dirserv.h"
-#include "dirvote.h"
#include "dns.h"
#include "dos.h"
#include "entrynodes.h"
@@ -104,12 +106,16 @@
#include "statefile.h"
#include "transports.h"
#include "ext_orport.h"
+#include "voting_schedule.h"
#ifdef _WIN32
#include <shlobj.h>
#endif
#include "procmon.h"
+#include "dirauth/dirvote.h"
+#include "dirauth/mode.h"
+
#ifdef HAVE_SYSTEMD
# if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__)
/* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse
@@ -126,6 +132,11 @@ static const char unix_socket_prefix[] = "unix:";
* configuration. */
static const char unix_q_socket_prefix[] = "unix:\"";
+/** macro to help with the bulk rename of *DownloadSchedule to
+ * *DowloadInitialDelay . */
+#define DOWNLOAD_SCHEDULE(name) \
+ { #name "DownloadSchedule", #name "DownloadInitialDelay", 0, 1 }
+
/** A list of abbreviations and aliases to map command-line options, obsolete
* option names, or alternative option names, to their current values. */
static config_abbrev_t option_abbrevs_[] = {
@@ -175,6 +186,16 @@ static config_abbrev_t option_abbrevs_[] = {
{ "_HSLayer2Nodes", "HSLayer2Nodes", 0, 1 },
{ "_HSLayer3Nodes", "HSLayer3Nodes", 0, 1 },
+ DOWNLOAD_SCHEDULE(ClientBootstrapConsensusAuthority),
+ DOWNLOAD_SCHEDULE(ClientBootstrapConsensusAuthorityOnly),
+ DOWNLOAD_SCHEDULE(ClientBootstrapConsensusFallback),
+ DOWNLOAD_SCHEDULE(TestingBridge),
+ DOWNLOAD_SCHEDULE(TestingBridgeBootstrap),
+ DOWNLOAD_SCHEDULE(TestingClient),
+ DOWNLOAD_SCHEDULE(TestingClientConsensus),
+ DOWNLOAD_SCHEDULE(TestingServer),
+ DOWNLOAD_SCHEDULE(TestingServerConsensus),
+
{ NULL, NULL, 0, 0},
};
@@ -259,7 +280,7 @@ static config_var_t option_vars_[] = {
V(BridgeRelay, BOOL, "0"),
V(BridgeDistribution, STRING, NULL),
VAR("CacheDirectory", FILENAME, CacheDirectory_option, NULL),
- V(CacheDirectoryGroupReadable, BOOL, "0"),
+ V(CacheDirectoryGroupReadable, AUTOBOOL, "auto"),
V(CellStatistics, BOOL, "0"),
V(PaddingStatistics, BOOL, "1"),
V(LearnCircuitBuildTimeout, BOOL, "1"),
@@ -267,7 +288,7 @@ static config_var_t option_vars_[] = {
OBSOLETE("CircuitIdleTimeout"),
V(CircuitsAvailableTimeout, INTERVAL, "0"),
V(CircuitStreamTimeout, INTERVAL, "0"),
- V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/
+ V(CircuitPriorityHalflife, DOUBLE, "-1.0"), /*negative:'Use default'*/
V(ClientDNSRejectInternalAddresses, BOOL,"1"),
V(ClientOnly, BOOL, "0"),
V(ClientPreferIPv6ORPort, AUTOBOOL, "auto"),
@@ -337,7 +358,7 @@ static config_var_t option_vars_[] = {
V(DownloadExtraInfo, BOOL, "0"),
V(TestingEnableConnBwEvent, BOOL, "0"),
V(TestingEnableCellStatsEvent, BOOL, "0"),
- V(TestingEnableTbEmptyEvent, BOOL, "0"),
+ OBSOLETE("TestingEnableTbEmptyEvent"),
V(EnforceDistinctSubnets, BOOL, "1"),
V(EntryNodes, ROUTERSET, NULL),
V(EntryStatistics, BOOL, "0"),
@@ -457,6 +478,7 @@ static config_var_t option_vars_[] = {
V(NumCPUs, UINT, "0"),
V(NumDirectoryGuards, UINT, "0"),
V(NumEntryGuards, UINT, "0"),
+ V(NumPrimaryGuards, UINT, "0"),
V(OfflineMasterKey, BOOL, "0"),
OBSOLETE("ORListenAddress"),
VPORT(ORPort),
@@ -495,8 +517,8 @@ static config_var_t option_vars_[] = {
V(TestingSigningKeySlop, INTERVAL, "1 day"),
V(OptimisticData, AUTOBOOL, "auto"),
- V(PortForwarding, BOOL, "0"),
- V(PortForwardingHelper, FILENAME, "tor-fw-helper"),
+ OBSOLETE("PortForwarding"),
+ OBSOLETE("PortForwardingHelper"),
OBSOLETE("PreferTunneledDirConns"),
V(ProtocolWarnings, BOOL, "0"),
V(PublishServerDescriptor, CSV, "1"),
@@ -599,16 +621,10 @@ static config_var_t option_vars_[] = {
VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL),
VAR("__OwningControllerFD",INT,OwningControllerFD, "-1"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"),
- V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, "
- "300, 900, 2147483647"),
- V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 60, 300, 600, "
- "2147483647"),
- V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, "
- "300, 600, 1800, 1800, 1800, 1800, "
- "1800, 3600, 7200"),
- V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, "
- "300, 600, 1800, 3600, 3600, 3600, "
- "10800, 21600, 43200"),
+ V(TestingServerDownloadInitialDelay, CSV_INTERVAL, "0"),
+ V(TestingClientDownloadInitialDelay, CSV_INTERVAL, "0"),
+ V(TestingServerConsensusDownloadInitialDelay, CSV_INTERVAL, "0"),
+ V(TestingClientConsensusDownloadInitialDelay, CSV_INTERVAL, "0"),
/* With the ClientBootstrapConsensus*Download* below:
* Clients with only authorities will try:
* - at least 3 authorities over 10 seconds, then exponentially backoff,
@@ -624,13 +640,11 @@ static config_var_t option_vars_[] = {
*
* When clients have authorities and fallbacks available, they use these
* schedules: (we stagger the times to avoid thundering herds) */
- V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
- "6, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */),
- V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
- "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"),
+ V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "6"),
+ V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"),
/* When clients only have authorities available, they use this schedule: */
- V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL,
- "0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800"),
+ V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL,
+ "0"),
/* We don't want to overwhelm slow networks (or mirrors whose replies are
* blocked), but we also don't want to fail if only some mirrors are
* blackholed. Clients will try 3 directories simultaneously.
@@ -638,14 +652,12 @@ static config_var_t option_vars_[] = {
V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"),
/* 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"),
+ V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL,"10800"),
/* 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(TestingBridgeBootstrapDownloadInitialDelay, CSV_INTERVAL, "0"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
OBSOLETE("TestingConsensusMaxDownloadTries"),
@@ -672,12 +684,10 @@ static const config_var_t testing_tor_network_defaults[] = {
V(EnforceDistinctSubnets, BOOL, "0"),
V(AssumeReachable, BOOL, "1"),
V(AuthDirMaxServersPerAddr, UINT, "0"),
- V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
- "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
- V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
- "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
- V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL,
- "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
+ V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "0"),
+ V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"),
+ V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL,
+ "0"),
V(ClientDNSRejectInternalAddresses, BOOL,"0"),
V(ClientRejectInternalAddresses, BOOL, "0"),
V(CountPrivateBandwidth, BOOL, "1"),
@@ -692,22 +702,16 @@ static const config_var_t testing_tor_network_defaults[] = {
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"),
V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
- V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 5, 10, 15, "
- "20, 30, 60"),
- V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, 15, 20, "
- "30, 60"),
- V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
- "15, 20, 30, 60"),
- V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
- "15, 20, 30, 60"),
- V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "10, 30, 60"),
- V(TestingBridgeBootstrapDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
- "15, 20, 30, 60"),
+ V(TestingServerDownloadInitialDelay, CSV_INTERVAL, "0"),
+ V(TestingClientDownloadInitialDelay, CSV_INTERVAL, "0"),
+ V(TestingServerConsensusDownloadInitialDelay, CSV_INTERVAL, "0"),
+ V(TestingClientConsensusDownloadInitialDelay, CSV_INTERVAL, "0"),
+ V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL, "10"),
+ V(TestingBridgeBootstrapDownloadInitialDelay, CSV_INTERVAL, "0"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
V(TestingEnableConnBwEvent, BOOL, "1"),
V(TestingEnableCellStatsEvent, BOOL, "1"),
- V(TestingEnableTbEmptyEvent, BOOL, "1"),
VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"),
V(RendPostPeriod, INTERVAL, "2 minutes"),
@@ -747,6 +751,8 @@ static int options_transition_affects_workers(
const or_options_t *old_options, const or_options_t *new_options);
static int options_transition_affects_descriptor(
const or_options_t *old_options, const or_options_t *new_options);
+static int options_transition_affects_dirauth_timing(
+ const or_options_t *old_options, const or_options_t *new_options);
static int normalize_nickname_list(config_line_t **normalized_out,
const config_line_t *lst, const char *name,
char **msg);
@@ -905,8 +911,13 @@ set_options(or_options_t *new_val, char **msg)
smartlist_free(elements);
}
- if (old_options != global_options)
+ if (old_options != global_options) {
or_options_free(old_options);
+ /* If we are here it means we've successfully applied the new options and
+ * that the global options have been changed to the new values. We'll
+ * check if we need to remove or add periodic events. */
+ periodic_events_on_new_options(global_options);
+ }
return 0;
}
@@ -1439,9 +1450,9 @@ options_act_reversible(const or_options_t *old_options, char **msg)
consider_hibernation(time(NULL));
/* Launch the listeners. (We do this before we setuid, so we can bind to
- * ports under 1024.) We don't want to rebind if we're hibernating. If
- * networking is disabled, this will close all but the control listeners,
- * but disable those. */
+ * ports under 1024.) We don't want to rebind if we're hibernating or
+ * shutting down. If networking is disabled, this will close all but the
+ * control listeners, but disable those. */
if (!we_are_hibernating()) {
if (retry_all_listeners(replaced_listeners, new_listeners,
options->DisableNetwork) < 0) {
@@ -1516,9 +1527,26 @@ options_act_reversible(const or_options_t *old_options, char **msg)
msg) < 0) {
goto done;
}
+
+ /* We need to handle the group-readable flag for the cache directory
+ * specially, since the directory defaults to being the same as the
+ * DataDirectory. */
+ int cache_dir_group_readable;
+ if (options->CacheDirectoryGroupReadable != -1) {
+ /* If the user specified a value, use their setting */
+ cache_dir_group_readable = options->CacheDirectoryGroupReadable;
+ } else if (!strcmp(options->CacheDirectory, options->DataDirectory)) {
+ /* If the user left the value as "auto", and the cache is the same as the
+ * datadirectory, use the datadirectory setting.
+ */
+ cache_dir_group_readable = options->DataDirectoryGroupReadable;
+ } else {
+ /* Otherwise, "auto" means "not group readable". */
+ cache_dir_group_readable = 0;
+ }
if (check_and_create_data_directory(running_tor /* create */,
options->CacheDirectory,
- options->CacheDirectoryGroupReadable,
+ cache_dir_group_readable,
options->User,
msg) < 0) {
goto done;
@@ -1545,6 +1573,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
tor_malloc_zero(sizeof(log_severity_list_t));
close_temp_logs();
add_callback_log(severity, control_event_logmsg);
+ logs_set_pending_callback_callback(control_event_logmsg_pending);
control_adjust_event_log_severity();
tor_free(severity);
tor_log_update_sigsafe_err_fds();
@@ -1652,8 +1681,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
int
options_need_geoip_info(const or_options_t *options, const char **reason_out)
{
- int bridge_usage =
- options->BridgeRelay && options->BridgeRecordUsageByCountry;
+ int bridge_usage = should_record_bridge_info(options);
int routerset_usage =
routerset_needs_geoip(options->EntryNodes) ||
routerset_needs_geoip(options->ExitNodes) ||
@@ -1743,6 +1771,32 @@ options_transition_affects_guards(const or_options_t *old_options,
return 0;
}
+/**
+ * Return true if changing the configuration from <b>old</b> to <b>new</b>
+ * affects the timing of the voting subsystem
+ */
+static int
+options_transition_affects_dirauth_timing(const or_options_t *old_options,
+ const or_options_t *new_options)
+{
+ tor_assert(old_options);
+ tor_assert(new_options);
+
+ if (authdir_mode_v3(old_options) != authdir_mode_v3(new_options))
+ return 1;
+ if (! authdir_mode_v3(new_options))
+ return 0;
+ YES_IF_CHANGED_INT(V3AuthVotingInterval);
+ YES_IF_CHANGED_INT(V3AuthVoteDelay);
+ YES_IF_CHANGED_INT(V3AuthDistDelay);
+ YES_IF_CHANGED_INT(TestingV3AuthInitialVotingInterval);
+ YES_IF_CHANGED_INT(TestingV3AuthInitialVoteDelay);
+ YES_IF_CHANGED_INT(TestingV3AuthInitialDistDelay);
+ YES_IF_CHANGED_INT(TestingV3AuthVotingStartOffset);
+
+ return 0;
+}
+
/** Fetch the active option list, and take actions based on it. All of the
* things we do should survive being done repeatedly. If present,
* <b>old_options</b> contains the previous value of the options.
@@ -1761,7 +1815,6 @@ options_act(const or_options_t *old_options)
char *msg=NULL;
const int transition_affects_workers =
old_options && options_transition_affects_workers(old_options, options);
- int old_ewma_enabled;
const int transition_affects_guards =
old_options && options_transition_affects_guards(old_options, options);
@@ -1966,6 +2019,9 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
+ /* See whether we need to enable/disable our once-a-second timer. */
+ reschedule_per_second_timer();
+
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||
@@ -2035,16 +2091,8 @@ options_act(const or_options_t *old_options)
if (accounting_is_enabled(options))
configure_accounting(time(NULL));
- old_ewma_enabled = cell_ewma_enabled();
/* Change the cell EWMA settings */
- cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus());
- /* If we just enabled ewma, set the cmux policy on all active channels */
- if (cell_ewma_enabled() && !old_ewma_enabled) {
- channel_set_cmux_policy_everywhere(&ewma_policy);
- } else if (!cell_ewma_enabled() && old_ewma_enabled) {
- /* Turn it off everywhere */
- channel_set_cmux_policy_everywhere(NULL);
- }
+ cmux_ewma_set_options(options, networkstatus_get_latest_consensus());
/* Update the BridgePassword's hashed version as needed. We store this as a
* digest so that we can do side-channel-proof comparisons on it.
@@ -2207,6 +2255,12 @@ options_act(const or_options_t *old_options)
options->PerConnBWBurst != old_options->PerConnBWBurst)
connection_or_update_token_buckets(get_connection_array(), options);
+ if (options->BandwidthRate != old_options->BandwidthRate ||
+ options->BandwidthBurst != old_options->BandwidthBurst ||
+ options->RelayBandwidthRate != old_options->RelayBandwidthRate ||
+ options->RelayBandwidthBurst != old_options->RelayBandwidthBurst)
+ connection_bucket_adjust(options);
+
if (options->MainloopStats != old_options->MainloopStats) {
reset_main_loop_counters();
}
@@ -2256,6 +2310,11 @@ options_act(const or_options_t *old_options)
}
if ((!old_options || !old_options->EntryStatistics) &&
options->EntryStatistics && !should_record_bridge_info(options)) {
+ /* If we get here, we've started recording bridge info when we didn't
+ * do so before. Note that "should_record_bridge_info()" will
+ * always be false at this point, because of the earlier block
+ * that cleared EntryStatistics when public_server_mode() was false.
+ * We're leaving it in as defensive programming. */
if (geoip_is_loaded(AF_INET) || geoip_is_loaded(AF_INET6)) {
geoip_entry_stats_init(now);
print_notice = 1;
@@ -2325,8 +2384,10 @@ options_act(const or_options_t *old_options)
/* We may need to reschedule some directory stuff if our status changed. */
if (old_options) {
- if (authdir_mode_v3(options) && !authdir_mode_v3(old_options))
- dirvote_recalculate_timing(options, time(NULL));
+ if (options_transition_affects_dirauth_timing(old_options, options)) {
+ voting_schedule_recalculate_timing(options, time(NULL));
+ reschedule_dirvote(options);
+ }
if (!bool_eq(directory_fetches_dir_info_early(options),
directory_fetches_dir_info_early(old_options)) ||
!bool_eq(directory_fetches_dir_info_later(options),
@@ -3474,12 +3535,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Versioning authoritative dir servers must set "
"Recommended*Versions.");
+#ifdef HAVE_MODULE_DIRAUTH
char *t;
/* Call these functions to produce warnings only. */
t = format_recommended_version_list(options->RecommendedClientVersions, 1);
tor_free(t);
t = format_recommended_version_list(options->RecommendedServerVersions, 1);
tor_free(t);
+#endif
if (options->UseEntryGuards) {
log_info(LD_CONFIG, "Authoritative directory servers can't set "
@@ -3779,6 +3842,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
"http://freehaven.net/anonbib/#hs-attack06 for details.");
}
+ if (options->NumPrimaryGuards && options->NumEntryGuards &&
+ options->NumEntryGuards > options->NumPrimaryGuards) {
+ REJECT("NumEntryGuards must not be greater than NumPrimaryGuards.");
+ }
+
if (options->EntryNodes &&
routerset_is_list(options->EntryNodes) &&
(routerset_len(options->EntryNodes) == 1) &&
@@ -3901,15 +3969,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->KeepalivePeriod < 1)
REJECT("KeepalivePeriod option must be positive.");
- if (options->PortForwarding && options->Sandbox) {
- REJECT("PortForwarding is not compatible with Sandbox; at most one can "
- "be set");
- }
- if (options->PortForwarding && options->NoExec) {
- COMPLAIN("Both PortForwarding and NoExec are set; PortForwarding will "
- "be ignored.");
- }
-
if (ensure_bandwidth_cap(&options->BandwidthRate,
"BandwidthRate", msg) < 0)
return -1;
@@ -4386,12 +4445,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
CHECK_DEFAULT(TestingV3AuthVotingStartOffset);
CHECK_DEFAULT(TestingAuthDirTimeToLearnReachability);
CHECK_DEFAULT(TestingEstimatedDescriptorPropagationTime);
- CHECK_DEFAULT(TestingServerDownloadSchedule);
- CHECK_DEFAULT(TestingClientDownloadSchedule);
- CHECK_DEFAULT(TestingServerConsensusDownloadSchedule);
- CHECK_DEFAULT(TestingClientConsensusDownloadSchedule);
- CHECK_DEFAULT(TestingBridgeDownloadSchedule);
- CHECK_DEFAULT(TestingBridgeBootstrapDownloadSchedule);
+ CHECK_DEFAULT(TestingServerDownloadInitialDelay);
+ CHECK_DEFAULT(TestingClientDownloadInitialDelay);
+ CHECK_DEFAULT(TestingServerConsensusDownloadInitialDelay);
+ CHECK_DEFAULT(TestingClientConsensusDownloadInitialDelay);
+ CHECK_DEFAULT(TestingBridgeDownloadInitialDelay);
+ CHECK_DEFAULT(TestingBridgeBootstrapDownloadInitialDelay);
CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest);
CHECK_DEFAULT(TestingDirConnectionMaxStall);
CHECK_DEFAULT(TestingAuthKeyLifetime);
@@ -4489,12 +4548,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
"Tor networks!");
}
- if (options->TestingEnableTbEmptyEvent &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
- REJECT("TestingEnableTbEmptyEvent may only be changed in testing "
- "Tor networks!");
- }
-
if (options->TestingTorNetwork) {
log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node "
"almost unusable in the public Tor network, and is "
@@ -4638,15 +4691,14 @@ have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem,
if (options->DirCache) {
if (total_mem < DIRCACHE_MIN_MEM_BYTES) {
if (options->BridgeRelay) {
- *msg = tor_strdup("Running a Bridge with less than "
- STRINGIFY(DIRCACHE_MIN_MEM_MB) " MB of memory is not "
- "recommended.");
+ tor_asprintf(msg, "Running a Bridge with less than %d MB of memory "
+ "is not recommended.", DIRCACHE_MIN_MEM_MB);
} else {
- *msg = tor_strdup("Being a directory cache (default) with less than "
- STRINGIFY(DIRCACHE_MIN_MEM_MB) " MB of memory is not "
- "recommended and may consume most of the available "
- "resources, consider disabling this functionality by "
- "setting the DirCache option to 0.");
+ tor_asprintf(msg, "Being a directory cache (default) with less than "
+ "%d MB of memory is not recommended and may consume "
+ "most of the available resources. Consider disabling "
+ "this functionality by setting the DirCache option "
+ "to 0.", DIRCACHE_MIN_MEM_MB);
}
}
} else {
@@ -8121,7 +8173,10 @@ getinfo_helper_config(control_connection_t *conn,
case CONFIG_TYPE_ISOTIME: type = "Time"; break;
case CONFIG_TYPE_ROUTERSET: type = "RouterList"; break;
case CONFIG_TYPE_CSV: type = "CommaList"; break;
- case CONFIG_TYPE_CSV_INTERVAL: type = "TimeIntervalCommaList"; break;
+ /* This type accepts more inputs than TimeInterval, but it ignores
+ * everything after the first entry, so we may as well pretend
+ * it's a TimeInterval. */
+ case CONFIG_TYPE_CSV_INTERVAL: type = "TimeInterval"; break;
case CONFIG_TYPE_LINELIST: type = "LineList"; break;
case CONFIG_TYPE_LINELIST_S: type = "Dependent"; break;
case CONFIG_TYPE_LINELIST_V: type = "Virtual"; break;
@@ -8408,3 +8463,17 @@ init_cookie_authentication(const char *fname, const char *header,
tor_free(cookie_file_str);
return retval;
}
+
+/**
+ * Return true if any option is set in <b>options</b> to make us behave
+ * as a client.
+ */
+int
+options_any_client_port_set(const or_options_t *options)
+{
+ return (options->SocksPort_set ||
+ options->TransPort_set ||
+ options->NATDPort_set ||
+ options->DNSPort_set ||
+ options->HTTPTunnelPort_set);
+}
diff --git a/src/or/config.h b/src/or/config.h
index 1d3c27217e..4b41274434 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -214,6 +214,9 @@ smartlist_t *get_options_from_transport_options_line(const char *line,
const char *transport);
smartlist_t *get_options_for_server_transport(const char *transport);
+/* Port helper functions. */
+int options_any_client_port_set(const or_options_t *options);
+
#ifdef CONFIG_PRIVATE
#define CL_PORT_NO_STREAM_OPTIONS (1u<<0)
diff --git a/src/or/confparse.c b/src/or/confparse.c
index 64ed9ee6bb..6bab790945 100644
--- a/src/or/confparse.c
+++ b/src/or/confparse.c
@@ -162,8 +162,6 @@ config_assign_value(const config_format_t *fmt, void *options,
int i, ok;
const config_var_t *var;
void *lvalue;
- int *csv_int;
- smartlist_t *csv_str;
CONFIG_CHECK(fmt, options);
@@ -195,6 +193,30 @@ config_assign_value(const config_format_t *fmt, void *options,
*(int *)lvalue = i;
break;
+ case CONFIG_TYPE_CSV_INTERVAL: {
+ /* We used to have entire smartlists here. But now that all of our
+ * download schedules use exponential backoff, only the first part
+ * matters. */
+ const char *comma = strchr(c->value, ',');
+ const char *val = c->value;
+ char *tmp = NULL;
+ if (comma) {
+ tmp = tor_strndup(c->value, comma - c->value);
+ val = tmp;
+ }
+
+ i = config_parse_interval(val, &ok);
+ if (!ok) {
+ tor_asprintf(msg,
+ "Interval '%s %s' is malformed or out of bounds.",
+ c->key, c->value);
+ return -1;
+ }
+ *(int *)lvalue = i;
+ tor_free(tmp);
+ break;
+ }
+
case CONFIG_TYPE_INTERVAL: {
i = config_parse_interval(c->value, &ok);
if (!ok) {
@@ -298,36 +320,6 @@ config_assign_value(const config_format_t *fmt, void *options,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
break;
- case CONFIG_TYPE_CSV_INTERVAL:
- if (*(smartlist_t**)lvalue) {
- SMARTLIST_FOREACH(*(smartlist_t**)lvalue, int *, cp, tor_free(cp));
- smartlist_clear(*(smartlist_t**)lvalue);
- } else {
- *(smartlist_t**)lvalue = smartlist_new();
- }
- csv_str = smartlist_new();
- smartlist_split_string(csv_str, c->value, ",",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH_BEGIN(csv_str, char *, str)
- {
- i = config_parse_interval(str, &ok);
- if (!ok) {
- tor_asprintf(msg,
- "Interval in '%s %s' is malformed or out of bounds.",
- c->key, c->value);
- SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp));
- smartlist_free(csv_str);
- return -1;
- }
- csv_int = tor_malloc_zero(sizeof(int));
- *csv_int = i;
- smartlist_add(*(smartlist_t**)lvalue, csv_int);
- }
- SMARTLIST_FOREACH_END(str);
- SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp));
- smartlist_free(csv_str);
- break;
-
case CONFIG_TYPE_LINELIST:
case CONFIG_TYPE_LINELIST_S:
{
@@ -528,7 +520,6 @@ config_get_assigned_option(const config_format_t *fmt, const void *options,
const config_var_t *var;
const void *value;
config_line_t *result;
- smartlist_t *csv_str;
tor_assert(options && key);
CONFIG_CHECK(fmt, options);
@@ -571,6 +562,7 @@ config_get_assigned_option(const config_format_t *fmt, const void *options,
break;
}
/* fall through */
+ case CONFIG_TYPE_CSV_INTERVAL:
case CONFIG_TYPE_INTERVAL:
case CONFIG_TYPE_MSEC_INTERVAL:
case CONFIG_TYPE_UINT:
@@ -611,20 +603,6 @@ config_get_assigned_option(const config_format_t *fmt, const void *options,
else
result->value = tor_strdup("");
break;
- case CONFIG_TYPE_CSV_INTERVAL:
- if (*(smartlist_t**)value) {
- csv_str = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(*(smartlist_t**)value, int *, i)
- {
- smartlist_add_asprintf(csv_str, "%d", *i);
- }
- SMARTLIST_FOREACH_END(i);
- result->value = smartlist_join_strings(csv_str, ",", 0, NULL);
- SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp));
- smartlist_free(csv_str);
- } else
- result->value = tor_strdup("");
- break;
case CONFIG_TYPE_OBSOLETE:
log_fn(LOG_INFO, LD_CONFIG,
"You asked me for the value of an obsolete config option '%s'.",
@@ -789,6 +767,7 @@ config_clear(const config_format_t *fmt, void *options,
case CONFIG_TYPE_ISOTIME:
*(time_t*)lvalue = 0;
break;
+ case CONFIG_TYPE_CSV_INTERVAL:
case CONFIG_TYPE_INTERVAL:
case CONFIG_TYPE_MSEC_INTERVAL:
case CONFIG_TYPE_UINT:
@@ -816,13 +795,6 @@ config_clear(const config_format_t *fmt, void *options,
*(smartlist_t **)lvalue = NULL;
}
break;
- case CONFIG_TYPE_CSV_INTERVAL:
- if (*(smartlist_t**)lvalue) {
- SMARTLIST_FOREACH(*(smartlist_t **)lvalue, int *, cp, tor_free(cp));
- smartlist_free(*(smartlist_t **)lvalue);
- *(smartlist_t **)lvalue = NULL;
- }
- break;
case CONFIG_TYPE_LINELIST:
case CONFIG_TYPE_LINELIST_S:
config_free_lines(*(config_line_t **)lvalue);
diff --git a/src/or/confparse.h b/src/or/confparse.h
index f1f2030343..4b4bf0adb4 100644
--- a/src/or/confparse.h
+++ b/src/or/confparse.h
@@ -28,7 +28,9 @@ typedef enum config_type_t {
* optional whitespace. */
CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and
* optional whitespace, representing intervals in
- * seconds, with optional units */
+ * seconds, with optional units. We allow
+ * multiple values here for legacy reasons, but
+ * ignore every value after the first. */
CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */
CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines,
* mixed with other keywords. */
@@ -62,7 +64,7 @@ typedef union {
int *AUTOBOOL;
time_t *ISOTIME;
smartlist_t **CSV;
- smartlist_t **CSV_INTERVAL;
+ int *CSV_INTERVAL;
config_line_t **LINELIST;
config_line_t **LINELIST_S;
config_line_t **LINELIST_V;
diff --git a/src/or/connection.c b/src/or/connection.c
index 2a6b10763e..0a2a635096 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -1,4 +1,4 @@
- /* Copyright (c) 2001 Matej Pfajfar.
+/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2017, The Tor Project, Inc. */
@@ -76,6 +76,7 @@
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
+#include "crypto_util.h"
#include "directory.h"
#include "dirserv.h"
#include "dns.h"
@@ -85,6 +86,7 @@
#include "ext_orport.h"
#include "geoip.h"
#include "main.h"
+#include "hibernate.h"
#include "hs_common.h"
#include "hs_ident.h"
#include "nodelist.h"
@@ -101,7 +103,6 @@
#include "transports.h"
#include "routerparse.h"
#include "sandbox.h"
-#include "transports.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
@@ -120,8 +121,6 @@ static connection_t *connection_listener_new(
static void connection_init(time_t now, connection_t *conn, int type,
int socket_family);
static int connection_handle_listener_read(connection_t *conn, int new_type);
-static int connection_bucket_should_increase(int bucket,
- or_connection_t *conn);
static int connection_finished_flushing(connection_t *conn);
static int connection_flushed_some(connection_t *conn);
static int connection_finished_connecting(connection_t *conn);
@@ -140,6 +139,8 @@ static const char *proxy_type_to_string(int proxy_type);
static int get_proxy_type(void);
const tor_addr_t *conn_get_outbound_address(sa_family_t family,
const or_options_t *options, unsigned int conn_type);
+static void reenable_blocked_connection_init(const or_options_t *options);
+static void reenable_blocked_connection_schedule(void);
/** The last addresses that our network interface seemed to have been
* binding to. We use this as one way to detect when our IP changes.
@@ -460,8 +461,8 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family)
}
conn->timestamp_created = now;
- conn->timestamp_lastread = now;
- conn->timestamp_lastwritten = now;
+ conn->timestamp_last_read_allowed = now;
+ conn->timestamp_last_write_allowed = now;
}
/** Create a link between <b>conn_a</b> and <b>conn_b</b>. */
@@ -775,8 +776,8 @@ connection_close_immediate(connection_t *conn)
connection_unregister_events(conn);
/* Prevent the event from getting unblocked. */
- conn->read_blocked_on_bw =
- conn->write_blocked_on_bw = 0;
+ conn->read_blocked_on_bw = 0;
+ conn->write_blocked_on_bw = 0;
if (SOCKET_OK(conn->s))
tor_close_socket(conn->s);
@@ -859,7 +860,7 @@ connection_mark_for_close_internal_, (connection_t *conn,
/* in case we're going to be held-open-til-flushed, reset
* the number of seconds since last successful write, so
* we get our whole 15 seconds */
- conn->timestamp_lastwritten = time(NULL);
+ conn->timestamp_last_write_allowed = time(NULL);
}
/** Find each connection that has hold_open_until_flushed set to
@@ -881,7 +882,7 @@ connection_expire_held_open(void)
*/
if (conn->hold_open_until_flushed) {
tor_assert(conn->marked_for_close);
- if (now - conn->timestamp_lastwritten >= 15) {
+ if (now - conn->timestamp_last_write_allowed >= 15) {
int severity;
if (conn->type == CONN_TYPE_EXIT ||
(conn->type == CONN_TYPE_DIR &&
@@ -1764,13 +1765,13 @@ connection_connect_sockaddr,(connection_t *conn,
tor_assert(sa);
tor_assert(socket_error);
- if (get_options()->DisableNetwork) {
- /* We should never even try to connect anyplace if DisableNetwork is set.
- * Warn if we do, and refuse to make the connection.
+ if (net_is_completely_disabled()) {
+ /* We should never even try to connect anyplace if the network is
+ * completely shut off.
*
- * We only check DisableNetwork here, not we_are_hibernating(), since
- * we'll still try to fulfill client requests sometimes in the latter case
- * (if it is soft hibernation) */
+ * (We don't check net_is_disabled() here, since we still sometimes
+ * want to open connections when we're in soft hibernation.)
+ */
static ratelim_t disablenet_violated = RATELIM_INIT(30*60);
*socket_error = SOCK_ERRNO(ENETUNREACH);
log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG,
@@ -2820,10 +2821,10 @@ connection_is_rate_limited(connection_t *conn)
return 1;
}
-/** Did either global write bucket run dry last second? If so,
- * we are likely to run dry again this second, so be stingy with the
- * tokens we just put in. */
-static int write_buckets_empty_last_second = 0;
+/** When was either global write bucket last empty? If this was recent, then
+ * we're probably low on bandwidth, and we should be stingy with our bandwidth
+ * usage. */
+static time_t write_buckets_last_empty_at = -100;
/** How many seconds of no active local circuits will make the
* connection revert to the "relayed" bandwidth class? */
@@ -2851,25 +2852,25 @@ connection_counts_as_relayed_traffic(connection_t *conn, time_t now)
* write many of them or just a few; and <b>conn_bucket</b> (if
* non-negative) provides an upper limit for our answer. */
static ssize_t
-connection_bucket_round_robin(int base, int priority,
- ssize_t global_bucket, ssize_t conn_bucket)
+connection_bucket_get_share(int base, int priority,
+ ssize_t global_bucket_val, ssize_t conn_bucket)
{
ssize_t at_most;
ssize_t num_bytes_high = (priority ? 32 : 16) * base;
ssize_t num_bytes_low = (priority ? 4 : 2) * base;
- /* Do a rudimentary round-robin so one circuit can't hog a connection.
+ /* Do a rudimentary limiting so one circuit can't hog a connection.
* Pick at most 32 cells, at least 4 cells if possible, and if we're in
* the middle pick 1/8 of the available bandwidth. */
- at_most = global_bucket / 8;
+ at_most = global_bucket_val / 8;
at_most -= (at_most % base); /* round down */
if (at_most > num_bytes_high) /* 16 KB, or 8 KB for low-priority */
at_most = num_bytes_high;
else if (at_most < num_bytes_low) /* 2 KB, or 1 KB for low-priority */
at_most = num_bytes_low;
- if (at_most > global_bucket)
- at_most = global_bucket;
+ if (at_most > global_bucket_val)
+ at_most = global_bucket_val;
if (conn_bucket >= 0 && at_most > conn_bucket)
at_most = conn_bucket;
@@ -2885,13 +2886,13 @@ connection_bucket_read_limit(connection_t *conn, time_t now)
{
int base = RELAY_PAYLOAD_SIZE;
int priority = conn->type != CONN_TYPE_DIR;
- int conn_bucket = -1;
- int global_bucket = global_read_bucket;
+ ssize_t conn_bucket = -1;
+ size_t global_bucket_val = token_bucket_rw_get_read(&global_bucket);
if (connection_speaks_cells(conn)) {
or_connection_t *or_conn = TO_OR_CONN(conn);
if (conn->state == OR_CONN_STATE_OPEN)
- conn_bucket = or_conn->read_bucket;
+ conn_bucket = token_bucket_rw_get_read(&or_conn->bucket);
base = get_cell_network_size(or_conn->wide_circ_ids);
}
@@ -2900,12 +2901,13 @@ connection_bucket_read_limit(connection_t *conn, time_t now)
return conn_bucket>=0 ? conn_bucket : 1<<14;
}
- if (connection_counts_as_relayed_traffic(conn, now) &&
- global_relayed_read_bucket <= global_read_bucket)
- global_bucket = global_relayed_read_bucket;
+ if (connection_counts_as_relayed_traffic(conn, now)) {
+ size_t relayed = token_bucket_rw_get_read(&global_relayed_bucket);
+ global_bucket_val = MIN(global_bucket_val, relayed);
+ }
- return connection_bucket_round_robin(base, priority,
- global_bucket, conn_bucket);
+ return connection_bucket_get_share(base, priority,
+ global_bucket_val, conn_bucket);
}
/** How many bytes at most can we write onto this connection? */
@@ -2914,8 +2916,8 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
{
int base = RELAY_PAYLOAD_SIZE;
int priority = conn->type != CONN_TYPE_DIR;
- int conn_bucket = (int)conn->outbuf_flushlen;
- int global_bucket = global_write_bucket;
+ size_t conn_bucket = conn->outbuf_flushlen;
+ size_t global_bucket_val = token_bucket_rw_get_write(&global_bucket);
if (!connection_is_rate_limited(conn)) {
/* be willing to write to local conns even if our buckets are empty */
@@ -2923,22 +2925,21 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
}
if (connection_speaks_cells(conn)) {
- /* use the per-conn write limit if it's lower, but if it's less
- * than zero just use zero */
+ /* use the per-conn write limit if it's lower */
or_connection_t *or_conn = TO_OR_CONN(conn);
if (conn->state == OR_CONN_STATE_OPEN)
- if (or_conn->write_bucket < conn_bucket)
- conn_bucket = or_conn->write_bucket >= 0 ?
- or_conn->write_bucket : 0;
+ conn_bucket = MIN(conn_bucket,
+ token_bucket_rw_get_write(&or_conn->bucket));
base = get_cell_network_size(or_conn->wide_circ_ids);
}
- if (connection_counts_as_relayed_traffic(conn, now) &&
- global_relayed_write_bucket <= global_write_bucket)
- global_bucket = global_relayed_write_bucket;
+ if (connection_counts_as_relayed_traffic(conn, now)) {
+ size_t relayed = token_bucket_rw_get_write(&global_relayed_bucket);
+ global_bucket_val = MIN(global_bucket_val, relayed);
+ }
- return connection_bucket_round_robin(base, priority,
- global_bucket, conn_bucket);
+ return connection_bucket_get_share(base, priority,
+ global_bucket_val, conn_bucket);
}
/** Return 1 if the global write buckets are low enough that we
@@ -2963,27 +2964,31 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
int
global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
{
- int smaller_bucket = global_write_bucket < global_relayed_write_bucket ?
- global_write_bucket : global_relayed_write_bucket;
+ size_t smaller_bucket =
+ MIN(token_bucket_rw_get_write(&global_bucket),
+ token_bucket_rw_get_write(&global_relayed_bucket));
if (authdir_mode(get_options()) && priority>1)
return 0; /* there's always room to answer v2 if we're an auth dir */
if (!connection_is_rate_limited(conn))
return 0; /* local conns don't get limited */
- if (smaller_bucket < (int)attempt)
+ if (smaller_bucket < attempt)
return 1; /* not enough space no matter the priority */
- if (write_buckets_empty_last_second)
- return 1; /* we're already hitting our limits, no more please */
+ {
+ const time_t diff = approx_time() - write_buckets_last_empty_at;
+ if (diff <= 1)
+ return 1; /* we're already hitting our limits, no more please */
+ }
if (priority == 1) { /* old-style v1 query */
/* Could we handle *two* of these requests within the next two seconds? */
const or_options_t *options = get_options();
- int64_t can_write = (int64_t)smaller_bucket
+ size_t can_write = (size_t) (smaller_bucket
+ 2*(options->RelayBandwidthRate ? options->RelayBandwidthRate :
- options->BandwidthRate);
- if (can_write < 2*(int64_t)attempt)
+ options->BandwidthRate));
+ if (can_write < 2*attempt)
return 1;
} else { /* v2 query */
/* no further constraints yet */
@@ -2991,6 +2996,10 @@ global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
return 0;
}
+/** When did we last tell the accounting subsystem about transmitted
+ * bandwidth? */
+static time_t last_recorded_accounting_at = 0;
+
/** Helper: adjusts our bandwidth history and informs the controller as
* appropriate, given that we have just read <b>num_read</b> bytes and written
* <b>num_written</b> bytes on <b>conn</b>. */
@@ -3021,59 +3030,22 @@ record_num_bytes_transferred_impl(connection_t *conn,
}
if (conn->type == CONN_TYPE_EXIT)
rep_hist_note_exit_bytes(conn->port, num_written, num_read);
-}
-/** Helper: convert given <b>tvnow</b> time value to milliseconds since
- * midnight. */
-static uint32_t
-msec_since_midnight(const struct timeval *tvnow)
-{
- return (uint32_t)(((tvnow->tv_sec % 86400L) * 1000L) +
- ((uint32_t)tvnow->tv_usec / (uint32_t)1000L));
-}
+ /* Remember these bytes towards statistics. */
+ stats_increment_bytes_read_and_written(num_read, num_written);
-/** Helper: return the time in milliseconds since <b>last_empty_time</b>
- * when a bucket ran empty that previously had <b>tokens_before</b> tokens
- * now has <b>tokens_after</b> tokens after refilling at timestamp
- * <b>tvnow</b>, capped at <b>milliseconds_elapsed</b> milliseconds since
- * last refilling that bucket. Return 0 if the bucket has not been empty
- * since the last refill or has not been refilled. */
-uint32_t
-bucket_millis_empty(int tokens_before, uint32_t last_empty_time,
- int tokens_after, int milliseconds_elapsed,
- const struct timeval *tvnow)
-{
- uint32_t result = 0, refilled;
- if (tokens_before <= 0 && tokens_after > tokens_before) {
- refilled = msec_since_midnight(tvnow);
- result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) %
- (86400L * 1000L));
- if (result > (uint32_t)milliseconds_elapsed)
- result = (uint32_t)milliseconds_elapsed;
- }
- return result;
-}
-
-/** Check if a bucket which had <b>tokens_before</b> tokens and which got
- * <b>tokens_removed</b> tokens removed at timestamp <b>tvnow</b> has run
- * out of tokens, and if so, note the milliseconds since midnight in
- * <b>timestamp_var</b> for the next TB_EMPTY event. */
-void
-connection_buckets_note_empty_ts(uint32_t *timestamp_var,
- int tokens_before, size_t tokens_removed,
- const struct timeval *tvnow)
-{
- if (tokens_before > 0 && (uint32_t)tokens_before <= tokens_removed)
- *timestamp_var = msec_since_midnight(tvnow);
+ /* Remember these bytes towards accounting. */
+ if (accounting_is_enabled(get_options())) {
+ if (now > last_recorded_accounting_at && last_recorded_accounting_at) {
+ accounting_add_bytes(num_read, num_written,
+ (int)(now - last_recorded_accounting_at));
+ } else {
+ accounting_add_bytes(num_read, num_written, 0);
+ }
+ last_recorded_accounting_at = now;
+ }
}
-/** Last time at which the global or relay buckets were emptied in msec
- * since midnight. */
-static uint32_t global_relayed_read_emptied = 0,
- global_relayed_write_emptied = 0,
- global_read_emptied = 0,
- global_write_emptied = 0;
-
/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes
* onto <b>conn</b>. Decrement buckets appropriately. */
static void
@@ -3098,45 +3070,54 @@ connection_buckets_decrement(connection_t *conn, time_t now,
if (!connection_is_rate_limited(conn))
return; /* local IPs are free */
- /* If one or more of our token buckets ran dry just now, note the
- * timestamp for TB_EMPTY events. */
- if (get_options()->TestingEnableTbEmptyEvent) {
- struct timeval tvnow;
- tor_gettimeofday_cached(&tvnow);
- if (connection_counts_as_relayed_traffic(conn, now)) {
- connection_buckets_note_empty_ts(&global_relayed_read_emptied,
- global_relayed_read_bucket, num_read, &tvnow);
- connection_buckets_note_empty_ts(&global_relayed_write_emptied,
- global_relayed_write_bucket, num_written, &tvnow);
- }
- connection_buckets_note_empty_ts(&global_read_emptied,
- global_read_bucket, num_read, &tvnow);
- connection_buckets_note_empty_ts(&global_write_emptied,
- global_write_bucket, num_written, &tvnow);
- if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
- or_connection_t *or_conn = TO_OR_CONN(conn);
- connection_buckets_note_empty_ts(&or_conn->read_emptied_time,
- or_conn->read_bucket, num_read, &tvnow);
- connection_buckets_note_empty_ts(&or_conn->write_emptied_time,
- or_conn->write_bucket, num_written, &tvnow);
- }
+ unsigned flags = 0;
+ if (connection_counts_as_relayed_traffic(conn, now)) {
+ flags = token_bucket_rw_dec(&global_relayed_bucket, num_read, num_written);
}
+ flags |= token_bucket_rw_dec(&global_bucket, num_read, num_written);
- if (connection_counts_as_relayed_traffic(conn, now)) {
- global_relayed_read_bucket -= (int)num_read;
- global_relayed_write_bucket -= (int)num_written;
+ if (flags & TB_WRITE) {
+ write_buckets_last_empty_at = now;
}
- global_read_bucket -= (int)num_read;
- global_write_bucket -= (int)num_written;
if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
- TO_OR_CONN(conn)->read_bucket -= (int)num_read;
- TO_OR_CONN(conn)->write_bucket -= (int)num_written;
+ or_connection_t *or_conn = TO_OR_CONN(conn);
+ token_bucket_rw_dec(&or_conn->bucket, num_read, num_written);
}
}
+/**
+ * Mark <b>conn</b> as needing to stop reading because bandwidth has been
+ * exhausted. If <b>is_global_bw</b>, it is closing because global bandwidth
+ * limit has been exhausted. Otherwise, it is closing because its own
+ * bandwidth limit has been exhausted.
+ */
+void
+connection_read_bw_exhausted(connection_t *conn, bool is_global_bw)
+{
+ (void)is_global_bw;
+ conn->read_blocked_on_bw = 1;
+ connection_stop_reading(conn);
+ reenable_blocked_connection_schedule();
+}
+
+/**
+ * Mark <b>conn</b> as needing to stop reading because write bandwidth has
+ * been exhausted. If <b>is_global_bw</b>, it is closing because global
+ * bandwidth limit has been exhausted. Otherwise, it is closing because its
+ * own bandwidth limit has been exhausted.
+*/
+void
+connection_write_bw_exhausted(connection_t *conn, bool is_global_bw)
+{
+ (void)is_global_bw;
+ conn->write_blocked_on_bw = 1;
+ connection_stop_writing(conn);
+ reenable_blocked_connection_schedule();
+}
+
/** If we have exhausted our global buckets, or the buckets for conn,
* stop reading. */
-static void
+void
connection_consider_empty_read_buckets(connection_t *conn)
{
const char *reason;
@@ -3144,26 +3125,28 @@ connection_consider_empty_read_buckets(connection_t *conn)
if (!connection_is_rate_limited(conn))
return; /* Always okay. */
- if (global_read_bucket <= 0) {
+ int is_global = 1;
+
+ if (token_bucket_rw_get_read(&global_bucket) <= 0) {
reason = "global read bucket exhausted. Pausing.";
} else if (connection_counts_as_relayed_traffic(conn, approx_time()) &&
- global_relayed_read_bucket <= 0) {
+ token_bucket_rw_get_read(&global_relayed_bucket) <= 0) {
reason = "global relayed read bucket exhausted. Pausing.";
} else if (connection_speaks_cells(conn) &&
conn->state == OR_CONN_STATE_OPEN &&
- TO_OR_CONN(conn)->read_bucket <= 0) {
+ token_bucket_rw_get_read(&TO_OR_CONN(conn)->bucket) <= 0) {
reason = "connection read bucket exhausted. Pausing.";
+ is_global = false;
} else
return; /* all good, no need to stop it */
LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "%s", reason));
- conn->read_blocked_on_bw = 1;
- connection_stop_reading(conn);
+ connection_read_bw_exhausted(conn, is_global);
}
/** If we have exhausted our global buckets, or the buckets for conn,
* stop writing. */
-static void
+void
connection_consider_empty_write_buckets(connection_t *conn)
{
const char *reason;
@@ -3171,233 +3154,166 @@ connection_consider_empty_write_buckets(connection_t *conn)
if (!connection_is_rate_limited(conn))
return; /* Always okay. */
- if (global_write_bucket <= 0) {
+ bool is_global = true;
+ if (token_bucket_rw_get_write(&global_bucket) <= 0) {
reason = "global write bucket exhausted. Pausing.";
} else if (connection_counts_as_relayed_traffic(conn, approx_time()) &&
- global_relayed_write_bucket <= 0) {
+ token_bucket_rw_get_write(&global_relayed_bucket) <= 0) {
reason = "global relayed write bucket exhausted. Pausing.";
} else if (connection_speaks_cells(conn) &&
conn->state == OR_CONN_STATE_OPEN &&
- TO_OR_CONN(conn)->write_bucket <= 0) {
+ token_bucket_rw_get_write(&TO_OR_CONN(conn)->bucket) <= 0) {
reason = "connection write bucket exhausted. Pausing.";
+ is_global = false;
} else
return; /* all good, no need to stop it */
LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "%s", reason));
- conn->write_blocked_on_bw = 1;
- connection_stop_writing(conn);
+ connection_write_bw_exhausted(conn, is_global);
}
-/** Initialize the global read bucket to options-\>BandwidthBurst. */
+/** Initialize the global buckets to the values configured in the
+ * options */
void
connection_bucket_init(void)
{
const or_options_t *options = get_options();
- /* start it at max traffic */
- global_read_bucket = (int)options->BandwidthBurst;
- global_write_bucket = (int)options->BandwidthBurst;
+ const uint32_t now_ts = monotime_coarse_get_stamp();
+ token_bucket_rw_init(&global_bucket,
+ (int32_t)options->BandwidthRate,
+ (int32_t)options->BandwidthBurst,
+ now_ts);
if (options->RelayBandwidthRate) {
- global_relayed_read_bucket = (int)options->RelayBandwidthBurst;
- global_relayed_write_bucket = (int)options->RelayBandwidthBurst;
+ token_bucket_rw_init(&global_relayed_bucket,
+ (int32_t)options->RelayBandwidthRate,
+ (int32_t)options->RelayBandwidthBurst,
+ now_ts);
} else {
- global_relayed_read_bucket = (int)options->BandwidthBurst;
- global_relayed_write_bucket = (int)options->BandwidthBurst;
+ token_bucket_rw_init(&global_relayed_bucket,
+ (int32_t)options->BandwidthRate,
+ (int32_t)options->BandwidthBurst,
+ now_ts);
}
+
+ reenable_blocked_connection_init(options);
}
-/** Refill a single <b>bucket</b> called <b>name</b> with bandwidth rate per
- * second <b>rate</b> and bandwidth burst <b>burst</b>, assuming that
- * <b>milliseconds_elapsed</b> milliseconds have passed since the last
- * call. */
-static void
-connection_bucket_refill_helper(int *bucket, int rate, int burst,
- int milliseconds_elapsed,
- const char *name)
+/** Update the global connection bucket settings to a new value. */
+void
+connection_bucket_adjust(const or_options_t *options)
{
- int starting_bucket = *bucket;
- if (starting_bucket < burst && milliseconds_elapsed > 0) {
- int64_t incr = (((int64_t)rate) * milliseconds_elapsed) / 1000;
- if ((burst - starting_bucket) < incr) {
- *bucket = burst; /* We would overflow the bucket; just set it to
- * the maximum. */
- } else {
- *bucket += (int)incr;
- if (*bucket > burst || *bucket < starting_bucket) {
- /* If we overflow the burst, or underflow our starting bucket,
- * cap the bucket value to burst. */
- /* XXXX this might be redundant now, but it doesn't show up
- * in profiles. Remove it after analysis. */
- *bucket = burst;
- }
- }
- log_debug(LD_NET,"%s now %d.", name, *bucket);
+ token_bucket_rw_adjust(&global_bucket,
+ (int32_t)options->BandwidthRate,
+ (int32_t)options->BandwidthBurst);
+ if (options->RelayBandwidthRate) {
+ token_bucket_rw_adjust(&global_relayed_bucket,
+ (int32_t)options->RelayBandwidthRate,
+ (int32_t)options->RelayBandwidthBurst);
+ } else {
+ token_bucket_rw_adjust(&global_relayed_bucket,
+ (int32_t)options->BandwidthRate,
+ (int32_t)options->BandwidthBurst);
}
}
-/** Time has passed; increment buckets appropriately. */
-void
-connection_bucket_refill(int milliseconds_elapsed, time_t now)
+/**
+ * Cached value of the last coarse-timestamp when we refilled the
+ * global buckets.
+ */
+static uint32_t last_refilled_global_buckets_ts=0;
+/**
+ * Refill the token buckets for a single connection <b>conn</b>, and the
+ * global token buckets as appropriate. Requires that <b>now_ts</b> is
+ * the time in coarse timestamp units.
+ */
+static void
+connection_bucket_refill_single(connection_t *conn, uint32_t now_ts)
{
- const or_options_t *options = get_options();
- smartlist_t *conns = get_connection_array();
- int bandwidthrate, bandwidthburst, relayrate, relayburst;
+ /* Note that we only check for equality here: the underlying
+ * token bucket functions can handle moving backwards in time if they
+ * need to. */
+ if (now_ts != last_refilled_global_buckets_ts) {
+ token_bucket_rw_refill(&global_bucket, now_ts);
+ token_bucket_rw_refill(&global_relayed_bucket, now_ts);
+ last_refilled_global_buckets_ts = now_ts;
+ }
- int prev_global_read = global_read_bucket;
- int prev_global_write = global_write_bucket;
- int prev_relay_read = global_relayed_read_bucket;
- int prev_relay_write = global_relayed_write_bucket;
- struct timeval tvnow; /*< Only used if TB_EMPTY events are enabled. */
+ if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
+ or_connection_t *or_conn = TO_OR_CONN(conn);
+ token_bucket_rw_refill(&or_conn->bucket, now_ts);
+ }
+}
- bandwidthrate = (int)options->BandwidthRate;
- bandwidthburst = (int)options->BandwidthBurst;
+/**
+ * Event to re-enable all connections that were previously blocked on read or
+ * write.
+ */
+static mainloop_event_t *reenable_blocked_connections_ev = NULL;
- if (options->RelayBandwidthRate) {
- relayrate = (int)options->RelayBandwidthRate;
- relayburst = (int)options->RelayBandwidthBurst;
- } else {
- relayrate = bandwidthrate;
- relayburst = bandwidthburst;
- }
-
- tor_assert(milliseconds_elapsed >= 0);
-
- write_buckets_empty_last_second =
- global_relayed_write_bucket <= 0 || global_write_bucket <= 0;
-
- /* refill the global buckets */
- connection_bucket_refill_helper(&global_read_bucket,
- bandwidthrate, bandwidthburst,
- milliseconds_elapsed,
- "global_read_bucket");
- connection_bucket_refill_helper(&global_write_bucket,
- bandwidthrate, bandwidthburst,
- milliseconds_elapsed,
- "global_write_bucket");
- connection_bucket_refill_helper(&global_relayed_read_bucket,
- relayrate, relayburst,
- milliseconds_elapsed,
- "global_relayed_read_bucket");
- connection_bucket_refill_helper(&global_relayed_write_bucket,
- relayrate, relayburst,
- milliseconds_elapsed,
- "global_relayed_write_bucket");
-
- /* If buckets were empty before and have now been refilled, tell any
- * interested controllers. */
- if (get_options()->TestingEnableTbEmptyEvent) {
- uint32_t global_read_empty_time, global_write_empty_time,
- relay_read_empty_time, relay_write_empty_time;
- tor_gettimeofday_cached(&tvnow);
- global_read_empty_time = bucket_millis_empty(prev_global_read,
- global_read_emptied, global_read_bucket,
- milliseconds_elapsed, &tvnow);
- global_write_empty_time = bucket_millis_empty(prev_global_write,
- global_write_emptied, global_write_bucket,
- milliseconds_elapsed, &tvnow);
- control_event_tb_empty("GLOBAL", global_read_empty_time,
- global_write_empty_time, milliseconds_elapsed);
- relay_read_empty_time = bucket_millis_empty(prev_relay_read,
- global_relayed_read_emptied,
- global_relayed_read_bucket,
- milliseconds_elapsed, &tvnow);
- relay_write_empty_time = bucket_millis_empty(prev_relay_write,
- global_relayed_write_emptied,
- global_relayed_write_bucket,
- milliseconds_elapsed, &tvnow);
- control_event_tb_empty("RELAY", relay_read_empty_time,
- relay_write_empty_time, milliseconds_elapsed);
- }
-
- /* refill the per-connection buckets */
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- if (connection_speaks_cells(conn)) {
- or_connection_t *or_conn = TO_OR_CONN(conn);
- int orbandwidthrate = or_conn->bandwidthrate;
- int orbandwidthburst = or_conn->bandwidthburst;
-
- int prev_conn_read = or_conn->read_bucket;
- int prev_conn_write = or_conn->write_bucket;
-
- if (connection_bucket_should_increase(or_conn->read_bucket, or_conn)) {
- connection_bucket_refill_helper(&or_conn->read_bucket,
- orbandwidthrate,
- orbandwidthburst,
- milliseconds_elapsed,
- "or_conn->read_bucket");
- }
- if (connection_bucket_should_increase(or_conn->write_bucket, or_conn)) {
- connection_bucket_refill_helper(&or_conn->write_bucket,
- orbandwidthrate,
- orbandwidthburst,
- milliseconds_elapsed,
- "or_conn->write_bucket");
- }
+/** True iff reenable_blocked_connections_ev is currently scheduled. */
+static int reenable_blocked_connections_is_scheduled = 0;
- /* If buckets were empty before and have now been refilled, tell any
- * interested controllers. */
- if (get_options()->TestingEnableTbEmptyEvent) {
- char *bucket;
- uint32_t conn_read_empty_time, conn_write_empty_time;
- tor_asprintf(&bucket, "ORCONN ID="U64_FORMAT,
- U64_PRINTF_ARG(or_conn->base_.global_identifier));
- conn_read_empty_time = bucket_millis_empty(prev_conn_read,
- or_conn->read_emptied_time,
- or_conn->read_bucket,
- milliseconds_elapsed, &tvnow);
- conn_write_empty_time = bucket_millis_empty(prev_conn_write,
- or_conn->write_emptied_time,
- or_conn->write_bucket,
- milliseconds_elapsed, &tvnow);
- control_event_tb_empty(bucket, conn_read_empty_time,
- conn_write_empty_time,
- milliseconds_elapsed);
- tor_free(bucket);
- }
- }
+/** Delay after which to run reenable_blocked_connections_ev. */
+static struct timeval reenable_blocked_connections_delay;
- if (conn->read_blocked_on_bw == 1 /* marked to turn reading back on now */
- && global_read_bucket > 0 /* and we're allowed to read */
- && (!connection_counts_as_relayed_traffic(conn, now) ||
- global_relayed_read_bucket > 0) /* even if we're relayed traffic */
- && (!connection_speaks_cells(conn) ||
- conn->state != OR_CONN_STATE_OPEN ||
- TO_OR_CONN(conn)->read_bucket > 0)) {
- /* and either a non-cell conn or a cell conn with non-empty bucket */
- LOG_FN_CONN(conn, (LOG_DEBUG,LD_NET,
- "waking up conn (fd %d) for read", (int)conn->s));
- conn->read_blocked_on_bw = 0;
+/**
+ * Re-enable all connections that were previously blocked on read or write.
+ * This event is scheduled after enough time has elapsed to be sure
+ * that the buckets will refill when the connections have something to do.
+ */
+static void
+reenable_blocked_connections_cb(mainloop_event_t *ev, void *arg)
+{
+ (void)ev;
+ (void)arg;
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ if (conn->read_blocked_on_bw == 1) {
connection_start_reading(conn);
+ conn->read_blocked_on_bw = 0;
}
-
- if (conn->write_blocked_on_bw == 1
- && global_write_bucket > 0 /* and we're allowed to write */
- && (!connection_counts_as_relayed_traffic(conn, now) ||
- global_relayed_write_bucket > 0) /* even if it's relayed traffic */
- && (!connection_speaks_cells(conn) ||
- conn->state != OR_CONN_STATE_OPEN ||
- TO_OR_CONN(conn)->write_bucket > 0)) {
- LOG_FN_CONN(conn, (LOG_DEBUG,LD_NET,
- "waking up conn (fd %d) for write", (int)conn->s));
- conn->write_blocked_on_bw = 0;
+ if (conn->write_blocked_on_bw == 1) {
connection_start_writing(conn);
+ conn->write_blocked_on_bw = 0;
}
} SMARTLIST_FOREACH_END(conn);
+
+ reenable_blocked_connections_is_scheduled = 0;
}
-/** Is the <b>bucket</b> for connection <b>conn</b> low enough that we
- * should add another pile of tokens to it?
+/**
+ * Initialize the mainloop event that we use to wake up connections that
+ * find themselves blocked on bandwidth.
*/
-static int
-connection_bucket_should_increase(int bucket, or_connection_t *conn)
+static void
+reenable_blocked_connection_init(const or_options_t *options)
{
- tor_assert(conn);
-
- if (conn->base_.state != OR_CONN_STATE_OPEN)
- return 0; /* only open connections play the rate limiting game */
- if (bucket >= conn->bandwidthburst)
- return 0;
+ if (! reenable_blocked_connections_ev) {
+ reenable_blocked_connections_ev =
+ mainloop_event_new(reenable_blocked_connections_cb, NULL);
+ reenable_blocked_connections_is_scheduled = 0;
+ }
+ time_t sec = options->TokenBucketRefillInterval / 1000;
+ int msec = (options->TokenBucketRefillInterval % 1000);
+ reenable_blocked_connections_delay.tv_sec = sec;
+ reenable_blocked_connections_delay.tv_usec = msec * 1000;
+}
- return 1;
+/**
+ * Called when we have blocked a connection for being low on bandwidth:
+ * schedule an event to reenable such connections, if it is not already
+ * scheduled.
+ */
+static void
+reenable_blocked_connection_schedule(void)
+{
+ if (reenable_blocked_connections_is_scheduled)
+ return;
+ if (BUG(reenable_blocked_connections_ev == NULL)) {
+ reenable_blocked_connection_init(get_options());
+ }
+ mainloop_event_schedule(reenable_blocked_connections_ev,
+ &reenable_blocked_connections_delay);
+ reenable_blocked_connections_is_scheduled = 1;
}
/** Read bytes from conn-\>s and process them.
@@ -3418,7 +3334,9 @@ connection_handle_read_impl(connection_t *conn)
if (conn->marked_for_close)
return 0; /* do nothing */
- conn->timestamp_lastread = approx_time();
+ conn->timestamp_last_read_allowed = approx_time();
+
+ connection_bucket_refill_single(conn, monotime_coarse_get_stamp());
switch (conn->type) {
case CONN_TYPE_OR_LISTENER:
@@ -3525,8 +3443,7 @@ int
connection_handle_read(connection_t *conn)
{
int res;
-
- tor_gettimeofday_cache_clear();
+ update_current_time(time(NULL));
res = connection_handle_read_impl(conn);
return res;
}
@@ -3678,25 +3595,15 @@ connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read,
/* change *max_to_read */
*max_to_read = at_most - n_read;
- /* Update edge_conn->n_read and ocirc->n_read_circ_bw */
+ /* Update edge_conn->n_read */
if (conn->type == CONN_TYPE_AP) {
edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
- circuit_t *circ = circuit_get_by_edge_conn(edge_conn);
- origin_circuit_t *ocirc;
/* Check for overflow: */
if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_read > n_read))
edge_conn->n_read += (int)n_read;
else
edge_conn->n_read = UINT32_MAX;
-
- if (circ && CIRCUIT_IS_ORIGIN(circ)) {
- ocirc = TO_ORIGIN_CIRCUIT(circ);
- if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_read_circ_bw > n_read))
- ocirc->n_read_circ_bw += (int)n_read;
- else
- ocirc->n_read_circ_bw = UINT32_MAX;
- }
}
/* If CONN_BW events are enabled, update conn->n_read_conn_bw for
@@ -3782,7 +3689,6 @@ connection_outbuf_too_full(connection_t *conn)
* This should help fix slow upload rates.
*/
static void
-
update_send_buffer_size(tor_socket_t sock)
{
#ifdef _WIN32
@@ -3819,7 +3725,7 @@ update_send_buffer_size(tor_socket_t sock)
* when libevent tells us that conn wants to write, or below
* from connection_buf_add() when an entire TLS record is ready.
*
- * Update <b>conn</b>-\>timestamp_lastwritten to now, and call flush_buf
+ * Update <b>conn</b>-\>timestamp_last_write_allowed to now, and call flush_buf
* or flush_buf_tls appropriately. If it succeeds and there are no more
* more bytes on <b>conn</b>-\>outbuf, then call connection_finished_flushing
* on it too.
@@ -3852,7 +3758,9 @@ connection_handle_write_impl(connection_t *conn, int force)
return 0;
}
- conn->timestamp_lastwritten = now;
+ conn->timestamp_last_write_allowed = now;
+
+ connection_bucket_refill_single(conn, monotime_coarse_get_stamp());
/* Sometimes, "writable" means "connected". */
if (connection_state_is_connecting(conn)) {
@@ -3967,8 +3875,7 @@ connection_handle_write_impl(connection_t *conn, int force)
/* Make sure to avoid a loop if the receive buckets are empty. */
log_debug(LD_NET,"wanted read.");
if (!connection_is_reading(conn)) {
- connection_stop_writing(conn);
- conn->write_blocked_on_bw = 1;
+ connection_write_bw_exhausted(conn, true);
/* we'll start reading again when we get more tokens in our
* read bucket; then we'll start writing again too.
*/
@@ -4014,22 +3921,12 @@ connection_handle_write_impl(connection_t *conn, int force)
if (n_written && conn->type == CONN_TYPE_AP) {
edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
- circuit_t *circ = circuit_get_by_edge_conn(edge_conn);
- origin_circuit_t *ocirc;
/* Check for overflow: */
if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_written > n_written))
edge_conn->n_written += (int)n_written;
else
edge_conn->n_written = UINT32_MAX;
-
- if (circ && CIRCUIT_IS_ORIGIN(circ)) {
- ocirc = TO_ORIGIN_CIRCUIT(circ);
- if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_written_circ_bw > n_written))
- ocirc->n_written_circ_bw += (int)n_written;
- else
- ocirc->n_written_circ_bw = UINT32_MAX;
- }
}
/* If CONN_BW events are enabled, update conn->n_written_conn_bw for
@@ -4089,7 +3986,7 @@ int
connection_handle_write(connection_t *conn, int force)
{
int res;
- tor_gettimeofday_cache_clear();
+ update_current_time(time(NULL));
conn->in_connection_handle_write = 1;
res = connection_handle_write_impl(conn, force);
conn->in_connection_handle_write = 0;
@@ -4528,8 +4425,6 @@ alloc_http_authenticator(const char *authenticator)
static void
client_check_address_changed(tor_socket_t sock)
{
- struct sockaddr_storage out_sockaddr;
- socklen_t out_addr_len = (socklen_t) sizeof(out_sockaddr);
tor_addr_t out_addr, iface_addr;
tor_addr_t **last_interface_ip_ptr;
sa_family_t family;
@@ -4537,13 +4432,12 @@ client_check_address_changed(tor_socket_t sock)
if (!outgoing_addrs)
outgoing_addrs = smartlist_new();
- if (getsockname(sock, (struct sockaddr*)&out_sockaddr, &out_addr_len)<0) {
+ if (tor_addr_from_getsockname(&out_addr, sock) < 0) {
int e = tor_socket_errno(sock);
log_warn(LD_NET, "getsockname() to check for address change failed: %s",
tor_socket_strerror(e));
return;
}
- tor_addr_from_sockaddr(&out_addr, (struct sockaddr*)&out_sockaddr, NULL);
family = tor_addr_family(&out_addr);
if (family == AF_INET)
@@ -5331,6 +5225,11 @@ connection_free_all(void)
tor_free(last_interface_ipv4);
tor_free(last_interface_ipv6);
+ last_recorded_accounting_at = 0;
+
+ mainloop_event_free(reenable_blocked_connections_ev);
+ reenable_blocked_connections_is_scheduled = 0;
+ memset(&reenable_blocked_connections_delay, 0, sizeof(struct timeval));
}
/** Log a warning, and possibly emit a control event, that <b>received</b> came
@@ -5338,10 +5237,10 @@ connection_free_all(void)
* that we had more faith in and therefore the warning level should have higher
* severity.
*/
-void
-clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted,
- log_domain_mask_t domain, const char *received,
- const char *source)
+MOCK_IMPL(void,
+clock_skew_warning, (const connection_t *conn, long apparent_skew, int trusted,
+ log_domain_mask_t domain, const char *received,
+ const char *source))
{
char dbuf[64];
char *ext_source = NULL, *warn = NULL;
diff --git a/src/or/connection.h b/src/or/connection.h
index 6bc5a7cfd0..ad3129c9d8 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -122,7 +122,13 @@ void connection_mark_all_noncontrol_connections(void);
ssize_t connection_bucket_write_limit(connection_t *conn, time_t now);
int global_write_bucket_low(connection_t *conn, size_t attempt, int priority);
void connection_bucket_init(void);
-void connection_bucket_refill(int seconds_elapsed, time_t now);
+void connection_bucket_adjust(const or_options_t *options);
+void connection_bucket_refill_all(time_t now,
+ uint32_t now_ts);
+void connection_read_bw_exhausted(connection_t *conn, bool is_global_bw);
+void connection_write_bw_exhausted(connection_t *conn, bool is_global_bw);
+void connection_consider_empty_read_buckets(connection_t *conn);
+void connection_consider_empty_write_buckets(connection_t *conn);
int connection_handle_read(connection_t *conn);
@@ -248,9 +254,10 @@ void assert_connection_ok(connection_t *conn, time_t now);
int connection_or_nonopen_was_started_here(or_connection_t *conn);
void connection_dump_buffer_mem_stats(int severity);
-void clock_skew_warning(const connection_t *conn, long apparent_skew,
- int trusted, log_domain_mask_t domain,
- const char *received, const char *source);
+MOCK_DECL(void, clock_skew_warning,
+ (const connection_t *conn, long apparent_skew, int trusted,
+ log_domain_mask_t domain, const char *received,
+ const char *source));
/** Check if a connection is on the way out so the OOS handler doesn't try
* to kill more than it needs. */
@@ -272,13 +279,6 @@ void connection_check_oos(int n_socks, int failed);
STATIC void connection_free_minimal(connection_t *conn);
/* Used only by connection.c and test*.c */
-uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time,
- int tokens_after, int milliseconds_elapsed,
- const struct timeval *tvnow);
-void connection_buckets_note_empty_ts(uint32_t *timestamp_var,
- int tokens_before,
- size_t tokens_removed,
- const struct timeval *tvnow);
MOCK_DECL(STATIC int,connection_connect_sockaddr,
(connection_t *conn,
const struct sockaddr *sa,
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 003f84adb3..88e346e9b8 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -70,6 +70,7 @@
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
+#include "crypto_util.h"
#include "dns.h"
#include "dnsserv.h"
#include "directory.h"
@@ -429,6 +430,12 @@ connection_edge_end(edge_connection_t *conn, uint8_t reason)
if (circ && !circ->marked_for_close) {
log_debug(LD_EDGE,"Sending end on conn (fd "TOR_SOCKET_T_FORMAT").",
conn->base_.s);
+
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ connection_half_edge_add(conn, origin_circ);
+ }
+
connection_edge_send_command(conn, RELAY_COMMAND_END,
payload, payload_len);
/* We'll log warn if the connection was an hidden service and couldn't be
@@ -445,6 +452,236 @@ connection_edge_end(edge_connection_t *conn, uint8_t reason)
return 0;
}
+/**
+ * Helper function for bsearch.
+ *
+ * As per smartlist_bsearch, return < 0 if key preceeds member,
+ * > 0 if member preceeds key, and 0 if they are equal.
+ *
+ * This is equivalent to subtraction of the values of key - member
+ * (why does no one ever say that explicitly?).
+ */
+static int
+connection_half_edge_compare_bsearch(const void *key, const void **member)
+{
+ const half_edge_t *e2;
+ tor_assert(key);
+ tor_assert(member && *(half_edge_t**)member);
+ e2 = *(const half_edge_t **)member;
+
+ return *(const streamid_t*)key - e2->stream_id;
+}
+
+/** Total number of half_edge_t objects allocated */
+static size_t n_half_conns_allocated = 0;
+
+/**
+ * Add a half-closed connection to the list, to watch for activity.
+ *
+ * These connections are removed from the list upon receiving an end
+ * cell.
+ */
+STATIC void
+connection_half_edge_add(const edge_connection_t *conn,
+ origin_circuit_t *circ)
+{
+ half_edge_t *half_conn = NULL;
+ int insert_at = 0;
+ int ignored;
+
+ /* Double-check for re-insertion. This should not happen,
+ * but this check is cheap compared to the sort anyway */
+ if (connection_half_edge_find_stream_id(circ->half_streams,
+ conn->stream_id)) {
+ log_warn(LD_BUG, "Duplicate stream close for stream %d on circuit %d",
+ conn->stream_id, circ->global_identifier);
+ return;
+ }
+
+ half_conn = tor_malloc_zero(sizeof(half_edge_t));
+ ++n_half_conns_allocated;
+
+ if (!circ->half_streams) {
+ circ->half_streams = smartlist_new();
+ }
+
+ half_conn->stream_id = conn->stream_id;
+
+ // How many sendme's should I expect?
+ half_conn->sendmes_pending =
+ (STREAMWINDOW_START-conn->package_window)/STREAMWINDOW_INCREMENT;
+
+ // Is there a connected cell pending?
+ half_conn->connected_pending = conn->base_.state ==
+ AP_CONN_STATE_CONNECT_WAIT;
+
+ /* Data should only arrive if we're not waiting on a resolved cell.
+ * It can arrive after waiting on connected, because of optimistic
+ * data. */
+ if (conn->base_.state != AP_CONN_STATE_RESOLVE_WAIT) {
+ // How many more data cells can arrive on this id?
+ half_conn->data_pending = conn->deliver_window;
+ }
+
+ insert_at = smartlist_bsearch_idx(circ->half_streams, &half_conn->stream_id,
+ connection_half_edge_compare_bsearch,
+ &ignored);
+ smartlist_insert(circ->half_streams, insert_at, half_conn);
+}
+
+/** Release space held by <b>he</b> */
+void
+half_edge_free_(half_edge_t *he)
+{
+ if (!he)
+ return;
+ --n_half_conns_allocated;
+ tor_free(he);
+}
+
+/** Return the number of bytes devoted to storing info on half-open streams. */
+size_t
+half_streams_get_total_allocation(void)
+{
+ return n_half_conns_allocated * sizeof(half_edge_t);
+}
+
+/**
+ * Find a stream_id_t in the list in O(lg(n)).
+ *
+ * Returns NULL if the list is empty or element is not found.
+ * Returns a pointer to the element if found.
+ */
+STATIC half_edge_t *
+connection_half_edge_find_stream_id(const smartlist_t *half_conns,
+ streamid_t stream_id)
+{
+ if (!half_conns)
+ return NULL;
+
+ return smartlist_bsearch(half_conns, &stream_id,
+ connection_half_edge_compare_bsearch);
+}
+
+/**
+ * Check if this stream_id is in a half-closed state. If so,
+ * check if it still has data cells pending, and decrement that
+ * window if so.
+ *
+ * Return 1 if the data window was not empty.
+ * Return 0 otherwise.
+ */
+int
+connection_half_edge_is_valid_data(const smartlist_t *half_conns,
+ streamid_t stream_id)
+{
+ half_edge_t *half = connection_half_edge_find_stream_id(half_conns,
+ stream_id);
+
+ if (!half)
+ return 0;
+
+ if (half->data_pending > 0) {
+ half->data_pending--;
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Check if this stream_id is in a half-closed state. If so,
+ * check if it still has a connected cell pending, and decrement
+ * that window if so.
+ *
+ * Return 1 if the connected window was not empty.
+ * Return 0 otherwise.
+ */
+int
+connection_half_edge_is_valid_connected(const smartlist_t *half_conns,
+ streamid_t stream_id)
+{
+ half_edge_t *half = connection_half_edge_find_stream_id(half_conns,
+ stream_id);
+
+ if (!half)
+ return 0;
+
+ if (half->connected_pending) {
+ half->connected_pending = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Check if this stream_id is in a half-closed state. If so,
+ * check if it still has sendme cells pending, and decrement that
+ * window if so.
+ *
+ * Return 1 if the sendme window was not empty.
+ * Return 0 otherwise.
+ */
+int
+connection_half_edge_is_valid_sendme(const smartlist_t *half_conns,
+ streamid_t stream_id)
+{
+ half_edge_t *half = connection_half_edge_find_stream_id(half_conns,
+ stream_id);
+
+ if (!half)
+ return 0;
+
+ if (half->sendmes_pending > 0) {
+ half->sendmes_pending--;
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Check if this stream_id is in a half-closed state. If so, remove
+ * it from the list. No other data should come after the END cell.
+ *
+ * Return 1 if stream_id was in half-closed state.
+ * Return 0 otherwise.
+ */
+int
+connection_half_edge_is_valid_end(smartlist_t *half_conns,
+ streamid_t stream_id)
+{
+ half_edge_t *half;
+ int found, remove_idx;
+
+ if (!half_conns)
+ return 0;
+
+ remove_idx = smartlist_bsearch_idx(half_conns, &stream_id,
+ connection_half_edge_compare_bsearch,
+ &found);
+ if (!found)
+ return 0;
+
+ half = smartlist_get(half_conns, remove_idx);
+ smartlist_del_keeporder(half_conns, remove_idx);
+ half_edge_free(half);
+ return 1;
+}
+
+/**
+ * Streams that were used to send a RESOLVE cell are closed
+ * when they get the RESOLVED, without an end. So treat
+ * a RESOLVED just like an end, and remove from the list.
+ */
+int
+connection_half_edge_is_valid_resolved(smartlist_t *half_conns,
+ streamid_t stream_id)
+{
+ return connection_half_edge_is_valid_end(half_conns, stream_id);
+}
+
/** An error has just occurred on an operation on an edge connection
* <b>conn</b>. Extract the errno; convert it to an end reason, and send an
* appropriate relay end cell to the other end of the connection's circuit.
@@ -611,6 +848,12 @@ static smartlist_t *pending_entry_connections = NULL;
static int untried_pending_connections = 0;
+/**
+ * Mainloop event to tell us to scan for pending connections that can
+ * be attached.
+ */
+static mainloop_event_t *attach_pending_entry_connections_ev = NULL;
+
/** Common code to connection_(ap|exit)_about_to_close. */
static void
connection_edge_about_to_close(edge_connection_t *edge_conn)
@@ -739,7 +982,7 @@ connection_ap_expire_beginning(void)
/* if it's an internal linked connection, don't yell its status. */
severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port)
? LOG_INFO : LOG_NOTICE;
- seconds_idle = (int)( now - base_conn->timestamp_lastread );
+ seconds_idle = (int)( now - base_conn->timestamp_last_read_allowed );
seconds_since_born = (int)( now - base_conn->timestamp_created );
if (base_conn->state == AP_CONN_STATE_OPEN)
@@ -825,7 +1068,7 @@ connection_ap_expire_beginning(void)
mark_circuit_unusable_for_new_conns(TO_ORIGIN_CIRCUIT(circ));
/* give our stream another 'cutoff' seconds to try */
- conn->base_.timestamp_lastread += cutoff;
+ conn->base_.timestamp_last_read_allowed += cutoff;
if (entry_conn->num_socks_retries < 250) /* avoid overflow */
entry_conn->num_socks_retries++;
/* move it back into 'pending' state, and try to attach. */
@@ -956,6 +1199,14 @@ connection_ap_attach_pending(int retry)
untried_pending_connections = 0;
}
+static void
+attach_pending_entry_connections_cb(mainloop_event_t *ev, void *arg)
+{
+ (void)ev;
+ (void)arg;
+ connection_ap_attach_pending(0);
+}
+
/** Mark <b>entry_conn</b> as needing to get attached to a circuit.
*
* And <b>entry_conn</b> must be in AP_CONN_STATE_CIRCUIT_WAIT,
@@ -973,9 +1224,13 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
if (conn->marked_for_close)
return;
- if (PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ if (PREDICT_UNLIKELY(NULL == pending_entry_connections)) {
pending_entry_connections = smartlist_new();
-
+ }
+ if (PREDICT_UNLIKELY(NULL == attach_pending_entry_connections_ev)) {
+ attach_pending_entry_connections_ev = mainloop_event_postloop_new(
+ attach_pending_entry_connections_cb, NULL);
+ }
if (PREDICT_UNLIKELY(smartlist_contains(pending_entry_connections,
entry_conn))) {
log_warn(LD_BUG, "What?? pending_entry_connections already contains %p! "
@@ -999,14 +1254,7 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
untried_pending_connections = 1;
smartlist_add(pending_entry_connections, entry_conn);
- /* Work-around for bug 19969: we handle pending_entry_connections at
- * the end of run_main_loop_once(), but in many cases that function will
- * take a very long time, if ever, to finish its call to event_base_loop().
- *
- * So the fix is to tell it right now that it ought to finish its loop at
- * its next available opportunity.
- */
- tell_event_loop_to_run_external_code();
+ mainloop_event_activate(attach_pending_entry_connections_ev);
}
/** Mark <b>entry_conn</b> as no longer waiting for a circuit. */
@@ -1135,7 +1383,7 @@ connection_ap_detach_retriable(entry_connection_t *conn,
int reason)
{
control_event_stream_status(conn, STREAM_EVENT_FAILED_RETRIABLE, reason);
- ENTRY_TO_CONN(conn)->timestamp_lastread = time(NULL);
+ ENTRY_TO_CONN(conn)->timestamp_last_read_allowed = time(NULL);
/* Roll back path bias use state so that we probe the circuit
* if nothing else succeeds on it */
@@ -2544,8 +2792,11 @@ connection_ap_process_http_connect(entry_connection_t *conn)
err:
if (BUG(errmsg == NULL))
errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
- log_warn(LD_EDGE, "Saying %s", escaped(errmsg));
+ log_info(LD_EDGE, "HTTP tunnel error: saying %s", escaped(errmsg));
connection_buf_add(errmsg, strlen(errmsg), ENTRY_TO_CONN(conn));
+ /* Mark it as "has_finished" so that we don't try to send an extra socks
+ * reply. */
+ conn->socks_request->has_finished = 1;
connection_mark_unattached_ap(conn,
END_STREAM_REASON_HTTPPROTOCOL|
END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
@@ -2581,6 +2832,11 @@ get_unique_stream_id_by_circ(origin_circuit_t *circ)
for (tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream)
if (tmpconn->stream_id == test_stream_id)
goto again;
+
+ if (connection_half_edge_find_stream_id(circ->half_streams,
+ test_stream_id))
+ goto again;
+
return test_stream_id;
}
@@ -3515,16 +3771,24 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
n_stream->deliver_window = STREAMWINDOW_START;
if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
+ int ret;
tor_free(address);
/* We handle this circuit and stream in this function for all supported
* hidden service version. */
- return handle_hs_exit_conn(circ, n_stream);
+ ret = handle_hs_exit_conn(circ, n_stream);
+
+ if (ret == 0) {
+ /* This was a valid cell. Count it as delivered + overhead. */
+ circuit_read_valid_data(origin_circ, rh.length);
+ }
+ return ret;
}
tor_strlower(address);
n_stream->base_.address = address;
n_stream->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
/* default to failed, change in dns_resolve if it turns out not to fail */
+ /* If we're hibernating or shutting down, we refuse to open new streams. */
if (we_are_hibernating()) {
relay_send_end_cell_from_edge(rh.stream_id, circ,
END_STREAM_REASON_HIBERNATING, NULL);
@@ -4165,4 +4429,5 @@ connection_edge_free_all(void)
untried_pending_connections = 0;
smartlist_free(pending_entry_connections);
pending_entry_connections = NULL;
+ mainloop_event_free(attach_pending_entry_connections_ev);
}
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index c6583d3845..8bef97dc41 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -122,6 +122,22 @@ void connection_ap_warn_and_unmark_if_pending_circ(
entry_connection_t *entry_conn,
const char *where);
+int connection_half_edge_is_valid_data(const smartlist_t *half_conns,
+ streamid_t stream_id);
+int connection_half_edge_is_valid_sendme(const smartlist_t *half_conns,
+ streamid_t stream_id);
+int connection_half_edge_is_valid_connected(const smartlist_t *half_conns,
+ streamid_t stream_id);
+int connection_half_edge_is_valid_end(smartlist_t *half_conns,
+ streamid_t stream_id);
+int connection_half_edge_is_valid_resolved(smartlist_t *half_conns,
+ streamid_t stream_id);
+
+size_t half_streams_get_total_allocation(void);
+void half_edge_free_(half_edge_t *he);
+#define half_edge_free(he) \
+ FREE_AND_NULL(half_edge_t, half_edge_free_, (he))
+
/** @name Begin-cell flags
*
* These flags are used in RELAY_BEGIN cells to change the default behavior
@@ -191,7 +207,12 @@ STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn,
rewrite_result_t *out);
STATIC int connection_ap_process_http_connect(entry_connection_t *conn);
+struct half_edge_t;
+STATIC void connection_half_edge_add(const edge_connection_t *conn,
+ origin_circuit_t *circ);
+STATIC struct half_edge_t *connection_half_edge_find_stream_id(
+ const smartlist_t *half_conns,
+ streamid_t stream_id);
#endif /* defined(CONNECTION_EDGE_PRIVATE) */
#endif /* !defined(TOR_CONNECTION_EDGE_H) */
-
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 221b8dc8ad..99c0ac077f 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -39,6 +39,8 @@
#include "connection.h"
#include "connection_or.h"
#include "control.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "dirserv.h"
#include "entrynodes.h"
#include "geoip.h"
@@ -794,18 +796,10 @@ connection_or_update_token_buckets_helper(or_connection_t *conn, int reset,
(int)options->BandwidthBurst, 1, INT32_MAX);
}
- conn->bandwidthrate = rate;
- conn->bandwidthburst = burst;
- if (reset) { /* set up the token buckets to be full */
- conn->read_bucket = conn->write_bucket = burst;
- return;
+ token_bucket_rw_adjust(&conn->bucket, rate, burst);
+ if (reset) {
+ token_bucket_rw_reset(&conn->bucket, monotime_coarse_get_stamp());
}
- /* If the new token bucket is smaller, take out the extra tokens.
- * (If it's larger, don't -- the buckets can grow to reach the cap.) */
- if (conn->read_bucket > burst)
- conn->read_bucket = burst;
- if (conn->write_bucket > burst)
- conn->write_bucket = burst;
}
/** Either our set of relays or our per-conn rate limits have changed.
@@ -2843,9 +2837,15 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
char label[128];
tor_snprintf(label, sizeof(label),
"EXPORTER FOR TOR TLS CLIENT BINDING %s", authtype_str);
- tor_tls_export_key_material(conn->tls, auth->tlssecrets,
- auth->cid, sizeof(auth->cid),
- label);
+ int r = tor_tls_export_key_material(conn->tls, auth->tlssecrets,
+ auth->cid, sizeof(auth->cid),
+ label);
+ if (r < 0) {
+ if (r != -2)
+ log_warn(LD_BUG, "TLS key export failed for unknown reason.");
+ // If r == -2, this was openssl bug 7712.
+ goto err;
+ }
}
/* 8 octets were reserved for the current time, but we're trying to get out
@@ -2973,14 +2973,11 @@ connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype))
get_current_auth_keypair(),
0 /* not server */);
if (! cell) {
- /* LCOV_EXCL_START */
- log_warn(LD_BUG, "Unable to compute authenticate cell!");
+ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unable to compute authenticate cell!");
return -1;
- /* LCOV_EXCL_STOP */
}
connection_or_write_var_cell_to_buf(cell, conn);
var_cell_free(cell);
return 0;
}
-
diff --git a/src/or/conscache.c b/src/or/conscache.c
index e25ac5f40b..51dc9d621f 100644
--- a/src/or/conscache.c
+++ b/src/or/conscache.c
@@ -5,6 +5,7 @@
#include "config.h"
#include "conscache.h"
+#include "crypto_util.h"
#include "storagedir.h"
#define CCE_MAGIC 0x17162253
diff --git a/src/or/consdiffmgr.c b/src/or/consdiffmgr.c
index 02b905a520..323f4f9ca0 100644
--- a/src/or/consdiffmgr.c
+++ b/src/or/consdiffmgr.c
@@ -99,6 +99,14 @@ static const compress_method_t compress_diffs_with[] = {
#endif
};
+/**
+ * Event for rescanning the cache.
+ */
+static mainloop_event_t *consdiffmgr_rescan_ev = NULL;
+
+static void consdiffmgr_rescan_cb(mainloop_event_t *ev, void *arg);
+static void mark_cdm_cache_dirty(void);
+
/** How many different methods will we try to use for diff compression? */
STATIC unsigned
n_diff_compression_methods(void)
@@ -372,7 +380,9 @@ cdm_cache_init(void)
} else {
consdiffmgr_set_cache_flags();
}
- cdm_cache_dirty = 1;
+ consdiffmgr_rescan_ev =
+ mainloop_event_postloop_new(consdiffmgr_rescan_cb, NULL);
+ mark_cdm_cache_dirty();
cdm_cache_loaded = 0;
}
@@ -1095,6 +1105,24 @@ consdiffmgr_rescan(void)
cdm_cache_dirty = 0;
}
+/** Callback wrapper for consdiffmgr_rescan */
+static void
+consdiffmgr_rescan_cb(mainloop_event_t *ev, void *arg)
+{
+ (void)ev;
+ (void)arg;
+ consdiffmgr_rescan();
+}
+
+/** Mark the cache as dirty, and schedule a rescan event. */
+static void
+mark_cdm_cache_dirty(void)
+{
+ cdm_cache_dirty = 1;
+ tor_assert(consdiffmgr_rescan_ev);
+ mainloop_event_activate(consdiffmgr_rescan_ev);
+}
+
/**
* Helper: compare two files by their from-valid-after and valid-after labels,
* trying to sort in ascending order by from-valid-after (when present) and
@@ -1219,6 +1247,7 @@ consdiffmgr_free_all(void)
memset(latest_consensus, 0, sizeof(latest_consensus));
consensus_cache_free(cons_diff_cache);
cons_diff_cache = NULL;
+ mainloop_event_free(consdiffmgr_rescan_ev);
}
/* =====
@@ -1750,7 +1779,7 @@ consensus_compress_worker_replyfn(void *work_)
compress_consensus_with,
job->out,
"consensus");
- cdm_cache_dirty = 1;
+ mark_cdm_cache_dirty();
unsigned u;
consensus_flavor_t f = job->flavor;
diff --git a/src/or/control.c b/src/or/control.c
index 3f677ba348..1115ef7b1d 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1,3 +1,4 @@
+
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
@@ -52,6 +53,8 @@
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "directory.h"
#include "dirserv.h"
#include "dnsserv.h"
@@ -76,15 +79,13 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
-#include "shared_random.h"
+#include "shared_random_client.h"
#ifndef _WIN32
#include <pwd.h>
#include <sys/resource.h>
#endif
-#include <event2/event.h>
-
#include "crypto_s2k.h"
#include "procmon.h"
@@ -112,6 +113,10 @@ static int disable_log_messages = 0;
#define EVENT_IS_INTERESTING(e) \
(!! (global_event_mask & EVENT_MASK_(e)))
+/** Macro: true if any event from the bitfield 'e' is interesting. */
+#define ANY_EVENT_IS_INTERESTING(e) \
+ (!! (global_event_mask & (e)))
+
/** If we're using cookie-type authentication, how long should our cookies be?
*/
#define AUTHENTICATION_COOKIE_LEN 32
@@ -216,9 +221,10 @@ static void orconn_target_get_name(char *buf, size_t len,
static int get_cached_network_liveness(void);
static void set_cached_network_liveness(int liveness);
-static void flush_queued_events_cb(evutil_socket_t fd, short what, void *arg);
+static void flush_queued_events_cb(mainloop_event_t *event, void *arg);
static char * download_status_to_string(const download_status_t *dl);
+static void control_get_bytes_rw_last_sec(uint64_t *r, uint64_t *w);
/** Given a control event code for a message event, return the corresponding
* log severity. */
@@ -259,6 +265,8 @@ clear_circ_bw_fields(void)
continue;
ocirc = TO_ORIGIN_CIRCUIT(circ);
ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
+ ocirc->n_overhead_written_circ_bw = ocirc->n_overhead_read_circ_bw = 0;
+ ocirc->n_delivered_written_circ_bw = ocirc->n_delivered_read_circ_bw = 0;
}
SMARTLIST_FOREACH_END(circ);
}
@@ -271,6 +279,7 @@ control_update_global_event_mask(void)
smartlist_t *conns = get_connection_array();
event_mask_t old_mask, new_mask;
old_mask = global_event_mask;
+ int any_old_per_sec_events = control_any_per_second_event_enabled();
global_event_mask = 0;
SMARTLIST_FOREACH(conns, connection_t *, _conn,
@@ -288,10 +297,13 @@ control_update_global_event_mask(void)
* we want to hear...*/
control_adjust_event_log_severity();
+ /* Macro: true if ev was false before and is true now. */
+#define NEWLY_ENABLED(ev) \
+ (! (old_mask & (ev)) && (new_mask & (ev)))
+
/* ...then, if we've started logging stream or circ bw, clear the
* appropriate fields. */
- if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) &&
- (new_mask & EVENT_STREAM_BANDWIDTH_USED)) {
+ if (NEWLY_ENABLED(EVENT_STREAM_BANDWIDTH_USED)) {
SMARTLIST_FOREACH(conns, connection_t *, conn,
{
if (conn->type == CONN_TYPE_AP) {
@@ -300,10 +312,18 @@ control_update_global_event_mask(void)
}
});
}
- if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) &&
- (new_mask & EVENT_CIRC_BANDWIDTH_USED)) {
+ if (NEWLY_ENABLED(EVENT_CIRC_BANDWIDTH_USED)) {
clear_circ_bw_fields();
}
+ if (NEWLY_ENABLED(EVENT_BANDWIDTH_USED)) {
+ uint64_t r, w;
+ control_get_bytes_rw_last_sec(&r, &w);
+ }
+ if (any_old_per_sec_events != control_any_per_second_event_enabled()) {
+ reschedule_per_second_timer();
+ }
+
+#undef NEWLY_ENABLED
}
/** Adjust the log severities that result in control_event_logmsg being called
@@ -352,6 +372,65 @@ control_event_is_interesting(int event)
return EVENT_IS_INTERESTING(event);
}
+/** Return true if any event that needs to fire once a second is enabled. */
+int
+control_any_per_second_event_enabled(void)
+{
+ return ANY_EVENT_IS_INTERESTING(
+ EVENT_MASK_(EVENT_BANDWIDTH_USED) |
+ EVENT_MASK_(EVENT_CELL_STATS) |
+ EVENT_MASK_(EVENT_CIRC_BANDWIDTH_USED) |
+ EVENT_MASK_(EVENT_CONN_BW) |
+ EVENT_MASK_(EVENT_STREAM_BANDWIDTH_USED)
+ );
+}
+
+/* The value of 'get_bytes_read()' the previous time that
+ * control_get_bytes_rw_last_sec() as called. */
+static uint64_t stats_prev_n_read = 0;
+/* The value of 'get_bytes_written()' the previous time that
+ * control_get_bytes_rw_last_sec() as called. */
+static uint64_t stats_prev_n_written = 0;
+
+/**
+ * Set <b>n_read</b> and <b>n_written</b> to the total number of bytes read
+ * and written by Tor since the last call to this function.
+ *
+ * Call this only from the main thread.
+ */
+static void
+control_get_bytes_rw_last_sec(uint64_t *n_read,
+ uint64_t *n_written)
+{
+ const uint64_t stats_n_bytes_read = get_bytes_read();
+ const uint64_t stats_n_bytes_written = get_bytes_written();
+
+ *n_read = stats_n_bytes_read - stats_prev_n_read;
+ *n_written = stats_n_bytes_written - stats_prev_n_written;
+ stats_prev_n_read = stats_n_bytes_read;
+ stats_prev_n_written = stats_n_bytes_written;
+}
+
+/**
+ * Run all the controller events (if any) that are scheduled to trigger once
+ * per second.
+ */
+void
+control_per_second_events(void)
+{
+ if (!control_any_per_second_event_enabled())
+ return;
+
+ uint64_t bytes_read, bytes_written;
+ control_get_bytes_rw_last_sec(&bytes_read, &bytes_written);
+ control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
+
+ control_event_stream_bandwidth_used();
+ control_event_conn_bandwidth_used();
+ control_event_circ_bandwidth_used();
+ control_event_circuit_cell_stats();
+}
+
/** Append a NUL-terminated string <b>s</b> to the end of
* <b>conn</b>-\>outbuf.
*/
@@ -691,7 +770,7 @@ static tor_mutex_t *queued_control_events_lock = NULL;
/** An event that should fire in order to flush the contents of
* queued_control_events. */
-static struct event *flush_queued_events_event = NULL;
+static mainloop_event_t *flush_queued_events_event = NULL;
void
control_initialize_event_queue(void)
@@ -703,9 +782,8 @@ control_initialize_event_queue(void)
if (flush_queued_events_event == NULL) {
struct event_base *b = tor_libevent_get_base();
if (b) {
- flush_queued_events_event = tor_event_new(b,
- -1, 0, flush_queued_events_cb,
- NULL);
+ flush_queued_events_event =
+ mainloop_event_new(flush_queued_events_cb, NULL);
tor_assert(flush_queued_events_event);
}
}
@@ -781,7 +859,7 @@ queue_control_event_string,(uint16_t event, char *msg))
*/
if (activate_event) {
tor_assert(flush_queued_events_event);
- event_active(flush_queued_events_event, EV_READ, 1);
+ mainloop_event_activate(flush_queued_events_event);
}
}
@@ -806,6 +884,9 @@ queued_event_free_(queued_event_t *ev)
static void
queued_events_flush_all(int force)
{
+ /* Make sure that we get all the pending log events, if there are any. */
+ flush_pending_log_callbacks();
+
if (PREDICT_UNLIKELY(queued_control_events == NULL)) {
return;
}
@@ -863,10 +944,9 @@ queued_events_flush_all(int force)
/** Libevent callback: Flushes pending events to controllers that are
* interested in them. */
static void
-flush_queued_events_cb(evutil_socket_t fd, short what, void *arg)
+flush_queued_events_cb(mainloop_event_t *event, void *arg)
{
- (void) fd;
- (void) what;
+ (void) event;
(void) arg;
queued_events_flush_all(0);
}
@@ -1218,7 +1298,6 @@ static const struct control_event_t control_event_table[] = {
{ EVENT_CONF_CHANGED, "CONF_CHANGED"},
{ EVENT_CONN_BW, "CONN_BW" },
{ EVENT_CELL_STATS, "CELL_STATS" },
- { EVENT_TB_EMPTY, "TB_EMPTY" },
{ EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" },
{ EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" },
{ EVENT_HS_DESC, "HS_DESC" },
@@ -1784,24 +1863,24 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
} else if (!strcmp(question, "process/pid")) {
int myPid = -1;
- #ifdef _WIN32
+#ifdef _WIN32
myPid = _getpid();
- #else
+#else
myPid = getpid();
- #endif
+#endif
tor_asprintf(answer, "%d", myPid);
} else if (!strcmp(question, "process/uid")) {
- #ifdef _WIN32
+#ifdef _WIN32
*answer = tor_strdup("-1");
- #else
+#else
int myUid = geteuid();
tor_asprintf(answer, "%d", myUid);
#endif /* defined(_WIN32) */
} else if (!strcmp(question, "process/user")) {
- #ifdef _WIN32
+#ifdef _WIN32
*answer = tor_strdup("");
- #else
+#else
int myUid = geteuid();
const struct passwd *myPwEntry = tor_getpwuid(myUid);
@@ -1940,6 +2019,31 @@ getinfo_helper_listeners(control_connection_t *control_conn,
return 0;
}
+/** Implementation helper for GETINFO: answers requests for information about
+ * the current time in both local and UTF forms. */
+STATIC int
+getinfo_helper_current_time(control_connection_t *control_conn,
+ const char *question,
+ char **answer, const char **errmsg)
+{
+ (void)control_conn;
+ (void)errmsg;
+
+ struct timeval now;
+ tor_gettimeofday(&now);
+ char timebuf[ISO_TIME_LEN+1];
+
+ if (!strcmp(question, "current-time/local"))
+ format_local_iso_time_nospace(timebuf, (time_t)now.tv_sec);
+ else if (!strcmp(question, "current-time/utc"))
+ format_iso_time_nospace(timebuf, (time_t)now.tv_sec);
+ else
+ return 0;
+
+ *answer = tor_strdup(timebuf);
+ return 0;
+}
+
/** Implementation helper for GETINFO: knows the answers for questions about
* directory information. */
STATIC int
@@ -3082,6 +3186,9 @@ static const getinfo_item_t getinfo_items[] = {
DOC("config/defaults",
"List of default values for configuration options. "
"See also config/names"),
+ PREFIX("current-time/", current_time, "Current time."),
+ DOC("current-time/local", "Current time on the local system."),
+ DOC("current-time/utc", "Current UTC time."),
PREFIX("downloads/networkstatus/", downloads,
"Download statuses for networkstatus objects"),
DOC("downloads/networkstatus/ns",
@@ -4499,7 +4606,7 @@ handle_control_add_onion(control_connection_t *conn,
const char *body)
{
smartlist_t *args;
- size_t arg_len;
+ int arg_len;
(void) len; /* body is nul-terminated; it's safe to ignore the length */
args = getargs_helper("ADD_ONION", conn, body, 2, -1);
if (!args)
@@ -4520,7 +4627,7 @@ handle_control_add_onion(control_connection_t *conn,
rend_auth_type_t auth_type = REND_NO_AUTH;
/* Default to adding an anonymous hidden service if no flag is given */
int non_anonymous = 0;
- for (size_t i = 1; i < arg_len; i++) {
+ for (int i = 1; i < arg_len; i++) {
static const char *port_prefix = "Port=";
static const char *flags_prefix = "Flags=";
static const char *max_s_prefix = "MaxStreams=";
@@ -5817,8 +5924,6 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp,
int
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)) {
@@ -5834,12 +5939,6 @@ control_event_stream_bandwidth(edge_connection_t *edge_conn)
(unsigned long)edge_conn->n_written,
tbuf);
- circ = circuit_get_by_edge_conn(edge_conn);
- if (circ && CIRCUIT_IS_ORIGIN(circ)) {
- ocirc = TO_ORIGIN_CIRCUIT(circ);
- ocirc->n_read_circ_bw += edge_conn->n_read;
- ocirc->n_written_circ_bw += edge_conn->n_written;
- }
edge_conn->n_written = edge_conn->n_read = 0;
}
@@ -5902,13 +6001,20 @@ control_event_circ_bandwidth_used(void)
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 "
- "TIME=%s\r\n",
+ "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu TIME=%s "
+ "DELIVERED_READ=%lu OVERHEAD_READ=%lu "
+ "DELIVERED_WRITTEN=%lu OVERHEAD_WRITTEN=%lu\r\n",
ocirc->global_identifier,
(unsigned long)ocirc->n_read_circ_bw,
(unsigned long)ocirc->n_written_circ_bw,
- tbuf);
+ tbuf,
+ (unsigned long)ocirc->n_delivered_read_circ_bw,
+ (unsigned long)ocirc->n_overhead_read_circ_bw,
+ (unsigned long)ocirc->n_delivered_written_circ_bw,
+ (unsigned long)ocirc->n_overhead_written_circ_bw);
ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
+ ocirc->n_overhead_written_circ_bw = ocirc->n_overhead_read_circ_bw = 0;
+ ocirc->n_delivered_written_circ_bw = ocirc->n_delivered_read_circ_bw = 0;
}
SMARTLIST_FOREACH_END(circ);
@@ -6093,28 +6199,6 @@ control_event_circuit_cell_stats(void)
return 0;
}
-/** Tokens in <b>bucket</b> have been refilled: the read bucket was empty
- * for <b>read_empty_time</b> millis, the write bucket was empty for
- * <b>write_empty_time</b> millis, and buckets were last refilled
- * <b>milliseconds_elapsed</b> millis ago. Only emit TB_EMPTY event if
- * either read or write bucket have been empty before. */
-int
-control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
- uint32_t write_empty_time,
- int milliseconds_elapsed)
-{
- if (get_options()->TestingEnableTbEmptyEvent &&
- EVENT_IS_INTERESTING(EVENT_TB_EMPTY) &&
- (read_empty_time > 0 || write_empty_time > 0)) {
- send_control_event(EVENT_TB_EMPTY,
- "650 TB_EMPTY %s READ=%d WRITTEN=%d "
- "LAST=%d\r\n",
- bucket, read_empty_time, write_empty_time,
- milliseconds_elapsed);
- }
- return 0;
-}
-
/* about 5 minutes worth. */
#define N_BW_EVENTS_TO_CACHE 300
/* Index into cached_bw_events to next write. */
@@ -6202,7 +6286,7 @@ control_event_logmsg(int severity, uint32_t domain, const char *msg)
int event;
/* Don't even think of trying to add stuff to a buffer from a cpuworker
- * thread. */
+ * thread. (See #25987 for plan to fix.) */
if (! in_main_thread())
return;
@@ -6248,6 +6332,23 @@ control_event_logmsg(int severity, uint32_t domain, const char *msg)
}
}
+/**
+ * Logging callback: called when there is a queued pending log callback.
+ */
+void
+control_event_logmsg_pending(void)
+{
+ if (! in_main_thread()) {
+ /* We can't handle this case yet, since we're using a
+ * mainloop_event_t to invoke queued_events_flush_all. We ought to
+ * use a different mechanism instead: see #25987.
+ **/
+ return;
+ }
+ tor_assert(flush_queued_events_event);
+ mainloop_event_activate(flush_queued_events_event);
+}
+
/** Called whenever we receive new router descriptors: tell any
* interested control connections. <b>routers</b> is a list of
* routerinfo_t's.
@@ -7029,6 +7130,8 @@ control_event_bootstrap_problem(const char *warn, const char *reason,
if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
dowarn = 1;
+ /* Don't warn about our bootstrapping status if we are hibernating or
+ * shutting down. */
if (we_are_hibernating())
dowarn = 0;
@@ -7598,20 +7701,31 @@ control_event_hs_descriptor_upload_failed(const char *id_digest,
void
control_free_all(void)
{
+ smartlist_t *queued_events = NULL;
+
+ stats_prev_n_read = stats_prev_n_written = 0;
+
if (authentication_cookie) /* Free the auth cookie */
tor_free(authentication_cookie);
if (detached_onion_services) { /* Free the detached onion services */
SMARTLIST_FOREACH(detached_onion_services, char *, cp, tor_free(cp));
smartlist_free(detached_onion_services);
}
- if (queued_control_events) {
- SMARTLIST_FOREACH(queued_control_events, queued_event_t *, ev,
- queued_event_free(ev));
- smartlist_free(queued_control_events);
+
+ if (queued_control_events_lock) {
+ tor_mutex_acquire(queued_control_events_lock);
+ flush_queued_event_pending = 0;
+ queued_events = queued_control_events;
queued_control_events = NULL;
+ tor_mutex_release(queued_control_events_lock);
+ }
+ if (queued_events) {
+ SMARTLIST_FOREACH(queued_events, queued_event_t *, ev,
+ queued_event_free(ev));
+ smartlist_free(queued_events);
}
if (flush_queued_events_event) {
- tor_event_free(flush_queued_events_event);
+ mainloop_event_free(flush_queued_events_event);
flush_queued_events_event = NULL;
}
bootstrap_percent = BOOTSTRAP_STATUS_UNDEF;
@@ -7621,7 +7735,6 @@ control_free_all(void)
global_event_mask = 0;
disable_log_messages = 0;
memset(last_sent_bootstrap_message, 0, sizeof(last_sent_bootstrap_message));
- flush_queued_event_pending = 0;
}
#ifdef TOR_UNIT_TESTS
@@ -7632,3 +7745,4 @@ control_testing_set_global_event_mask(uint64_t mask)
global_event_mask = mask;
}
#endif /* defined(TOR_UNIT_TESTS) */
+
diff --git a/src/or/control.h b/src/or/control.h
index 28ffeaed86..92cbf866dd 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -40,6 +40,9 @@ int connection_control_process_inbuf(control_connection_t *conn);
#define EVENT_NS 0x000F
int control_event_is_interesting(int event);
+void control_per_second_events(void);
+int control_any_per_second_event_enabled(void);
+
int control_event_circuit_status(origin_circuit_t *circ,
circuit_status_event_t e, int reason);
int control_event_circuit_purpose_changed(origin_circuit_t *circ,
@@ -59,10 +62,8 @@ int control_event_circ_bandwidth_used(void);
int control_event_conn_bandwidth(connection_t *conn);
int control_event_conn_bandwidth_used(void);
int control_event_circuit_cell_stats(void);
-int control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
- uint32_t write_empty_time,
- int milliseconds_elapsed);
void control_event_logmsg(int severity, uint32_t domain, const char *msg);
+void control_event_logmsg_pending(void);
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,
@@ -194,7 +195,7 @@ void control_free_all(void);
#define EVENT_CONF_CHANGED 0x0019
#define EVENT_CONN_BW 0x001A
#define EVENT_CELL_STATS 0x001B
-#define EVENT_TB_EMPTY 0x001C
+/* UNUSED : 0x001C */
#define EVENT_CIRC_BANDWIDTH_USED 0x001D
#define EVENT_TRANSPORT_LAUNCHED 0x0020
#define EVENT_HS_DESC 0x0021
@@ -314,6 +315,10 @@ STATIC int getinfo_helper_dir(
control_connection_t *control_conn,
const char *question, char **answer,
const char **errmsg);
+STATIC int getinfo_helper_current_time(
+ control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg);
#endif /* defined(CONTROL_PRIVATE) */
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index 50761dd4d3..15ef6869cf 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -24,14 +24,14 @@
#include "connection_or.h"
#include "config.h"
#include "cpuworker.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "main.h"
#include "onion.h"
#include "rephist.h"
#include "router.h"
#include "workqueue.h"
-#include <event2/event.h>
-
static void queue_pending_tasks(void);
typedef struct worker_state_s {
@@ -69,22 +69,12 @@ worker_state_free_void(void *arg)
static replyqueue_t *replyqueue = NULL;
static threadpool_t *threadpool = NULL;
-static struct event *reply_event = NULL;
static tor_weak_rng_t request_sample_rng = TOR_WEAK_RNG_INIT;
static int total_pending_tasks = 0;
static int max_pending_tasks = 128;
-static void
-replyqueue_process_cb(evutil_socket_t sock, short events, void *arg)
-{
- replyqueue_t *rq = arg;
- (void) sock;
- (void) events;
- replyqueue_process(rq);
-}
-
/** Initialize the cpuworker subsystem. It is OK to call this more than once
* during Tor's lifetime.
*/
@@ -94,14 +84,6 @@ cpu_init(void)
if (!replyqueue) {
replyqueue = replyqueue_new(0);
}
- if (!reply_event) {
- reply_event = tor_event_new(tor_libevent_get_base(),
- replyqueue_get_socket(replyqueue),
- EV_READ|EV_PERSIST,
- replyqueue_process_cb,
- replyqueue);
- event_add(reply_event, NULL);
- }
if (!threadpool) {
/*
In our threadpool implementation, half the threads are permissive and
@@ -115,7 +97,12 @@ cpu_init(void)
worker_state_new,
worker_state_free_void,
NULL);
+
+ int r = threadpool_register_reply_event(threadpool, NULL);
+
+ tor_assert(r == 0);
}
+
/* Total voodoo. Can we make this more sensible? */
max_pending_tasks = get_num_cpus(get_options()) * 64;
crypto_seed_weak_rng(&request_sample_rng);
@@ -547,7 +534,7 @@ assign_onionskin_to_cpuworker(or_circuit_t *circ,
return 0;
}
- if (connection_or_digest_is_known_relay(circ->p_chan->identity_digest))
+ if (!channel_is_client(circ->p_chan))
rep_hist_note_circuit_handshake_assigned(onionskin->handshake_type);
should_time = should_time_request(onionskin->handshake_type);
diff --git a/src/or/dircollate.c b/src/or/dirauth/dircollate.c
index ce4534ff6c..dec6f75154 100644
--- a/src/or/dircollate.c
+++ b/src/or/dirauth/dircollate.c
@@ -25,7 +25,6 @@
#include "dircollate.h"
#include "dirvote.h"
-static void dircollator_collate_by_rsa(dircollator_t *dc);
static void dircollator_collate_by_ed25519(dircollator_t *dc);
/** Hashtable entry mapping a pair of digests (actually an ed25519 key and an
@@ -208,49 +207,18 @@ dircollator_add_vote(dircollator_t *dc, networkstatus_t *v)
void
dircollator_collate(dircollator_t *dc, int consensus_method)
{
+ (void) consensus_method;
+
tor_assert(!dc->is_collated);
dc->all_rsa_sha1_lst = smartlist_new();
- if (consensus_method < MIN_METHOD_FOR_ED25519_ID_VOTING)
- dircollator_collate_by_rsa(dc);
- else
- dircollator_collate_by_ed25519(dc);
+ dircollator_collate_by_ed25519(dc);
smartlist_sort_digests(dc->all_rsa_sha1_lst);
dc->is_collated = 1;
}
/**
- * Collation function for RSA-only consensuses: collate the votes for each
- * entry in <b>dc</b> by their RSA keys.
- *
- * The rule is:
- * If an RSA identity key is listed by more than half of the authorities,
- * include that identity, and treat all descriptors with that RSA identity
- * as describing the same router.
- */
-static void
-dircollator_collate_by_rsa(dircollator_t *dc)
-{
- const int total_authorities = dc->n_authorities;
-
- DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) {
- int n = 0, i;
- for (i = 0; i < dc->n_votes; ++i) {
- if (vrs_lst[i] != NULL)
- ++n;
- }
-
- if (n <= total_authorities / 2)
- continue;
-
- smartlist_add(dc->all_rsa_sha1_lst, (char *)k);
- } DIGESTMAP_FOREACH_END;
-
- dc->by_collated_rsa_sha1 = dc->by_rsa_sha1;
-}
-
-/**
* Collation function for ed25519 consensuses: collate the votes for each
* entry in <b>dc</b> by ed25519 key and by RSA key.
*
diff --git a/src/or/dircollate.h b/src/or/dirauth/dircollate.h
index 0584b2fe06..0584b2fe06 100644
--- a/src/or/dircollate.h
+++ b/src/or/dirauth/dircollate.h
diff --git a/src/or/dirvote.c b/src/or/dirauth/dirvote.c
index 7023d4f951..bf05286b3d 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirauth/dirvote.c
@@ -9,9 +9,10 @@
#include "dircollate.h"
#include "directory.h"
#include "dirserv.h"
-#include "dirvote.h"
#include "microdesc.h"
#include "networkstatus.h"
+#include "nodelist.h"
+#include "parsecommon.h"
#include "policies.h"
#include "protover.h"
#include "rephist.h"
@@ -21,7 +22,11 @@
#include "routerparse.h"
#include "entrynodes.h" /* needed for guardfraction methods */
#include "torcert.h"
-#include "shared_random_state.h"
+#include "voting_schedule.h"
+
+#include "dirauth/dirvote.h"
+#include "dirauth/mode.h"
+#include "dirauth/shared_random_state.h"
/**
* \file dirvote.c
@@ -92,6 +97,30 @@ static int dirvote_compute_consensuses(void);
static int dirvote_publish_consensus(void);
/* =====
+ * Certificate functions
+ * ===== */
+
+/** Allocate and return a new authority_cert_t with the same contents as
+ * <b>cert</b>. */
+STATIC authority_cert_t *
+authority_cert_dup(authority_cert_t *cert)
+{
+ authority_cert_t *out = tor_malloc(sizeof(authority_cert_t));
+ tor_assert(cert);
+
+ memcpy(out, cert, sizeof(authority_cert_t));
+ /* Now copy pointed-to things. */
+ out->cache_info.signed_descriptor_body =
+ tor_strndup(cert->cache_info.signed_descriptor_body,
+ cert->cache_info.signed_descriptor_len);
+ out->cache_info.saved_location = SAVED_NOWHERE;
+ out->identity_key = crypto_pk_dup_key(cert->identity_key);
+ out->signing_key = crypto_pk_dup_key(cert->signing_key);
+
+ return out;
+}
+
+/* =====
* Voting
* =====*/
@@ -347,10 +376,73 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
return status;
}
+/** Set *<b>timing_out</b> to the intervals at which we would like to vote.
+ * Note that these aren't the intervals we'll use to vote; they're the ones
+ * that we'll vote to use. */
+static void
+dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out)
+{
+ const or_options_t *options = get_options();
+
+ tor_assert(timing_out);
+
+ timing_out->vote_interval = options->V3AuthVotingInterval;
+ timing_out->n_intervals_valid = options->V3AuthNIntervalsValid;
+ timing_out->vote_delay = options->V3AuthVoteDelay;
+ timing_out->dist_delay = options->V3AuthDistDelay;
+}
+
/* =====
* Consensus generation
* ===== */
+/** If <b>vrs</b> has a hash made for the consensus method <b>method</b> with
+ * the digest algorithm <b>alg</b>, decode it and copy it into
+ * <b>digest256_out</b> and return 0. Otherwise return -1. */
+static int
+vote_routerstatus_find_microdesc_hash(char *digest256_out,
+ const vote_routerstatus_t *vrs,
+ int method,
+ digest_algorithm_t alg)
+{
+ /* XXXX only returns the sha256 method. */
+ const vote_microdesc_hash_t *h;
+ char mstr[64];
+ size_t mlen;
+ char dstr[64];
+
+ tor_snprintf(mstr, sizeof(mstr), "%d", method);
+ mlen = strlen(mstr);
+ tor_snprintf(dstr, sizeof(dstr), " %s=",
+ crypto_digest_algorithm_get_name(alg));
+
+ for (h = vrs->microdesc; h; h = h->next) {
+ const char *cp = h->microdesc_hash_line;
+ size_t num_len;
+ /* cp looks like \d+(,\d+)* (digesttype=val )+ . Let's hunt for mstr in
+ * the first part. */
+ while (1) {
+ num_len = strspn(cp, "1234567890");
+ if (num_len == mlen && fast_memeq(mstr, cp, mlen)) {
+ /* This is the line. */
+ char buf[BASE64_DIGEST256_LEN+1];
+ /* XXXX ignores extraneous stuff if the digest is too long. This
+ * seems harmless enough, right? */
+ cp = strstr(cp, dstr);
+ if (!cp)
+ return -1;
+ cp += strlen(dstr);
+ strlcpy(buf, cp, sizeof(buf));
+ return digest256_from_base64(digest256_out, buf);
+ }
+ if (num_len == 0 || cp[num_len] != ',')
+ break;
+ cp += num_len + 1;
+ }
+ }
+ return -1;
+}
+
/** Given a vote <b>vote</b> (not a consensus!), return its associated
* networkstatus_voter_info_t. */
static networkstatus_voter_info_t *
@@ -363,20 +455,6 @@ get_voter(const networkstatus_t *vote)
return smartlist_get(vote->voters, 0);
}
-/** Return the signature made by <b>voter</b> using the algorithm
- * <b>alg</b>, or NULL if none is found. */
-document_signature_t *
-voter_get_sig_by_algorithm(const networkstatus_voter_info_t *voter,
- digest_algorithm_t alg)
-{
- if (!voter->sigs)
- return NULL;
- SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
- if (sig->alg == alg)
- return sig);
- return NULL;
-}
-
/** Temporary structure used in constructing a list of dir-source entries
* for a consensus. One of these is generated for every vote, and one more
* for every legacy key in each vote. */
@@ -549,12 +627,12 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method,
tor_assert(most);
- /* If we're producing "a" lines, vote on potential alternative (sets
- * of) OR port(s) in the winning routerstatuses.
+ /* Vote on potential alternative (sets of) OR port(s) in the winning
+ * routerstatuses.
*
* XXX prop186 There's at most one alternative OR port (_the_ IPv6
* port) for now. */
- if (consensus_method >= MIN_METHOD_FOR_A_LINES && best_alt_orport_out) {
+ if (best_alt_orport_out) {
smartlist_t *alt_orports = smartlist_new();
const tor_addr_port_t *most_alt_orport = NULL;
@@ -664,13 +742,6 @@ compute_consensus_method(smartlist_t *votes)
static int
consensus_method_is_supported(int method)
{
- if (method == MIN_METHOD_FOR_ED25519_ID_IN_MD) {
- /* This method was broken due to buggy code accidentally left in
- * dircollate.c; do not actually use it.
- */
- return 0;
- }
-
return (method >= MIN_SUPPORTED_CONSENSUS_METHOD) &&
(method <= MAX_SUPPORTED_CONSENSUS_METHOD);
}
@@ -1343,7 +1414,7 @@ compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes)
* behavior, and make the new behavior conditional on a new-enough
* consensus_method.
**/
-char *
+STATIC char *
networkstatus_compute_consensus(smartlist_t *votes,
int total_authorities,
crypto_pk_t *identity_key,
@@ -1463,19 +1534,14 @@ networkstatus_compute_consensus(smartlist_t *votes,
n_versioning_servers);
client_versions = compute_consensus_versions_list(combined_client_versions,
n_versioning_clients);
- if (consensus_method >= MIN_METHOD_FOR_PACKAGE_LINES) {
- packages = compute_consensus_package_lines(votes);
- } else {
- packages = tor_strdup("");
- }
+ packages = compute_consensus_package_lines(votes);
SMARTLIST_FOREACH(combined_server_versions, char *, cp, tor_free(cp));
SMARTLIST_FOREACH(combined_client_versions, char *, cp, tor_free(cp));
smartlist_free(combined_server_versions);
smartlist_free(combined_client_versions);
- if (consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING)
- smartlist_add_strdup(flags, "NoEdConsensus");
+ smartlist_add_strdup(flags, "NoEdConsensus");
smartlist_sort_strings(flags);
smartlist_uniq_strings(flags);
@@ -1524,7 +1590,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
tor_free(flaglist);
}
- if (consensus_method >= MIN_METHOD_FOR_RECOMMENDED_PROTOCOLS) {
+ {
int num_dirauth = get_n_authorities(V3_DIRINFO);
int idx;
for (idx = 0; idx < 4; ++idx) {
@@ -1544,7 +1610,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_add_strdup(chunks, "\n");
}
- if (consensus_method >= MIN_METHOD_FOR_SHARED_RANDOM) {
+ {
int num_dirauth = get_n_authorities(V3_DIRINFO);
/* Default value of this is 2/3 of the total number of authorities. For
* instance, if we have 9 dirauth, the default value is 6. The following
@@ -1609,7 +1675,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_free(dir_sources);
}
- if (consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW) {
+ {
char *max_unmeasured_param = NULL;
/* XXXX Extract this code into a common function. Or don't! see #19011 */
if (params) {
@@ -1871,7 +1937,6 @@ networkstatus_compute_consensus(smartlist_t *votes,
continue;
if (ed_consensus > 0) {
- tor_assert(consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING);
if (ed_consensus <= total_authorities / 2) {
log_warn(LD_BUG, "Not enough entries had ed_consensus set; how "
"can we have a consensus of %d?", ed_consensus);
@@ -1898,10 +1963,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
rs_out.published_on = rs->status.published_on;
rs_out.dir_port = rs->status.dir_port;
rs_out.or_port = rs->status.or_port;
- if (consensus_method >= MIN_METHOD_FOR_A_LINES) {
- tor_addr_copy(&rs_out.ipv6_addr, &alt_orport.addr);
- rs_out.ipv6_orport = alt_orport.port;
- }
+ tor_addr_copy(&rs_out.ipv6_addr, &alt_orport.addr);
+ rs_out.ipv6_orport = alt_orport.port;
rs_out.has_bandwidth = 0;
rs_out.has_exitsummary = 0;
@@ -1931,8 +1994,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
} else if (!strcmp(fl, "Unnamed")) {
if (is_unnamed)
smartlist_add(chosen_flags, (char*)fl);
- } else if (!strcmp(fl, "NoEdConsensus") &&
- consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING) {
+ } else if (!strcmp(fl, "NoEdConsensus")) {
if (ed_consensus <= total_authorities/2)
smartlist_add(chosen_flags, (char*)fl);
} else {
@@ -1959,8 +2021,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* Starting with consensus method 24, we don't list servers
* that are not valid in a consensus. See Proposal 272 */
- if (!is_valid &&
- consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES)
+ if (!is_valid)
continue;
/* Pick the version. */
@@ -1981,8 +2042,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* If it's a guard and we have enough guardfraction votes,
calculate its consensus guardfraction value. */
- if (is_guard && num_guardfraction_inputs > 2 &&
- consensus_method >= MIN_METHOD_FOR_GUARDFRACTION) {
+ if (is_guard && num_guardfraction_inputs > 2) {
rs_out.has_guardfraction = 1;
rs_out.guardfraction_percentage = median_uint32(measured_guardfraction,
num_guardfraction_inputs);
@@ -1999,8 +2059,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
rs_out.has_bandwidth = 1;
rs_out.bw_is_unmeasured = 1;
rs_out.bandwidth_kb = median_uint32(bandwidths_kb, num_bandwidths);
- if (consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW &&
- n_authorities_measuring_bandwidth > 2) {
+ if (n_authorities_measuring_bandwidth > 2) {
/* Cap non-measured bandwidths. */
if (rs_out.bandwidth_kb > max_unmeasured_bw_kb) {
rs_out.bandwidth_kb = max_unmeasured_bw_kb;
@@ -2140,8 +2199,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* Now the weight line. */
if (rs_out.has_bandwidth) {
char *guardfraction_str = NULL;
- int unmeasured = rs_out.bw_is_unmeasured &&
- consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW;
+ int unmeasured = rs_out.bw_is_unmeasured;
/* If we have guardfraction info, include it in the 'w' line. */
if (rs_out.has_guardfraction) {
@@ -2400,7 +2458,7 @@ compute_consensus_package_lines(smartlist_t *votes)
* new signature is verifiable.) Return the number of signatures added or
* changed, or -1 if the document signed by <b>sigs</b> isn't the same
* document as <b>target</b>. */
-int
+STATIC int
networkstatus_add_detached_signatures(networkstatus_t *target,
ns_detached_signatures_t *sigs,
const char *source,
@@ -2484,7 +2542,7 @@ networkstatus_add_detached_signatures(networkstatus_t *target,
continue;
}
- old_sig = voter_get_sig_by_algorithm(target_voter, sig->alg);
+ old_sig = networkstatus_get_voter_sig_by_alg(target_voter, sig->alg);
/* If the target already has a good signature from this voter, then skip
* this one. */
@@ -2592,7 +2650,7 @@ networkstatus_format_signatures(networkstatus_t *consensus,
* corresponding to the signatures on <b>consensuses</b>, which must contain
* exactly one FLAV_NS consensus, and no more than one consensus for each
* other flavor. */
-char *
+STATIC char *
networkstatus_get_detached_signatures(smartlist_t *consensuses)
{
smartlist_t *elements;
@@ -2697,219 +2755,16 @@ get_detached_signatures_from_pending_consensuses(pending_consensus_t *pending,
return signatures;
}
-/** Release all storage held in <b>s</b>. */
-void
-ns_detached_signatures_free_(ns_detached_signatures_t *s)
-{
- if (!s)
- return;
- if (s->signatures) {
- STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) {
- SMARTLIST_FOREACH(sigs, document_signature_t *, sig,
- document_signature_free(sig));
- smartlist_free(sigs);
- } STRMAP_FOREACH_END;
- strmap_free(s->signatures, NULL);
- strmap_free(s->digests, tor_free_);
- }
-
- tor_free(s);
-}
-
-/* =====
- * Certificate functions
- * ===== */
-
-/** Allocate and return a new authority_cert_t with the same contents as
- * <b>cert</b>. */
-authority_cert_t *
-authority_cert_dup(authority_cert_t *cert)
-{
- authority_cert_t *out = tor_malloc(sizeof(authority_cert_t));
- tor_assert(cert);
-
- memcpy(out, cert, sizeof(authority_cert_t));
- /* Now copy pointed-to things. */
- out->cache_info.signed_descriptor_body =
- tor_strndup(cert->cache_info.signed_descriptor_body,
- cert->cache_info.signed_descriptor_len);
- out->cache_info.saved_location = SAVED_NOWHERE;
- out->identity_key = crypto_pk_dup_key(cert->identity_key);
- out->signing_key = crypto_pk_dup_key(cert->signing_key);
-
- return out;
-}
-
-/* =====
- * Vote scheduling
- * ===== */
-
-/** Set *<b>timing_out</b> to the intervals at which we would like to vote.
- * Note that these aren't the intervals we'll use to vote; they're the ones
- * that we'll vote to use. */
-void
-dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out)
-{
- const or_options_t *options = get_options();
-
- tor_assert(timing_out);
-
- timing_out->vote_interval = options->V3AuthVotingInterval;
- timing_out->n_intervals_valid = options->V3AuthNIntervalsValid;
- timing_out->vote_delay = options->V3AuthVoteDelay;
- timing_out->dist_delay = options->V3AuthDistDelay;
-}
-
-/** Return the start of the next interval of size <b>interval</b> (in
- * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always
- * starts a fresh interval, and if the last interval of a day would be
- * truncated to less than half its size, it is rolled into the
- * previous interval. */
-time_t
-dirvote_get_start_of_next_interval(time_t now, int interval, int offset)
-{
- struct tm tm;
- time_t midnight_today=0;
- time_t midnight_tomorrow;
- time_t next;
-
- tor_gmtime_r(&now, &tm);
- tm.tm_hour = 0;
- tm.tm_min = 0;
- tm.tm_sec = 0;
-
- if (tor_timegm(&tm, &midnight_today) < 0) {
- log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight.");
- }
- midnight_tomorrow = midnight_today + (24*60*60);
-
- next = midnight_today + ((now-midnight_today)/interval + 1)*interval;
-
- /* Intervals never cross midnight. */
- if (next > midnight_tomorrow)
- next = midnight_tomorrow;
-
- /* If the interval would only last half as long as it's supposed to, then
- * skip over to the next day. */
- if (next + interval/2 > midnight_tomorrow)
- next = midnight_tomorrow;
-
- next += offset;
- if (next - interval > now)
- next -= interval;
-
- return next;
-}
-
-/* Populate and return a new voting_schedule_t that can be used to schedule
- * voting. The object is allocated on the heap and it's the responsibility of
- * the caller to free it. Can't fail. */
-static voting_schedule_t *
-get_voting_schedule(const or_options_t *options, time_t now, int severity)
-{
- int interval, vote_delay, dist_delay;
- time_t start;
- time_t end;
- networkstatus_t *consensus;
- voting_schedule_t *new_voting_schedule;
-
- new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t));
-
- consensus = networkstatus_get_live_consensus(now);
-
- if (consensus) {
- interval = (int)( consensus->fresh_until - consensus->valid_after );
- vote_delay = consensus->vote_seconds;
- dist_delay = consensus->dist_seconds;
- } else {
- interval = options->TestingV3AuthInitialVotingInterval;
- vote_delay = options->TestingV3AuthInitialVoteDelay;
- dist_delay = options->TestingV3AuthInitialDistDelay;
- }
-
- tor_assert(interval > 0);
-
- if (vote_delay + dist_delay > interval/2)
- vote_delay = dist_delay = interval / 4;
-
- start = new_voting_schedule->interval_starts =
- dirvote_get_start_of_next_interval(now,interval,
- options->TestingV3AuthVotingStartOffset);
- end = dirvote_get_start_of_next_interval(start+1, interval,
- options->TestingV3AuthVotingStartOffset);
-
- tor_assert(end > start);
-
- new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2);
- new_voting_schedule->voting_ends = start - dist_delay;
- new_voting_schedule->fetch_missing_votes =
- start - dist_delay - (vote_delay/2);
- new_voting_schedule->voting_starts = start - dist_delay - vote_delay;
-
- {
- char tbuf[ISO_TIME_LEN+1];
- format_iso_time(tbuf, new_voting_schedule->interval_starts);
- tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: "
- "consensus_set=%d, interval=%d",
- tbuf, consensus?1:0, interval);
- }
-
- return new_voting_schedule;
-}
-
-#define voting_schedule_free(s) \
- FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s))
-
-/** Frees a voting_schedule_t. This should be used instead of the generic
- * tor_free. */
-static void
-voting_schedule_free_(voting_schedule_t *voting_schedule_to_free)
-{
- if (!voting_schedule_to_free)
- return;
- tor_free(voting_schedule_to_free);
-}
-
-static voting_schedule_t voting_schedule;
-
-/* Using the time <b>now</b>, return the next voting valid-after time. */
+/**
+ * Entry point: Take whatever voting actions are pending as of <b>now</b>.
+ *
+ * Return the time at which the next action should be taken.
+ */
time_t
-dirvote_get_next_valid_after_time(void)
-{
- /* This is a safe guard in order to make sure that the voting schedule
- * static object is at least initialized. Using this function with a zeroed
- * voting schedule can lead to bugs. */
- if (tor_mem_is_zero((const char *) &voting_schedule,
- sizeof(voting_schedule))) {
- dirvote_recalculate_timing(get_options(), time(NULL));
- voting_schedule.created_on_demand = 1;
- }
- return voting_schedule.interval_starts;
-}
-
-/** Set voting_schedule to hold the timing for the next vote we should be
- * doing. All type of tor do that because HS subsystem needs the timing as
- * well to function properly. */
-void
-dirvote_recalculate_timing(const or_options_t *options, time_t now)
-{
- voting_schedule_t *new_voting_schedule;
-
- /* get the new voting schedule */
- new_voting_schedule = get_voting_schedule(options, now, LOG_INFO);
- tor_assert(new_voting_schedule);
-
- /* Fill in the global static struct now */
- memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule));
- voting_schedule_free(new_voting_schedule);
-}
-
-/** Entry point: Take whatever voting actions are pending as of <b>now</b>. */
-void
dirvote_act(const or_options_t *options, time_t now)
{
if (!authdir_mode_v3(options))
- return;
+ return TIME_MAX;
tor_assert_nonfatal(voting_schedule.voting_starts);
/* If we haven't initialized this object through this codeflow, we need to
* recalculate the timings to match our vote. The reason to do that is if we
@@ -2923,35 +2778,43 @@ dirvote_act(const or_options_t *options, time_t now)
"Mine is %s.",
keys, hex_str(c->cache_info.identity_digest, DIGEST_LEN));
tor_free(keys);
- dirvote_recalculate_timing(options, now);
+ voting_schedule_recalculate_timing(options, now);
}
- if (voting_schedule.voting_starts < now && !voting_schedule.have_voted) {
+
+#define IF_TIME_FOR_NEXT_ACTION(when_field, done_field) \
+ if (! voting_schedule.done_field) { \
+ if (voting_schedule.when_field > now) { \
+ return voting_schedule.when_field; \
+ } else {
+#define ENDIF \
+ } \
+ }
+
+ IF_TIME_FOR_NEXT_ACTION(voting_starts, have_voted) {
log_notice(LD_DIR, "Time to vote.");
dirvote_perform_vote();
voting_schedule.have_voted = 1;
- }
- if (voting_schedule.fetch_missing_votes < now &&
- !voting_schedule.have_fetched_missing_votes) {
+ } ENDIF
+ IF_TIME_FOR_NEXT_ACTION(fetch_missing_votes, have_fetched_missing_votes) {
log_notice(LD_DIR, "Time to fetch any votes that we're missing.");
dirvote_fetch_missing_votes();
voting_schedule.have_fetched_missing_votes = 1;
- }
- if (voting_schedule.voting_ends < now &&
- !voting_schedule.have_built_consensus) {
+ } ENDIF
+ IF_TIME_FOR_NEXT_ACTION(voting_ends, have_built_consensus) {
log_notice(LD_DIR, "Time to compute a consensus.");
dirvote_compute_consensuses();
/* XXXX We will want to try again later if we haven't got enough
* votes yet. Implement this if it turns out to ever happen. */
voting_schedule.have_built_consensus = 1;
- }
- if (voting_schedule.fetch_missing_signatures < now &&
- !voting_schedule.have_fetched_missing_signatures) {
+ } ENDIF
+ IF_TIME_FOR_NEXT_ACTION(fetch_missing_signatures,
+ have_fetched_missing_signatures) {
log_notice(LD_DIR, "Time to fetch any signatures that we're missing.");
dirvote_fetch_missing_signatures();
voting_schedule.have_fetched_missing_signatures = 1;
- }
- if (voting_schedule.interval_starts < now &&
- !voting_schedule.have_published_consensus) {
+ } ENDIF
+ IF_TIME_FOR_NEXT_ACTION(interval_starts,
+ have_published_consensus) {
log_notice(LD_DIR, "Time to publish the consensus and discard old votes");
dirvote_publish_consensus();
dirvote_clear_votes(0);
@@ -2961,8 +2824,15 @@ dirvote_act(const or_options_t *options, time_t now)
networkstatus_get_latest_consensus_by_flavor(FLAV_NS));
/* XXXX We will want to try again later if we haven't got enough
* signatures yet. Implement this if it turns out to ever happen. */
- dirvote_recalculate_timing(options, now);
- }
+ voting_schedule_recalculate_timing(options, now);
+ return voting_schedule.voting_starts;
+ } ENDIF
+
+ tor_assert_nonfatal_unreached();
+ return now + 1;
+
+#undef ENDIF
+#undef IF_TIME_FOR_NEXT_ACTION
}
/** A vote networkstatus_t and its unparsed body: held around so we can
@@ -3826,7 +3696,7 @@ dirvote_get_vote(const char *fp, int flags)
/** Construct and return a new microdescriptor from a routerinfo <b>ri</b>
* according to <b>consensus_method</b>.
**/
-microdesc_t *
+STATIC microdesc_t *
dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
{
microdesc_t *result = NULL;
@@ -3843,8 +3713,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
smartlist_add_asprintf(chunks, "onion-key\n%s", key);
- if (consensus_method >= MIN_METHOD_FOR_NTOR_KEY &&
- ri->onion_curve25519_pkey) {
+ if (ri->onion_curve25519_pkey) {
char kbuf[128];
base64_encode(kbuf, sizeof(kbuf),
(const char*)ri->onion_curve25519_pkey->public_key,
@@ -3854,8 +3723,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
/* We originally put a lines in the micrdescriptors, but then we worked out
* that we needed them in the microdesc consensus. See #20916. */
- if (consensus_method >= MIN_METHOD_FOR_A_LINES &&
- consensus_method < MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC &&
+ if (consensus_method < MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC &&
!tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport)
smartlist_add_asprintf(chunks, "a %s\n",
fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport));
@@ -3866,8 +3734,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
if (summary && strcmp(summary, "reject 1-65535"))
smartlist_add_asprintf(chunks, "p %s\n", summary);
- if (consensus_method >= MIN_METHOD_FOR_P6_LINES &&
- ri->ipv6_exit_policy) {
+ if (ri->ipv6_exit_policy) {
/* XXXX+++ This doesn't match proposal 208, which says these should
* be taken unchanged from the routerinfo. That's bogosity, IMO:
* the proposal should have said to do this instead.*/
@@ -3877,11 +3744,10 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
tor_free(p6);
}
- if (consensus_method >= MIN_METHOD_FOR_ID_HASH_IN_MD) {
+ {
char idbuf[ED25519_BASE64_LEN+1];
const char *keytype;
- if (consensus_method >= MIN_METHOD_FOR_ED25519_ID_IN_MD &&
- ri->cache_info.signing_key_cert &&
+ if (ri->cache_info.signing_key_cert &&
ri->cache_info.signing_key_cert->signing_key_included) {
keytype = "ed25519";
ed25519_public_to_base64(idbuf,
@@ -3925,7 +3791,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
* in a consensus vote document. Write it into the <b>out_len</b>-byte buffer
* in <b>out</b>. Return -1 on failure and the number of characters written
* on success. */
-ssize_t
+static ssize_t
dirvote_format_microdesc_vote_line(char *out_buf, size_t out_buf_len,
const microdesc_t *md,
int consensus_method_low,
@@ -3959,13 +3825,7 @@ static const struct consensus_method_range_t {
int low;
int high;
} microdesc_consensus_methods[] = {
- {MIN_SUPPORTED_CONSENSUS_METHOD, MIN_METHOD_FOR_A_LINES - 1},
- {MIN_METHOD_FOR_A_LINES, MIN_METHOD_FOR_P6_LINES - 1},
- {MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1},
- {MIN_METHOD_FOR_NTOR_KEY, MIN_METHOD_FOR_ID_HASH_IN_MD - 1},
- {MIN_METHOD_FOR_ID_HASH_IN_MD, MIN_METHOD_FOR_ED25519_ID_IN_MD - 1},
- {MIN_METHOD_FOR_ED25519_ID_IN_MD,
- MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC - 1},
+ {MIN_SUPPORTED_CONSENSUS_METHOD, MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC - 1},
{MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC, MAX_SUPPORTED_CONSENSUS_METHOD},
{-1, -1}
};
@@ -4039,49 +3899,692 @@ dirvote_format_all_microdesc_vote_lines(const routerinfo_t *ri, time_t now,
return result;
}
-/** If <b>vrs</b> has a hash made for the consensus method <b>method</b> with
- * the digest algorithm <b>alg</b>, decode it and copy it into
- * <b>digest256_out</b> and return 0. Otherwise return -1. */
-int
-vote_routerstatus_find_microdesc_hash(char *digest256_out,
- const vote_routerstatus_t *vrs,
- int method,
- digest_algorithm_t alg)
+/** Parse and extract all SR commits from <b>tokens</b> and place them in
+ * <b>ns</b>. */
+static void
+extract_shared_random_commits(networkstatus_t *ns, const smartlist_t *tokens)
{
- /* XXXX only returns the sha256 method. */
- const vote_microdesc_hash_t *h;
- char mstr[64];
- size_t mlen;
- char dstr[64];
+ smartlist_t *chunks = NULL;
- tor_snprintf(mstr, sizeof(mstr), "%d", method);
- mlen = strlen(mstr);
- tor_snprintf(dstr, sizeof(dstr), " %s=",
- crypto_digest_algorithm_get_name(alg));
+ tor_assert(ns);
+ tor_assert(tokens);
+ /* Commits are only present in a vote. */
+ tor_assert(ns->type == NS_TYPE_VOTE);
- for (h = vrs->microdesc; h; h = h->next) {
- const char *cp = h->microdesc_hash_line;
- size_t num_len;
- /* cp looks like \d+(,\d+)* (digesttype=val )+ . Let's hunt for mstr in
- * the first part. */
- while (1) {
- num_len = strspn(cp, "1234567890");
- if (num_len == mlen && fast_memeq(mstr, cp, mlen)) {
- /* This is the line. */
- char buf[BASE64_DIGEST256_LEN+1];
- /* XXXX ignores extraneous stuff if the digest is too long. This
- * seems harmless enough, right? */
- cp = strstr(cp, dstr);
- if (!cp)
- return -1;
- cp += strlen(dstr);
- strlcpy(buf, cp, sizeof(buf));
- return digest256_from_base64(digest256_out, buf);
+ ns->sr_info.commits = smartlist_new();
+
+ smartlist_t *commits = find_all_by_keyword(tokens, K_COMMIT);
+ /* It's normal that a vote might contain no commits even if it participates
+ * in the SR protocol. Don't treat it as an error. */
+ if (commits == NULL) {
+ goto end;
+ }
+
+ /* Parse the commit. We do NO validation of number of arguments or ordering
+ * for forward compatibility, it's the parse commit job to inform us if it's
+ * supported or not. */
+ chunks = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(commits, directory_token_t *, tok) {
+ /* Extract all arguments and put them in the chunks list. */
+ for (int i = 0; i < tok->n_args; i++) {
+ smartlist_add(chunks, tok->args[i]);
+ }
+ sr_commit_t *commit = sr_parse_commit(chunks);
+ smartlist_clear(chunks);
+ if (commit == NULL) {
+ /* Get voter identity so we can warn that this dirauth vote contains
+ * commit we can't parse. */
+ networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0);
+ tor_assert(voter);
+ log_warn(LD_DIR, "SR: Unable to parse commit %s from vote of voter %s.",
+ escaped(tok->object_body),
+ hex_str(voter->identity_digest,
+ sizeof(voter->identity_digest)));
+ /* Commitment couldn't be parsed. Continue onto the next commit because
+ * this one could be unsupported for instance. */
+ continue;
+ }
+ /* Add newly created commit object to the vote. */
+ smartlist_add(ns->sr_info.commits, commit);
+ } SMARTLIST_FOREACH_END(tok);
+
+ end:
+ smartlist_free(chunks);
+ smartlist_free(commits);
+}
+
+/* Using the given directory tokens in tokens, parse the shared random commits
+ * and put them in the given vote document ns.
+ *
+ * This also sets the SR participation flag if present in the vote. */
+void
+dirvote_parse_sr_commits(networkstatus_t *ns, const smartlist_t *tokens)
+{
+ /* Does this authority participates in the SR protocol? */
+ directory_token_t *tok = find_opt_by_keyword(tokens, K_SR_FLAG);
+ if (tok) {
+ ns->sr_info.participate = 1;
+ /* Get the SR commitments and reveals from the vote. */
+ extract_shared_random_commits(ns, tokens);
+ }
+}
+
+/* For the given vote, free the shared random commits if any. */
+void
+dirvote_clear_commits(networkstatus_t *ns)
+{
+ tor_assert(ns->type == NS_TYPE_VOTE);
+
+ if (ns->sr_info.commits) {
+ SMARTLIST_FOREACH(ns->sr_info.commits, sr_commit_t *, c,
+ sr_commit_free(c));
+ smartlist_free(ns->sr_info.commits);
+ }
+}
+
+/* The given url is the /tor/status-vote GET directory request. Populates the
+ * items list with strings that we can compress on the fly and dir_items with
+ * cached_dir_t objects that have a precompressed deflated version. */
+void
+dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items,
+ smartlist_t *dir_items)
+{
+ int current;
+
+ url += strlen("/tor/status-vote/");
+ current = !strcmpstart(url, "current/");
+ url = strchr(url, '/');
+ tor_assert(url);
+ ++url;
+ if (!strcmp(url, "consensus")) {
+ const char *item;
+ tor_assert(!current); /* we handle current consensus specially above,
+ * since it wants to be spooled. */
+ if ((item = dirvote_get_pending_consensus(FLAV_NS)))
+ smartlist_add(items, (char*)item);
+ } else if (!current && !strcmp(url, "consensus-signatures")) {
+ /* XXXX the spec says that we should implement
+ * current/consensus-signatures too. It doesn't seem to be needed,
+ * though. */
+ const char *item;
+ if ((item=dirvote_get_pending_detached_signatures()))
+ smartlist_add(items, (char*)item);
+ } else if (!strcmp(url, "authority")) {
+ const cached_dir_t *d;
+ int flags = DGV_BY_ID |
+ (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING);
+ if ((d=dirvote_get_vote(NULL, flags)))
+ smartlist_add(dir_items, (cached_dir_t*)d);
+ } else {
+ const cached_dir_t *d;
+ smartlist_t *fps = smartlist_new();
+ int flags;
+ if (!strcmpstart(url, "d/")) {
+ url += 2;
+ flags = DGV_INCLUDE_PENDING | DGV_INCLUDE_PREVIOUS;
+ } else {
+ flags = DGV_BY_ID |
+ (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING);
+ }
+ dir_split_resource_into_fingerprints(url, fps, NULL,
+ DSR_HEX|DSR_SORT_UNIQ);
+ SMARTLIST_FOREACH(fps, char *, fp, {
+ if ((d = dirvote_get_vote(fp, flags)))
+ smartlist_add(dir_items, (cached_dir_t*)d);
+ tor_free(fp);
+ });
+ smartlist_free(fps);
+ }
+}
+
+/** Get the best estimate of a router's bandwidth for dirauth purposes,
+ * preferring measured to advertised values if available. */
+static uint32_t
+dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri)
+{
+ uint32_t bw_kb = 0;
+ /*
+ * Yeah, measured bandwidths in measured_bw_line_t are (implicitly
+ * signed) longs and the ones router_get_advertised_bandwidth() returns
+ * are uint32_t.
+ */
+ long mbw_kb = 0;
+
+ if (ri) {
+ /*
+ * * First try to see if we have a measured bandwidth; don't bother with
+ * as_of_out here, on the theory that a stale measured bandwidth is still
+ * better to trust than an advertised one.
+ */
+ if (dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest,
+ &mbw_kb, NULL)) {
+ /* Got one! */
+ bw_kb = (uint32_t)mbw_kb;
+ } else {
+ /* If not, fall back to advertised */
+ bw_kb = router_get_advertised_bandwidth(ri) / 1000;
+ }
+ }
+
+ return bw_kb;
+}
+
+/** Helper for sorting: compares two routerinfos first by address, and then by
+ * descending order of "usefulness". (An authority is more useful than a
+ * non-authority; a running router is more useful than a non-running router;
+ * and a router with more bandwidth is more useful than one with less.)
+ **/
+static int
+compare_routerinfo_by_ip_and_bw_(const void **a, const void **b)
+{
+ routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b;
+ int first_is_auth, second_is_auth;
+ uint32_t bw_kb_first, bw_kb_second;
+ const node_t *node_first, *node_second;
+ int first_is_running, second_is_running;
+
+ /* we return -1 if first should appear before second... that is,
+ * if first is a better router. */
+ if (first->addr < second->addr)
+ return -1;
+ else if (first->addr > second->addr)
+ return 1;
+
+ /* Potentially, this next bit could cause k n lg n memeq calls. But in
+ * reality, we will almost never get here, since addresses will usually be
+ * different. */
+
+ first_is_auth =
+ router_digest_is_trusted_dir(first->cache_info.identity_digest);
+ second_is_auth =
+ router_digest_is_trusted_dir(second->cache_info.identity_digest);
+
+ if (first_is_auth && !second_is_auth)
+ return -1;
+ else if (!first_is_auth && second_is_auth)
+ return 1;
+
+ node_first = node_get_by_id(first->cache_info.identity_digest);
+ node_second = node_get_by_id(second->cache_info.identity_digest);
+ first_is_running = node_first && node_first->is_running;
+ second_is_running = node_second && node_second->is_running;
+
+ if (first_is_running && !second_is_running)
+ return -1;
+ else if (!first_is_running && second_is_running)
+ return 1;
+
+ bw_kb_first = dirserv_get_bandwidth_for_router_kb(first);
+ bw_kb_second = dirserv_get_bandwidth_for_router_kb(second);
+
+ if (bw_kb_first > bw_kb_second)
+ return -1;
+ else if (bw_kb_first < bw_kb_second)
+ return 1;
+
+ /* They're equal! Compare by identity digest, so there's a
+ * deterministic order and we avoid flapping. */
+ return fast_memcmp(first->cache_info.identity_digest,
+ second->cache_info.identity_digest,
+ DIGEST_LEN);
+}
+
+/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t
+ * whose keys are the identity digests of those routers that we're going to
+ * exclude for Sybil-like appearance. */
+static digestmap_t *
+get_possible_sybil_list(const smartlist_t *routers)
+{
+ const or_options_t *options = get_options();
+ digestmap_t *omit_as_sybil;
+ smartlist_t *routers_by_ip = smartlist_new();
+ uint32_t last_addr;
+ int addr_count;
+ /* Allow at most this number of Tor servers on a single IP address, ... */
+ int max_with_same_addr = options->AuthDirMaxServersPerAddr;
+ if (max_with_same_addr <= 0)
+ max_with_same_addr = INT_MAX;
+
+ smartlist_add_all(routers_by_ip, routers);
+ smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_);
+ omit_as_sybil = digestmap_new();
+
+ last_addr = 0;
+ addr_count = 0;
+ SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) {
+ if (last_addr != ri->addr) {
+ last_addr = ri->addr;
+ addr_count = 1;
+ } else if (++addr_count > max_with_same_addr) {
+ digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri);
+ }
+ } SMARTLIST_FOREACH_END(ri);
+
+ smartlist_free(routers_by_ip);
+ return omit_as_sybil;
+}
+
+/** Given a platform string as in a routerinfo_t (possibly null), return a
+ * newly allocated version string for a networkstatus document, or NULL if the
+ * platform doesn't give a Tor version. */
+static char *
+version_from_platform(const char *platform)
+{
+ if (platform && !strcmpstart(platform, "Tor ")) {
+ const char *eos = find_whitespace(platform+4);
+ if (eos && !strcmpstart(eos, " (r")) {
+ /* XXXX Unify this logic with the other version extraction
+ * logic in routerparse.c. */
+ eos = find_whitespace(eos+1);
+ }
+ if (eos) {
+ return tor_strndup(platform, eos-platform);
+ }
+ }
+ return NULL;
+}
+
+/** Given a (possibly empty) list of config_line_t, each line of which contains
+ * a list of comma-separated version numbers surrounded by optional space,
+ * allocate and return a new string containing the version numbers, in order,
+ * separated by commas. Used to generate Recommended(Client|Server)?Versions
+ */
+char *
+format_recommended_version_list(const config_line_t *ln, int warn)
+{
+ smartlist_t *versions;
+ char *result;
+ versions = smartlist_new();
+ for ( ; ln; ln = ln->next) {
+ smartlist_split_string(versions, ln->value, ",",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ }
+
+ /* Handle the case where a dirauth operator has accidentally made some
+ * versions space-separated instead of comma-separated. */
+ smartlist_t *more_versions = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(versions, char *, v) {
+ if (strchr(v, ' ')) {
+ if (warn)
+ log_warn(LD_DIRSERV, "Unexpected space in versions list member %s. "
+ "(These are supposed to be comma-separated; I'll pretend you "
+ "used commas instead.)", escaped(v));
+ SMARTLIST_DEL_CURRENT(versions, v);
+ smartlist_split_string(more_versions, v, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ tor_free(v);
+ }
+ } SMARTLIST_FOREACH_END(v);
+ smartlist_add_all(versions, more_versions);
+ smartlist_free(more_versions);
+
+ /* Check to make sure everything looks like a version. */
+ if (warn) {
+ SMARTLIST_FOREACH_BEGIN(versions, const char *, v) {
+ tor_version_t ver;
+ if (tor_version_parse(v, &ver) < 0) {
+ log_warn(LD_DIRSERV, "Recommended version %s does not look valid. "
+ " (I'll include it anyway, since you told me to.)",
+ escaped(v));
}
- if (num_len == 0 || cp[num_len] != ',')
- break;
- cp += num_len + 1;
+ } SMARTLIST_FOREACH_END(v);
+ }
+
+ sort_version_list(versions, 1);
+ result = smartlist_join_strings(versions,",",0,NULL);
+ SMARTLIST_FOREACH(versions,char *,s,tor_free(s));
+ smartlist_free(versions);
+ return result;
+}
+
+/** If there are entries in <b>routers</b> with exactly the same ed25519 keys,
+ * remove the older one. If they are exactly the same age, remove the one
+ * with the greater descriptor digest. May alter the order of the list. */
+static void
+routers_make_ed_keys_unique(smartlist_t *routers)
+{
+ routerinfo_t *ri2;
+ digest256map_t *by_ed_key = digest256map_new();
+
+ SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
+ ri->omit_from_vote = 0;
+ if (ri->cache_info.signing_key_cert == NULL)
+ continue; /* No ed key */
+ const uint8_t *pk = ri->cache_info.signing_key_cert->signing_key.pubkey;
+ if ((ri2 = digest256map_get(by_ed_key, pk))) {
+ /* Duplicate; must omit one. Set the omit_from_vote flag in whichever
+ * one has the earlier published_on. */
+ const time_t ri_pub = ri->cache_info.published_on;
+ const time_t ri2_pub = ri2->cache_info.published_on;
+ if (ri2_pub < ri_pub ||
+ (ri2_pub == ri_pub &&
+ fast_memcmp(ri->cache_info.signed_descriptor_digest,
+ ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) {
+ digest256map_set(by_ed_key, pk, ri);
+ ri2->omit_from_vote = 1;
+ } else {
+ ri->omit_from_vote = 1;
+ }
+ } else {
+ /* Add to map */
+ digest256map_set(by_ed_key, pk, ri);
}
+ } SMARTLIST_FOREACH_END(ri);
+
+ digest256map_free(by_ed_key, NULL);
+
+ /* Now remove every router where the omit_from_vote flag got set. */
+ SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
+ if (ri->omit_from_vote) {
+ SMARTLIST_DEL_CURRENT(routers, ri);
+ }
+ } SMARTLIST_FOREACH_END(ri);
+}
+
+/** Routerstatus <b>rs</b> is part of a group of routers that are on
+ * too narrow an IP-space. Clear out its flags since we don't want it be used
+ * because of its Sybil-like appearance.
+ *
+ * Leave its BadExit flag alone though, since if we think it's a bad exit,
+ * we want to vote that way in case all the other authorities are voting
+ * Running and Exit.
+ */
+static void
+clear_status_flags_on_sybil(routerstatus_t *rs)
+{
+ rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
+ rs->is_flagged_running = rs->is_named = rs->is_valid =
+ rs->is_hs_dir = rs->is_v2_dir = rs->is_possible_guard = 0;
+ /* FFFF we might want some mechanism to check later on if we
+ * missed zeroing any flags: it's easy to add a new flag but
+ * forget to add it to this clause. */
+}
+
+/** Return a new networkstatus_t* containing our current opinion. (For v3
+ * authorities) */
+networkstatus_t *
+dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
+ authority_cert_t *cert)
+{
+ const or_options_t *options = get_options();
+ networkstatus_t *v3_out = NULL;
+ uint32_t addr;
+ char *hostname = NULL, *client_versions = NULL, *server_versions = NULL;
+ const char *contact;
+ smartlist_t *routers, *routerstatuses;
+ char identity_digest[DIGEST_LEN];
+ char signing_key_digest[DIGEST_LEN];
+ int listbadexits = options->AuthDirListBadExits;
+ routerlist_t *rl = router_get_routerlist();
+ time_t now = time(NULL);
+ time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
+ networkstatus_voter_info_t *voter = NULL;
+ vote_timing_t timing;
+ digestmap_t *omit_as_sybil = NULL;
+ const int vote_on_reachability = running_long_enough_to_decide_unreachable();
+ smartlist_t *microdescriptors = NULL;
+
+ tor_assert(private_key);
+ tor_assert(cert);
+
+ if (crypto_pk_get_digest(private_key, signing_key_digest)<0) {
+ log_err(LD_BUG, "Error computing signing key digest");
+ return NULL;
}
- return -1;
+ if (crypto_pk_get_digest(cert->identity_key, identity_digest)<0) {
+ log_err(LD_BUG, "Error computing identity key digest");
+ return NULL;
+ }
+ if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) {
+ log_warn(LD_NET, "Couldn't resolve my hostname");
+ return NULL;
+ }
+ if (!hostname || !strchr(hostname, '.')) {
+ tor_free(hostname);
+ hostname = tor_dup_ip(addr);
+ }
+
+ if (options->VersioningAuthoritativeDir) {
+ client_versions =
+ format_recommended_version_list(options->RecommendedClientVersions, 0);
+ server_versions =
+ format_recommended_version_list(options->RecommendedServerVersions, 0);
+ }
+
+ contact = get_options()->ContactInfo;
+ if (!contact)
+ contact = "(none)";
+
+ /*
+ * Do this so dirserv_compute_performance_thresholds() and
+ * set_routerstatus_from_routerinfo() see up-to-date bandwidth info.
+ */
+ if (options->V3BandwidthsFile) {
+ dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL);
+ } else {
+ /*
+ * No bandwidths file; clear the measured bandwidth cache in case we had
+ * one last time around.
+ */
+ if (dirserv_get_measured_bw_cache_size() > 0) {
+ dirserv_clear_measured_bw_cache();
+ }
+ }
+
+ /* precompute this part, since we need it to decide what "stable"
+ * means. */
+ SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
+ dirserv_set_router_is_running(ri, now);
+ });
+
+ routers = smartlist_new();
+ smartlist_add_all(routers, rl->routers);
+ routers_make_ed_keys_unique(routers);
+ /* After this point, don't use rl->routers; use 'routers' instead. */
+ routers_sort_by_identity(routers);
+ omit_as_sybil = get_possible_sybil_list(routers);
+
+ DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) {
+ (void) ignore;
+ rep_hist_make_router_pessimal(sybil_id, now);
+ } DIGESTMAP_FOREACH_END;
+
+ /* Count how many have measured bandwidths so we know how to assign flags;
+ * this must come before dirserv_compute_performance_thresholds() */
+ dirserv_count_measured_bws(routers);
+
+ dirserv_compute_performance_thresholds(omit_as_sybil);
+
+ routerstatuses = smartlist_new();
+ microdescriptors = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
+ /* If it has a protover list and contains a protocol name greater than
+ * MAX_PROTOCOL_NAME_LENGTH, skip it. */
+ if (ri->protocol_list &&
+ protover_contains_long_protocol_names(ri->protocol_list)) {
+ continue;
+ }
+ if (ri->cache_info.published_on >= cutoff) {
+ routerstatus_t *rs;
+ vote_routerstatus_t *vrs;
+ node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+ if (!node)
+ continue;
+
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ set_routerstatus_from_routerinfo(rs, node, ri, now,
+ listbadexits);
+
+ if (ri->cache_info.signing_key_cert) {
+ memcpy(vrs->ed25519_id,
+ ri->cache_info.signing_key_cert->signing_key.pubkey,
+ ED25519_PUBKEY_LEN);
+ }
+
+ if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
+ clear_status_flags_on_sybil(rs);
+
+ if (!vote_on_reachability)
+ rs->is_flagged_running = 0;
+
+ vrs->version = version_from_platform(ri->platform);
+ if (ri->protocol_list) {
+ vrs->protocols = tor_strdup(ri->protocol_list);
+ } else {
+ vrs->protocols = tor_strdup(
+ protover_compute_for_old_tor(vrs->version));
+ }
+ vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now,
+ microdescriptors);
+
+ smartlist_add(routerstatuses, vrs);
+ }
+ } SMARTLIST_FOREACH_END(ri);
+
+ {
+ smartlist_t *added =
+ microdescs_add_list_to_cache(get_microdesc_cache(),
+ microdescriptors, SAVED_NOWHERE, 0);
+ smartlist_free(added);
+ smartlist_free(microdescriptors);
+ }
+
+ smartlist_free(routers);
+ digestmap_free(omit_as_sybil, NULL);
+
+ /* Apply guardfraction information to routerstatuses. */
+ if (options->GuardfractionFile) {
+ dirserv_read_guardfraction_file(options->GuardfractionFile,
+ routerstatuses);
+ }
+
+ /* This pass through applies the measured bw lines to the routerstatuses */
+ if (options->V3BandwidthsFile) {
+ dirserv_read_measured_bandwidths(options->V3BandwidthsFile,
+ routerstatuses);
+ } else {
+ /*
+ * No bandwidths file; clear the measured bandwidth cache in case we had
+ * one last time around.
+ */
+ if (dirserv_get_measured_bw_cache_size() > 0) {
+ dirserv_clear_measured_bw_cache();
+ }
+ }
+
+ v3_out = tor_malloc_zero(sizeof(networkstatus_t));
+
+ v3_out->type = NS_TYPE_VOTE;
+ dirvote_get_preferred_voting_intervals(&timing);
+ v3_out->published = now;
+ {
+ char tbuf[ISO_TIME_LEN+1];
+ networkstatus_t *current_consensus =
+ networkstatus_get_live_consensus(now);
+ long last_consensus_interval; /* only used to pick a valid_after */
+ if (current_consensus)
+ last_consensus_interval = current_consensus->fresh_until -
+ current_consensus->valid_after;
+ else
+ last_consensus_interval = options->TestingV3AuthInitialVotingInterval;
+ v3_out->valid_after =
+ voting_schedule_get_start_of_next_interval(now,
+ (int)last_consensus_interval,
+ options->TestingV3AuthVotingStartOffset);
+ format_iso_time(tbuf, v3_out->valid_after);
+ log_notice(LD_DIR,"Choosing valid-after time in vote as %s: "
+ "consensus_set=%d, last_interval=%d",
+ tbuf, current_consensus?1:0, (int)last_consensus_interval);
+ }
+ v3_out->fresh_until = v3_out->valid_after + timing.vote_interval;
+ v3_out->valid_until = v3_out->valid_after +
+ (timing.vote_interval * timing.n_intervals_valid);
+ v3_out->vote_seconds = timing.vote_delay;
+ v3_out->dist_seconds = timing.dist_delay;
+ tor_assert(v3_out->vote_seconds > 0);
+ tor_assert(v3_out->dist_seconds > 0);
+ tor_assert(timing.n_intervals_valid > 0);
+
+ v3_out->client_versions = client_versions;
+ v3_out->server_versions = server_versions;
+
+ /* These are hardwired, to avoid disaster. */
+ v3_out->recommended_relay_protocols =
+ tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=4 Microdesc=1-2 Relay=2");
+ v3_out->recommended_client_protocols =
+ tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=4 Microdesc=1-2 Relay=2");
+ v3_out->required_client_protocols =
+ tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=4 Microdesc=1-2 Relay=2");
+ v3_out->required_relay_protocols =
+ tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=3-4 Microdesc=1 Relay=1-2");
+
+ /* We are not allowed to vote to require anything we don't have. */
+ tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL));
+ tor_assert(protover_all_supported(v3_out->required_client_protocols, NULL));
+
+ /* We should not recommend anything we don't have. */
+ tor_assert_nonfatal(protover_all_supported(
+ v3_out->recommended_relay_protocols, NULL));
+ tor_assert_nonfatal(protover_all_supported(
+ v3_out->recommended_client_protocols, NULL));
+
+ v3_out->package_lines = smartlist_new();
+ {
+ config_line_t *cl;
+ for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) {
+ if (validate_recommended_package_line(cl->value))
+ smartlist_add_strdup(v3_out->package_lines, cl->value);
+ }
+ }
+
+ v3_out->known_flags = smartlist_new();
+ smartlist_split_string(v3_out->known_flags,
+ "Authority Exit Fast Guard Stable V2Dir Valid HSDir",
+ 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ if (vote_on_reachability)
+ smartlist_add_strdup(v3_out->known_flags, "Running");
+ if (listbadexits)
+ smartlist_add_strdup(v3_out->known_flags, "BadExit");
+ smartlist_sort_strings(v3_out->known_flags);
+
+ if (options->ConsensusParams) {
+ v3_out->net_params = smartlist_new();
+ smartlist_split_string(v3_out->net_params,
+ options->ConsensusParams, NULL, 0, 0);
+ smartlist_sort_strings(v3_out->net_params);
+ }
+
+ voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+ voter->nickname = tor_strdup(options->Nickname);
+ memcpy(voter->identity_digest, identity_digest, DIGEST_LEN);
+ voter->sigs = smartlist_new();
+ voter->address = hostname;
+ voter->addr = addr;
+ voter->dir_port = router_get_advertised_dir_port(options, 0);
+ voter->or_port = router_get_advertised_or_port(options);
+ voter->contact = tor_strdup(contact);
+ if (options->V3AuthUseLegacyKey) {
+ authority_cert_t *c = get_my_v3_legacy_cert();
+ if (c) {
+ if (crypto_pk_get_digest(c->identity_key, voter->legacy_id_digest)) {
+ log_warn(LD_BUG, "Unable to compute digest of legacy v3 identity key");
+ memset(voter->legacy_id_digest, 0, DIGEST_LEN);
+ }
+ }
+ }
+
+ v3_out->voters = smartlist_new();
+ smartlist_add(v3_out->voters, voter);
+ v3_out->cert = authority_cert_dup(cert);
+ v3_out->routerstatus_list = routerstatuses;
+ /* Note: networkstatus_digest is unset; it won't get set until we actually
+ * format the vote. */
+
+ return v3_out;
}
diff --git a/src/or/dirvote.h b/src/or/dirauth/dirvote.h
index deeb27bfe1..b69bbbf5d9 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirauth/dirvote.h
@@ -12,8 +12,6 @@
#ifndef TOR_DIRVOTE_H
#define TOR_DIRVOTE_H
-#include "testsupport.h"
-
/*
* Ideally, assuming synced clocks, we should only need 1 second for each of:
* - Vote
@@ -56,57 +54,11 @@
#define ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD 0
/** The lowest consensus method that we currently support. */
-#define MIN_SUPPORTED_CONSENSUS_METHOD 13
+#define MIN_SUPPORTED_CONSENSUS_METHOD 25
/** The highest consensus method that we currently support. */
#define MAX_SUPPORTED_CONSENSUS_METHOD 28
-/** Lowest consensus method where microdesc consensuses omit any entry
- * with no microdesc. */
-#define MIN_METHOD_FOR_MANDATORY_MICRODESC 13
-
-/** Lowest consensus method that contains "a" lines. */
-#define MIN_METHOD_FOR_A_LINES 14
-
-/** Lowest consensus method where microdescs may include a "p6" line. */
-#define MIN_METHOD_FOR_P6_LINES 15
-
-/** Lowest consensus method where microdescs may include an onion-key-ntor
- * line */
-#define MIN_METHOD_FOR_NTOR_KEY 16
-
-/** Lowest consensus method that ensures that authorities output an
- * Unmeasured=1 flag for unmeasured bandwidths */
-#define MIN_METHOD_TO_CLIP_UNMEASURED_BW 17
-
-/** Lowest consensus method where authorities may include an "id" line in
- * microdescriptors. */
-#define MIN_METHOD_FOR_ID_HASH_IN_MD 18
-
-/** Lowest consensus method where we include "package" lines*/
-#define MIN_METHOD_FOR_PACKAGE_LINES 19
-
-/** Lowest consensus method where authorities may include
- * GuardFraction information in microdescriptors. */
-#define MIN_METHOD_FOR_GUARDFRACTION 20
-
-/** Lowest consensus method where authorities may include an "id" line for
- * ed25519 identities in microdescriptors. (Broken; see
- * consensus_method_is_supported() for more info.) */
-#define MIN_METHOD_FOR_ED25519_ID_IN_MD 21
-
-/** Lowest consensus method where authorities vote on ed25519 ids and ensure
- * ed25519 id consistency. */
-#define MIN_METHOD_FOR_ED25519_ID_VOTING 22
-
-/** Lowest consensus method where authorities may include a shared random
- * value(s). */
-#define MIN_METHOD_FOR_SHARED_RANDOM 23
-
-/** Lowest consensus method where authorities drop all nodes that don't get
- * the Valid flag. */
-#define MIN_METHOD_FOR_EXCLUDING_INVALID_NODES 24
-
/** Lowest consensus method where authorities vote on required/recommended
* protocols. */
#define MIN_METHOD_FOR_RECOMMENDED_PROTOCOLS 25
@@ -132,74 +84,27 @@
* get confused with the above macros.) */
#define DEFAULT_MAX_UNMEASURED_BW_KB 20
+/* Directory Get Vote (DGV) flags for dirvote_get_vote(). */
+#define DGV_BY_ID 1
+#define DGV_INCLUDE_PENDING 2
+#define DGV_INCLUDE_PREVIOUS 4
+
+/*
+ * Public API. Used outside of the dirauth subsystem.
+ *
+ * We need to nullify them if the module is disabled.
+ */
+#ifdef HAVE_MODULE_DIRAUTH
+
+time_t dirvote_act(const or_options_t *options, time_t now);
void dirvote_free_all(void);
-/* vote manipulation */
-char *networkstatus_compute_consensus(smartlist_t *votes,
- int total_authorities,
- crypto_pk_t *identity_key,
- crypto_pk_t *signing_key,
- const char *legacy_identity_key_digest,
- crypto_pk_t *legacy_signing_key,
- consensus_flavor_t flavor);
-int networkstatus_add_detached_signatures(networkstatus_t *target,
- ns_detached_signatures_t *sigs,
- const char *source,
- int severity,
- const char **msg_out);
-char *networkstatus_get_detached_signatures(smartlist_t *consensuses);
-void ns_detached_signatures_free_(ns_detached_signatures_t *s);
-#define ns_detached_signatures_free(s) \
- FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s))
-
-/* cert manipulation */
-authority_cert_t *authority_cert_dup(authority_cert_t *cert);
-
-/* vote scheduling */
-
-/** Scheduling information for a voting interval. */
-typedef struct {
- /** When do we generate and distribute our vote for this interval? */
- time_t voting_starts;
- /** When do we send an HTTP request for any votes that we haven't
- * been posted yet?*/
- time_t fetch_missing_votes;
- /** When do we give up on getting more votes and generate a consensus? */
- time_t voting_ends;
- /** When do we send an HTTP request for any signatures we're expecting to
- * see on the consensus? */
- time_t fetch_missing_signatures;
- /** When do we publish the consensus? */
- time_t interval_starts;
-
- /* True iff we have generated and distributed our vote. */
- int have_voted;
- /* True iff we've requested missing votes. */
- int have_fetched_missing_votes;
- /* True iff we have built a consensus and sent the signatures around. */
- int have_built_consensus;
- /* True iff we've fetched missing signatures. */
- int have_fetched_missing_signatures;
- /* True iff we have published our consensus. */
- int have_published_consensus;
-
- /* True iff this voting schedule was set on demand meaning not through the
- * normal vote operation of a dirauth or when a consensus is set. This only
- * applies to a directory authority that needs to recalculate the voting
- * timings only for the first vote even though this object was initilized
- * prior to voting. */
- int created_on_demand;
-} voting_schedule_t;
-
-void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out);
-time_t dirvote_get_start_of_next_interval(time_t now,
- int interval,
- int offset);
-void dirvote_recalculate_timing(const or_options_t *options, time_t now);
-void dirvote_act(const or_options_t *options, time_t now);
-time_t dirvote_get_next_valid_after_time(void);
-
-/* invoked on timers and by outside triggers. */
+void dirvote_parse_sr_commits(networkstatus_t *ns, const smartlist_t *tokens);
+void dirvote_clear_commits(networkstatus_t *ns);
+void dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items,
+ smartlist_t *dir_items);
+
+/* Storing signatures and votes functions */
struct pending_vote_t * dirvote_add_vote(const char *vote_body,
const char **msg_out,
int *status_out);
@@ -207,15 +112,82 @@ int dirvote_add_signatures(const char *detached_signatures_body,
const char *source,
const char **msg_out);
+#else /* HAVE_MODULE_DIRAUTH */
+
+static inline time_t
+dirvote_act(const or_options_t *options, time_t now)
+{
+ (void) options;
+ (void) now;
+ return TIME_MAX;
+}
+
+static inline void
+dirvote_free_all(void)
+{
+}
+
+static inline void
+dirvote_parse_sr_commits(networkstatus_t *ns, const smartlist_t *tokens)
+{
+ (void) ns;
+ (void) tokens;
+}
+
+static inline void
+dirvote_clear_commits(networkstatus_t *ns)
+{
+ (void) ns;
+}
+
+static inline void
+dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items,
+ smartlist_t *dir_items)
+{
+ (void) url;
+ (void) items;
+ (void) dir_items;
+}
+
+static inline struct pending_vote_t *
+dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
+{
+ (void) vote_body;
+ /* If the dirauth module is disabled, this should NEVER be called else we
+ * failed to safeguard the dirauth module. */
+ tor_assert_nonfatal_unreached();
+
+ /* We need to send out an error code. */
+ *status_out = 400;
+ *msg_out = "No directory authority support";
+ return NULL;
+}
+
+static inline int
+dirvote_add_signatures(const char *detached_signatures_body, const char *source,
+ const char **msg_out)
+{
+ (void) detached_signatures_body;
+ (void) source;
+ (void) msg_out;
+ /* If the dirauth module is disabled, this should NEVER be called else we
+ * failed to safeguard the dirauth module. */
+ tor_assert_nonfatal_unreached();
+ return 0;
+}
+
+#endif /* HAVE_MODULE_DIRAUTH */
+
/* Item access */
MOCK_DECL(const char*, dirvote_get_pending_consensus,
(consensus_flavor_t flav));
MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void));
-
-#define DGV_BY_ID 1
-#define DGV_INCLUDE_PENDING 2
-#define DGV_INCLUDE_PREVIOUS 4
const cached_dir_t *dirvote_get_vote(const char *fp, int flags);
+
+/*
+ * API used _only_ by the dirauth subsystem.
+ */
+
void set_routerstatus_from_routerinfo(routerstatus_t *rs,
node_t *node,
routerinfo_t *ri, time_t now,
@@ -224,26 +196,18 @@ networkstatus_t *
dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
authority_cert_t *cert);
-microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri,
- int consensus_method);
-ssize_t dirvote_format_microdesc_vote_line(char *out, size_t out_len,
- const microdesc_t *md,
- int consensus_method_low,
- int consensus_method_high);
vote_microdesc_hash_t *dirvote_format_all_microdesc_vote_lines(
const routerinfo_t *ri,
time_t now,
smartlist_t *microdescriptors_out);
-int vote_routerstatus_find_microdesc_hash(char *digest256_out,
- const vote_routerstatus_t *vrs,
- int method,
- digest_algorithm_t alg);
-document_signature_t *voter_get_sig_by_algorithm(
- const networkstatus_voter_info_t *voter,
- digest_algorithm_t alg);
-
+/*
+ * Exposed functions for unit tests.
+ */
#ifdef DIRVOTE_PRIVATE
+
+/* Cert manipulation */
+STATIC authority_cert_t *authority_cert_dup(authority_cert_t *cert);
STATIC int32_t dirvote_get_intermediate_param_value(
const smartlist_t *param_list,
const char *keyword,
@@ -258,6 +222,25 @@ STATIC int
networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G,
int64_t M, int64_t E, int64_t D,
int64_t T, int64_t weight_scale);
+STATIC
+char *networkstatus_compute_consensus(smartlist_t *votes,
+ int total_authorities,
+ crypto_pk_t *identity_key,
+ crypto_pk_t *signing_key,
+ const char *legacy_identity_key_digest,
+ crypto_pk_t *legacy_signing_key,
+ consensus_flavor_t flavor);
+STATIC
+int networkstatus_add_detached_signatures(networkstatus_t *target,
+ ns_detached_signatures_t *sigs,
+ const char *source,
+ int severity,
+ const char **msg_out);
+STATIC
+char *networkstatus_get_detached_signatures(smartlist_t *consensuses);
+STATIC microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri,
+ int consensus_method);
+
#endif /* defined(DIRVOTE_PRIVATE) */
#endif /* !defined(TOR_DIRVOTE_H) */
diff --git a/src/or/dirauth/mode.h b/src/or/dirauth/mode.h
new file mode 100644
index 0000000000..8a0d3142f1
--- /dev/null
+++ b/src/or/dirauth/mode.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file mode.h
+ * \brief Standalone header file for directory authority mode.
+ **/
+
+#ifndef TOR_DIRAUTH_MODE_H
+#define TOR_DIRAUTH_MODE_H
+
+#ifdef HAVE_MODULE_DIRAUTH
+
+#include "router.h"
+
+/* Return true iff we believe ourselves to be a v3 authoritative directory
+ * server. */
+static inline int
+authdir_mode_v3(const or_options_t *options)
+{
+ return authdir_mode(options) && options->V3AuthoritativeDir != 0;
+}
+
+#else /* HAVE_MODULE_DIRAUTH */
+
+/* Without the dirauth module, we can't be a v3 directory authority, ever. */
+
+static inline int
+authdir_mode_v3(const or_options_t *options)
+{
+ (void) options;
+ return 0;
+}
+
+#endif /* HAVE_MODULE_DIRAUTH */
+
+#endif /* TOR_MODE_H */
+
diff --git a/src/or/shared_random.c b/src/or/dirauth/shared_random.c
index 13416d6bc7..6dd1f330e0 100644
--- a/src/or/shared_random.c
+++ b/src/or/dirauth/shared_random.c
@@ -91,13 +91,19 @@
#include "shared_random.h"
#include "config.h"
#include "confparse.h"
-#include "dirvote.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "networkstatus.h"
-#include "routerkeys.h"
#include "router.h"
+#include "routerkeys.h"
#include "routerlist.h"
+#include "shared_random_client.h"
#include "shared_random_state.h"
#include "util.h"
+#include "voting_schedule.h"
+
+#include "dirauth/dirvote.h"
+#include "dirauth/mode.h"
/* String prefix of shared random values in votes/consensuses. */
static const char previous_srv_str[] = "shared-rand-previous-value";
@@ -498,20 +504,6 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
return vote_line;
}
-/* Convert a given srv object to a string for the control port. This doesn't
- * fail and the srv object MUST be valid. */
-static char *
-srv_to_control_string(const sr_srv_t *srv)
-{
- char *srv_str;
- char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
- tor_assert(srv);
-
- sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
- tor_asprintf(&srv_str, "%s", srv_hash_encoded);
- return srv_str;
-}
-
/* Return a heap allocated string that contains the given <b>srv</b> string
* representation formatted for a networkstatus document using the
* <b>key</b> as the start of the line. This doesn't return NULL. */
@@ -874,27 +866,6 @@ get_majority_srv_from_votes(const smartlist_t *votes, int current)
return the_srv;
}
-/* Encode the given shared random value and put it in dst. Destination
- * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
-void
-sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
-{
- int ret;
- /* Extra byte for the NULL terminated char. */
- char buf[SR_SRV_VALUE_BASE64_LEN + 1];
-
- tor_assert(dst);
- tor_assert(srv);
- tor_assert(dst_len >= sizeof(buf));
-
- ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
- sizeof(srv->value), 0);
- /* Always expect the full length without the NULL byte. */
- tor_assert(ret == (sizeof(buf) - 1));
- tor_assert(ret <= (int) dst_len);
- strlcpy(dst, buf, dst_len);
-}
-
/* Free a commit object. */
void
sr_commit_free_(sr_commit_t *commit)
@@ -1036,55 +1007,6 @@ sr_compute_srv(void)
tor_free(reveals);
}
-/* Parse a list of arguments from a SRV value either from a vote, consensus
- * or from our disk state and return a newly allocated srv object. NULL is
- * returned on error.
- *
- * The arguments' order:
- * num_reveals, value
- */
-sr_srv_t *
-sr_parse_srv(const smartlist_t *args)
-{
- char *value;
- int ok, ret;
- uint64_t num_reveals;
- sr_srv_t *srv = NULL;
-
- tor_assert(args);
-
- if (smartlist_len(args) < 2) {
- goto end;
- }
-
- /* First argument is the number of reveal values */
- num_reveals = tor_parse_uint64(smartlist_get(args, 0),
- 10, 0, UINT64_MAX, &ok, NULL);
- if (!ok) {
- goto end;
- }
- /* Second and last argument is the shared random value it self. */
- value = smartlist_get(args, 1);
- if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) {
- goto end;
- }
-
- srv = tor_malloc_zero(sizeof(*srv));
- srv->num_reveals = num_reveals;
- /* We subtract one byte from the srclen because the function ignores the
- * '=' character in the given buffer. This is broken but it's a documented
- * behavior of the implementation. */
- ret = base64_decode((char *) srv->value, sizeof(srv->value), value,
- SR_SRV_VALUE_BASE64_LEN - 1);
- if (ret != sizeof(srv->value)) {
- tor_free(srv);
- srv = NULL;
- goto end;
- }
- end:
- return srv;
-}
-
/* Parse a commit from a vote or from our disk state and return a newly
* allocated commit object. NULL is returned on error.
*
@@ -1333,7 +1255,7 @@ sr_act_post_consensus(const networkstatus_t *consensus)
}
/* Prepare our state so that it's ready for the next voting period. */
- sr_state_update(dirvote_get_next_valid_after_time());
+ sr_state_update(voting_schedule_get_next_valid_after_time());
}
/* Initialize shared random subsystem. This MUST be called early in the boot
@@ -1352,84 +1274,6 @@ sr_save_and_cleanup(void)
sr_cleanup();
}
-/* Return the current SRV string representation for the control port. Return a
- * newly allocated string on success containing the value else "" if not found
- * or if we don't have a valid consensus yet. */
-char *
-sr_get_current_for_control(void)
-{
- char *srv_str;
- const networkstatus_t *c = networkstatus_get_latest_consensus();
- if (c && c->sr_info.current_srv) {
- srv_str = srv_to_control_string(c->sr_info.current_srv);
- } else {
- srv_str = tor_strdup("");
- }
- return srv_str;
-}
-
-/* Return the previous SRV string representation for the control port. Return
- * a newly allocated string on success containing the value else "" if not
- * found or if we don't have a valid consensus yet. */
-char *
-sr_get_previous_for_control(void)
-{
- char *srv_str;
- const networkstatus_t *c = networkstatus_get_latest_consensus();
- if (c && c->sr_info.previous_srv) {
- srv_str = srv_to_control_string(c->sr_info.previous_srv);
- } else {
- srv_str = tor_strdup("");
- }
- return srv_str;
-}
-
-/* Return current shared random value from the latest consensus. Caller can
- * NOT keep a reference to the returned pointer. Return NULL if none. */
-const sr_srv_t *
-sr_get_current(const networkstatus_t *ns)
-{
- const networkstatus_t *consensus;
-
- /* Use provided ns else get a live one */
- if (ns) {
- consensus = ns;
- } else {
- consensus = networkstatus_get_live_consensus(approx_time());
- }
- /* Ideally we would never be asked for an SRV without a live consensus. Make
- * sure this assumption is correct. */
- tor_assert_nonfatal(consensus);
-
- if (consensus) {
- return consensus->sr_info.current_srv;
- }
- return NULL;
-}
-
-/* Return previous shared random value from the latest consensus. Caller can
- * NOT keep a reference to the returned pointer. Return NULL if none. */
-const sr_srv_t *
-sr_get_previous(const networkstatus_t *ns)
-{
- const networkstatus_t *consensus;
-
- /* Use provided ns else get a live one */
- if (ns) {
- consensus = ns;
- } else {
- consensus = networkstatus_get_live_consensus(approx_time());
- }
- /* Ideally we would never be asked for an SRV without a live consensus. Make
- * sure this assumption is correct. */
- tor_assert_nonfatal(consensus);
-
- if (consensus) {
- return consensus->sr_info.previous_srv;
- }
- return NULL;
-}
-
#ifdef TOR_UNIT_TESTS
/* Set the global value of number of SRV agreements so the test can play
diff --git a/src/or/shared_random.h b/src/or/dirauth/shared_random.h
index 675a8d8b06..1778ce8f09 100644
--- a/src/or/shared_random.h
+++ b/src/or/dirauth/shared_random.h
@@ -101,21 +101,48 @@ typedef struct sr_commit_t {
/* API */
-/* Public methods: */
+/* Public methods used _outside_ of the module.
+ *
+ * We need to nullify them if the module is disabled. */
+#ifdef HAVE_MODULE_DIRAUTH
int sr_init(int save_to_disk);
void sr_save_and_cleanup(void);
void sr_act_post_consensus(const networkstatus_t *consensus);
+
+#else /* HAVE_MODULE_DIRAUTH */
+
+static inline int
+sr_init(int save_to_disk)
+{
+ (void) save_to_disk;
+ /* Always return success. */
+ return 0;
+}
+
+static inline void
+sr_save_and_cleanup(void)
+{
+}
+
+static inline void
+sr_act_post_consensus(const networkstatus_t *consensus)
+{
+ (void) consensus;
+}
+
+#endif /* HAVE_MODULE_DIRAUTH */
+
+/* Public methods used only by dirauth code. */
+
void sr_handle_received_commits(smartlist_t *commits,
crypto_pk_t *voter_key);
sr_commit_t *sr_parse_commit(const smartlist_t *args);
-sr_srv_t *sr_parse_srv(const smartlist_t *args);
char *sr_get_string_for_vote(void);
char *sr_get_string_for_consensus(const smartlist_t *votes,
int32_t num_srv_agreements);
void sr_commit_free_(sr_commit_t *commit);
#define sr_commit_free(sr) FREE_AND_NULL(sr_commit_t, sr_commit_free_, (sr))
-void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv);
/* Private methods (only used by shared_random_state.c): */
static inline
@@ -128,12 +155,6 @@ void sr_compute_srv(void);
sr_commit_t *sr_generate_our_commit(time_t timestamp,
const authority_cert_t *my_rsa_cert);
-char *sr_get_current_for_control(void);
-char *sr_get_previous_for_control(void);
-
-const sr_srv_t *sr_get_current(const networkstatus_t *ns);
-const sr_srv_t *sr_get_previous(const networkstatus_t *ns);
-
#ifdef SHARED_RANDOM_PRIVATE
/* Encode */
diff --git a/src/or/shared_random_state.c b/src/or/dirauth/shared_random_state.c
index 7bac8e9482..fc0e4e5630 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/dirauth/shared_random_state.c
@@ -11,13 +11,16 @@
#define SHARED_RANDOM_STATE_PRIVATE
#include "or.h"
-#include "shared_random.h"
#include "config.h"
#include "confparse.h"
-#include "dirvote.h"
+#include "crypto_util.h"
+#include "dirauth/dirvote.h"
#include "networkstatus.h"
#include "router.h"
+#include "shared_random.h"
+#include "shared_random_client.h"
#include "shared_random_state.h"
+#include "voting_schedule.h"
/* Default filename of the shared random state on disk. */
static const char default_fname[] = "sr-state";
@@ -53,10 +56,6 @@ DUMMY_TYPECHECK_INSTANCE(sr_disk_state_t);
VAR(#member, conftype, member, initvalue)
/* Our persistent state magic number. */
#define SR_DISK_STATE_MAGIC 0x98AB1254
-/* Each protocol phase has 12 rounds */
-#define SHARED_RANDOM_N_ROUNDS 12
-/* Number of phase we have in a protocol. */
-#define SHARED_RANDOM_N_PHASES 2
static int
disk_state_validate_cb(void *old_state, void *state, void *default_state,
@@ -115,81 +114,6 @@ get_phase_str(sr_phase_t phase)
return the_string;
}
-
-/* Return the voting interval of the tor vote subsystem. */
-static int
-get_voting_interval(void)
-{
- int interval;
- networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL));
-
- if (consensus) {
- interval = (int)(consensus->fresh_until - consensus->valid_after);
- } else {
- /* Same for both a testing and real network. We voluntarily ignore the
- * InitialVotingInterval since it complexifies things and it doesn't
- * affect the SR protocol. */
- interval = get_options()->V3AuthVotingInterval;
- }
- tor_assert(interval > 0);
- return interval;
-}
-
-/* Given the time <b>now</b>, return the start time of the current round of
- * the SR protocol. For example, if it's 23:47:08, the current round thus
- * started at 23:47:00 for a voting interval of 10 seconds. */
-STATIC time_t
-get_start_time_of_current_round(void)
-{
- const or_options_t *options = get_options();
- int voting_interval = get_voting_interval();
- /* First, get the start time of the next round */
- time_t next_start = dirvote_get_next_valid_after_time();
- /* Now roll back next_start by a voting interval to find the start time of
- the current round. */
- time_t curr_start = dirvote_get_start_of_next_interval(
- next_start - voting_interval - 1,
- voting_interval,
- options->TestingV3AuthVotingStartOffset);
- return curr_start;
-}
-
-/** Return the start time of the current SR protocol run. For example, if the
- * time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this
- * function should return 23/06/2017 00:00:00. */
-time_t
-sr_state_get_start_time_of_current_protocol_run(time_t now)
-{
- int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
- int voting_interval = get_voting_interval();
- /* Find the time the current round started. */
- time_t beginning_of_current_round = get_start_time_of_current_round();
-
- /* Get current SR protocol round */
- int current_round = (now / voting_interval) % total_rounds;
-
- /* Get start time by subtracting the time elapsed from the beginning of the
- protocol run */
- time_t time_elapsed_since_start_of_run = current_round * voting_interval;
- return beginning_of_current_round - time_elapsed_since_start_of_run;
-}
-
-/** Return the time (in seconds) it takes to complete a full SR protocol phase
- * (e.g. the commit phase). */
-unsigned int
-sr_state_get_phase_duration(void)
-{
- return SHARED_RANDOM_N_ROUNDS * get_voting_interval();
-}
-
-/** Return the time (in seconds) it takes to complete a full SR protocol run */
-unsigned int
-sr_state_get_protocol_run_duration(void)
-{
- int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
- return total_protocol_rounds * get_voting_interval();
-}
-
/* Return the time we should expire the state file created at <b>now</b>.
* We expire the state file in the beginning of the next protocol run. */
STATIC time_t
@@ -1370,7 +1294,7 @@ sr_state_init(int save_to_disk, int read_from_disk)
/* We have a state in memory, let's make sure it's updated for the current
* and next voting round. */
{
- time_t valid_after = dirvote_get_next_valid_after_time();
+ time_t valid_after = voting_schedule_get_next_valid_after_time();
sr_state_update(valid_after);
}
return 0;
diff --git a/src/or/shared_random_state.h b/src/or/dirauth/shared_random_state.h
index fdbbf4919a..60a326f86c 100644
--- a/src/or/shared_random_state.h
+++ b/src/or/dirauth/shared_random_state.h
@@ -121,16 +121,11 @@ int sr_state_is_initialized(void);
void sr_state_save(void);
void sr_state_free_all(void);
-time_t sr_state_get_start_time_of_current_protocol_run(time_t now);
-unsigned int sr_state_get_phase_duration(void);
-unsigned int sr_state_get_protocol_run_duration(void);
-
#ifdef SHARED_RANDOM_STATE_PRIVATE
STATIC int disk_state_load_from_disk_impl(const char *fname);
STATIC sr_phase_t get_sr_protocol_phase(time_t valid_after);
-STATIC time_t get_start_time_of_current_round(void);
STATIC time_t get_state_valid_until_time(time_t now);
STATIC const char *get_phase_str(sr_phase_t phase);
diff --git a/src/or/directory.c b/src/or/directory.c
index 8636f68410..efb6cd0e94 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -18,9 +18,10 @@
#include "consdiffmgr.h"
#include "control.h"
#include "compat.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "directory.h"
#include "dirserv.h"
-#include "dirvote.h"
#include "entrynodes.h"
#include "geoip.h"
#include "hs_cache.h"
@@ -41,7 +42,6 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
-#include "shared_random.h"
#if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
#if !defined(OpenBSD)
@@ -49,6 +49,10 @@
#endif
#endif
+#include "dirauth/dirvote.h"
+#include "dirauth/mode.h"
+#include "dirauth/shared_random.h"
+
/**
* \file directory.c
* \brief Code to send and fetch information from directory authorities and
@@ -794,9 +798,9 @@ directory_choose_address_routerstatus(const routerstatus_t *status,
* Use the preferred address and port if they are reachable, otherwise,
* use the alternate address and port (if any).
*/
- have_or = fascist_firewall_choose_address_rs(status,
- FIREWALL_OR_CONNECTION, 0,
- use_or_ap);
+ fascist_firewall_choose_address_rs(status, FIREWALL_OR_CONNECTION, 0,
+ use_or_ap);
+ have_or = tor_addr_port_is_valid_ap(use_or_ap, 0);
}
/* DirPort connections
@@ -805,9 +809,9 @@ directory_choose_address_routerstatus(const routerstatus_t *status,
indirection == DIRIND_ANON_DIRPORT ||
(indirection == DIRIND_ONEHOP
&& !directory_must_use_begindir(options))) {
- have_dir = fascist_firewall_choose_address_rs(status,
- FIREWALL_DIR_CONNECTION, 0,
- use_dir_ap);
+ fascist_firewall_choose_address_rs(status, FIREWALL_DIR_CONNECTION, 0,
+ use_dir_ap);
+ have_dir = tor_addr_port_is_valid_ap(use_dir_ap, 0);
}
/* We rejected all addresses in the relay's status. This means we can't
@@ -2438,7 +2442,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
* and the date header. (We used to check now-date_header, but that's
* inaccurate if we spend a lot of time downloading.)
*/
- apparent_skew = conn->base_.timestamp_lastwritten - date_header;
+ apparent_skew = conn->base_.timestamp_last_write_allowed - date_header;
if (labs(apparent_skew)>ALLOW_DIRECTORY_TIME_SKEW) {
int trusted = router_digest_is_trusted_dir(conn->identity_digest);
clock_skew_warning(TO_CONN(conn), apparent_skew, trusted, LD_HTTP,
@@ -3091,7 +3095,7 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
case 200:
/* We got something: Try storing it in the cache. */
if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) {
- log_warn(LD_REND, "Failed to store hidden service descriptor");
+ log_info(LD_REND, "Failed to store hidden service descriptor");
/* Fire control port FAILED event. */
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
"BAD_DESC");
@@ -4439,59 +4443,15 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
{
const char *url = args->url;
{
- int current;
ssize_t body_len = 0;
ssize_t estimated_len = 0;
+ int lifetime = 60; /* XXXX?? should actually use vote intervals. */
/* This smartlist holds strings that we can compress on the fly. */
smartlist_t *items = smartlist_new();
/* This smartlist holds cached_dir_t objects that have a precompressed
* deflated version. */
smartlist_t *dir_items = smartlist_new();
- int lifetime = 60; /* XXXX?? should actually use vote intervals. */
- url += strlen("/tor/status-vote/");
- current = !strcmpstart(url, "current/");
- url = strchr(url, '/');
- tor_assert(url);
- ++url;
- if (!strcmp(url, "consensus")) {
- const char *item;
- tor_assert(!current); /* we handle current consensus specially above,
- * since it wants to be spooled. */
- if ((item = dirvote_get_pending_consensus(FLAV_NS)))
- smartlist_add(items, (char*)item);
- } else if (!current && !strcmp(url, "consensus-signatures")) {
- /* XXXX the spec says that we should implement
- * current/consensus-signatures too. It doesn't seem to be needed,
- * though. */
- const char *item;
- if ((item=dirvote_get_pending_detached_signatures()))
- smartlist_add(items, (char*)item);
- } else if (!strcmp(url, "authority")) {
- const cached_dir_t *d;
- int flags = DGV_BY_ID |
- (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING);
- if ((d=dirvote_get_vote(NULL, flags)))
- smartlist_add(dir_items, (cached_dir_t*)d);
- } else {
- const cached_dir_t *d;
- smartlist_t *fps = smartlist_new();
- int flags;
- if (!strcmpstart(url, "d/")) {
- url += 2;
- flags = DGV_INCLUDE_PENDING | DGV_INCLUDE_PREVIOUS;
- } else {
- flags = DGV_BY_ID |
- (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING);
- }
- dir_split_resource_into_fingerprints(url, fps, NULL,
- DSR_HEX|DSR_SORT_UNIQ);
- SMARTLIST_FOREACH(fps, char *, fp, {
- if ((d = dirvote_get_vote(fp, flags)))
- smartlist_add(dir_items, (cached_dir_t*)d);
- tor_free(fp);
- });
- smartlist_free(fps);
- }
+ dirvote_dirreq_get_status_vote(url, items, dir_items);
if (!smartlist_len(dir_items) && !smartlist_len(items)) {
write_short_http_response(conn, 404, "Not found");
goto vote_done;
@@ -5219,6 +5179,9 @@ connection_dir_finished_flushing(dir_connection_t *conn)
tor_assert(conn);
tor_assert(conn->base_.type == CONN_TYPE_DIR);
+ if (conn->base_.marked_for_close)
+ return 0;
+
/* Note that we have finished writing the directory response. For direct
* connections this means we're done; for tunneled connections it's only
* an intermediate step. */
@@ -5302,84 +5265,71 @@ connection_dir_finished_connecting(dir_connection_t *conn)
/** Decide which download schedule we want to use based on descriptor type
* in <b>dls</b> and <b>options</b>.
- * Then return a list of int pointers defining download delays in seconds.
+ *
+ * Then, return the initial delay for that download schedule, in seconds.
+ *
* Helper function for download_status_increment_failure(),
* download_status_reset(), and download_status_increment_attempt(). */
-STATIC const smartlist_t *
-find_dl_schedule(const download_status_t *dls, const or_options_t *options)
+STATIC int
+find_dl_min_delay(const download_status_t *dls, const or_options_t *options)
{
+ tor_assert(dls);
+ tor_assert(options);
+
switch (dls->schedule) {
case DL_SCHED_GENERIC:
/* Any other directory document */
if (dir_server_mode(options)) {
/* A directory authority or directory mirror */
- return options->TestingServerDownloadSchedule;
+ return options->TestingServerDownloadInitialDelay;
} else {
- return options->TestingClientDownloadSchedule;
+ return options->TestingClientDownloadInitialDelay;
}
case DL_SCHED_CONSENSUS:
if (!networkstatus_consensus_can_use_multiple_directories(options)) {
/* A public relay */
- return options->TestingServerConsensusDownloadSchedule;
+ return options->TestingServerConsensusDownloadInitialDelay;
} else {
/* 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;
+ return options->
+ ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay;
} else if (dls->want_authority) {
/* A bootstrapping client with extra fallback directories, but
* connecting to an authority */
return
- options->ClientBootstrapConsensusAuthorityDownloadSchedule;
+ options->ClientBootstrapConsensusAuthorityDownloadInitialDelay;
} else {
/* A bootstrapping client connecting to extra fallback directories
*/
return
- options->ClientBootstrapConsensusFallbackDownloadSchedule;
+ options->ClientBootstrapConsensusFallbackDownloadInitialDelay;
}
} else {
/* A client with a reasonably live consensus, with or without
* certificates */
- return options->TestingClientConsensusDownloadSchedule;
+ return options->TestingClientConsensusDownloadInitialDelay;
}
}
case DL_SCHED_BRIDGE:
if (options->UseBridges && num_bridges_usable(0) > 0) {
/* A bridge client that is sure that one or more of its bridges are
* running can afford to wait longer to update bridge descriptors. */
- return options->TestingBridgeDownloadSchedule;
+ return options->TestingBridgeDownloadInitialDelay;
} else {
/* A bridge client which might have no running bridges, must try to
* get bridge descriptors straight away. */
- return options->TestingBridgeBootstrapDownloadSchedule;
+ return options->TestingBridgeBootstrapDownloadInitialDelay;
}
default:
tor_assert(0);
}
/* Impossible, but gcc will fail with -Werror without a `return`. */
- return NULL;
-}
-
-/** Decide which minimum delay step we want to use based on
- * descriptor type in <b>dls</b> and <b>options</b>.
- * Helper function for download_status_schedule_get_delay(). */
-STATIC int
-find_dl_min_delay(download_status_t *dls, const or_options_t *options)
-{
- tor_assert(dls);
- tor_assert(options);
-
- /*
- * For now, just use the existing schedule config stuff and pick the
- * first/last entries off to get min/max delay for backoff purposes
- */
- const smartlist_t *schedule = find_dl_schedule(dls, options);
- tor_assert(schedule != NULL && smartlist_len(schedule) >= 2);
- return *(int *)(smartlist_get(schedule, 0));
+ return 0;
}
/** As next_random_exponential_delay() below, but does not compute a random
@@ -5636,10 +5586,9 @@ download_status_increment_attempt(download_status_t *dls, const char *item,
static time_t
download_status_get_initial_delay_from_now(const download_status_t *dls)
{
- const smartlist_t *schedule = find_dl_schedule(dls, get_options());
/* We use constant initial delays, even in exponential backoff
* schedules. */
- return time(NULL) + *(int *)smartlist_get(schedule, 0);
+ return time(NULL) + find_dl_min_delay(dls, get_options());
}
/** Reset <b>dls</b> so that it will be considered downloadable
@@ -5966,4 +5915,3 @@ dir_split_resource_into_spoolable(const char *resource,
smartlist_free(fingerprints);
return r;
}
-
diff --git a/src/or/directory.h b/src/or/directory.h
index aa4d29a5bb..5f5ff7eca6 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -259,9 +259,7 @@ STATIC char* authdir_type_to_string(dirinfo_type_t auth);
STATIC const char * dir_conn_purpose_to_string(int purpose);
STATIC int should_use_directory_guards(const or_options_t *options);
STATIC compression_level_t choose_compression_level(ssize_t n_bytes);
-STATIC const smartlist_t *find_dl_schedule(const download_status_t *dls,
- const or_options_t *options);
-STATIC int find_dl_min_delay(download_status_t *dls,
+STATIC int find_dl_min_delay(const download_status_t *dls,
const or_options_t *options);
STATIC int next_random_exponential_delay(int delay,
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index ced40899d0..4e09c1c65b 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -18,7 +18,6 @@
#include "control.h"
#include "directory.h"
#include "dirserv.h"
-#include "dirvote.h"
#include "hibernate.h"
#include "keypin.h"
#include "main.h"
@@ -33,6 +32,9 @@
#include "routerparse.h"
#include "routerset.h"
#include "torcert.h"
+#include "voting_schedule.h"
+
+#include "dirauth/dirvote.h"
/**
* \file dirserv.c
@@ -86,7 +88,6 @@ static const signed_descriptor_t *get_signed_descriptor_by_fp(
int extrainfo);
static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei,
const char **msg);
-static uint32_t dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri);
static uint32_t dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri);
static int spooled_resource_lookup_body(const spooled_resource_t *spooled,
@@ -258,11 +259,12 @@ dirserv_load_fingerprint_file(void)
* identity to stop doing so. This is going to be essential for good identity
* security: otherwise anybody who can attack RSA-1024 but not Ed25519 could
* just sign fake descriptors missing the Ed25519 key. But we won't actually
- * be able to prevent that kind of thing until we're confident that there
- * isn't actually a legit reason to downgrade to 0.2.5. So for now, we have
- * to leave this #undef.
+ * be able to prevent that kind of thing until we're confident that there isn't
+ * actually a legit reason to downgrade to 0.2.5. Now we are not recommending
+ * 0.2.5 anymore so there is no reason to keep the #undef.
*/
-#undef DISABLE_DISABLING_ED25519
+
+#define DISABLE_DISABLING_ED25519
/** Check whether <b>router</b> has a nickname/identity key combination that
* we recognize from the fingerprint list, or an IP we automatically act on
@@ -857,13 +859,13 @@ directory_remove_invalid(void)
SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) {
const char *msg = NULL;
+ const char *description;
routerinfo_t *ent = node->ri;
- char description[NODE_DESC_BUF_LEN];
uint32_t r;
if (!ent)
continue;
r = dirserv_router_get_status(ent, &msg, LOG_INFO);
- router_get_description(description, ent);
+ description = router_describe(ent);
if (r & FP_REJECT) {
log_info(LD_DIRSERV, "Router %s is now rejected: %s",
description, msg?msg:"");
@@ -920,7 +922,7 @@ list_single_server_status(const routerinfo_t *desc, int is_live)
}
/* DOCDOC running_long_enough_to_decide_unreachable */
-static inline int
+int
running_long_enough_to_decide_unreachable(void)
{
return time_of_process_start
@@ -953,7 +955,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
tor_assert(node);
if (router_is_me(router)) {
- /* We always know if we are down ourselves. */
+ /* We always know if we are shutting down or hibernating ourselves. */
answer = ! we_are_hibernating();
} else if (router->is_hibernating &&
(router->cache_info.published_on +
@@ -1055,59 +1057,6 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
return 0;
}
-/** Given a (possibly empty) list of config_line_t, each line of which contains
- * a list of comma-separated version numbers surrounded by optional space,
- * allocate and return a new string containing the version numbers, in order,
- * separated by commas. Used to generate Recommended(Client|Server)?Versions
- */
-char *
-format_recommended_version_list(const config_line_t *ln, int warn)
-{
- smartlist_t *versions;
- char *result;
- versions = smartlist_new();
- for ( ; ln; ln = ln->next) {
- smartlist_split_string(versions, ln->value, ",",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- }
-
- /* Handle the case where a dirauth operator has accidentally made some
- * versions space-separated instead of comma-separated. */
- smartlist_t *more_versions = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(versions, char *, v) {
- if (strchr(v, ' ')) {
- if (warn)
- log_warn(LD_DIRSERV, "Unexpected space in versions list member %s. "
- "(These are supposed to be comma-separated; I'll pretend you "
- "used commas instead.)", escaped(v));
- SMARTLIST_DEL_CURRENT(versions, v);
- smartlist_split_string(more_versions, v, NULL,
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- tor_free(v);
- }
- } SMARTLIST_FOREACH_END(v);
- smartlist_add_all(versions, more_versions);
- smartlist_free(more_versions);
-
- /* Check to make sure everything looks like a version. */
- if (warn) {
- SMARTLIST_FOREACH_BEGIN(versions, const char *, v) {
- tor_version_t ver;
- if (tor_version_parse(v, &ver) < 0) {
- log_warn(LD_DIRSERV, "Recommended version %s does not look valid. "
- " (I'll include it anyway, since you told me to.)",
- escaped(v));
- }
- } SMARTLIST_FOREACH_END(v);
- }
-
- sort_version_list(versions, 1);
- result = smartlist_join_strings(versions,",",0,NULL);
- SMARTLIST_FOREACH(versions,char *,s,tor_free(s));
- smartlist_free(versions);
- return result;
-}
-
/** Return 1 if <b>ri</b>'s descriptor is "active" -- running, valid,
* not hibernating, having observed bw greater 0, and not too old. Else
* return 0.
@@ -1453,7 +1402,7 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
* tests aren't instant. If we haven't been running long enough,
* trust the relay. */
- if (stats_n_seconds_working >
+ if (get_uptime() >
get_options()->MinUptimeHidServDirectoryV2 * 1.1)
uptime = MIN(rep_hist_get_uptime(router->cache_info.identity_digest, now),
real_uptime(router, now));
@@ -1497,6 +1446,24 @@ router_counts_toward_thresholds(const node_t *node, time_t now,
(have_mbw || !require_mbw);
}
+/** Look through the routerlist, and using the measured bandwidth cache count
+ * how many measured bandwidths we know. This is used to decide whether we
+ * ever trust advertised bandwidths for purposes of assigning flags. */
+void
+dirserv_count_measured_bws(const smartlist_t *routers)
+{
+ /* Initialize this first */
+ routers_with_measured_bw = 0;
+
+ /* Iterate over the routerlist and count measured bandwidths */
+ SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
+ /* Check if we know a measured bandwidth for this one */
+ if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) {
+ ++routers_with_measured_bw;
+ }
+ } SMARTLIST_FOREACH_END(ri);
+}
+
/** Look through the routerlist, the Mean Time Between Failure history, and
* the Weighted Fractional Uptime history, and use them to set thresholds for
* the Stable, Fast, and Guard flags. Update the fields stable_uptime,
@@ -1504,7 +1471,7 @@ router_counts_toward_thresholds(const node_t *node, time_t now,
* guard_bandwidth_including_exits, and guard_bandwidth_excluding_exits.
*
* Also, set the is_exit flag of each router appropriately. */
-static void
+void
dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil)
{
int n_active, n_active_nonexit, n_familiar;
@@ -1735,7 +1702,7 @@ dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
}
/** Clear and free the measured bandwidth cache */
-STATIC void
+void
dirserv_clear_measured_bw_cache(void)
{
if (mbw_cache) {
@@ -1767,18 +1734,10 @@ dirserv_expire_measured_bw_cache(time_t now)
}
}
-/** Get the current size of the measured bandwidth cache */
-STATIC int
-dirserv_get_measured_bw_cache_size(void)
-{
- if (mbw_cache) return digestmap_size(mbw_cache);
- else return 0;
-}
-
/** Query the cache by identity digest, return value indicates whether
* we found it. The bw_out and as_of_out pointers receive the cached
* bandwidth value and the time it was cached if not NULL. */
-STATIC int
+int
dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out,
time_t *as_of_out)
{
@@ -1799,61 +1758,18 @@ dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out,
}
/** Predicate wrapper for dirserv_query_measured_bw_cache() */
-STATIC int
+int
dirserv_has_measured_bw(const char *node_id)
{
return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL);
}
-/** Get the best estimate of a router's bandwidth for dirauth purposes,
- * preferring measured to advertised values if available. */
-
-static uint32_t
-dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri)
-{
- uint32_t bw_kb = 0;
- /*
- * Yeah, measured bandwidths in measured_bw_line_t are (implicitly
- * signed) longs and the ones router_get_advertised_bandwidth() returns
- * are uint32_t.
- */
- long mbw_kb = 0;
-
- if (ri) {
- /*
- * * First try to see if we have a measured bandwidth; don't bother with
- * as_of_out here, on the theory that a stale measured bandwidth is still
- * better to trust than an advertised one.
- */
- if (dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest,
- &mbw_kb, NULL)) {
- /* Got one! */
- bw_kb = (uint32_t)mbw_kb;
- } else {
- /* If not, fall back to advertised */
- bw_kb = router_get_advertised_bandwidth(ri) / 1000;
- }
- }
-
- return bw_kb;
-}
-
-/** Look through the routerlist, and using the measured bandwidth cache count
- * how many measured bandwidths we know. This is used to decide whether we
- * ever trust advertised bandwidths for purposes of assigning flags. */
-static void
-dirserv_count_measured_bws(const smartlist_t *routers)
+/** Get the current size of the measured bandwidth cache */
+int
+dirserv_get_measured_bw_cache_size(void)
{
- /* Initialize this first */
- routers_with_measured_bw = 0;
-
- /* Iterate over the routerlist and count measured bandwidths */
- SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
- /* Check if we know a measured bandwidth for this one */
- if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) {
- ++routers_with_measured_bw;
- }
- } SMARTLIST_FOREACH_END(ri);
+ if (mbw_cache) return digestmap_size(mbw_cache);
+ else return 0;
}
/** Return the bandwidth we believe for assigning flags; prefer measured
@@ -1916,26 +1832,6 @@ dirserv_get_flag_thresholds_line(void)
return result;
}
-/** Given a platform string as in a routerinfo_t (possibly null), return a
- * newly allocated version string for a networkstatus document, or NULL if the
- * platform doesn't give a Tor version. */
-static char *
-version_from_platform(const char *platform)
-{
- if (platform && !strcmpstart(platform, "Tor ")) {
- const char *eos = find_whitespace(platform+4);
- if (eos && !strcmpstart(eos, " (r")) {
- /* XXXX Unify this logic with the other version extraction
- * logic in routerparse.c. */
- eos = find_whitespace(eos+1);
- }
- if (eos) {
- return tor_strndup(platform, eos-platform);
- }
- }
- return NULL;
-}
-
/** Helper: write the router-status information in <b>rs</b> into a newly
* allocated character buffer. Use the same format as in network-status
* documents. If <b>version</b> is non-NULL, add a "v" line for the platform.
@@ -2124,145 +2020,6 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
return result;
}
-/** Helper for sorting: compares two routerinfos first by address, and then by
- * descending order of "usefulness". (An authority is more useful than a
- * non-authority; a running router is more useful than a non-running router;
- * and a router with more bandwidth is more useful than one with less.)
- **/
-static int
-compare_routerinfo_by_ip_and_bw_(const void **a, const void **b)
-{
- routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b;
- int first_is_auth, second_is_auth;
- uint32_t bw_kb_first, bw_kb_second;
- const node_t *node_first, *node_second;
- int first_is_running, second_is_running;
-
- /* we return -1 if first should appear before second... that is,
- * if first is a better router. */
- if (first->addr < second->addr)
- return -1;
- else if (first->addr > second->addr)
- return 1;
-
- /* Potentially, this next bit could cause k n lg n memeq calls. But in
- * reality, we will almost never get here, since addresses will usually be
- * different. */
-
- first_is_auth =
- router_digest_is_trusted_dir(first->cache_info.identity_digest);
- second_is_auth =
- router_digest_is_trusted_dir(second->cache_info.identity_digest);
-
- if (first_is_auth && !second_is_auth)
- return -1;
- else if (!first_is_auth && second_is_auth)
- return 1;
-
- node_first = node_get_by_id(first->cache_info.identity_digest);
- node_second = node_get_by_id(second->cache_info.identity_digest);
- first_is_running = node_first && node_first->is_running;
- second_is_running = node_second && node_second->is_running;
-
- if (first_is_running && !second_is_running)
- return -1;
- else if (!first_is_running && second_is_running)
- return 1;
-
- bw_kb_first = dirserv_get_bandwidth_for_router_kb(first);
- bw_kb_second = dirserv_get_bandwidth_for_router_kb(second);
-
- if (bw_kb_first > bw_kb_second)
- return -1;
- else if (bw_kb_first < bw_kb_second)
- return 1;
-
- /* They're equal! Compare by identity digest, so there's a
- * deterministic order and we avoid flapping. */
- return fast_memcmp(first->cache_info.identity_digest,
- second->cache_info.identity_digest,
- DIGEST_LEN);
-}
-
-/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t
- * whose keys are the identity digests of those routers that we're going to
- * exclude for Sybil-like appearance. */
-static digestmap_t *
-get_possible_sybil_list(const smartlist_t *routers)
-{
- const or_options_t *options = get_options();
- digestmap_t *omit_as_sybil;
- smartlist_t *routers_by_ip = smartlist_new();
- uint32_t last_addr;
- int addr_count;
- /* Allow at most this number of Tor servers on a single IP address, ... */
- int max_with_same_addr = options->AuthDirMaxServersPerAddr;
- if (max_with_same_addr <= 0)
- max_with_same_addr = INT_MAX;
-
- smartlist_add_all(routers_by_ip, routers);
- smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_);
- omit_as_sybil = digestmap_new();
-
- last_addr = 0;
- addr_count = 0;
- SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) {
- if (last_addr != ri->addr) {
- last_addr = ri->addr;
- addr_count = 1;
- } else if (++addr_count > max_with_same_addr) {
- digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri);
- }
- } SMARTLIST_FOREACH_END(ri);
-
- smartlist_free(routers_by_ip);
- return omit_as_sybil;
-}
-
-/** If there are entries in <b>routers</b> with exactly the same ed25519 keys,
- * remove the older one. If they are exactly the same age, remove the one
- * with the greater descriptor digest. May alter the order of the list. */
-static void
-routers_make_ed_keys_unique(smartlist_t *routers)
-{
- routerinfo_t *ri2;
- digest256map_t *by_ed_key = digest256map_new();
-
- SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
- ri->omit_from_vote = 0;
- if (ri->cache_info.signing_key_cert == NULL)
- continue; /* No ed key */
- const uint8_t *pk = ri->cache_info.signing_key_cert->signing_key.pubkey;
- if ((ri2 = digest256map_get(by_ed_key, pk))) {
- /* Duplicate; must omit one. Set the omit_from_vote flag in whichever
- * one has the earlier published_on. */
- const time_t ri_pub = ri->cache_info.published_on;
- const time_t ri2_pub = ri2->cache_info.published_on;
- if (ri2_pub < ri_pub ||
- (ri2_pub == ri_pub &&
- fast_memcmp(ri->cache_info.signed_descriptor_digest,
- ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) {
- digest256map_set(by_ed_key, pk, ri);
- ri2->omit_from_vote = 1;
- } else {
- ri->omit_from_vote = 1;
- }
- } else {
- /* Add to map */
- digest256map_set(by_ed_key, pk, ri);
- }
- } SMARTLIST_FOREACH_END(ri);
-
- digest256map_free(by_ed_key, NULL);
-
- /* Now remove every router where the omit_from_vote flag got set. */
- SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
- if (ri->omit_from_vote) {
- SMARTLIST_DEL_CURRENT(routers, ri);
- }
- } SMARTLIST_FOREACH_END(ri);
-}
-
/** Extract status information from <b>ri</b> and from other authority
* functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is
* set.
@@ -2375,25 +2132,6 @@ dirserv_set_routerstatus_testing(routerstatus_t *rs)
}
}
-/** Routerstatus <b>rs</b> is part of a group of routers that are on
- * too narrow an IP-space. Clear out its flags since we don't want it be used
- * because of its Sybil-like appearance.
- *
- * Leave its BadExit flag alone though, since if we think it's a bad exit,
- * we want to vote that way in case all the other authorities are voting
- * Running and Exit.
- */
-static void
-clear_status_flags_on_sybil(routerstatus_t *rs)
-{
- rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
- rs->is_flagged_running = rs->is_named = rs->is_valid =
- rs->is_hs_dir = rs->is_v2_dir = rs->is_possible_guard = 0;
- /* FFFF we might want some mechanism to check later on if we
- * missed zeroing any flags: it's easy to add a new flag but
- * forget to add it to this clause. */
-}
-
/** The guardfraction of the guard with identity fingerprint <b>guard_id</b>
* is <b>guardfraction_percentage</b>. See if we have a vote routerstatus for
* this guard in <b>vote_routerstatuses</b>, and if we do, register the
@@ -2710,17 +2448,38 @@ dirserv_read_guardfraction_file(const char *fname,
/**
* Helper function to parse out a line in the measured bandwidth file
- * into a measured_bw_line_t output structure. Returns -1 on failure
- * or 0 on success.
+ * into a measured_bw_line_t output structure.
+ *
+ * If <b>line_is_after_headers</b> is true, then if we encounter an incomplete
+ * bw line, return -1 and warn, since we are after the headers and we should
+ * only parse bw lines. Return 0 otherwise.
+ *
+ * If <b>line_is_after_headers</b> is false then it means that we are not past
+ * the header block yet. If we encounter an incomplete bw line, return -1 but
+ * don't warn since there could be additional header lines coming. If we
+ * encounter a proper bw line, return 0 (and we got past the headers).
*/
STATIC int
-measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line)
+measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line,
+ int line_is_after_headers)
{
char *line = tor_strdup(orig_line);
char *cp = line;
int got_bw = 0;
int got_node_id = 0;
char *strtok_state; /* lame sauce d'jour */
+
+ if (strlen(line) == 0) {
+ log_warn(LD_DIRSERV, "Empty line in bandwidth file");
+ tor_free(line);
+ return -1;
+ }
+
+ /* Remove end of line character, so that is not part of the token */
+ if (line[strlen(line) - 1] == '\n') {
+ line[strlen(line) - 1] = '\0';
+ }
+
cp = tor_strtok_r(cp, " \t", &strtok_state);
if (!cp) {
@@ -2782,6 +2541,13 @@ measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line)
if (got_bw && got_node_id) {
tor_free(line);
return 0;
+ } else if (line_is_after_headers == 0) {
+ /* There could be additional header lines, therefore do not give warnings
+ * but returns -1 since it's not a complete bw line. */
+ log_debug(LD_DIRSERV, "Missing bw or node_id in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
} else {
log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
escaped(orig_line));
@@ -2830,6 +2596,11 @@ dirserv_read_measured_bandwidths(const char *from_file,
int applied_lines = 0;
time_t file_time, now;
int ok;
+ /* This flag will be 1 only when the first successful bw measurement line
+ * has been encountered, so that measured_bw_line_parse don't give warnings
+ * if there are additional header lines, as introduced in Bandwidth List spec
+ * version 1.1.0 */
+ int line_is_after_headers = 0;
/* Initialise line, so that we can't possibly run off the end. */
memset(line, 0, sizeof(line));
@@ -2877,7 +2648,11 @@ dirserv_read_measured_bandwidths(const char *from_file,
while (!feof(fp)) {
measured_bw_line_t parsed_line;
if (fgets(line, sizeof(line), fp) && strlen(line)) {
- if (measured_bw_line_parse(&parsed_line, line) != -1) {
+ if (measured_bw_line_parse(&parsed_line, line,
+ line_is_after_headers) != -1) {
+ /* This condition will be true when the first complete valid bw line
+ * has been encountered, which means the end of the header lines. */
+ line_is_after_headers = 1;
/* Also cache the line for dirserv_get_bandwidth_for_router() */
dirserv_cache_measured_bw(&parsed_line, file_time);
if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0)
@@ -2896,294 +2671,6 @@ dirserv_read_measured_bandwidths(const char *from_file,
return 0;
}
-/** Return a new networkstatus_t* containing our current opinion. (For v3
- * authorities) */
-networkstatus_t *
-dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
- authority_cert_t *cert)
-{
- const or_options_t *options = get_options();
- networkstatus_t *v3_out = NULL;
- uint32_t addr;
- char *hostname = NULL, *client_versions = NULL, *server_versions = NULL;
- const char *contact;
- smartlist_t *routers, *routerstatuses;
- char identity_digest[DIGEST_LEN];
- char signing_key_digest[DIGEST_LEN];
- int listbadexits = options->AuthDirListBadExits;
- routerlist_t *rl = router_get_routerlist();
- time_t now = time(NULL);
- time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
- networkstatus_voter_info_t *voter = NULL;
- vote_timing_t timing;
- digestmap_t *omit_as_sybil = NULL;
- const int vote_on_reachability = running_long_enough_to_decide_unreachable();
- smartlist_t *microdescriptors = NULL;
-
- tor_assert(private_key);
- tor_assert(cert);
-
- if (crypto_pk_get_digest(private_key, signing_key_digest)<0) {
- log_err(LD_BUG, "Error computing signing key digest");
- return NULL;
- }
- if (crypto_pk_get_digest(cert->identity_key, identity_digest)<0) {
- log_err(LD_BUG, "Error computing identity key digest");
- return NULL;
- }
- if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) {
- log_warn(LD_NET, "Couldn't resolve my hostname");
- return NULL;
- }
- if (!hostname || !strchr(hostname, '.')) {
- tor_free(hostname);
- hostname = tor_dup_ip(addr);
- }
-
- if (options->VersioningAuthoritativeDir) {
- client_versions =
- format_recommended_version_list(options->RecommendedClientVersions, 0);
- server_versions =
- format_recommended_version_list(options->RecommendedServerVersions, 0);
- }
-
- contact = get_options()->ContactInfo;
- if (!contact)
- contact = "(none)";
-
- /*
- * Do this so dirserv_compute_performance_thresholds() and
- * set_routerstatus_from_routerinfo() see up-to-date bandwidth info.
- */
- if (options->V3BandwidthsFile) {
- dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL);
- } else {
- /*
- * No bandwidths file; clear the measured bandwidth cache in case we had
- * one last time around.
- */
- if (dirserv_get_measured_bw_cache_size() > 0) {
- dirserv_clear_measured_bw_cache();
- }
- }
-
- /* precompute this part, since we need it to decide what "stable"
- * means. */
- SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
- dirserv_set_router_is_running(ri, now);
- });
-
- routers = smartlist_new();
- smartlist_add_all(routers, rl->routers);
- routers_make_ed_keys_unique(routers);
- /* After this point, don't use rl->routers; use 'routers' instead. */
- routers_sort_by_identity(routers);
- omit_as_sybil = get_possible_sybil_list(routers);
-
- DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) {
- (void) ignore;
- rep_hist_make_router_pessimal(sybil_id, now);
- } DIGESTMAP_FOREACH_END;
-
- /* Count how many have measured bandwidths so we know how to assign flags;
- * this must come before dirserv_compute_performance_thresholds() */
- dirserv_count_measured_bws(routers);
-
- dirserv_compute_performance_thresholds(omit_as_sybil);
-
- routerstatuses = smartlist_new();
- microdescriptors = smartlist_new();
-
- SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
- /* If it has a protover list and contains a protocol name greater than
- * MAX_PROTOCOL_NAME_LENGTH, skip it. */
- if (ri->protocol_list &&
- protover_contains_long_protocol_names(ri->protocol_list)) {
- continue;
- }
- if (ri->cache_info.published_on >= cutoff) {
- routerstatus_t *rs;
- vote_routerstatus_t *vrs;
- node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
- if (!node)
- continue;
-
- vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- rs = &vrs->status;
- set_routerstatus_from_routerinfo(rs, node, ri, now,
- listbadexits);
-
- if (ri->cache_info.signing_key_cert) {
- memcpy(vrs->ed25519_id,
- ri->cache_info.signing_key_cert->signing_key.pubkey,
- ED25519_PUBKEY_LEN);
- }
-
- if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
- clear_status_flags_on_sybil(rs);
-
- if (!vote_on_reachability)
- rs->is_flagged_running = 0;
-
- vrs->version = version_from_platform(ri->platform);
- if (ri->protocol_list) {
- vrs->protocols = tor_strdup(ri->protocol_list);
- } else {
- vrs->protocols = tor_strdup(
- protover_compute_for_old_tor(vrs->version));
- }
- vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now,
- microdescriptors);
-
- smartlist_add(routerstatuses, vrs);
- }
- } SMARTLIST_FOREACH_END(ri);
-
- {
- smartlist_t *added =
- microdescs_add_list_to_cache(get_microdesc_cache(),
- microdescriptors, SAVED_NOWHERE, 0);
- smartlist_free(added);
- smartlist_free(microdescriptors);
- }
-
- smartlist_free(routers);
- digestmap_free(omit_as_sybil, NULL);
-
- /* Apply guardfraction information to routerstatuses. */
- if (options->GuardfractionFile) {
- dirserv_read_guardfraction_file(options->GuardfractionFile,
- routerstatuses);
- }
-
- /* This pass through applies the measured bw lines to the routerstatuses */
- if (options->V3BandwidthsFile) {
- dirserv_read_measured_bandwidths(options->V3BandwidthsFile,
- routerstatuses);
- } else {
- /*
- * No bandwidths file; clear the measured bandwidth cache in case we had
- * one last time around.
- */
- if (dirserv_get_measured_bw_cache_size() > 0) {
- dirserv_clear_measured_bw_cache();
- }
- }
-
- v3_out = tor_malloc_zero(sizeof(networkstatus_t));
-
- v3_out->type = NS_TYPE_VOTE;
- dirvote_get_preferred_voting_intervals(&timing);
- v3_out->published = now;
- {
- char tbuf[ISO_TIME_LEN+1];
- networkstatus_t *current_consensus =
- networkstatus_get_live_consensus(now);
- long last_consensus_interval; /* only used to pick a valid_after */
- if (current_consensus)
- last_consensus_interval = current_consensus->fresh_until -
- current_consensus->valid_after;
- else
- last_consensus_interval = options->TestingV3AuthInitialVotingInterval;
- v3_out->valid_after =
- dirvote_get_start_of_next_interval(now, (int)last_consensus_interval,
- options->TestingV3AuthVotingStartOffset);
- format_iso_time(tbuf, v3_out->valid_after);
- log_notice(LD_DIR,"Choosing valid-after time in vote as %s: "
- "consensus_set=%d, last_interval=%d",
- tbuf, current_consensus?1:0, (int)last_consensus_interval);
- }
- v3_out->fresh_until = v3_out->valid_after + timing.vote_interval;
- v3_out->valid_until = v3_out->valid_after +
- (timing.vote_interval * timing.n_intervals_valid);
- v3_out->vote_seconds = timing.vote_delay;
- v3_out->dist_seconds = timing.dist_delay;
- tor_assert(v3_out->vote_seconds > 0);
- tor_assert(v3_out->dist_seconds > 0);
- tor_assert(timing.n_intervals_valid > 0);
-
- v3_out->client_versions = client_versions;
- v3_out->server_versions = server_versions;
-
- /* These are hardwired, to avoid disaster. */
- v3_out->recommended_relay_protocols =
- tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
- "Link=4 Microdesc=1-2 Relay=2");
- v3_out->recommended_client_protocols =
- tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
- "Link=4 Microdesc=1-2 Relay=2");
- v3_out->required_client_protocols =
- tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
- "Link=4 Microdesc=1-2 Relay=2");
- v3_out->required_relay_protocols =
- tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
- "Link=3-4 Microdesc=1 Relay=1-2");
-
- /* We are not allowed to vote to require anything we don't have. */
- tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL));
- tor_assert(protover_all_supported(v3_out->required_client_protocols, NULL));
-
- /* We should not recommend anything we don't have. */
- tor_assert_nonfatal(protover_all_supported(
- v3_out->recommended_relay_protocols, NULL));
- tor_assert_nonfatal(protover_all_supported(
- v3_out->recommended_client_protocols, NULL));
-
- v3_out->package_lines = smartlist_new();
- {
- config_line_t *cl;
- for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) {
- if (validate_recommended_package_line(cl->value))
- smartlist_add_strdup(v3_out->package_lines, cl->value);
- }
- }
-
- v3_out->known_flags = smartlist_new();
- smartlist_split_string(v3_out->known_flags,
- "Authority Exit Fast Guard Stable V2Dir Valid HSDir",
- 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- if (vote_on_reachability)
- smartlist_add_strdup(v3_out->known_flags, "Running");
- if (listbadexits)
- smartlist_add_strdup(v3_out->known_flags, "BadExit");
- smartlist_sort_strings(v3_out->known_flags);
-
- if (options->ConsensusParams) {
- v3_out->net_params = smartlist_new();
- smartlist_split_string(v3_out->net_params,
- options->ConsensusParams, NULL, 0, 0);
- smartlist_sort_strings(v3_out->net_params);
- }
-
- voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
- voter->nickname = tor_strdup(options->Nickname);
- memcpy(voter->identity_digest, identity_digest, DIGEST_LEN);
- voter->sigs = smartlist_new();
- voter->address = hostname;
- voter->addr = addr;
- voter->dir_port = router_get_advertised_dir_port(options, 0);
- voter->or_port = router_get_advertised_or_port(options);
- voter->contact = tor_strdup(contact);
- if (options->V3AuthUseLegacyKey) {
- authority_cert_t *c = get_my_v3_legacy_cert();
- if (c) {
- if (crypto_pk_get_digest(c->identity_key, voter->legacy_id_digest)) {
- log_warn(LD_BUG, "Unable to compute digest of legacy v3 identity key");
- memset(voter->legacy_id_digest, 0, DIGEST_LEN);
- }
- }
- }
-
- v3_out->voters = smartlist_new();
- smartlist_add(v3_out->voters, voter);
- v3_out->cert = authority_cert_dup(cert);
- v3_out->routerstatus_list = routerstatuses;
- /* Note: networkstatus_digest is unset; it won't get set until we actually
- * format the vote. */
-
- return v3_out;
-}
-
/** As dirserv_get_routerdescs(), but instead of getting signed_descriptor_t
* pointers, adds copies of digests to fps_out, and doesn't use the
* /tor/server/ prefix. For a /d/ request, adds descriptor digests; for other
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index cb7c628387..b243439cc2 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -157,6 +157,15 @@ void cached_dir_decref(cached_dir_t *d);
cached_dir_t *new_cached_dir(char *s, time_t published);
char *format_recommended_version_list(const config_line_t *line, int warn);
int validate_recommended_package_line(const char *line);
+int dirserv_query_measured_bw_cache_kb(const char *node_id,
+ long *bw_out,
+ time_t *as_of_out);
+void dirserv_clear_measured_bw_cache(void);
+int dirserv_has_measured_bw(const char *node_id);
+int dirserv_get_measured_bw_cache_size(void);
+void dirserv_count_measured_bws(const smartlist_t *routers);
+int running_long_enough_to_decide_unreachable(void);
+void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil);
#ifdef DIRSERV_PRIVATE
@@ -165,20 +174,15 @@ STATIC void dirserv_set_routerstatus_testing(routerstatus_t *rs);
/* Put the MAX_MEASUREMENT_AGE #define here so unit tests can see it */
#define MAX_MEASUREMENT_AGE (3*24*60*60) /* 3 days */
-STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *line);
+STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *line,
+ int line_is_after_headers);
STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line,
smartlist_t *routerstatuses);
STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
time_t as_of);
-STATIC void dirserv_clear_measured_bw_cache(void);
STATIC void dirserv_expire_measured_bw_cache(time_t now);
-STATIC int dirserv_get_measured_bw_cache_size(void);
-STATIC int dirserv_query_measured_bw_cache_kb(const char *node_id,
- long *bw_out,
- time_t *as_of_out);
-STATIC int dirserv_has_measured_bw(const char *node_id);
STATIC int
dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
diff --git a/src/or/dns.c b/src/or/dns.c
index 411e2d5aa6..ba734ed900 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -56,6 +56,7 @@
#include "connection.h"
#include "connection_edge.h"
#include "control.h"
+#include "crypto_rand.h"
#include "dns.h"
#include "main.h"
#include "policies.h"
diff --git a/src/or/dos.c b/src/or/dos.c
index 2cb3470582..ee731accea 100644
--- a/src/or/dos.c
+++ b/src/or/dos.c
@@ -11,6 +11,7 @@
#include "or.h"
#include "channel.h"
#include "config.h"
+#include "crypto_rand.h"
#include "geoip.h"
#include "main.h"
#include "networkstatus.h"
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 54638810fa..27d760f1a8 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -118,11 +118,13 @@
#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitlist.h"
+#include "circuituse.h"
#include "circuitstats.h"
#include "config.h"
#include "confparse.h"
#include "connection.h"
#include "control.h"
+#include "crypto_rand.h"
#include "directory.h"
#include "entrynodes.h"
#include "main.h"
@@ -432,14 +434,15 @@ get_guard_confirmed_min_lifetime(void)
STATIC int
get_n_primary_guards(void)
{
- const int n = get_options()->NumEntryGuards;
- const int n_dir = get_options()->NumDirectoryGuards;
- if (n > 5) {
- return MAX(n_dir, n + n / 2);
- } else if (n >= 1) {
- return MAX(n_dir, n * 2);
+ /* If the user has explicitly configured the number of primary guards, do
+ * what the user wishes to do */
+ const int configured_primaries = get_options()->NumPrimaryGuards;
+ if (configured_primaries) {
+ return configured_primaries;
}
+ /* otherwise check for consensus parameter and if that's not set either, just
+ * use the default value. */
return networkstatus_get_param(NULL,
"guard-n-primary-guards",
DFLT_N_PRIMARY_GUARDS, 1, INT32_MAX);
@@ -454,6 +457,9 @@ get_n_primary_guards_to_use(guard_usage_t usage)
int configured;
const char *param_name;
int param_default;
+
+ /* If the user has explicitly configured the amount of guards, use
+ that. Otherwise, fall back to the default value. */
if (usage == GUARD_USAGE_DIRGUARD) {
configured = get_options()->NumDirectoryGuards;
param_name = "guard-n-primary-dir-guards-to-use";
@@ -2335,7 +2341,7 @@ entry_guard_cancel(circuit_guard_state_t **guard_state_p)
}
/**
- * Called by the circuit building module when a circuit has succeeded:
+ * Called by the circuit building module when a circuit has failed:
* informs the guards code that the guard in *<b>guard_state_p</b> is
* not working, and advances the state of the guard module.
*/
@@ -3474,12 +3480,18 @@ guards_update_all(void)
used. */
const node_t *
guards_choose_guard(cpath_build_state_t *state,
- circuit_guard_state_t **guard_state_out)
+ uint8_t purpose,
+ circuit_guard_state_t **guard_state_out)
{
const node_t *r = NULL;
const uint8_t *exit_id = NULL;
entry_guard_restriction_t *rst = NULL;
- if (state && (exit_id = build_state_get_exit_rsa_id(state))) {
+
+ /* Only apply restrictions if we have a specific exit node in mind, and only
+ * if we are not doing vanguard circuits: we don't want to apply guard
+ * restrictions to vanguard circuits. */
+ if (state && !circuit_should_use_vanguards(purpose) &&
+ (exit_id = build_state_get_exit_rsa_id(state))) {
/* We're building to a targeted exit node, so that node can't be
* chosen as our guard for this circuit. Remember that fact in a
* restriction. */
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index d562498313..e8c91da41b 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -322,6 +322,7 @@ struct circuit_guard_state_t {
/* Common entry points for old and new guard code */
int guards_update_all(void);
const node_t *guards_choose_guard(cpath_build_state_t *state,
+ uint8_t purpose,
circuit_guard_state_t **guard_state_out);
const node_t *guards_choose_dirguard(uint8_t dir_purpose,
circuit_guard_state_t **guard_state_out);
diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c
index 16a250fa58..b842442caf 100644
--- a/src/or/ext_orport.c
+++ b/src/or/ext_orport.c
@@ -20,9 +20,11 @@
#include "or.h"
#include "connection.h"
#include "connection_or.h"
-#include "ext_orport.h"
#include "control.h"
#include "config.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
+#include "ext_orport.h"
#include "main.h"
#include "proto_ext_or.h"
#include "util.h"
diff --git a/src/or/fallback_dirs.inc b/src/or/fallback_dirs.inc
index c446152e6a..9f60f309f8 100644
--- a/src/or/fallback_dirs.inc
+++ b/src/or/fallback_dirs.inc
@@ -1,73 +1,82 @@
/* type=fallback */
/* version=2.0.0 */
-/* timestamp=20180106205601 */
-/* ===== */
-/* Whitelist & blacklist excluded 810 of 1009 candidates. */
+/* timestamp=20181207055710 */
+/* timestamp0=20181207055710 */
+/* timestamp1=20181207193756 */
+/* timestamp2=20181207195255 */
+/* ===== */
+/* 0: Whitelist excluded 1275 of 1462 candidates. */
+/* 1: Whitelist excluded 1279 of 1470 candidates. */
+/* 2: Whitelist excluded 1278 of 1469 candidates. */
/* Checked IPv4 DirPorts served a consensus within 15.0s. */
/*
-Final Count: 143 (Eligible 198, Target 230 (1154 * 0.20), Max 200)
-Excluded: 55 (Same Operator 34, Failed/Skipped Download 14, Excess 7)
-Bandwidth Range: 0.9 - 131.1 MByte/s
+0:
+Final Count: 148 (Eligible 187, Target 351 (1757 * 0.20), Max 200)
+Excluded: 39 (Same Operator 28, Failed/Skipped Download 7, Excess 4)
+Bandwidth Range: 0.8 - 43.8 MByte/s
+
+MERGED WITH:
+
+1:
+Final Count: 138 (Eligible 191, Target 353 (1768 * 0.20), Max 200)
+Excluded: 53 (Same Operator 29, Failed/Skipped Download 20, Excess 4)
+Bandwidth Range: 1.0 - 46.9 MByte/s
MERGED WITH:
-Final Count: 139 (Eligible 199, Target 232 (1161 * 0.20), Max 200)
-Excluded: 60 (Same Operator 34, Failed/Skipped Download 19, Excess 7)
-Bandwidth Range: 1.1 - 131.1 MByte/s
+2:
+Final Count: 145 (Eligible 191, Target 353 (1768 * 0.20), Max 200)
+Excluded: 46 (Same Operator 29, Failed/Skipped Download 13, Excess 4)
+Bandwidth Range: 1.0 - 46.9 MByte/s
*/
/*
-Onionoo Source: details Date: 2018-01-06 20:00:00 Version: 5.0
+0: Onionoo Source: details Date: 2018-12-07 05:00:00 Version: 7.0
+1: Onionoo Source: details Date: 2018-12-07 18:00:00 Version: 7.0
+2: Onionoo Source: details Date: 2018-12-07 18:00:00 Version: 7.0
URL: https:onionoo.torproject.orgdetails?fieldsfingerprint%2Cnickname%2Ccontact%2Clast_changed_address_or_port%2Cconsensus_weight%2Cadvertised_bandwidth%2Cor_addresses%2Cdir_address%2Crecommended_version%2Cflags%2Ceffective_family%2Cplatform&flagV2Dir&typerelay&last_seen_days-0&first_seen_days90-
*/
/*
-Onionoo Source: uptime Date: 2018-01-06 20:00:00 Version: 5.0
+0: Onionoo Source: uptime Date: 2018-12-07 05:00:00 Version: 7.0
+1: Onionoo Source: uptime Date: 2018-12-07 18:00:00 Version: 7.0
+2: Onionoo Source: uptime Date: 2018-12-07 18:00:00 Version: 7.0
URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&last_seen_days-0
*/
/* ===== */
-"185.13.39.197:80 orport=443 id=001524DD403D729F08F7E5D77813EF12756CFA8D"
-/* nickname=Neldoreth */
-/* extrainfo=0 */
-/* ===== */
-,
"176.10.104.240:80 orport=443 id=0111BA9B604669E636FFD5B503F382A4B7AD6E80"
/* nickname=DigiGesTor1e1 */
/* extrainfo=0 */
/* ===== */
,
-"185.100.85.61:80 orport=443 id=025B66CEBC070FCB0519D206CF0CF4965C20C96E"
-/* nickname=nibbana */
+"193.171.202.146:9030 orport=9001 id=01A9258A46E97FF8B2CAC7910577862C14F2C524"
+" ipv6=[2001:628:200a:f001:20::146]:9001"
+/* nickname=ins0 */
/* extrainfo=0 */
/* ===== */
,
-"5.9.110.236:9030 orport=9001 id=0756B7CD4DFC8182BE23143FAC0642F515182CEB"
-" ipv6=[2a01:4f8:162:51e2::2]:9001"
-/* nickname=rueckgrat */
-/* extrainfo=1 */
-/* ===== */
-,
"163.172.149.155:80 orport=443 id=0B85617241252517E8ECF2CFC7F4C1A32DCD153F"
/* nickname=niij02 */
/* extrainfo=0 */
/* ===== */
,
-"5.39.92.199:80 orport=443 id=0BEA4A88D069753218EAAAD6D22EA87B9A1319D6"
-" ipv6=[2001:41d0:8:b1c7::1]:443"
-/* nickname=BaelorTornodePw */
+"5.200.21.144:80 orport=443 id=0C039F35C2E40DCB71CD8A07E97C7FD7787D42D6"
+/* nickname=libel */
/* extrainfo=0 */
/* ===== */
,
-"163.172.25.118:80 orport=22 id=0CF8F3E6590F45D50B70F2F7DA6605ECA6CD408F"
-/* nickname=torpidsFRonline4 */
+"5.196.88.122:9030 orport=9001 id=0C2C599AFCB26F5CFC2C7592435924C1D63D9484"
+" ipv6=[2001:41d0:a:fb7a::1]:9001"
+/* nickname=ATo */
/* extrainfo=0 */
/* ===== */
,
-"178.62.197.82:80 orport=443 id=0D3EBA17E1C78F1E9900BABDB23861D46FCAF163"
-/* nickname=HY100 */
+"185.100.86.100:80 orport=443 id=0E8C0C8315B66DB5F703804B3889A1DD66C67CE0"
+/* nickname=saveyourprivacyex1 */
/* extrainfo=0 */
/* ===== */
,
-"185.100.86.100:80 orport=443 id=0E8C0C8315B66DB5F703804B3889A1DD66C67CE0"
-/* nickname=saveyourprivacyex1 */
+"37.252.185.182:9030 orport=8080 id=113143469021882C3A4B82F084F8125B08EE471E"
+" ipv6=[2a00:63c1:a:182::2]:8080"
+/* nickname=parasol */
/* extrainfo=0 */
/* ===== */
,
@@ -83,31 +92,36 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"37.157.195.87:8030 orport=443 id=12FD624EE73CEF37137C90D38B2406A66F68FAA2"
-/* nickname=thanatosCZ */
+"193.234.15.59:80 orport=443 id=136F9299A5009A4E0E96494E723BDB556FB0A26B"
+" ipv6=[2a00:1c20:4089:1234:bff6:e1bb:1ce3:8dc6]:443"
+/* nickname=bakunin2 */
/* extrainfo=0 */
/* ===== */
,
-"178.16.208.59:80 orport=443 id=136F9299A5009A4E0E96494E723BDB556FB0A26B"
-" ipv6=[2a00:1c20:4089:1234:bff6:e1bb:1ce3:8dc6]:443"
-/* nickname=bakunin2 */
+"144.76.14.145:110 orport=143 id=14419131033443AE6E21DA82B0D307F7CAE42BDB"
+" ipv6=[2a01:4f8:190:9490::dead]:443"
+/* nickname=PedicaboMundi */
/* extrainfo=0 */
/* ===== */
,
-"163.172.138.22:80 orport=443 id=16102E458460349EE45C0901DAA6C30094A9BBEA"
-" ipv6=[2001:bc8:4400:2100::1:3]:443"
-/* nickname=mkultra */
+"185.220.101.9:10009 orport=20009 id=14877C6384A9E793F422C8D1DDA447CACA4F7C4B"
+/* nickname=niftywoodmouse */
/* extrainfo=0 */
/* ===== */
,
-"178.62.60.37:80 orport=443 id=175921396C7C426309AB03775A9930B6F611F794"
-/* nickname=lovejoy */
+"54.37.138.138:8080 orport=993 id=1576BE143D8727745BB2BCDDF183291B3C3EFEFC"
+/* nickname=anotherone */
/* extrainfo=0 */
/* ===== */
,
-"171.25.193.25:80 orport=443 id=185663B7C12777F052B2C2D23D7A239D8DA88A0F"
-" ipv6=[2001:67c:289c::25]:443"
-/* nickname=DFRI5 */
+"51.15.78.0:9030 orport=9001 id=15BE17C99FACE24470D40AF782D6A9C692AB36D6"
+" ipv6=[2001:bc8:4700:2300::16:c0b]:9001"
+/* nickname=rofltor07 */
+/* extrainfo=0 */
+/* ===== */
+,
+"204.11.50.131:9030 orport=9001 id=185F2A57B0C4620582602761097D17DB81654F70"
+/* nickname=BoingBoing */
/* extrainfo=0 */
/* ===== */
,
@@ -118,7 +132,7 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
,
"81.7.14.253:9001 orport=443 id=1AE039EE0B11DB79E4B4B29CBA9F752864A0259E"
/* nickname=Ichotolot60 */
-/* extrainfo=0 */
+/* extrainfo=1 */
/* ===== */
,
"163.172.53.84:143 orport=21 id=1C90D3AEADFF3BCD079810632C8B85637924A58E"
@@ -127,46 +141,25 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"46.101.151.222:80 orport=443 id=1DBAED235E3957DE1ABD25B4206BE71406FB61F8"
-/* nickname=flanders */
-/* extrainfo=0 */
-/* ===== */
-,
-"91.219.237.229:80 orport=443 id=1ECD73B936CB6E6B3CD647CC204F108D9DF2C9F7"
-/* nickname=JakeDidNothingWrong */
-/* extrainfo=0 */
-/* ===== */
-,
"199.184.246.250:80 orport=443 id=1F6ABD086F40B890A33C93CC4606EE68B31C9556"
" ipv6=[2620:124:1009:1::171]:443"
/* nickname=dao */
/* extrainfo=0 */
/* ===== */
,
-"185.129.249.124:9030 orport=9001 id=1FA8F638298645BE58AC905276680889CB795A94"
-/* nickname=treadstone */
-/* extrainfo=0 */
-/* ===== */
-,
"212.47.229.2:9030 orport=9001 id=20462CBA5DA4C2D963567D17D0B7249718114A68"
" ipv6=[2001:bc8:4400:2100::f03]:9001"
/* nickname=scaletor */
/* extrainfo=0 */
/* ===== */
,
-"77.247.181.164:80 orport=443 id=204DFD2A2C6A0DC1FA0EACB495218E0B661704FD"
-/* nickname=HaveHeart */
-/* extrainfo=0 */
-/* ===== */
-,
"163.172.176.167:80 orport=443 id=230A8B2A8BA861210D9B4BA97745AEC217A94207"
/* nickname=niij01 */
/* extrainfo=0 */
/* ===== */
,
-"37.200.98.5:80 orport=443 id=231C2B9C8C31C295C472D031E06964834B745996"
-" ipv6=[2a00:1158:3::11a]:993"
-/* nickname=torpidsDEdomainf */
+"185.220.101.8:10008 orport=20008 id=24E91955D969AEA1D80413C64FE106FAE7FD2EA9"
+/* nickname=niftymouse */
/* extrainfo=0 */
/* ===== */
,
@@ -175,7 +168,7 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"178.16.208.56:80 orport=443 id=2CDCFED0142B28B002E89D305CBA2E26063FADE2"
+"193.234.15.56:80 orport=443 id=2CDCFED0142B28B002E89D305CBA2E26063FADE2"
" ipv6=[2a00:1c20:4089:1234:cd49:b58a:9ebe:67ec]:443"
/* nickname=jaures */
/* extrainfo=0 */
@@ -186,14 +179,14 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"64.113.32.29:9030 orport=9001 id=30C19B81981F450C402306E2E7CFB6C3F79CB6B2"
-/* nickname=Libero */
+"94.230.208.147:8080 orport=8443 id=311A4533F7A2415F42346A6C8FA77E6FD279594C"
+" ipv6=[2a02:418:6017::147]:8443"
+/* nickname=DigiGesTor3e2 */
/* extrainfo=0 */
/* ===== */
,
-"80.127.117.180:80 orport=443 id=328E54981C6DDD7D89B89E418724A4A7881E3192"
-" ipv6=[2001:985:e77:10::4]:443"
-/* nickname=sjc01 */
+"212.83.154.33:8080 orport=8443 id=322C6E3A973BC10FC36DE3037AD27BC89F14723B"
+/* nickname=bauruine204 */
/* extrainfo=0 */
/* ===== */
,
@@ -203,21 +196,25 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"163.172.13.165:9030 orport=9001 id=33DA0CAB7C27812EFF2E22C9705630A54D101FEB"
-" ipv6=[2001:bc8:38cb:201::8]:9001"
-/* nickname=mullbinde9 */
+"54.37.17.235:9030 orport=9001 id=360CBA08D1E24F513162047BDB54A1015E531534"
+/* nickname=Aerodynamik06 */
/* extrainfo=0 */
/* ===== */
,
-"91.121.23.100:9030 orport=9001 id=3711E80B5B04494C971FB0459D4209AB7F2EA799"
-/* nickname=0x3d002 */
+"37.157.255.35:9030 orport=9090 id=361D33C96D0F161275EE67E2C91EE10B276E778B"
+/* nickname=cxx4freedom */
/* extrainfo=0 */
/* ===== */
,
-"176.126.252.12:21 orport=8080 id=379FB450010D17078B3766C2273303C358C3A442"
-" ipv6=[2a02:59e0:0:7::12]:81"
-/* nickname=aurora */
-/* extrainfo=1 */
+"37.187.22.87:9030 orport=9001 id=36B9E7AC1E36B62A9D6F330ABEB6012BA7F0D400"
+" ipv6=[2001:41d0:a:1657::1]:9001"
+/* nickname=kimsufi321 */
+/* extrainfo=0 */
+/* ===== */
+,
+"64.79.152.132:80 orport=443 id=375DCBB2DBD94E5263BC0C015F0C9E756669617E"
+/* nickname=ebola */
+/* extrainfo=0 */
/* ===== */
,
"62.210.92.11:9130 orport=9101 id=387B065A38E4DAA16D9D41C2964ECBC4B31D30FF"
@@ -231,24 +228,9 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"164.132.77.175:9030 orport=9001 id=3B33F6FCA645AD4E91428A3AF7DC736AD9FB727B"
-/* nickname=rofltor1 */
-/* extrainfo=0 */
-/* ===== */
-,
-"212.83.154.33:8888 orport=443 id=3C79699D4FBC37DE1A212D5033B56DAE079AC0EF"
-/* nickname=bauruine203 */
-/* extrainfo=0 */
-/* ===== */
-,
-"176.10.107.180:9030 orport=9001 id=3D7E274A87D9A89AF064C13D1EE4CA1F184F2600"
-/* nickname=schokomilch */
-/* extrainfo=0 */
-/* ===== */
-,
-"217.79.179.177:9030 orport=9001 id=3E53D3979DB07EFD736661C934A1DED14127B684"
-" ipv6=[2001:4ba0:fff9:131:6c4f::90d3]:9001"
-/* nickname=Unnamed */
+"66.111.2.16:9030 orport=9001 id=3F092986E9B87D3FDA09B71FA3A602378285C77A"
+" ipv6=[2610:1c0:0:5::16]:9001"
+/* nickname=NYCBUG1 */
/* extrainfo=0 */
/* ===== */
,
@@ -257,19 +239,21 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"199.249.223.61:80 orport=443 id=40E7D6CE5085E4CDDA31D51A29D1457EB53F12AD"
-/* nickname=Quintex12 */
+"195.191.81.7:9030 orport=9001 id=41A3C16269C7B63DB6EB741DBDDB4E1F586B1592"
+" ipv6=[2a00:1908:fffc:ffff:c0a6:ccff:fe62:e1a1]:9001"
+/* nickname=rofltor02 */
/* extrainfo=0 */
/* ===== */
,
"178.17.170.156:9030 orport=9001 id=41C59606AFE1D1AA6EC6EF6719690B856F0B6587"
+" ipv6=[2a00:1dc0:caff:48::9257]:9001"
/* nickname=TorExitMoldova2 */
/* extrainfo=0 */
/* ===== */
,
-"178.62.86.96:9030 orport=9001 id=439D0447772CB107B886F7782DBC201FA26B92D1"
-" ipv6=[2a03:b0c0:1:d0::3cf:7001]:9050"
-/* nickname=pablobm001 */
+"81.7.10.251:80 orport=443 id=45362E8ECD651CCAC521156FFBD2FF7F27FA8E88"
+" ipv6=[2a02:180:1:1::517:afb]:443"
+/* nickname=torpidsDEisppro2 */
/* extrainfo=0 */
/* ===== */
,
@@ -278,19 +262,23 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
+"132.248.241.5:9030 orport=9001 id=4661DE96D3F8E923994B05218F23760C8D7935A4"
+/* nickname=toritounam */
+/* extrainfo=0 */
+/* ===== */
+,
"31.31.78.49:80 orport=443 id=46791D156C9B6C255C2665D4D8393EC7DBAA7798"
/* nickname=KrigHaBandolo */
/* extrainfo=0 */
/* ===== */
,
-"193.70.43.76:9030 orport=9001 id=484A10BA2B8D48A5F0216674C8DD50EF27BC32F3"
-/* nickname=Aerodynamik03 */
+"185.220.101.34:10034 orport=20034 id=47C42E2094EE482E7C9B586B10BABFB67557030B"
+/* nickname=niftysugarglider */
/* extrainfo=0 */
/* ===== */
,
-"37.187.102.186:9030 orport=9001 id=489D94333DF66D57FFE34D9D59CC2D97E2CB0053"
-" ipv6=[2001:41d0:a:26ba::1]:9001"
-/* nickname=txtfileTorNode65536 */
+"193.70.43.76:9030 orport=9001 id=484A10BA2B8D48A5F0216674C8DD50EF27BC32F3"
+/* nickname=Aerodynamik03 */
/* extrainfo=0 */
/* ===== */
,
@@ -299,9 +287,10 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"108.53.208.157:80 orport=443 id=4F0DB7E687FC7C0AE55C8F243DA8B0EB27FBF1F2"
-/* nickname=Binnacle */
-/* extrainfo=1 */
+"81.7.13.84:80 orport=443 id=4EB55679FA91363B97372554F8DC7C63F4E5B101"
+" ipv6=[2a02:180:1:1::5b8f:538c]:443"
+/* nickname=torpidsDEisppro */
+/* extrainfo=0 */
/* ===== */
,
"212.51.134.123:9030 orport=9001 id=50586E25BE067FD1F739998550EDDCB1A14CA5B2"
@@ -320,6 +309,18 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
+"192.160.102.166:80 orport=9001 id=547DA56F6B88B6C596B3E3086803CDA4F0EF8F21"
+" ipv6=[2620:132:300c:c01d::6]:9002"
+/* nickname=chaucer */
+/* extrainfo=0 */
+/* ===== */
+,
+"192.160.102.170:80 orport=9001 id=557ACEC850F54EEE65839F83CACE2B0825BE811E"
+" ipv6=[2620:132:300c:c01d::a]:9002"
+/* nickname=ogopogo */
+/* extrainfo=0 */
+/* ===== */
+,
"95.130.12.119:80 orport=443 id=587E0A9552E4274B251F29B5B2673D38442EE4BF"
/* nickname=Nuath */
/* extrainfo=0 */
@@ -331,19 +332,19 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"172.98.193.43:80 orport=443 id=5E56738E7F97AA81DEEF59AF28494293DFBFCCDF"
-/* nickname=Backplane */
+"193.234.15.62:80 orport=443 id=5CF8AFA5E4B0BB88942A44A3F3AAE08C3BDFD60B"
+" ipv6=[2a00:1c20:4089:1234:a6a4:2926:d0af:dfee]:443"
+/* nickname=jaures4 */
/* extrainfo=0 */
/* ===== */
,
-"199.249.223.74:80 orport=443 id=5F4CD12099AF20FAF9ADFDCEC65316A376D0201C"
-/* nickname=QuintexAirVPN7 */
+"172.98.193.43:80 orport=443 id=5E56738E7F97AA81DEEF59AF28494293DFBFCCDF"
+/* nickname=Backplane */
/* extrainfo=0 */
/* ===== */
,
-"95.128.43.164:80 orport=443 id=616081EC829593AF4232550DE6FFAA1D75B37A90"
-" ipv6=[2a02:ec0:209:10::4]:443"
-/* nickname=AquaRayTerminus */
+"185.220.101.28:10028 orport=20028 id=609E598FB6A00BCF7872906B602B705B64541C50"
+/* nickname=niftychipmunk */
/* extrainfo=0 */
/* ===== */
,
@@ -352,88 +353,84 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"85.214.62.48:80 orport=443 id=6A7551EEE18F78A9813096E82BF84F740D32B911"
-/* nickname=TorMachine */
-/* extrainfo=0 */
-/* ===== */
-,
"80.127.137.19:80 orport=443 id=6EF897645B79B6CB35E853B32506375014DE3621"
" ipv6=[2001:981:47c1:1::6]:443"
/* nickname=d6relay */
/* extrainfo=0 */
/* ===== */
,
-"85.235.250.88:80 orport=443 id=72B2B12A3F60408BDBC98C6DF53988D3A0B3F0EE"
-/* nickname=TykRelay01 */
+"37.139.8.104:9030 orport=9001 id=7088D485934E8A403B81531F8C90BDC75FA43C98"
+" ipv6=[2a03:b0c0:0:1010::24c:1001]:9001"
+/* nickname=Basil */
/* extrainfo=0 */
/* ===== */
,
-"81.7.14.31:9001 orport=443 id=7600680249A22080ECC6173FBBF64D6FCF330A61"
-/* nickname=Ichotolot62 */
+"188.138.88.42:80 orport=443 id=70C55A114C0EF3DC5784A4FAEE64388434A3398F"
+/* nickname=torpidsFRplusserver */
/* extrainfo=0 */
/* ===== */
,
-"134.119.36.135:80 orport=443 id=763C9556602BD6207771A7A3D958091D44C43228"
-" ipv6=[2a00:1158:3::2a8]:993"
-/* nickname=torpidsDEdomainf2 */
+"185.220.101.30:10030 orport=20030 id=71CFDEB4D9E00CCC3E31EC4E8A29E109BBC1FB36"
+/* nickname=niftypedetidae */
/* extrainfo=0 */
/* ===== */
,
-"188.166.133.133:9030 orport=9001 id=774555642FDC1E1D4FDF2E0C31B7CA9501C5C9C7"
-" ipv6=[2a03:b0c0:2:d0::26c0:1]:9001"
-/* nickname=dropsy */
+"85.235.250.88:80 orport=443 id=72B2B12A3F60408BDBC98C6DF53988D3A0B3F0EE"
+/* nickname=TykRelay01 */
/* extrainfo=0 */
/* ===== */
,
+"81.7.14.31:9001 orport=443 id=7600680249A22080ECC6173FBBF64D6FCF330A61"
+/* nickname=Ichotolot62 */
+/* extrainfo=1 */
+/* ===== */
+,
"5.196.23.64:9030 orport=9001 id=775B0FAFDE71AADC23FFC8782B7BEB1D5A92733E"
/* nickname=Aerodynamik01 */
/* extrainfo=0 */
/* ===== */
,
-"81.30.158.213:9030 orport=9001 id=789EA6C9AE9ADDD8760903171CFA9AC5741B0C70"
-" ipv6=[2001:4ba0:cafe:e84::1]:9001"
-/* nickname=dumpster */
+"51.254.136.195:80 orport=443 id=7BB70F8585DFC27E75D692970C0EEB0F22983A63"
+/* nickname=torproxy02 */
/* extrainfo=0 */
/* ===== */
,
-"104.200.20.46:80 orport=9001 id=78E2BE744A53631B4AAB781468E94C52AB73968B"
-/* nickname=bynumlawtor */
+"185.100.84.82:80 orport=443 id=7D05A38E39FC5D29AFE6BE487B9B4DC9E635D09E"
+/* nickname=saveyourprivacyexit */
/* extrainfo=0 */
/* ===== */
,
-"62.210.129.246:80 orport=443 id=79E169B25E4C7CE99584F6ED06F379478F23E2B8"
-/* nickname=MilesPrower */
+"193.11.114.45:9031 orport=9002 id=80AAF8D5956A43C197104CEF2550CD42D165C6FB"
+/* nickname=mdfnet2 */
/* extrainfo=0 */
/* ===== */
,
-"82.223.21.74:9030 orport=9001 id=7A32C9519D80CA458FC8B034A28F5F6815649A98"
-" ipv6=[2001:470:53e0::cafe]:9050"
-/* nickname=silentrocket */
+"51.254.96.208:9030 orport=9001 id=8101421BEFCCF4C271D5483C5AABCAAD245BBB9D"
+" ipv6=[2001:41d0:401:3100::30dc]:9001"
+/* nickname=rofltor01 */
/* extrainfo=0 */
/* ===== */
,
-"51.254.136.195:80 orport=443 id=7BB70F8585DFC27E75D692970C0EEB0F22983A63"
-/* nickname=torproxy02 */
+"217.12.199.190:80 orport=443 id=81AFA888F8F8F4A024AB58ECA0ADDEBB93FF01DA"
+" ipv6=[2a02:27a8:0:2::486]:993"
+/* nickname=torpidsUAitlas */
/* extrainfo=0 */
/* ===== */
,
-"77.247.181.162:80 orport=443 id=7BFB908A3AA5B491DA4CA72CCBEE0E1F2A939B55"
-/* nickname=sofia */
+"192.42.116.16:80 orport=443 id=81B75D534F91BFB7C57AB67DA10BCEF622582AE8"
+/* nickname=hviv104 */
/* extrainfo=0 */
/* ===== */
,
-"185.100.84.82:80 orport=443 id=7D05A38E39FC5D29AFE6BE487B9B4DC9E635D09E"
-/* nickname=saveyourprivacyexit */
+"192.160.102.164:80 orport=9001 id=823AA81E277F366505545522CEDC2F529CE4DC3F"
+" ipv6=[2620:132:300c:c01d::4]:9002"
+/* nickname=snowfall */
/* extrainfo=0 */
/* ===== */
,
-"199.249.223.69:80 orport=443 id=7FA8E7E44F1392A4E40FFC3B69DB3B00091B7FD3"
-/* nickname=Quintex20 */
-/* extrainfo=0 */
-/* ===== */
-,
-"193.11.114.45:9031 orport=9002 id=80AAF8D5956A43C197104CEF2550CD42D165C6FB"
-/* nickname=mdfnet2 */
+"192.87.28.82:9030 orport=9001 id=844AE9CAD04325E955E2BE1521563B79FE7094B7"
+" ipv6=[2001:678:230:3028:192:87:28:82]:9001"
+/* nickname=Smeerboel */
/* extrainfo=0 */
/* ===== */
,
@@ -442,23 +439,19 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"85.230.184.93:9030 orport=443 id=855BC2DABE24C861CD887DB9B2E950424B49FC34"
-/* nickname=Logforme */
-/* extrainfo=0 */
-/* ===== */
-,
-"72.52.75.27:9030 orport=9001 id=8567AD0A6369ED08527A8A8533A5162AC00F7678"
-/* nickname=piecoopdotnet */
+"185.96.88.29:80 orport=443 id=86C281AD135058238D7A337D546C902BE8505DDE"
+/* nickname=TykRelay05 */
/* extrainfo=0 */
/* ===== */
,
-"185.96.88.29:80 orport=443 id=86C281AD135058238D7A337D546C902BE8505DDE"
-/* nickname=TykRelay05 */
+"93.180.156.84:9030 orport=9001 id=8844D87E9B038BE3270938F05AF797E1D3C74C0F"
+/* nickname=BARACUDA */
/* extrainfo=0 */
/* ===== */
,
-"176.10.104.243:80 orport=443 id=88487BDD980BF6E72092EE690E8C51C0AA4A538C"
-/* nickname=DigiGesTor2e1 */
+"51.15.205.214:9030 orport=9001 id=8B6556601612F1E2AFCE2A12FFFAF8482A76DD1F"
+" ipv6=[2001:bc8:4400:2500::5:b07]:9001"
+/* nickname=titania1 */
/* extrainfo=0 */
/* ===== */
,
@@ -468,14 +461,8 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"5.189.169.190:8030 orport=8080 id=8D79F73DCD91FC4F5017422FAC70074D6DB8DD81"
-/* nickname=thanatosDE */
-/* extrainfo=0 */
-/* ===== */
-,
-"151.80.42.103:9030 orport=9001 id=9007C1D8E4F03D506A4A011B907A9E8D04E3C605"
-" ipv6=[2001:41d0:e:f67::114]:9001"
-/* nickname=matlink */
+"81.7.11.96:9030 orport=9001 id=8FA37B93397015B2BC5A525C908485260BE9F422"
+/* nickname=Doedel22 */
/* extrainfo=0 */
/* ===== */
,
@@ -485,19 +472,29 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"62.138.7.171:8030 orport=8001 id=9285B22F7953D7874604EEE2B470609AD81C74E9"
-/* nickname=0x3d005 */
+"51.255.41.65:9030 orport=9001 id=9231DF741915AA1630031A93026D88726877E93A"
+/* nickname=PrisnCellRelayFR1 */
+/* extrainfo=0 */
+/* ===== */
+,
+"54.37.73.111:9030 orport=9001 id=92412EA1B9AA887D462B51D816777002F4D58907"
+/* nickname=Aerodynamik05 */
/* extrainfo=0 */
/* ===== */
,
-"178.16.208.57:80 orport=443 id=92CFD9565B24646CAC2D172D3DB503D69E777B8A"
+"96.253.78.108:80 orport=443 id=924B24AFA7F075D059E8EEB284CC400B33D3D036"
+/* nickname=NSDFreedom */
+/* extrainfo=0 */
+/* ===== */
+,
+"193.234.15.57:80 orport=443 id=92CFD9565B24646CAC2D172D3DB503D69E777B8A"
" ipv6=[2a00:1c20:4089:1234:7825:2c5d:1ecd:c66f]:443"
/* nickname=bakunin */
/* extrainfo=0 */
/* ===== */
,
-"91.219.237.244:80 orport=443 id=92ECC9E0E2AF81BB954719B189AC362E254AD4A5"
-/* nickname=lewwerDuarUesSlaav */
+"204.8.156.142:80 orport=443 id=94C4B7B8C50C86A92B6A20107539EE2678CF9A28"
+/* nickname=BostonUCompSci */
/* extrainfo=0 */
/* ===== */
,
@@ -506,8 +503,8 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"163.172.223.200:80 orport=443 id=998BF3ED7F70E33D1C307247B9626D9E7573C438"
-/* nickname=Outfall2 */
+"173.212.254.192:31336 orport=31337 id=99E246DB480B313A3012BC3363093CC26CD209C7"
+/* nickname=ViDiSrv */
/* extrainfo=0 */
/* ===== */
,
@@ -517,22 +514,24 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* ===== */
,
"66.111.2.20:9030 orport=9001 id=9A68B85A02318F4E7E87F2828039FBD5D75B0142"
+" ipv6=[2610:1c0:0:5::20]:9001"
/* nickname=NYCBUG0 */
/* extrainfo=0 */
/* ===== */
,
"185.100.86.128:9030 orport=9001 id=9B31F1F1C1554F9FFB3455911F82E818EF7C7883"
+" ipv6=[2a06:1700:1::11]:9001"
/* nickname=TorExitFinland */
/* extrainfo=0 */
/* ===== */
,
-"146.185.177.103:80 orport=9030 id=9EC5E097663862DF861A18C32B37C5F82284B27D"
-/* nickname=Winter */
+"86.105.212.130:9030 orport=443 id=9C900A7F6F5DD034CFFD192DAEC9CCAA813DB022"
+/* nickname=firstor2 */
/* extrainfo=0 */
/* ===== */
,
-"199.249.223.64:80 orport=443 id=9F2856F6D2B89AD4EF6D5723FAB167DB5A53519A"
-/* nickname=Quintex15 */
+"31.185.104.19:80 orport=443 id=9EAD5B2D3DBD96DBC80DCE423B0C345E920A758D"
+/* nickname=Digitalcourage3ip1 */
/* extrainfo=0 */
/* ===== */
,
@@ -541,11 +540,6 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"91.121.84.137:4952 orport=4052 id=9FBEB75E8BC142565F12CBBE078D63310236A334"
-/* nickname=lindon */
-/* extrainfo=0 */
-/* ===== */
-,
"46.165.230.5:80 orport=443 id=A0F06C2FADF88D3A39AA3072B406F09D7095AC9E"
/* nickname=Dhalgren */
/* extrainfo=1 */
@@ -557,9 +551,14 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
+"87.118.122.120:80 orport=443 id=A2A6616723B511D8E068BB71705191763191F6B2"
+/* nickname=otheontelth */
+/* extrainfo=0 */
+/* ===== */
+,
"81.7.3.67:993 orport=443 id=A2E6BB5C391CD46B38C55B4329C35304540771F1"
/* nickname=BeastieJoy62 */
-/* extrainfo=0 */
+/* extrainfo=1 */
/* ===== */
,
"171.25.193.78:80 orport=443 id=A478E421F83194C114F41E94F95999672AED51FE"
@@ -568,23 +567,31 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"178.16.208.58:80 orport=443 id=A4C98CEA3F34E05299417E9F885A642C88EF6029"
+"193.234.15.58:80 orport=443 id=A4C98CEA3F34E05299417E9F885A642C88EF6029"
" ipv6=[2a00:1c20:4089:1234:cdae:1b3e:cc38:3d45]:443"
/* nickname=jaures2 */
/* extrainfo=0 */
/* ===== */
,
-"163.172.149.122:80 orport=443 id=A9406A006D6E7B5DA30F2C6D4E42A338B5E340B2"
-/* nickname=niij03 */
+"128.31.0.13:80 orport=443 id=A53C46F5B157DD83366D45A8E99A244934A14C46"
+/* nickname=csailmitexit */
+/* extrainfo=0 */
+/* ===== */
+,
+"94.142.242.84:80 orport=443 id=AA0D167E03E298F9A8CD50F448B81FBD7FA80D56"
+" ipv6=[2a02:898:24:84::1]:443"
+/* nickname=rejozenger */
/* extrainfo=0 */
/* ===== */
,
"195.154.164.243:80 orport=443 id=AC66FFA4AB35A59EBBF5BF4C70008BF24D8A7A5C"
+" ipv6=[2001:bc8:399f:f000::1]:993"
/* nickname=torpidsFRonline3 */
/* extrainfo=0 */
/* ===== */
,
"86.59.119.88:80 orport=443 id=ACD889D86E02EDDAB1AFD81F598C0936238DC6D0"
+" ipv6=[2001:858:2:30:86:59:119:88]:443"
/* nickname=ph3x */
/* extrainfo=0 */
/* ===== */
@@ -601,10 +608,15 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"176.126.252.11:443 orport=9001 id=B0279A521375F3CB2AE210BDBFC645FDD2E1973A"
-" ipv6=[2a02:59e0:0:7::11]:9003"
-/* nickname=chulak */
-/* extrainfo=1 */
+"31.185.104.20:80 orport=443 id=ADB2C26629643DBB9F8FE0096E7D16F9414B4F8D"
+/* nickname=Digitalcourage3ip2 */
+/* extrainfo=0 */
+/* ===== */
+,
+"45.79.108.130:9030 orport=9001 id=AEDAC7081AE14B8D241ECF0FF17A2858AB4383D0"
+" ipv6=[2600:3c01:e000:131::8000:0]:9001"
+/* nickname=linss */
+/* extrainfo=0 */
/* ===== */
,
"5.9.147.226:9030 orport=9001 id=B0553175AADB0501E5A61FC61CEA3970BE130FF2"
@@ -614,15 +626,11 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* ===== */
,
"178.17.174.14:9030 orport=9001 id=B06F093A3D4DFAD3E923F4F28A74901BD4F74EB1"
+" ipv6=[2a00:1dc0:caff:8b::5b9a]:9001"
/* nickname=TorExitMoldova */
/* extrainfo=0 */
/* ===== */
,
-"199.249.223.40:80 orport=443 id=B0CD9F9B5B60651ADC5919C0F1EAA87DBA1D9249"
-/* nickname=Quintex31 */
-/* extrainfo=0 */
-/* ===== */
-,
"212.129.62.232:80 orport=443 id=B143D439B72D239A419F8DCE07B8A8EB1B486FA7"
/* nickname=wardsback */
/* extrainfo=0 */
@@ -633,8 +641,9 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"212.47.233.86:9030 orport=9001 id=B4CAFD9CBFB34EC5DAAC146920DC7DFAFE91EA20"
-/* nickname=netimanmu */
+"193.234.15.60:80 orport=443 id=B44FBE5366AD98B46D829754FA4AC599BAE41A6A"
+" ipv6=[2a00:1c20:4089:1234:67bc:79f3:61c0:6e49]:443"
+/* nickname=jaures3 */
/* extrainfo=0 */
/* ===== */
,
@@ -649,39 +658,51 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
+"185.220.101.32:10032 orport=20032 id=B771AA877687F88E6F1CA5354756DF6C8A7B6B24"
+/* nickname=niftypika */
+/* extrainfo=0 */
+/* ===== */
+,
"193.11.114.46:9032 orport=9003 id=B83DC1558F0D34353BB992EF93AFEAFDB226A73E"
/* nickname=mdfnet3 */
/* extrainfo=0 */
/* ===== */
,
+"85.248.227.164:444 orport=9002 id=B84F248233FEA90CAD439F292556A3139F6E1B82"
+" ipv6=[2a00:1298:8011:212::164]:9004"
+/* nickname=tollana */
+/* extrainfo=0 */
+/* ===== */
+,
"81.7.11.186:1080 orport=443 id=B86137AE9681701901C6720E55C16805B46BD8E3"
/* nickname=BeastieJoy60 */
-/* extrainfo=0 */
+/* extrainfo=1 */
/* ===== */
,
-"197.231.221.211:9030 orport=443 id=BC630CBBB518BE7E9F4E09712AB0269E9DC7D626"
-/* nickname=IPredator */
+"213.141.138.174:9030 orport=9001 id=BD552C165E2ED2887D3F1CCE9CFF155DDA2D86E6"
+/* nickname=Schakalium */
/* extrainfo=0 */
/* ===== */
,
-"198.96.155.3:8080 orport=5001 id=BCEDF6C193AA687AE471B8A22EBF6BC57C2D285E"
-/* nickname=gurgle */
+"148.251.190.229:9030 orport=9010 id=BF0FB582E37F738CD33C3651125F2772705BB8E8"
+" ipv6=[2a01:4f8:211:c68::2]:9010"
+/* nickname=quadhead */
/* extrainfo=0 */
/* ===== */
,
-"128.199.55.207:9030 orport=9001 id=BCEF908195805E03E92CCFE669C48738E556B9C5"
-" ipv6=[2a03:b0c0:2:d0::158:3001]:9001"
-/* nickname=EldritchReaper */
+"104.192.5.248:9030 orport=443 id=BF735F669481EE1CCC348F0731551C933D1E2278"
+/* nickname=Freeway1a1 */
/* extrainfo=0 */
/* ===== */
,
-"213.141.138.174:9030 orport=9001 id=BD552C165E2ED2887D3F1CCE9CFF155DDA2D86E6"
-/* nickname=Schakalium */
+"192.160.102.169:80 orport=9001 id=C0192FF43E777250084175F4E59AC1BA2290CE38"
+" ipv6=[2620:132:300c:c01d::9]:9002"
+/* nickname=manipogo */
/* extrainfo=0 */
/* ===== */
,
-"104.192.5.248:9030 orport=9001 id=BF735F669481EE1CCC348F0731551C933D1E2278"
-/* nickname=Freeway11 */
+"185.220.101.6:10006 orport=20006 id=C08DE49658E5B3CFC6F2A952B453C4B608C9A16A"
+/* nickname=niftyvolcanorabbit */
/* extrainfo=0 */
/* ===== */
,
@@ -701,18 +722,31 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"199.249.223.66:80 orport=443 id=C5A53BCC174EF8FD0DCB223E4AA929FA557DEDB2"
-/* nickname=Quintex17 */
+"193.234.15.55:80 orport=443 id=C4AEA05CF380BAD2230F193E083B8869B4A29937"
+" ipv6=[2a00:1c20:4089:1234:7b2c:11c5:5221:903e]:443"
+/* nickname=bakunin4 */
/* extrainfo=0 */
/* ===== */
,
-"85.25.213.211:465 orport=80 id=CE47F0356D86CF0A1A2008D97623216D560FB0A8"
-/* nickname=BeastieJoy61 */
+"85.248.227.163:443 orport=9001 id=C793AB88565DDD3C9E4C6F15CCB9D8C7EF964CE9"
+" ipv6=[2a00:1298:8011:212::163]:9003"
+/* nickname=ori */
+/* extrainfo=0 */
+/* ===== */
+,
+"192.160.102.165:80 orport=9001 id=C90CA3B7FE01A146B8268D56977DC4A2C024B9EA"
+" ipv6=[2620:132:300c:c01d::5]:9002"
+/* nickname=cowcat */
/* extrainfo=0 */
/* ===== */
,
-"51.15.13.245:9030 orport=9001 id=CED527EAC230E7B56E5B363F839671829C3BA01B"
-/* nickname=0x3d006 */
+"176.31.103.150:9030 orport=9001 id=CBD0D1BD110EC52963082D839AC6A89D0AE243E7"
+/* nickname=UV74S7mjxRcYVrGsAMw */
+/* extrainfo=0 */
+/* ===== */
+,
+"85.25.213.211:465 orport=80 id=CE47F0356D86CF0A1A2008D97623216D560FB0A8"
+/* nickname=BeastieJoy61 */
/* extrainfo=0 */
/* ===== */
,
@@ -721,19 +755,24 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"31.171.155.108:9030 orport=9001 id=D3E5EDDBE5159388704D6785BE51930AAFACEC6F"
-/* nickname=TorNodeAlbania */
+"5.45.111.149:80 orport=443 id=D405FCCF06ADEDF898DF2F29C9348DCB623031BA"
+" ipv6=[2a03:4000:6:2388:df98:15f9:b34d:443]:443"
+/* nickname=gGDHjdcC6zAlM8k08lY */
/* extrainfo=0 */
/* ===== */
,
-"37.221.162.226:9030 orport=9001 id=D64366987CB39F61AD21DBCF8142FA0577B92811"
-/* nickname=kasperskytor01 */
+"37.187.115.157:9030 orport=9001 id=D5039E1EBFD96D9A3F9846BF99EC9F75EDDE902A"
+/* nickname=Janky328891 */
/* extrainfo=0 */
/* ===== */
,
-"46.101.169.151:9030 orport=9001 id=D760C5B436E42F93D77EF2D969157EEA14F9B39C"
-" ipv6=[2a03:b0c0:3:d0::74f:a001]:9001"
-/* nickname=DanWin1210 */
+"217.182.51.248:80 orport=443 id=D6BA940D3255AB40DC5EE5B0B285FA143E1F9865"
+/* nickname=Cosworth02 */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.34.33.2:9265 orport=31415 id=D71B1CA1C9DC7E8CA64158E106AD770A21160FEE"
+/* nickname=lqdn */
/* extrainfo=0 */
/* ===== */
,
@@ -748,6 +787,11 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
+"54.36.237.163:80 orport=443 id=DB2682153AC0CCAECD2BD1E9EBE99C6815807A1E"
+/* nickname=GermanCraft2 */
+/* extrainfo=0 */
+/* ===== */
+,
"178.33.183.251:80 orport=443 id=DD823AFB415380A802DCAEB9461AE637604107FB"
" ipv6=[2001:41d0:2:a683::251]:443"
/* nickname=grenouille */
@@ -760,34 +804,36 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
-"92.222.38.67:80 orport=443 id=DED6892FF89DBD737BA689698A171B2392EB3E82"
-/* nickname=ThorExit */
+"83.212.99.68:80 orport=443 id=DDBB2A38252ADDA53E4492DDF982CA6CC6E10EC0"
+" ipv6=[2001:648:2ffc:1225:a800:bff:fe3d:67b5]:443"
+/* nickname=zouzounella */
/* extrainfo=0 */
/* ===== */
,
-"166.70.207.2:9030 orport=9001 id=E3DB2E354B883B59E8DC56B3E7A353DDFD457812"
-/* nickname=xmission */
+"92.222.38.67:80 orport=443 id=DED6892FF89DBD737BA689698A171B2392EB3E82"
+" ipv6=[2001:41d0:52:100::112a]:443"
+/* nickname=ThorExit */
/* extrainfo=0 */
/* ===== */
,
-"199.249.223.43:80 orport=443 id=E480D577F58E782A5BC4FA6F49A6650E9389302F"
-/* nickname=Quintex34 */
+"185.100.86.182:9030 orport=8080 id=E51620B90DCB310138ED89EDEDD0A5C361AAE24E"
+/* nickname=NormalCitizen */
/* extrainfo=0 */
/* ===== */
,
-"46.252.26.2:45212 orport=49991 id=E589316576A399C511A9781A73DA4545640B479D"
-/* nickname=marlen */
+"212.47.244.38:8080 orport=443 id=E81EF60A73B3809F8964F73766B01BAA0A171E20"
+/* nickname=Chimborazo */
/* extrainfo=0 */
/* ===== */
,
-"176.31.180.157:143 orport=22 id=E781F4EC69671B3F1864AE2753E0890351506329"
-" ipv6=[2001:41d0:8:eb9d::1]:22"
-/* nickname=armbrust */
+"51.254.147.57:80 orport=443 id=EB80A8D52F07238B576C42CEAB98ADD084EE075E"
+/* nickname=Cosworth01 */
/* extrainfo=0 */
/* ===== */
,
-"212.47.244.38:8080 orport=443 id=E81EF60A73B3809F8964F73766B01BAA0A171E20"
-/* nickname=Chimborazo */
+"192.87.28.28:9030 orport=9001 id=ED2338CAC2711B3E331392E1ED2831219B794024"
+" ipv6=[2001:678:230:3028:192:87:28:28]:9001"
+/* nickname=SEC6xFreeBSD64 */
/* extrainfo=0 */
/* ===== */
,
@@ -796,31 +842,60 @@ URL: https:onionoo.torproject.orguptime?first_seen_days90-&flagV2Dir&typerelay&l
/* extrainfo=0 */
/* ===== */
,
+"193.70.112.165:80 orport=443 id=F10BDE279AE71515DDCCCC61DC19AC8765F8A3CC"
+/* nickname=ParkBenchInd001 */
+/* extrainfo=0 */
+/* ===== */
+,
+"129.13.131.140:80 orport=443 id=F2DFE5FA1E4CF54F8E761A6D304B9B4EC69BDAE8"
+" ipv6=[2a00:1398:5:f604:cafe:cafe:cafe:9001]:443"
+/* nickname=AlleKochenKaffee */
+/* extrainfo=0 */
+/* ===== */
+,
"37.187.102.108:80 orport=443 id=F4263275CF54A6836EE7BD527B1328836A6F06E1"
" ipv6=[2001:41d0:a:266c::1]:443"
/* nickname=EvilMoe */
/* extrainfo=0 */
/* ===== */
,
-"46.28.109.231:9030 orport=9001 id=F70B7C5CD72D74C7F9F2DC84FA9D20D51BA13610"
-" ipv6=[2a02:2b88:2:1::4205:1]:9001"
-/* nickname=wedostor */
+"192.160.102.168:80 orport=9001 id=F6A358DD367B3282D6EF5824C9D45E1A19C7E815"
+" ipv6=[2620:132:300c:c01d::8]:9002"
+/* nickname=prawksi */
+/* extrainfo=0 */
+/* ===== */
+,
+"163.172.154.162:9030 orport=9001 id=F741E5124CB12700DA946B78C9B2DD175D6CD2A1"
+" ipv6=[2001:bc8:4400:2100::17:419]:9001"
+/* nickname=rofltor06 */
/* extrainfo=0 */
/* ===== */
,
+"78.47.18.110:443 orport=80 id=F8D27B163B9247B232A2EEE68DD8B698695C28DE"
+" ipv6=[2a01:4f8:120:4023::110]:80"
+/* nickname=fluxe3 */
+/* extrainfo=1 */
+/* ===== */
+,
+"178.254.19.101:80 orport=443 id=F9246DEF2B653807236DA134F2AEAB103D58ABFE"
+/* nickname=Freebird31 */
+/* extrainfo=1 */
+/* ===== */
+,
"185.96.180.29:80 orport=443 id=F93D8F37E35C390BCAD9F9069E13085B745EC216"
/* nickname=TykRelay06 */
/* extrainfo=0 */
/* ===== */
,
"86.59.119.83:80 orport=443 id=FC9AC8EA0160D88BCCFDE066940D7DD9FA45495B"
+" ipv6=[2001:858:2:30:86:59:119:83]:443"
/* nickname=ph3x */
/* extrainfo=0 */
/* ===== */
,
"149.56.45.200:9030 orport=9001 id=FE296180018833AF03A8EACD5894A614623D3F76"
" ipv6=[2607:5300:201:3000::17d3]:9002"
-/* nickname=PiotrTorpotkinOne */
+/* nickname=PyotrTorpotkinOne */
/* extrainfo=0 */
/* ===== */
,
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 0ff1c6ce0d..d59043a7f6 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -150,7 +150,7 @@ geoip_add_entry(const tor_addr_t *low, const tor_addr_t *high,
idx = ((uintptr_t)idxplus1_)-1;
}
{
- geoip_country_t *c = smartlist_get(geoip_countries, idx);
+ geoip_country_t *c = smartlist_get(geoip_countries, (int)idx);
tor_assert(!strcasecmp(c->countrycode, country));
}
@@ -628,8 +628,7 @@ geoip_note_client_seen(geoip_client_action_t action,
/* Only remember statistics if the DoS mitigation subsystem is enabled. If
* not, only if as entry guard or as bridge. */
if (!dos_enabled()) {
- if (!options->EntryStatistics &&
- (!(options->BridgeRelay && options->BridgeRecordUsageByCountry))) {
+ if (!options->EntryStatistics && !should_record_bridge_info(options)) {
return;
}
}
@@ -1881,5 +1880,8 @@ geoip_free_all(void)
clear_geoip_db();
tor_free(bridge_stats_extrainfo);
+
+ memset(geoip_digest, 0, sizeof(geoip_digest));
+ memset(geoip6_digest, 0, sizeof(geoip6_digest));
}
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 4dc35f68d0..d7d259470f 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -36,6 +36,7 @@ hibernating, phase 2:
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
+#include "crypto_rand.h"
#include "hibernate.h"
#include "main.h"
#include "router.h"
@@ -51,6 +52,10 @@ static time_t hibernate_end_time = 0;
* we aren't shutting down. */
static time_t shutdown_time = 0;
+/** A timed event that we'll use when it's time to wake up from
+ * hibernation. */
+static mainloop_event_t *wakeup_event = NULL;
+
/** Possible accounting periods. */
typedef enum {
UNIT_MONTH=1, UNIT_WEEK=2, UNIT_DAY=3,
@@ -130,6 +135,8 @@ static time_t start_of_accounting_period_after(time_t now);
static time_t start_of_accounting_period_containing(time_t now);
static void accounting_set_wakeup_time(void);
static void on_hibernate_state_change(hibernate_state_t prev_state);
+static void hibernate_schedule_wakeup_event(time_t now, time_t end_time);
+static void wakeup_event_callback(mainloop_event_t *ev, void *data);
/**
* Return the human-readable name for the hibernation state <b>state</b>
@@ -297,7 +304,7 @@ accounting_get_end_time,(void))
return interval_end_time;
}
-/** Called from main.c to tell us that <b>seconds</b> seconds have
+/** Called from connection.c to tell us that <b>seconds</b> seconds have
* passed, <b>n_read</b> bytes have been read, and <b>n_written</b>
* bytes have been written. */
void
@@ -866,7 +873,7 @@ hibernate_end(hibernate_state_t new_state)
hibernate_state = new_state;
hibernate_end_time = 0; /* no longer hibernating */
- stats_n_seconds_working = 0; /* reset published uptime */
+ reset_uptime(); /* reset published uptime */
}
/** A wrapper around hibernate_begin, for when we get SIGINT. */
@@ -876,13 +883,26 @@ hibernate_begin_shutdown(void)
hibernate_begin(HIBERNATE_STATE_EXITING, time(NULL));
}
-/** Return true iff we are currently hibernating. */
+/**
+ * Return true iff we are currently hibernating -- that is, if we are in
+ * any non-live state.
+ */
MOCK_IMPL(int,
we_are_hibernating,(void))
{
return hibernate_state != HIBERNATE_STATE_LIVE;
}
+/**
+ * Return true iff we are currently _fully_ hibernating -- that is, if we are
+ * in a state where we expect to handle no network activity at all.
+ */
+MOCK_IMPL(int,
+we_are_fully_hibernating,(void))
+{
+ return hibernate_state == HIBERNATE_STATE_DORMANT;
+}
+
/** If we aren't currently dormant, close all connections and become
* dormant. */
static void
@@ -935,6 +955,63 @@ hibernate_go_dormant(time_t now)
or_state_mark_dirty(get_or_state(),
get_options()->AvoidDiskWrites ? now+600 : 0);
+
+ hibernate_schedule_wakeup_event(now, hibernate_end_time);
+}
+
+/**
+ * Schedule a mainloop event at <b>end_time</b> to wake up from a dormant
+ * state. We can't rely on this happening from second_elapsed_callback,
+ * since second_elapsed_callback will be shut down when we're dormant.
+ *
+ * (Note that We might immediately go back to sleep after we set the next
+ * wakeup time.)
+ */
+static void
+hibernate_schedule_wakeup_event(time_t now, time_t end_time)
+{
+ struct timeval delay = { 0, 0 };
+
+ if (now >= end_time) {
+ // In these cases we always wait at least a second, to avoid running
+ // the callback in a tight loop.
+ delay.tv_sec = 1;
+ } else {
+ delay.tv_sec = (end_time - now);
+ }
+
+ if (!wakeup_event) {
+ wakeup_event = mainloop_event_postloop_new(wakeup_event_callback, NULL);
+ }
+
+ mainloop_event_schedule(wakeup_event, &delay);
+}
+
+/**
+ * Called at the end of the interval, or at the wakeup time of the current
+ * interval, to exit the dormant state.
+ **/
+static void
+wakeup_event_callback(mainloop_event_t *ev, void *data)
+{
+ (void) ev;
+ (void) data;
+
+ const time_t now = time(NULL);
+ accounting_run_housekeeping(now);
+ consider_hibernation(now);
+ if (hibernate_state != HIBERNATE_STATE_DORMANT) {
+ /* We woke up, so everything's great here */
+ return;
+ }
+
+ /* We're still dormant. */
+ if (now < interval_wakeup_time)
+ hibernate_end_time = interval_wakeup_time;
+ else
+ hibernate_end_time = interval_end_time;
+
+ hibernate_schedule_wakeup_event(now, hibernate_end_time);
}
/** Called when hibernate_end_time has arrived. */
@@ -1111,10 +1188,30 @@ getinfo_helper_accounting(control_connection_t *conn,
static void
on_hibernate_state_change(hibernate_state_t prev_state)
{
- (void)prev_state; /* Should we do something with this? */
control_event_server_status(LOG_NOTICE,
"HIBERNATION_STATUS STATUS=%s",
hibernate_state_to_string(hibernate_state));
+
+ /* We are changing hibernation state, this can affect the main loop event
+ * list. Rescan it to update the events state. We do this whatever the new
+ * hibernation state because they can each possibly affect an event. The
+ * initial state means we are booting up so we shouldn't scan here because
+ * at this point the events in the list haven't been initialized. */
+ if (prev_state != HIBERNATE_STATE_INITIAL) {
+ rescan_periodic_events(get_options());
+ }
+
+ reschedule_per_second_timer();
+}
+
+/** Free all resources held by the accounting module */
+void
+accounting_free_all(void)
+{
+ mainloop_event_free(wakeup_event);
+ hibernate_state = HIBERNATE_STATE_INITIAL;
+ hibernate_end_time = 0;
+ shutdown_time = 0;
}
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/hibernate.h b/src/or/hibernate.h
index 85fb42864b..453969d052 100644
--- a/src/or/hibernate.h
+++ b/src/or/hibernate.h
@@ -25,11 +25,13 @@ void accounting_add_bytes(size_t n_read, size_t n_written, int seconds);
int accounting_record_bandwidth_usage(time_t now, or_state_t *state);
void hibernate_begin_shutdown(void);
MOCK_DECL(int, we_are_hibernating, (void));
+MOCK_DECL(int, we_are_fully_hibernating,(void));
void consider_hibernation(time_t now);
int getinfo_helper_accounting(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
uint64_t get_accounting_max_total(void);
+void accounting_free_all(void);
#ifdef HIBERNATE_PRIVATE
/** Possible values of hibernate_state */
diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c
index df53efd32d..ecc845d17f 100644
--- a/src/or/hs_cache.c
+++ b/src/or/hs_cache.c
@@ -11,6 +11,7 @@
#include "or.h"
#include "config.h"
+#include "crypto_util.h"
#include "hs_ident.h"
#include "hs_common.h"
#include "hs_client.h"
diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h
index a141634cc4..0d0085ffdc 100644
--- a/src/or/hs_cache.h
+++ b/src/or/hs_cache.h
@@ -11,7 +11,6 @@
#include <stdint.h>
-#include "crypto.h"
#include "crypto_ed25519.h"
#include "hs_common.h"
#include "hs_descriptor.h"
diff --git a/src/or/hs_cell.c b/src/or/hs_cell.c
index 5244cfa3dd..03273a44f9 100644
--- a/src/or/hs_cell.c
+++ b/src/or/hs_cell.c
@@ -8,6 +8,7 @@
#include "or.h"
#include "config.h"
+#include "crypto_util.h"
#include "rendservice.h"
#include "replaycache.h"
#include "util.h"
@@ -369,7 +370,7 @@ introduce1_encrypt_and_encode(trn_cell_introduce1_t *cell,
crypto_cipher_free(cipher);
offset += encoded_enc_cell_len;
/* Compute MAC from the above and put it in the buffer. This function will
- * make the adjustment to the encryptled_len to ommit the MAC length. */
+ * make the adjustment to the encrypted_len to omit the MAC length. */
compute_introduce_mac(encoded_cell, encoded_cell_len,
encrypted, encrypted_len,
keys.mac_key, sizeof(keys.mac_key),
diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c
index 0a9999a190..d911f5fde4 100644
--- a/src/or/hs_circuit.c
+++ b/src/or/hs_circuit.c
@@ -13,6 +13,8 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "nodelist.h"
#include "policies.h"
#include "relay.h"
@@ -193,11 +195,8 @@ register_intro_circ(const hs_service_intro_point_t *ip,
tor_assert(circ);
if (ip->base.is_only_legacy) {
- uint8_t digest[DIGEST_LEN];
- if (BUG(crypto_pk_get_digest(ip->legacy_key, (char *) digest) < 0)) {
- return;
- }
- hs_circuitmap_register_intro_circ_v2_service_side(circ, digest);
+ hs_circuitmap_register_intro_circ_v2_service_side(circ,
+ ip->legacy_key_digest);
} else {
hs_circuitmap_register_intro_circ_v3_service_side(circ,
&ip->auth_key_kp.pubkey);
@@ -683,22 +682,14 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip,
origin_circuit_t *
hs_circ_service_get_intro_circ(const hs_service_intro_point_t *ip)
{
- origin_circuit_t *circ = NULL;
-
tor_assert(ip);
if (ip->base.is_only_legacy) {
- uint8_t digest[DIGEST_LEN];
- if (BUG(crypto_pk_get_digest(ip->legacy_key, (char *) digest) < 0)) {
- goto end;
- }
- circ = hs_circuitmap_get_intro_circ_v2_service_side(digest);
+ return hs_circuitmap_get_intro_circ_v2_service_side(ip->legacy_key_digest);
} else {
- circ = hs_circuitmap_get_intro_circ_v3_service_side(
+ return hs_circuitmap_get_intro_circ_v3_service_side(
&ip->auth_key_kp.pubkey);
}
- end:
- return circ;
}
/* Called when we fail building a rendezvous circuit at some point other than
diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h
index 2f5beaa168..f69137e1d5 100644
--- a/src/or/hs_circuit.h
+++ b/src/or/hs_circuit.h
@@ -10,7 +10,6 @@
#define TOR_HS_CIRCUIT_H
#include "or.h"
-#include "crypto.h"
#include "crypto_ed25519.h"
#include "hs_service.h"
diff --git a/src/or/hs_client.c b/src/or/hs_client.c
index 636226a1d5..8ecefc1209 100644
--- a/src/or/hs_client.c
+++ b/src/or/hs_client.c
@@ -9,31 +9,31 @@
#define HS_CLIENT_PRIVATE
#include "or.h"
-#include "hs_circuit.h"
-#include "hs_ident.h"
+#include "circpathbias.h"
+#include "circuitbuild.h"
+#include "circuitlist.h"
+#include "circuituse.h"
+#include "config.h"
+#include "connection.h"
#include "connection_edge.h"
#include "container.h"
-#include "rendclient.h"
-#include "hs_descriptor.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
+#include "directory.h"
#include "hs_cache.h"
#include "hs_cell.h"
-#include "hs_ident.h"
-#include "config.h"
-#include "directory.h"
+#include "hs_circuit.h"
#include "hs_client.h"
#include "hs_control.h"
-#include "router.h"
-#include "routerset.h"
-#include "circuitlist.h"
-#include "circuituse.h"
-#include "connection.h"
-#include "nodelist.h"
-#include "circpathbias.h"
-#include "connection.h"
+#include "hs_descriptor.h"
+#include "hs_ident.h"
#include "hs_ntor.h"
-#include "circuitbuild.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "reasons.h"
+#include "rendclient.h"
+#include "router.h"
+#include "routerset.h"
/* Return a human-readable string for the client fetch status code. */
static const char *
@@ -1225,10 +1225,6 @@ 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.");
- if (get_options()->SafeLogging_ == SAFELOG_SCRUB_NONE) {
- log_warn(LD_GENERAL, "%s", escaped(desc_str));
- }
goto err;
}
@@ -1429,7 +1425,7 @@ hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident)
/* We are unable to use the descriptor so remove the directory request
* from the cache so the next connection can try again. */
note_connection_attempt_succeeded(edge_conn->hs_ident);
- goto end;
+ continue;
}
log_info(LD_REND, "Descriptor has arrived. Launching circuits.");
@@ -1439,8 +1435,8 @@ hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident)
* connection is considered "fresh" and can continue without being closed
* too early. */
base_conn->timestamp_created = now;
- base_conn->timestamp_lastread = now;
- base_conn->timestamp_lastwritten = now;
+ base_conn->timestamp_last_read_allowed = now;
+ base_conn->timestamp_last_write_allowed = now;
/* Change connection's state into waiting for a circuit. */
base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index 10b56c0baa..c42ef0c97b 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -15,6 +15,8 @@
#include "config.h"
#include "circuitbuild.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "hs_cache.h"
@@ -28,9 +30,8 @@
#include "rendservice.h"
#include "routerset.h"
#include "router.h"
-#include "routerset.h"
-#include "shared_random.h"
-#include "shared_random_state.h"
+#include "shared_random_client.h"
+#include "dirauth/shared_random_state.h"
/* Trunnel */
#include "ed25519_cert.h"
@@ -104,7 +105,7 @@ 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->fetch, 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
@@ -115,7 +116,7 @@ compare_digest_to_store_first_hsdir_index(const void *_key,
{
const char *key = _key;
const node_t *node = *_member;
- return tor_memcmp(key, node->hsdir_index->store_first, 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
@@ -126,7 +127,7 @@ compare_digest_to_store_second_hsdir_index(const void *_key,
{
const char *key = _key;
const node_t *node = *_member;
- return tor_memcmp(key, node->hsdir_index->store_second, DIGEST256_LEN);
+ return tor_memcmp(key, node->hsdir_index.store_second, DIGEST256_LEN);
}
/* Helper function: Compare two node_t objects current hsdir_index. */
@@ -135,8 +136,8 @@ 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->fetch,
- node2->hsdir_index->fetch,
+ return tor_memcmp(node1->hsdir_index.fetch,
+ node2->hsdir_index.fetch,
DIGEST256_LEN);
}
@@ -146,8 +147,8 @@ 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->store_first,
- node2->hsdir_index->store_first,
+ return tor_memcmp(node1->hsdir_index.store_first,
+ node2->hsdir_index.store_first,
DIGEST256_LEN);
}
@@ -157,8 +158,8 @@ 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,
+ return tor_memcmp(node1->hsdir_index.store_second,
+ node2->hsdir_index.store_second,
DIGEST256_LEN);
}
@@ -523,7 +524,7 @@ rend_data_get_address(const rend_data_t *rend_data)
return TO_REND_DATA_V2(rend_data)->onion_address;
default:
/* We should always have a supported version. */
- tor_assert(0);
+ tor_assert_unreached();
}
}
@@ -546,7 +547,7 @@ rend_data_get_desc_id(const rend_data_t *rend_data, uint8_t replica,
return TO_REND_DATA_V2(rend_data)->descriptor_id[replica];
default:
/* We should always have a supported version. */
- tor_assert(0);
+ tor_assert_unreached();
}
}
@@ -569,7 +570,7 @@ rend_data_get_pk_digest(const rend_data_t *rend_data, size_t *len_out)
}
default:
/* We should always have a supported version. */
- tor_assert(0);
+ tor_assert_unreached();
}
}
@@ -1289,18 +1290,15 @@ node_has_hsdir_index(const node_t *node)
/* 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)) {
- return 0;
- }
- if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->fetch,
+ if (BUG(tor_mem_is_zero((const char*)node->hsdir_index.fetch,
DIGEST256_LEN))) {
return 0;
}
- if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->store_first,
+ 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,
+ if (BUG(tor_mem_is_zero((const char*)node->hsdir_index.store_second,
DIGEST256_LEN))) {
return 0;
}
@@ -1334,15 +1332,20 @@ hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
sorted_nodes = smartlist_new();
+ /* Make sure we actually have a live consensus */
+ networkstatus_t *c = networkstatus_get_live_consensus(approx_time());
+ if (!c || smartlist_len(c->routerstatus_list) == 0) {
+ log_warn(LD_REND, "No live consensus so we can't get the responsible "
+ "hidden service directories.");
+ goto done;
+ }
+
+ /* Ensure the nodelist is fresh, since it contains the HSDir indices. */
+ nodelist_ensure_freshness(c);
+
/* Add every node_t that support HSDir v3 for which we do have a valid
* hsdir_index already computed for them for this consensus. */
{
- networkstatus_t *c = networkstatus_get_latest_consensus();
- if (!c || smartlist_len(c->routerstatus_list) == 0) {
- log_warn(LD_REND, "No valid consensus so we can't get the responsible "
- "hidden service directories.");
- goto done;
- }
SMARTLIST_FOREACH_BEGIN(c->routerstatus_list, const routerstatus_t *, rs) {
/* Even though this node_t object won't be modified and should be const,
* we can't add const object in a smartlist_t. */
@@ -1814,4 +1817,3 @@ hs_inc_rdv_stream_counter(origin_circuit_t *circ)
tor_assert_nonfatal_unreached();
}
}
-
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 83ba1b8599..ef7d5dca2b 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -156,19 +156,6 @@ typedef struct rend_service_port_config_t {
char unix_addr[FLEXIBLE_ARRAY_MEMBER];
} 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 {
- /* 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);
void hs_free_all(void);
diff --git a/src/or/hs_control.c b/src/or/hs_control.c
index 87b4e3fca8..6b9b95c6d8 100644
--- a/src/or/hs_control.c
+++ b/src/or/hs_control.c
@@ -8,6 +8,7 @@
#include "or.h"
#include "control.h"
+#include "crypto_util.h"
#include "hs_common.h"
#include "hs_control.h"
#include "hs_descriptor.h"
@@ -39,9 +40,8 @@ hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk,
* can't pick a node without an hsdir_index. */
hsdir_node = node_get_by_id(hsdir_rs->identity_digest);
tor_assert(hsdir_node);
- tor_assert(hsdir_node->hsdir_index);
/* This is a fetch event. */
- hsdir_index = hsdir_node->hsdir_index->fetch;
+ hsdir_index = hsdir_node->hsdir_index.fetch;
/* Trigger the event. */
control_event_hs_descriptor_requested(onion_address, REND_NO_AUTH,
diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c
index 0298c37322..e85c29a24e 100644
--- a/src/or/hs_descriptor.c
+++ b/src/or/hs_descriptor.c
@@ -59,6 +59,8 @@
#include "ed25519_cert.h" /* Trunnel interface. */
#include "hs_descriptor.h"
#include "circuitbuild.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "parsecommon.h"
#include "rendcache.h"
#include "hs_cache.h"
@@ -1361,7 +1363,7 @@ decrypt_desc_layer,(const hs_descriptor_t *desc,
* This is a critical check that is making sure the computed MAC matches the
* one in the descriptor. */
if (!tor_memeq(our_mac, desc_mac, sizeof(our_mac))) {
- log_warn(LD_REND, "Encrypted service descriptor MAC check failed");
+ log_info(LD_REND, "Encrypted service descriptor MAC check failed");
goto err;
}
@@ -1544,7 +1546,6 @@ desc_decrypt_all(const hs_descriptor_t *desc, char **decrypted_out)
superencrypted_len,
&encrypted_blob);
if (!encrypted_len) {
- log_warn(LD_REND, "Decrypting encrypted desc failed.");
goto err;
}
tor_assert(encrypted_blob);
@@ -2046,7 +2047,11 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
* in the descriptor as a blob of bytes. */
message_len = desc_decrypt_all(desc, &message);
if (!message_len) {
- log_warn(LD_REND, "Service descriptor decryption failed.");
+ /* Inform at notice level that the onion address requested can't be
+ * reached without client authorization most likely. */
+ log_notice(LD_REND, "Fail to decrypt descriptor for requested onion "
+ "address. It is likely requiring client "
+ "authorization.");
goto err;
}
tor_assert(message);
diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h
index 09979410e1..8195c6efbc 100644
--- a/src/or/hs_descriptor.h
+++ b/src/or/hs_descriptor.h
@@ -16,6 +16,7 @@
#include "container.h"
#include "crypto.h"
#include "crypto_ed25519.h"
+#include "ed25519_cert.h" /* needed for trunnel */
#include "torcert.h"
/* Trunnel */
diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c
index 0bce2f625b..3603e329d4 100644
--- a/src/or/hs_ident.c
+++ b/src/or/hs_ident.c
@@ -7,6 +7,7 @@
* subsytem.
**/
+#include "crypto_util.h"
#include "hs_ident.h"
/* Return a newly allocated circuit identifier. The given public key is copied
diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h
index 91ec389aa4..8f9da30c35 100644
--- a/src/or/hs_ident.h
+++ b/src/or/hs_ident.h
@@ -21,7 +21,6 @@
#ifndef TOR_HS_IDENT_H
#define TOR_HS_IDENT_H
-#include "crypto.h"
#include "crypto_ed25519.h"
#include "hs_common.h"
diff --git a/src/or/hs_intropoint.c b/src/or/hs_intropoint.c
index 8c6453e6fd..ee79109ca9 100644
--- a/src/or/hs_intropoint.c
+++ b/src/or/hs_intropoint.c
@@ -12,7 +12,6 @@
#include "config.h"
#include "circuitlist.h"
#include "circuituse.h"
-#include "config.h"
#include "relay.h"
#include "rendmid.h"
#include "rephist.h"
@@ -59,7 +58,7 @@ get_auth_key_from_cell(ed25519_public_key_t *auth_key_out,
default:
/* Getting here is really bad as it means we got a unknown cell type from
* this file where every call has an hardcoded value. */
- tor_assert(0); /* LCOV_EXCL_LINE */
+ tor_assert_unreached(); /* LCOV_EXCL_LINE */
}
tor_assert(key_array);
tor_assert(auth_key_len == sizeof(auth_key_out->pubkey));
@@ -610,4 +609,3 @@ hs_intropoint_clear(hs_intropoint_t *ip)
smartlist_free(ip->link_specifiers);
memset(ip, 0, sizeof(hs_intropoint_t));
}
-
diff --git a/src/or/hs_ntor.c b/src/or/hs_ntor.c
index a416bc46c3..809fa83bb8 100644
--- a/src/or/hs_ntor.c
+++ b/src/or/hs_ntor.c
@@ -25,6 +25,7 @@
*/
#include "or.h"
+#include "crypto_util.h"
#include "hs_ntor.h"
/* String constants used by the ntor HS protocol */
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 4c67aff429..54ceb9dcad 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -15,6 +15,8 @@
#include "circuituse.h"
#include "config.h"
#include "connection.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "directory.h"
#include "main.h"
#include "networkstatus.h"
@@ -24,14 +26,13 @@
#include "router.h"
#include "routerkeys.h"
#include "routerlist.h"
-#include "shared_random_state.h"
+#include "shared_random_client.h"
#include "statefile.h"
#include "hs_circuit.h"
#include "hs_common.h"
#include "hs_config.h"
#include "hs_control.h"
-#include "hs_circuit.h"
#include "hs_descriptor.h"
#include "hs_ident.h"
#include "hs_intropoint.h"
@@ -81,6 +82,7 @@ static smartlist_t *hs_service_staging_list;
* reupload if needed */
static int consider_republishing_hs_descriptors = 0;
+/* Static declaration. */
static void set_descriptor_revision_counter(hs_descriptor_t *hs_desc);
static void move_descriptors(hs_service_t *src, hs_service_t *dst);
@@ -153,6 +155,12 @@ register_service(hs_service_ht *map, hs_service_t *service)
}
/* Taking ownership of the object at this point. */
HT_INSERT(hs_service_ht, map, service);
+
+ /* If we just modified the global map, we notify. */
+ if (map == hs_service_map) {
+ hs_service_map_has_changed();
+ }
+
return 0;
}
@@ -179,6 +187,11 @@ remove_service(hs_service_ht *map, hs_service_t *service)
"while removing service %s",
escaped(service->config.directory_path));
}
+
+ /* If we just modified the global map, we notify. */
+ if (map == hs_service_map) {
+ hs_service_map_has_changed();
+ }
}
/* Set the default values for a service configuration object <b>c</b>. */
@@ -434,6 +447,10 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy,
if (crypto_pk_generate_key(ip->legacy_key) < 0) {
goto err;
}
+ if (crypto_pk_get_digest(ip->legacy_key,
+ (char *) ip->legacy_key_digest) < 0) {
+ goto err;
+ }
}
if (ei == NULL) {
@@ -920,6 +937,11 @@ register_all_services(void)
smartlist_clear(hs_service_staging_list);
service_free_all();
hs_service_map = new_service_map;
+ /* We've just register services into the new map and now we've replaced the
+ * global map with it so we have to notify that the change happened. When
+ * registering a service, the notify is only triggered if the destination
+ * map is the global map for which in here it was not. */
+ hs_service_map_has_changed();
}
/* Write the onion address of a given service to the given filename fname_ in
@@ -2295,8 +2317,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 *idx = (is_next_desc) ? hsdir->hsdir_index->store_second:
- hsdir->hsdir_index->store_first;
+ const uint8_t *idx = (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),
@@ -2944,6 +2966,17 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list)
/* Public API */
/* ========== */
+/* This is called everytime the service map (v2 or v3) changes that is if an
+ * element is added or removed. */
+void
+hs_service_map_has_changed(void)
+{
+ /* If we now have services where previously we had not, we need to enable
+ * the HS service main loop event. If we changed to having no services, we
+ * need to disable the event. */
+ rescan_periodic_events(get_options());
+}
+
/* Upload an encoded descriptor in encoded_desc of the given version. This
* descriptor is for the service identity_pk and blinded_pk used to setup the
* directory connection identifier. It is uploaded to the directory hsdir_rs
@@ -3041,6 +3074,12 @@ hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
goto err;
}
+ /* Build the onion address for logging purposes but also the control port
+ * uses it for the HS_DESC event. */
+ hs_build_address(&service->keys.identity_pk,
+ (uint8_t) service->config.version,
+ service->onion_address);
+
/* The only way the registration can fail is if the service public key
* already exists. */
if (BUG(register_service(hs_service_map, service) < 0)) {
@@ -3050,14 +3089,10 @@ hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
goto err;
}
- /* Last step is to build the onion address. */
- hs_build_address(&service->keys.identity_pk,
- (uint8_t) service->config.version,
- service->onion_address);
- *address_out = tor_strdup(service->onion_address);
-
log_info(LD_CONFIG, "Added ephemeral v3 onion service: %s",
safe_str_client(service->onion_address));
+
+ *address_out = tor_strdup(service->onion_address);
ret = RSAE_OKAY;
goto end;
@@ -3105,8 +3140,10 @@ hs_service_del_ephemeral(const char *address)
goto err;
}
- /* Close circuits, remove from map and finally free. */
- close_service_circuits(service);
+ /* Close introduction circuits, remove from map and finally free. Notice
+ * that the rendezvous circuits aren't closed in order for any existing
+ * connections to finish. We let the application terminate them. */
+ close_service_intro_circuits(service);
remove_service(hs_service_map, service);
hs_service_free(service);
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index f3cd49e073..911e177702 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -50,6 +50,9 @@ typedef struct hs_service_intro_point_t {
/* Legacy key if that intro point doesn't support v3. This should be used if
* the base object legacy flag is set. */
crypto_pk_t *legacy_key;
+ /* Legacy key SHA1 public key digest. This should be used only if the base
+ * object legacy flag is set. */
+ uint8_t legacy_key_digest[DIGEST_LEN];
/* Amount of INTRODUCE2 cell accepted from this intro point. */
uint64_t introduce2_count;
@@ -260,6 +263,7 @@ void hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
int hs_service_set_conn_addr_port(const origin_circuit_t *circ,
edge_connection_t *conn);
+void hs_service_map_has_changed(void);
void hs_service_dir_info_changed(void);
void hs_service_run_scheduled_events(time_t now);
void hs_service_circuit_has_opened(origin_circuit_t *circ);
diff --git a/src/or/hs_stats.c b/src/or/hs_stats.c
index 3e183a5bfc..1e2a96945b 100644
--- a/src/or/hs_stats.c
+++ b/src/or/hs_stats.c
@@ -3,7 +3,7 @@
/**
* \file hs_stats.c
- * \brief Keeps stats about the activity of our hidden service.
+ * \brief Keeps stats about the activity of our onion service(s).
**/
#include "or.h"
@@ -42,14 +42,14 @@ hs_stats_get_n_introduce2_v2_cells(void)
return n_introduce2_v2;
}
-/** Note that we attempted to launch another circuit to a rendezvous point */
+/** Note that we attempted to launch another circuit to a rendezvous point. */
void
hs_stats_note_service_rendezvous_launch(void)
{
n_rendezvous_launches++;
}
-/** Return the number of rendezvous circuits we have attempted to launch */
+/** Return the number of rendezvous circuits we have attempted to launch. */
uint32_t
hs_stats_get_n_rendezvous_launches(void)
{
diff --git a/src/or/include.am b/src/or/include.am
index c1e23dd3d9..59d593a5e9 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -41,10 +41,8 @@ LIBTOR_A_SOURCES = \
src/or/consdiffmgr.c \
src/or/control.c \
src/or/cpuworker.c \
- src/or/dircollate.c \
src/or/directory.c \
src/or/dirserv.c \
- src/or/dirvote.c \
src/or/dns.c \
src/or/dnsserv.c \
src/or/dos.c \
@@ -76,8 +74,6 @@ LIBTOR_A_SOURCES = \
src/or/onion.c \
src/or/onion_fast.c \
src/or/onion_tap.c \
- src/or/shared_random.c \
- src/or/shared_random_state.c \
src/or/transports.c \
src/or/parsecommon.c \
src/or/periodic.c \
@@ -91,6 +87,7 @@ LIBTOR_A_SOURCES = \
src/or/policies.c \
src/or/reasons.c \
src/or/relay.c \
+ src/or/relay_crypto.c \
src/or/rendcache.c \
src/or/rendclient.c \
src/or/rendcommon.c \
@@ -106,15 +103,43 @@ LIBTOR_A_SOURCES = \
src/or/scheduler.c \
src/or/scheduler_kist.c \
src/or/scheduler_vanilla.c \
+ src/or/shared_random_client.c \
src/or/statefile.c \
src/or/status.c \
src/or/torcert.c \
src/or/tor_api.c \
+ src/or/voting_schedule.c \
src/or/onion_ntor.c \
$(tor_platform_source)
+#
+# Modules are conditionnally compiled in tor starting here. We add the C files
+# only if the modules has been enabled at configure time. We always add the
+# source files of every module to libtor-testing.a so we can build the unit
+# tests for everything. See the UNITTESTS_ENABLED branch below.
+#
+LIBTOR_TESTING_A_SOURCES = $(LIBTOR_A_SOURCES)
+
+# The Directory Authority module.
+MODULE_DIRAUTH_SOURCES = \
+ src/or/dirauth/dircollate.c \
+ src/or/dirauth/dirvote.c \
+ src/or/dirauth/shared_random.c \
+ src/or/dirauth/shared_random_state.c
+if BUILD_MODULE_DIRAUTH
+LIBTOR_A_SOURCES += $(MODULE_DIRAUTH_SOURCES)
+endif
+
src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES)
-src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES)
+if UNITTESTS_ENABLED
+
+# Add the sources of the modules that are needed for tests to work here.
+LIBTOR_TESTING_A_SOURCES += $(MODULE_DIRAUTH_SOURCES)
+
+src_or_libtor_testing_a_SOURCES = $(LIBTOR_TESTING_A_SOURCES)
+else
+src_or_libtor_testing_a_SOURCES =
+endif
src_or_tor_SOURCES = src/or/tor_main.c
AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or
@@ -184,10 +209,8 @@ ORHEADERS = \
src/or/consdiffmgr.h \
src/or/control.h \
src/or/cpuworker.h \
- src/or/dircollate.h \
src/or/directory.h \
src/or/dirserv.h \
- src/or/dirvote.h \
src/or/dns.h \
src/or/dns_structs.h \
src/or/dnsserv.h \
@@ -224,8 +247,6 @@ ORHEADERS = \
src/or/onion_ntor.h \
src/or/onion_tap.h \
src/or/or.h \
- src/or/shared_random.h \
- src/or/shared_random_state.h \
src/or/transports.h \
src/or/parsecommon.h \
src/or/periodic.h \
@@ -238,6 +259,7 @@ ORHEADERS = \
src/or/proto_socks.h \
src/or/reasons.h \
src/or/relay.h \
+ src/or/relay_crypto.h \
src/or/rendcache.h \
src/or/rendclient.h \
src/or/rendcommon.h \
@@ -252,10 +274,23 @@ ORHEADERS = \
src/or/routerset.h \
src/or/routerparse.h \
src/or/scheduler.h \
+ src/or/shared_random_client.h \
src/or/statefile.h \
src/or/status.h \
src/or/torcert.h \
- src/or/tor_api_internal.h
+ src/or/tor_api_internal.h \
+ src/or/voting_schedule.h
+
+# We add the headers of the modules even though they are disabled so we can
+# properly compiled the entry points stub.
+
+# The Directory Authority module headers.
+ORHEADERS += \
+ src/or/dirauth/dircollate.h \
+ src/or/dirauth/dirvote.h \
+ src/or/dirauth/mode.h \
+ src/or/dirauth/shared_random.h \
+ src/or/dirauth/shared_random_state.h
# This may someday want to be an installed file?
noinst_HEADERS += src/or/tor_api.h
diff --git a/src/or/keypin.c b/src/or/keypin.c
index 1698dc184f..97e16c1f78 100644
--- a/src/or/keypin.c
+++ b/src/or/keypin.c
@@ -12,7 +12,7 @@
#include "orconfig.h"
#include "compat.h"
-#include "crypto.h"
+#include "crypto_digest.h"
#include "crypto_format.h"
#include "di_ops.h"
#include "ht.h"
@@ -289,8 +289,10 @@ static int keypin_journal_fd = -1;
int
keypin_open_journal(const char *fname)
{
- /* O_SYNC ??*/
- int fd = tor_open_cloexec(fname, O_WRONLY|O_CREAT|O_BINARY, 0600);
+#ifndef O_SYNC
+#define O_SYNC 0
+#endif
+ int fd = tor_open_cloexec(fname, O_WRONLY|O_CREAT|O_BINARY|O_SYNC, 0600);
if (fd < 0)
goto err;
@@ -417,10 +419,11 @@ keypin_load_journal_impl(const char *data, size_t size)
++n_entries;
}
- int severity = (n_corrupt_lines || n_duplicates) ? LOG_WARN : LOG_INFO;
+ int severity = (n_corrupt_lines || n_duplicates) ? LOG_NOTICE : LOG_INFO;
tor_log(severity, LD_DIRSERV,
"Loaded %d entries from keypin journal. "
- "Found %d corrupt lines, %d duplicates, and %d conflicts.",
+ "Found %d corrupt lines (ignored), %d duplicates (harmless), "
+ "and %d conflicts (resolved in favor or more recent entry).",
n_entries, n_corrupt_lines, n_duplicates, n_conflicts);
return 0;
diff --git a/src/or/main.c b/src/or/main.c
index 3aa730dcec..c69a5b8b8b 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -59,6 +59,7 @@
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
+#include "circuitmux_ewma.h"
#include "command.h"
#include "compress.h"
#include "config.h"
@@ -70,9 +71,9 @@
#include "control.h"
#include "cpuworker.h"
#include "crypto_s2k.h"
+#include "crypto_rand.h"
#include "directory.h"
#include "dirserv.h"
-#include "dirvote.h"
#include "dns.h"
#include "dnsserv.h"
#include "dos.h"
@@ -103,7 +104,6 @@
#include "routerlist.h"
#include "routerparse.h"
#include "scheduler.h"
-#include "shared_random.h"
#include "statefile.h"
#include "status.h"
#include "tor_api.h"
@@ -118,6 +118,10 @@
#include <event2/event.h>
+#include "dirauth/dirvote.h"
+#include "dirauth/mode.h"
+#include "dirauth/shared_random.h"
+
#ifdef HAVE_SYSTEMD
# if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__)
/* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse
@@ -133,7 +137,7 @@ void evdns_shutdown(int);
#ifdef HAVE_RUST
// helper function defined in Rust to output a log message indicating if tor is
// running with Rust enabled. See src/rust/tor_util
-char *rust_welcome_string(void);
+void rust_log_welcome_string(void);
#endif
/********* PROTOTYPES **********/
@@ -152,24 +156,12 @@ static void shutdown_did_not_work_callback(evutil_socket_t fd, short event,
void *arg) ATTR_NORETURN;
/********* START VARIABLES **********/
-int global_read_bucket; /**< Max number of bytes I can read this second. */
-int global_write_bucket; /**< Max number of bytes I can write this second. */
-
-/** Max number of relayed (bandwidth class 1) bytes I can read this second. */
-int global_relayed_read_bucket;
-/** Max number of relayed (bandwidth class 1) bytes I can write this second. */
-int global_relayed_write_bucket;
-/** What was the read bucket before the last second_elapsed_callback() call?
- * (used to determine how many bytes we've read). */
-static int stats_prev_global_read_bucket;
-/** What was the write bucket before the last second_elapsed_callback() call?
- * (used to determine how many bytes we've written). */
-static int stats_prev_global_write_bucket;
-
-/* DOCDOC stats_prev_n_read */
-static uint64_t stats_prev_n_read = 0;
-/* DOCDOC stats_prev_n_written */
-static uint64_t stats_prev_n_written = 0;
+
+/* Token bucket for all traffic. */
+token_bucket_rw_t global_bucket;
+
+/* Token bucket for relayed traffic. */
+token_bucket_rw_t global_relayed_bucket;
/* XXX we might want to keep stats about global_relayed_*_bucket too. Or not.*/
/** How many bytes have we read since we started the process? */
@@ -179,7 +171,7 @@ static uint64_t stats_n_bytes_written = 0;
/** What time did this process start up? */
time_t time_of_process_start = 0;
/** How many seconds have we been running? */
-long stats_n_seconds_working = 0;
+static long stats_n_seconds_working = 0;
/** How many times have we returned from the main loop successfully? */
static uint64_t stats_n_main_loop_successes = 0;
/** How many times have we received an error from the main loop? */
@@ -193,6 +185,8 @@ static uint64_t stats_n_main_loop_idle = 0;
static time_t time_of_last_signewnym = 0;
/** Is there a signewnym request we're currently waiting to handle? */
static int signewnym_is_pending = 0;
+/** Mainloop event for the deferred signewnym call. */
+static mainloop_event_t *handle_deferred_signewnym_ev = NULL;
/** How many times have we called newnym? */
static unsigned newnym_epoch = 0;
@@ -410,6 +404,38 @@ connection_unlink(connection_t *conn)
connection_free(conn);
}
+/** Event that invokes schedule_active_linked_connections_cb. */
+static mainloop_event_t *schedule_active_linked_connections_event = NULL;
+
+/**
+ * Callback: used to activate read events for all linked connections, so
+ * libevent knows to call their read callbacks. This callback run as a
+ * postloop event, so that the events _it_ activates don't happen until
+ * Libevent has a chance to check for other events.
+ */
+static void
+schedule_active_linked_connections_cb(mainloop_event_t *event, void *arg)
+{
+ (void)event;
+ (void)arg;
+
+ /* All active linked conns should get their read events activated,
+ * so that libevent knows to run their callbacks. */
+ SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn,
+ event_active(conn->read_event, EV_READ, 1));
+
+ /* Reactivate the event if we still have connections in the active list.
+ *
+ * A linked connection doesn't get woken up by I/O but rather artificially
+ * by this event callback. It has directory data spooled in it and it is
+ * sent incrementally by small chunks unless spool_eagerly is true. For that
+ * to happen, we need to induce the activation of the read event so it can
+ * be flushed. */
+ if (smartlist_len(active_linked_connection_lst)) {
+ mainloop_event_activate(schedule_active_linked_connections_event);
+ }
+}
+
/** Initialize the global connection list, closeable connection list,
* and active connection list. */
STATIC void
@@ -431,6 +457,7 @@ add_connection_to_closeable_list(connection_t *conn)
tor_assert(conn->marked_for_close);
assert_connection_ok(conn, time(NULL));
smartlist_add(closeable_connection_lst, conn);
+ mainloop_schedule_postloop_cleanup();
}
/** Return 1 if conn is on the closeable list, else return 0. */
@@ -458,21 +485,37 @@ get_connection_array, (void))
return connection_array;
}
-/** Provides the traffic read and written over the life of the process. */
-
+/**
+ * Return the amount of network traffic read, in bytes, over the life of this
+ * process.
+ */
MOCK_IMPL(uint64_t,
get_bytes_read,(void))
{
return stats_n_bytes_read;
}
-/* DOCDOC get_bytes_written */
+/**
+ * Return the amount of network traffic read, in bytes, over the life of this
+ * process.
+ */
MOCK_IMPL(uint64_t,
get_bytes_written,(void))
{
return stats_n_bytes_written;
}
+/**
+ * Increment the amount of network traffic read and written, over the life of
+ * this process.
+ */
+void
+stats_increment_bytes_read_and_written(uint64_t r, uint64_t w)
+{
+ stats_n_bytes_read += r;
+ stats_n_bytes_written += w;
+}
+
/** Set the event mask on <b>conn</b> to <b>events</b>. (The event
* mask is a bitmask whose bits are READ_EVENT and WRITE_EVENT)
*/
@@ -710,20 +753,6 @@ connection_should_read_from_linked_conn(connection_t *conn)
return 0;
}
-/** If we called event_base_loop() and told it to never stop until it
- * runs out of events, now we've changed our mind: tell it we want it to
- * exit once the current round of callbacks is done, so that we can
- * run external code, and then return to the main loop. */
-void
-tell_event_loop_to_run_external_code(void)
-{
- if (!called_loop_once) {
- struct timeval tv = { 0, 0 };
- tor_event_base_loopexit(tor_libevent_get_base(), &tv);
- called_loop_once = 1; /* hack to avoid adding more exit events */
- }
-}
-
/** Event to run 'shutdown did not work callback'. */
static struct event *shutdown_did_not_work_event = NULL;
@@ -779,8 +808,9 @@ tor_shutdown_event_loop_and_exit(int exitcode)
shutdown_did_not_work_callback, NULL);
event_add(shutdown_did_not_work_event, &ten_seconds);
- /* Unlike loopexit, loopbreak prevents other callbacks from running. */
- tor_event_base_loopbreak(tor_libevent_get_base());
+ /* Unlike exit_loop_after_delay(), exit_loop_after_callback
+ * prevents other callbacks from running. */
+ tor_libevent_exit_loop_after_callback(tor_libevent_get_base());
}
/** Return true iff tor_shutdown_event_loop_and_exit() has been called. */
@@ -802,10 +832,7 @@ connection_start_reading_from_linked_conn(connection_t *conn)
if (!conn->active_on_link) {
conn->active_on_link = 1;
smartlist_add(active_linked_connection_lst, conn);
- /* make sure that the event_base_loop() function exits at
- * the end of its run through the current connections, so we can
- * activate read events for linked connections. */
- tell_event_loop_to_run_external_code();
+ mainloop_event_activate(schedule_active_linked_connections_event);
} else {
tor_assert(smartlist_contains(active_linked_connection_lst, conn));
}
@@ -1008,7 +1035,8 @@ conn_close_if_marked(int i)
LOG_FN_CONN(conn, (LOG_INFO,LD_NET,
"Holding conn (fd %d) open for more flushing.",
(int)conn->s));
- conn->timestamp_lastwritten = now; /* reset so we can flush more */
+ conn->timestamp_last_write_allowed = now; /* reset so we can flush
+ * more */
} else if (sz == 0) {
/* Also, retval==0. If we get here, we didn't want to write anything
* (because of rate-limiting) and we didn't. */
@@ -1019,20 +1047,18 @@ conn_close_if_marked(int i)
* busy Libevent loops where we keep ending up here and returning
* 0 until we are no longer blocked on bandwidth.
*/
- if (connection_is_writing(conn)) {
- conn->write_blocked_on_bw = 1;
- connection_stop_writing(conn);
- }
- if (connection_is_reading(conn)) {
- /* XXXX+ We should make this code unreachable; if a connection is
- * marked for close and flushing, there is no point in reading to it
- * at all. Further, checking at this point is a bit of a hack: it
- * would make much more sense to react in
- * connection_handle_read_impl, or to just stop reading in
- * mark_and_flush */
- conn->read_blocked_on_bw = 1;
- connection_stop_reading(conn);
+ connection_consider_empty_write_buckets(conn);
+ /* Make sure that consider_empty_buckets really disabled the
+ * connection: */
+ if (BUG(connection_is_writing(conn))) {
+ connection_write_bw_exhausted(conn, true);
}
+
+ /* The connection is being held due to write rate limit and thus will
+ * flush its data later. We need to stop reading because this
+ * connection is about to be closed once flushed. It should not
+ * process anything more coming in at this stage. */
+ connection_stop_reading(conn);
}
return 0;
}
@@ -1059,9 +1085,8 @@ conn_close_if_marked(int i)
* reason.
*/
static void
-directory_all_unreachable_cb(evutil_socket_t fd, short event, void *arg)
+directory_all_unreachable_cb(mainloop_event_t *event, void *arg)
{
- (void)fd;
(void)event;
(void)arg;
@@ -1081,7 +1106,7 @@ directory_all_unreachable_cb(evutil_socket_t fd, short event, void *arg)
control_event_general_error("DIR_ALL_UNREACHABLE");
}
-static struct event *directory_all_unreachable_cb_event = NULL;
+static mainloop_event_t *directory_all_unreachable_cb_event = NULL;
/** We've just tried every dirserver we know about, and none of
* them were reachable. Assume the network is down. Change state
@@ -1094,16 +1119,15 @@ directory_all_unreachable(time_t now)
{
(void)now;
- stats_n_seconds_working=0; /* reset it */
+ reset_uptime(); /* reset it */
if (!directory_all_unreachable_cb_event) {
directory_all_unreachable_cb_event =
- tor_event_new(tor_libevent_get_base(),
- -1, EV_READ, directory_all_unreachable_cb, NULL);
+ mainloop_event_new(directory_all_unreachable_cb, NULL);
tor_assert(directory_all_unreachable_cb_event);
}
- event_active(directory_all_unreachable_cb_event, EV_READ, 1);
+ mainloop_event_activate(directory_all_unreachable_cb_event);
}
/** This function is called whenever we successfully pull down some new
@@ -1143,7 +1167,7 @@ directory_info_has_arrived(time_t now, int from_cache, int suppress_logs)
if (server_mode(options) && !net_is_disabled() && !from_cache &&
(have_completed_a_circuit() || !any_predicted_circuits(now)))
- consider_testing_reachability(1, 1);
+ router_do_reachability_checks(1, 1);
}
/** Perform regular maintenance tasks for a single connection. This
@@ -1159,7 +1183,7 @@ run_connection_housekeeping(int i, time_t now)
channel_t *chan = NULL;
int have_any_circuits;
int past_keepalive =
- now >= conn->timestamp_lastwritten + options->KeepalivePeriod;
+ now >= conn->timestamp_last_write_allowed + options->KeepalivePeriod;
if (conn->outbuf && !connection_get_outbuf_len(conn) &&
conn->type == CONN_TYPE_OR)
@@ -1174,10 +1198,10 @@ run_connection_housekeeping(int i, time_t now)
* if a server or received if a client) for 5 min */
if (conn->type == CONN_TYPE_DIR &&
((DIR_CONN_IS_SERVER(conn) &&
- conn->timestamp_lastwritten
+ conn->timestamp_last_write_allowed
+ options->TestingDirConnectionMaxStall < now) ||
(!DIR_CONN_IS_SERVER(conn) &&
- conn->timestamp_lastread
+ conn->timestamp_last_read_allowed
+ options->TestingDirConnectionMaxStall < now))) {
log_info(LD_DIR,"Expiring wedged directory conn (fd %d, purpose %d)",
(int)conn->s, conn->purpose);
@@ -1235,7 +1259,8 @@ run_connection_housekeeping(int i, time_t now)
} else if (we_are_hibernating() &&
! have_any_circuits &&
!connection_get_outbuf_len(conn)) {
- /* We're hibernating, there's no circuits, and nothing to flush.*/
+ /* We're hibernating or shutting down, there's no circuits, and nothing to
+ * flush.*/
log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) "
"[Hibernating or exiting].",
(int)conn->s,conn->address, conn->port);
@@ -1253,13 +1278,14 @@ run_connection_housekeeping(int i, time_t now)
connection_or_close_normally(TO_OR_CONN(conn), 0);
} else if (
now >= or_conn->timestamp_lastempty + options->KeepalivePeriod*10 &&
- now >= conn->timestamp_lastwritten + options->KeepalivePeriod*10) {
+ now >=
+ conn->timestamp_last_write_allowed + options->KeepalivePeriod*10) {
log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,
"Expiring stuck OR connection to fd %d (%s:%d). (%d bytes to "
"flush; %d seconds since last write)",
(int)conn->s, conn->address, conn->port,
(int)connection_get_outbuf_len(conn),
- (int)(now-conn->timestamp_lastwritten));
+ (int)(now-conn->timestamp_last_write_allowed));
connection_or_close_normally(TO_OR_CONN(conn), 0);
} else if (past_keepalive && !connection_get_outbuf_len(conn)) {
/* send a padding cell */
@@ -1296,6 +1322,16 @@ signewnym_impl(time_t now)
control_event_signal(SIGNEWNYM);
}
+/** Callback: run a deferred signewnym. */
+static void
+handle_deferred_signewnym_cb(mainloop_event_t *event, void *arg)
+{
+ (void)event;
+ (void)arg;
+ log_info(LD_CONTROL, "Honoring delayed NEWNYM request");
+ signewnym_impl(time(NULL));
+}
+
/** Return the number of times that signewnym has been called. */
unsigned
get_signewnym_epoch(void)
@@ -1311,71 +1347,106 @@ static int periodic_events_initialized = 0;
#undef CALLBACK
#define CALLBACK(name) \
static int name ## _callback(time_t, const or_options_t *)
-CALLBACK(rotate_onion_key);
-CALLBACK(check_onion_keys_expiry_time);
-CALLBACK(check_ed_keys);
-CALLBACK(launch_descriptor_fetches);
-CALLBACK(rotate_x509_certificate);
CALLBACK(add_entropy);
-CALLBACK(launch_reachability_tests);
-CALLBACK(downrate_stability);
-CALLBACK(save_stability);
CALLBACK(check_authority_cert);
+CALLBACK(check_canonical_channels);
+CALLBACK(check_descriptor);
+CALLBACK(check_dns_honesty);
+CALLBACK(check_ed_keys);
CALLBACK(check_expired_networkstatus);
-CALLBACK(write_stats_file);
-CALLBACK(record_bridge_stats);
+CALLBACK(check_for_reachability_bw);
+CALLBACK(check_onion_keys_expiry_time);
CALLBACK(clean_caches);
+CALLBACK(clean_consdiffmgr);
+CALLBACK(dirvote);
+CALLBACK(downrate_stability);
+CALLBACK(expire_old_ciruits_serverside);
+CALLBACK(fetch_networkstatus);
+CALLBACK(heartbeat);
+CALLBACK(hs_service);
+CALLBACK(launch_descriptor_fetches);
+CALLBACK(launch_reachability_tests);
+CALLBACK(reachability_warnings);
+CALLBACK(record_bridge_stats);
CALLBACK(rend_cache_failure_clean);
+CALLBACK(reset_padding_counts);
CALLBACK(retry_dns);
-CALLBACK(check_descriptor);
-CALLBACK(check_for_reachability_bw);
-CALLBACK(fetch_networkstatus);
CALLBACK(retry_listeners);
-CALLBACK(expire_old_ciruits_serverside);
-CALLBACK(check_dns_honesty);
+CALLBACK(rotate_onion_key);
+CALLBACK(rotate_x509_certificate);
+CALLBACK(save_stability);
+CALLBACK(save_state);
CALLBACK(write_bridge_ns);
-CALLBACK(check_fw_helper_app);
-CALLBACK(heartbeat);
-CALLBACK(clean_consdiffmgr);
-CALLBACK(reset_padding_counts);
-CALLBACK(check_canonical_channels);
-CALLBACK(hs_service);
+CALLBACK(write_stats_file);
#undef CALLBACK
/* Now we declare an array of periodic_event_item_t for each periodic event */
-#define CALLBACK(name) PERIODIC_EVENT(name)
-
-static periodic_event_item_t periodic_events[] = {
- CALLBACK(rotate_onion_key),
- CALLBACK(check_onion_keys_expiry_time),
- CALLBACK(check_ed_keys),
- CALLBACK(launch_descriptor_fetches),
- CALLBACK(rotate_x509_certificate),
- CALLBACK(add_entropy),
- CALLBACK(launch_reachability_tests),
- CALLBACK(downrate_stability),
- CALLBACK(save_stability),
- CALLBACK(check_authority_cert),
- CALLBACK(check_expired_networkstatus),
- CALLBACK(write_stats_file),
- CALLBACK(record_bridge_stats),
- CALLBACK(clean_caches),
- CALLBACK(rend_cache_failure_clean),
- CALLBACK(retry_dns),
- CALLBACK(check_descriptor),
- CALLBACK(check_for_reachability_bw),
- CALLBACK(fetch_networkstatus),
- CALLBACK(retry_listeners),
- CALLBACK(expire_old_ciruits_serverside),
- CALLBACK(check_dns_honesty),
- CALLBACK(write_bridge_ns),
- CALLBACK(check_fw_helper_app),
- CALLBACK(heartbeat),
- CALLBACK(clean_consdiffmgr),
- CALLBACK(reset_padding_counts),
- CALLBACK(check_canonical_channels),
- CALLBACK(hs_service),
+#define CALLBACK(name, r, f) PERIODIC_EVENT(name, r, f)
+
+STATIC periodic_event_item_t periodic_events[] = {
+ /* Everyone needs to run those. */
+ CALLBACK(add_entropy, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(check_expired_networkstatus, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(clean_caches, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(fetch_networkstatus, PERIODIC_EVENT_ROLE_ALL,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(heartbeat, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(launch_descriptor_fetches, PERIODIC_EVENT_ROLE_ALL,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(reset_padding_counts, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(retry_listeners, PERIODIC_EVENT_ROLE_ALL,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(save_state, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(rotate_x509_certificate, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(write_stats_file, PERIODIC_EVENT_ROLE_ALL, 0),
+
+ /* Routers (bridge and relay) only. */
+ CALLBACK(check_descriptor, PERIODIC_EVENT_ROLE_ROUTER,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(check_ed_keys, PERIODIC_EVENT_ROLE_ROUTER, 0),
+ CALLBACK(check_for_reachability_bw, PERIODIC_EVENT_ROLE_ROUTER,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(check_onion_keys_expiry_time, PERIODIC_EVENT_ROLE_ROUTER, 0),
+ CALLBACK(expire_old_ciruits_serverside, PERIODIC_EVENT_ROLE_ROUTER,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(reachability_warnings, PERIODIC_EVENT_ROLE_ROUTER,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(retry_dns, PERIODIC_EVENT_ROLE_ROUTER, 0),
+ CALLBACK(rotate_onion_key, PERIODIC_EVENT_ROLE_ROUTER, 0),
+
+ /* Authorities (bridge and directory) only. */
+ CALLBACK(downrate_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0),
+ CALLBACK(launch_reachability_tests, PERIODIC_EVENT_ROLE_AUTHORITIES,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(save_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0),
+
+ /* Directory authority only. */
+ CALLBACK(check_authority_cert, PERIODIC_EVENT_ROLE_DIRAUTH, 0),
+ CALLBACK(dirvote, PERIODIC_EVENT_ROLE_DIRAUTH, PERIODIC_EVENT_FLAG_NEED_NET),
+
+ /* Relay only. */
+ CALLBACK(check_canonical_channels, PERIODIC_EVENT_ROLE_RELAY,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(check_dns_honesty, PERIODIC_EVENT_ROLE_RELAY,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+
+ /* Hidden Service service only. */
+ CALLBACK(hs_service, PERIODIC_EVENT_ROLE_HS_SERVICE,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+
+ /* Bridge only. */
+ CALLBACK(record_bridge_stats, PERIODIC_EVENT_ROLE_BRIDGE, 0),
+
+ /* Client only. */
+ CALLBACK(rend_cache_failure_clean, PERIODIC_EVENT_ROLE_CLIENT, 0),
+
+ /* Bridge Authority only. */
+ CALLBACK(write_bridge_ns, PERIODIC_EVENT_ROLE_BRIDGEAUTH, 0),
+
+ /* Directory server only. */
+ CALLBACK(clean_consdiffmgr, PERIODIC_EVENT_ROLE_DIRSERVER, 0),
+
END_OF_PERIODIC_EVENTS
};
#undef CALLBACK
@@ -1385,9 +1456,11 @@ static periodic_event_item_t periodic_events[] = {
* can access them by name. We also keep them inside periodic_events[]
* so that we can implement "reset all timers" in a reasonable way. */
static periodic_event_item_t *check_descriptor_event=NULL;
+static periodic_event_item_t *dirvote_event=NULL;
static periodic_event_item_t *fetch_networkstatus_event=NULL;
static periodic_event_item_t *launch_descriptor_fetches_event=NULL;
static periodic_event_item_t *check_dns_honesty_event=NULL;
+static periodic_event_item_t *save_state_event=NULL;
/** Reset all the periodic events so we'll do all our actions again as if we
* just started up.
@@ -1417,6 +1490,39 @@ find_periodic_event(const char *name)
return NULL;
}
+/** Return a bitmask of the roles this tor instance is configured for using
+ * the given options. */
+STATIC int
+get_my_roles(const or_options_t *options)
+{
+ tor_assert(options);
+
+ int roles = 0;
+ int is_bridge = options->BridgeRelay;
+ int is_relay = server_mode(options);
+ int is_dirauth = authdir_mode_v3(options);
+ int is_bridgeauth = authdir_mode_bridge(options);
+ int is_hidden_service = !!hs_service_get_num_services() ||
+ !!rend_num_services();
+ int is_dirserver = dir_server_mode(options);
+ /* We also consider tor to have the role of a client if the ControlPort is
+ * set because a lot of things can be done over the control port which
+ * requires tor to have basic functionnalities. */
+ int is_client = options_any_client_port_set(options) ||
+ options->ControlPort_set ||
+ options->OwningControllerFD >= 0;
+
+ if (is_bridge) roles |= PERIODIC_EVENT_ROLE_BRIDGE;
+ if (is_client) roles |= PERIODIC_EVENT_ROLE_CLIENT;
+ if (is_relay) roles |= PERIODIC_EVENT_ROLE_RELAY;
+ if (is_dirauth) roles |= PERIODIC_EVENT_ROLE_DIRAUTH;
+ if (is_bridgeauth) roles |= PERIODIC_EVENT_ROLE_BRIDGEAUTH;
+ if (is_hidden_service) roles |= PERIODIC_EVENT_ROLE_HS_SERVICE;
+ if (is_dirserver) roles |= PERIODIC_EVENT_ROLE_DIRSERVER;
+
+ return roles;
+}
+
/** Event to run initialize_periodic_events_cb */
static struct event *initialize_periodic_events_event = NULL;
@@ -1431,11 +1537,10 @@ initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data)
(void) fd;
(void) events;
(void) data;
+
tor_event_free(initialize_periodic_events_event);
- int i;
- for (i = 0; periodic_events[i].name; ++i) {
- periodic_event_launch(&periodic_events[i]);
- }
+
+ rescan_periodic_events(get_options());
}
/** Set up all the members of periodic_events[], and configure them all to be
@@ -1446,6 +1551,7 @@ initialize_periodic_events(void)
tor_assert(periodic_events_initialized == 0);
periodic_events_initialized = 1;
+ /* Set up all periodic events. We'll launch them by roles. */
int i;
for (i = 0; periodic_events[i].name; ++i) {
periodic_event_setup(&periodic_events[i]);
@@ -1455,9 +1561,11 @@ initialize_periodic_events(void)
STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END
NAMED_CALLBACK(check_descriptor);
+ NAMED_CALLBACK(dirvote);
NAMED_CALLBACK(fetch_networkstatus);
NAMED_CALLBACK(launch_descriptor_fetches);
NAMED_CALLBACK(check_dns_honesty);
+ NAMED_CALLBACK(save_state);
struct timeval one_second = { 1, 0 };
initialize_periodic_events_event = tor_evtimer_new(
@@ -1473,6 +1581,58 @@ teardown_periodic_events(void)
for (i = 0; periodic_events[i].name; ++i) {
periodic_event_destroy(&periodic_events[i]);
}
+ periodic_events_initialized = 0;
+}
+
+/** Do a pass at all our periodic events, disable those we don't need anymore
+ * and enable those we need now using the given options. */
+void
+rescan_periodic_events(const or_options_t *options)
+{
+ tor_assert(options);
+
+ /* Avoid scanning the event list if we haven't initialized it yet. This is
+ * particularly useful for unit tests in order to avoid initializing main
+ * loop events everytime. */
+ if (!periodic_events_initialized) {
+ return;
+ }
+
+ int roles = get_my_roles(options);
+
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+
+ /* Handle the event flags. */
+ if (net_is_disabled() &&
+ (item->flags & PERIODIC_EVENT_FLAG_NEED_NET)) {
+ continue;
+ }
+
+ /* Enable the event if needed. It is safe to enable an event that was
+ * already enabled. Same goes for disabling it. */
+ if (item->roles & roles) {
+ log_debug(LD_GENERAL, "Launching periodic event %s", item->name);
+ periodic_event_enable(item);
+ } else {
+ log_debug(LD_GENERAL, "Disabling periodic event %s", item->name);
+ periodic_event_disable(item);
+ }
+ }
+}
+
+/* We just got new options globally set, see if we need to enabled or disable
+ * periodic events. */
+void
+periodic_events_on_new_options(const or_options_t *options)
+{
+ /* Only if we've already initialized the events, rescan the list which will
+ * enable or disable events depending on our roles. This will be called at
+ * bootup and we don't want this function to initialize the events because
+ * they aren't set up at this stage. */
+ if (periodic_events_initialized) {
+ rescan_periodic_events(options);
+ }
}
/**
@@ -1483,8 +1643,9 @@ teardown_periodic_events(void)
void
reschedule_descriptor_update_check(void)
{
- tor_assert(check_descriptor_event);
- periodic_event_reschedule(check_descriptor_event);
+ if (check_descriptor_event) {
+ periodic_event_reschedule(check_descriptor_event);
+ }
}
/**
@@ -1501,6 +1662,35 @@ reschedule_directory_downloads(void)
periodic_event_reschedule(launch_descriptor_fetches_event);
}
+/** Mainloop callback: clean up circuits, channels, and connections
+ * that are pending close. */
+static void
+postloop_cleanup_cb(mainloop_event_t *ev, void *arg)
+{
+ (void)ev;
+ (void)arg;
+ circuit_close_all_marked();
+ close_closeable_connections();
+ channel_run_cleanup();
+ channel_listener_run_cleanup();
+}
+
+/** Event to run postloop_cleanup_cb */
+static mainloop_event_t *postloop_cleanup_ev=NULL;
+
+/** Schedule a post-loop event to clean up marked channels, connections, and
+ * circuits. */
+void
+mainloop_schedule_postloop_cleanup(void)
+{
+ if (PREDICT_UNLIKELY(postloop_cleanup_ev == NULL)) {
+ // (It's possible that we can get here if we decide to close a connection
+ // in the earliest stages of our configuration, before we create events.)
+ return;
+ }
+ mainloop_event_activate(postloop_cleanup_ev);
+}
+
#define LONGEST_TIMER_PERIOD (30 * 86400)
/** Helper: Return the number of seconds between <b>now</b> and <b>next</b>,
* clipped to the range [1 second, LONGEST_TIMER_PERIOD]. */
@@ -1538,17 +1728,6 @@ run_scheduled_events(time_t now)
*/
consider_hibernation(now);
- /* 0b. If we've deferred a signewnym, make sure it gets handled
- * eventually. */
- if (signewnym_is_pending &&
- time_of_last_signewnym + MAX_SIGNEWNYM_RATE <= now) {
- log_info(LD_CONTROL, "Honoring delayed NEWNYM request");
- signewnym_impl(now);
- }
-
- /* 0c. If we've deferred log messages for the controller, handle them now */
- flush_pending_log_callbacks();
-
/* Maybe enough time elapsed for us to reconsider a circuit. */
circuit_upgrade_circuits_from_guard_wait();
@@ -1562,10 +1741,6 @@ run_scheduled_events(time_t now)
accounting_run_housekeeping(now);
}
- if (authdir_mode_v3(options)) {
- dirvote_act(options, now);
- }
-
/* 3a. Every second, we examine pending circuits and prune the
* ones which have been pending for more than a few seconds.
* We do this before step 4, so it can try building more if
@@ -1599,12 +1774,6 @@ run_scheduled_events(time_t now)
circuit_expire_old_circs_as_needed(now);
}
- if (!net_is_disabled()) {
- /* This is usually redundant with circuit_build_needed_circs() above,
- * but it is very fast when there is no work to do. */
- connection_ap_attach_pending(0);
- }
-
/* 5. We do housekeeping for each connection... */
channel_update_bad_for_new_circs(NULL, 0);
int i;
@@ -1612,32 +1781,9 @@ run_scheduled_events(time_t now)
run_connection_housekeeping(i, now);
}
- /* 6. And remove any marked circuits... */
- circuit_close_all_marked();
-
- /* 8. and blow away any connections that need to die. have to do this now,
- * because if we marked a conn for close and left its socket -1, then
- * we'll pass it to poll/select and bad things will happen.
- */
- close_closeable_connections();
-
- /* 8b. And if anything in our state is ready to get flushed to disk, we
- * flush it. */
- or_state_save(now);
-
- /* 8c. Do channel cleanup just like for connections */
- channel_run_cleanup();
- channel_listener_run_cleanup();
-
/* 11b. check pending unconfigured managed proxies */
if (!net_is_disabled() && pt_proxies_configuration_pending())
pt_configure_remaining_proxies();
-
- /* 12. launch diff computations. (This is free if there are none to
- * launch.) */
- if (dir_server_mode(options)) {
- consdiffmgr_rescan();
- }
}
/* Periodic callback: rotate the onion keys after the period defined by the
@@ -1847,6 +1993,40 @@ check_authority_cert_callback(time_t now, const or_options_t *options)
}
/**
+ * Scheduled callback: Run directory-authority voting functionality.
+ *
+ * The schedule is a bit complicated here, so dirvote_act() manages the
+ * schedule itself.
+ **/
+static int
+dirvote_callback(time_t now, const or_options_t *options)
+{
+ if (!authdir_mode_v3(options)) {
+ tor_assert_nonfatal_unreached();
+ return 3600;
+ }
+
+ time_t next = dirvote_act(options, now);
+ if (BUG(next == TIME_MAX)) {
+ /* This shouldn't be returned unless we called dirvote_act() without
+ * being an authority. If it happens, maybe our configuration will
+ * fix itself in an hour or so? */
+ return 3600;
+ }
+ return safe_timer_diff(now, next);
+}
+
+/** Reschedule the directory-authority voting event. Run this whenever the
+ * schedule has changed. */
+void
+reschedule_dirvote(const or_options_t *options)
+{
+ if (periodic_events_initialized && authdir_mode_v3(options)) {
+ periodic_event_reschedule(dirvote_event);
+ }
+}
+
+/**
* Periodic callback: If our consensus is too old, recalculate whether
* we can actually use it.
*/
@@ -1869,6 +2049,34 @@ check_expired_networkstatus_callback(time_t now, const or_options_t *options)
}
/**
+ * Scheduled callback: Save the state file to disk if appropriate.
+ */
+static int
+save_state_callback(time_t now, const or_options_t *options)
+{
+ (void) options;
+ (void) or_state_save(now); // only saves if appropriate
+ const time_t next_write = get_or_state()->next_write;
+ if (next_write == TIME_MAX) {
+ return 86400;
+ }
+ return safe_timer_diff(now, next_write);
+}
+
+/** Reschedule the event for saving the state file.
+ *
+ * Run this when the state becomes dirty. */
+void
+reschedule_or_state_save(void)
+{
+ if (save_state_event == NULL) {
+ /* This can happen early on during startup. */
+ return;
+ }
+ periodic_event_reschedule(save_state_event);
+}
+
+/**
* Periodic callback: Write statistics to disk if appropriate.
*/
static int
@@ -1940,14 +2148,14 @@ reset_padding_counts_callback(time_t now, const or_options_t *options)
return REPHIST_CELL_PADDING_COUNTS_INTERVAL;
}
+static int should_init_bridge_stats = 1;
+
/**
* Periodic callback: Write bridge statistics to disk if appropriate.
*/
static int
record_bridge_stats_callback(time_t now, const or_options_t *options)
{
- static int should_init_bridge_stats = 1;
-
/* 1h. Check whether we should write bridge statistics to disk.
*/
if (should_record_bridge_info(options)) {
@@ -2062,8 +2270,8 @@ check_for_reachability_bw_callback(time_t now, const or_options_t *options)
if (server_mode(options) &&
(have_completed_a_circuit() || !any_predicted_circuits(now)) &&
!net_is_disabled()) {
- if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
- consider_testing_reachability(1, dirport_reachability_count==0);
+ if (get_uptime() < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
+ router_do_reachability_checks(1, dirport_reachability_count==0);
if (++dirport_reachability_count > 5)
dirport_reachability_count = 0;
return 1;
@@ -2141,6 +2349,56 @@ expire_old_ciruits_serverside_callback(time_t now, const or_options_t *options)
}
/**
+ * Callback: Send warnings if Tor doesn't find its ports reachable.
+ */
+static int
+reachability_warnings_callback(time_t now, const or_options_t *options)
+{
+ (void) now;
+
+ if (get_uptime() < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
+ return (int)(TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT - get_uptime());
+ }
+
+ if (server_mode(options) &&
+ !net_is_disabled() &&
+ have_completed_a_circuit()) {
+ /* every 20 minutes, check and complain if necessary */
+ const routerinfo_t *me = router_get_my_routerinfo();
+ if (me && !check_whether_orport_reachable(options)) {
+ char *address = tor_dup_ip(me->addr);
+ log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
+ "its ORPort is reachable. Relays do not publish descriptors "
+ "until their ORPort and DirPort are reachable. Please check "
+ "your firewalls, ports, address, /etc/hosts file, etc.",
+ address, me->or_port);
+ control_event_server_status(LOG_WARN,
+ "REACHABILITY_FAILED ORADDRESS=%s:%d",
+ address, me->or_port);
+ tor_free(address);
+ }
+
+ if (me && !check_whether_dirport_reachable(options)) {
+ char *address = tor_dup_ip(me->addr);
+ log_warn(LD_CONFIG,
+ "Your server (%s:%d) has not managed to confirm that its "
+ "DirPort is reachable. Relays do not publish descriptors "
+ "until their ORPort and DirPort are reachable. Please check "
+ "your firewalls, ports, address, /etc/hosts file, etc.",
+ address, me->dir_port);
+ control_event_server_status(LOG_WARN,
+ "REACHABILITY_FAILED DIRADDRESS=%s:%d",
+ address, me->dir_port);
+ tor_free(address);
+ }
+ }
+
+ return TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT;
+}
+
+static int dns_honesty_first_time = 1;
+
+/**
* Periodic event: if we're an exit, see if our DNS server is telling us
* obvious lies.
*/
@@ -2155,10 +2413,9 @@ check_dns_honesty_callback(time_t now, const or_options_t *options)
router_my_exit_policy_is_reject_star())
return PERIODIC_EVENT_NO_UPDATE;
- static int first_time = 1;
- if (first_time) {
+ if (dns_honesty_first_time) {
/* Don't launch right when we start */
- first_time = 0;
+ dns_honesty_first_time = 0;
return crypto_rand_int_range(60, 180);
}
@@ -2182,32 +2439,7 @@ write_bridge_ns_callback(time_t now, const or_options_t *options)
return PERIODIC_EVENT_NO_UPDATE;
}
-/**
- * Periodic callback: poke the tor-fw-helper app if we're using one.
- */
-static int
-check_fw_helper_app_callback(time_t now, const or_options_t *options)
-{
- if (net_is_disabled() ||
- ! server_mode(options) ||
- ! options->PortForwarding ||
- options->NoExec) {
- return PERIODIC_EVENT_NO_UPDATE;
- }
- /* 11. check the port forwarding app */
-
-#define PORT_FORWARDING_CHECK_INTERVAL 5
- smartlist_t *ports_to_forward = get_list_of_ports_to_forward();
- if (ports_to_forward) {
- tor_check_port_forwarding(options->PortForwardingHelper,
- ports_to_forward,
- now);
-
- SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
- smartlist_free(ports_to_forward);
- }
- return PORT_FORWARDING_CHECK_INTERVAL;
-}
+static int heartbeat_callback_first_time = 1;
/**
* Periodic callback: write the heartbeat message in the logs.
@@ -2218,16 +2450,14 @@ check_fw_helper_app_callback(time_t now, const or_options_t *options)
static int
heartbeat_callback(time_t now, const or_options_t *options)
{
- static int first = 1;
-
/* Check if heartbeat is disabled */
if (!options->HeartbeatPeriod) {
return PERIODIC_EVENT_NO_UPDATE;
}
/* Skip the first one. */
- if (first) {
- first = 0;
+ if (heartbeat_callback_first_time) {
+ heartbeat_callback_first_time = 0;
return options->HeartbeatPeriod;
}
@@ -2246,7 +2476,7 @@ static int
clean_consdiffmgr_callback(time_t now, const or_options_t *options)
{
(void)now;
- if (server_mode(options)) {
+ if (dir_server_mode(options)) {
consdiffmgr_cleanup();
}
return CDM_CLEAN_CALLBACK_INTERVAL;
@@ -2277,8 +2507,100 @@ hs_service_callback(time_t now, const or_options_t *options)
/** Timer: used to invoke second_elapsed_callback() once per second. */
static periodic_timer_t *second_timer = NULL;
-/** Number of libevent errors in the last second: we die if we get too many. */
-static int n_libevent_errors = 0;
+
+/**
+ * Enable or disable the per-second timer as appropriate, creating it if
+ * necessary.
+ */
+void
+reschedule_per_second_timer(void)
+{
+ struct timeval one_second;
+ one_second.tv_sec = 1;
+ one_second.tv_usec = 0;
+
+ if (! second_timer) {
+ second_timer = periodic_timer_new(tor_libevent_get_base(),
+ &one_second,
+ second_elapsed_callback,
+ NULL);
+ tor_assert(second_timer);
+ }
+
+ const bool run_per_second_events =
+ control_any_per_second_event_enabled() || ! net_is_completely_disabled();
+
+ if (run_per_second_events) {
+ periodic_timer_launch(second_timer, &one_second);
+ } else {
+ periodic_timer_disable(second_timer);
+ }
+}
+
+/** Last time that update_current_time was called. */
+static time_t current_second = 0;
+/** Last time that update_current_time updated current_second. */
+static monotime_coarse_t current_second_last_changed;
+
+/**
+ * Set the current time to "now", which should be the value returned by
+ * time(). Check for clock jumps and track the total number of seconds we
+ * have been running.
+ */
+void
+update_current_time(time_t now)
+{
+ if (PREDICT_LIKELY(now == current_second)) {
+ /* We call this function a lot. Most frequently, the current second
+ * will not have changed, so we just return. */
+ return;
+ }
+
+ const time_t seconds_elapsed = current_second ? (now - current_second) : 0;
+
+ /* Check the wall clock against the monotonic clock, so we can
+ * better tell idleness from clock jumps and/or other shenanigans. */
+ monotime_coarse_t last_updated;
+ memcpy(&last_updated, &current_second_last_changed, sizeof(last_updated));
+ monotime_coarse_get(&current_second_last_changed);
+
+ /** How much clock jumping do we tolerate? */
+#define NUM_JUMPED_SECONDS_BEFORE_WARN 100
+
+ /** How much idleness do we tolerate? */
+#define NUM_IDLE_SECONDS_BEFORE_WARN 3600
+
+ if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN) {
+ // moving back in time is always a bad sign.
+ circuit_note_clock_jumped(seconds_elapsed, false);
+ } else if (seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
+ /* Compare the monotonic clock to the result of time(). */
+ const int32_t monotime_msec_passed =
+ monotime_coarse_diff_msec32(&last_updated,
+ &current_second_last_changed);
+ const int monotime_sec_passed = monotime_msec_passed / 1000;
+ const int discrepancy = monotime_sec_passed - (int)seconds_elapsed;
+ /* If the monotonic clock deviates from time(NULL), we have a couple of
+ * possibilities. On some systems, this means we have been suspended or
+ * sleeping. Everywhere, it can mean that the wall-clock time has
+ * been changed -- for example, with settimeofday().
+ *
+ * On the other hand, if the monotonic time matches with the wall-clock
+ * time, we've probably just been idle for a while, with no events firing.
+ * we tolerate much more of that.
+ */
+ const bool clock_jumped = abs(discrepancy) > 2;
+
+ if (clock_jumped || seconds_elapsed >= NUM_IDLE_SECONDS_BEFORE_WARN) {
+ circuit_note_clock_jumped(seconds_elapsed, ! clock_jumped);
+ }
+ } else if (seconds_elapsed > 0) {
+ stats_n_seconds_working += seconds_elapsed;
+ }
+
+ update_approx_time(now);
+ current_second = now;
+}
/** Libevent callback: invoked once every second. */
static void
@@ -2287,83 +2609,22 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
/* XXXX This could be sensibly refactored into multiple callbacks, and we
* could use Libevent's timers for this rather than checking the current
* time against a bunch of timeouts every second. */
- static time_t current_second = 0;
time_t now;
- size_t bytes_written;
- size_t bytes_read;
- int seconds_elapsed;
- const or_options_t *options = get_options();
(void)timer;
(void)arg;
- n_libevent_errors = 0;
-
- /* log_notice(LD_GENERAL, "Tick."); */
now = time(NULL);
- update_approx_time(now);
-
- /* the second has rolled over. check more stuff. */
- seconds_elapsed = current_second ? (int)(now - current_second) : 0;
- bytes_read = (size_t)(stats_n_bytes_read - stats_prev_n_read);
- bytes_written = (size_t)(stats_n_bytes_written - stats_prev_n_written);
- stats_prev_n_read = stats_n_bytes_read;
- stats_prev_n_written = stats_n_bytes_written;
-
- control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
- control_event_stream_bandwidth_used();
- control_event_conn_bandwidth_used();
- control_event_circ_bandwidth_used();
- control_event_circuit_cell_stats();
-
- if (server_mode(options) &&
- !net_is_disabled() &&
- seconds_elapsed > 0 &&
- have_completed_a_circuit() &&
- stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT !=
- (stats_n_seconds_working+seconds_elapsed) /
- TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
- /* every 20 minutes, check and complain if necessary */
- const routerinfo_t *me = router_get_my_routerinfo();
- if (me && !check_whether_orport_reachable(options)) {
- char *address = tor_dup_ip(me->addr);
- log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
- "its ORPort is reachable. Relays do not publish descriptors "
- "until their ORPort and DirPort are reachable. Please check "
- "your firewalls, ports, address, /etc/hosts file, etc.",
- address, me->or_port);
- control_event_server_status(LOG_WARN,
- "REACHABILITY_FAILED ORADDRESS=%s:%d",
- address, me->or_port);
- tor_free(address);
- }
- if (me && !check_whether_dirport_reachable(options)) {
- char *address = tor_dup_ip(me->addr);
- log_warn(LD_CONFIG,
- "Your server (%s:%d) has not managed to confirm that its "
- "DirPort is reachable. Relays do not publish descriptors "
- "until their ORPort and DirPort are reachable. Please check "
- "your firewalls, ports, address, /etc/hosts file, etc.",
- address, me->dir_port);
- control_event_server_status(LOG_WARN,
- "REACHABILITY_FAILED DIRADDRESS=%s:%d",
- address, me->dir_port);
- tor_free(address);
- }
- }
+ /* We don't need to do this once-per-second any more: time-updating is
+ * only in this callback _because it is a callback_. It should be fine
+ * to disable this callback, and the time will still get updated.
+ */
+ update_current_time(now);
-/** If more than this many seconds have elapsed, probably the clock
- * jumped: doesn't count. */
-#define NUM_JUMPED_SECONDS_BEFORE_WARN 100
- if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN ||
- seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
- circuit_note_clock_jumped(seconds_elapsed);
- } else if (seconds_elapsed > 0)
- stats_n_seconds_working += seconds_elapsed;
+ /* Maybe some controller events are ready to fire */
+ control_per_second_events();
run_scheduled_events(now);
-
- current_second = now; /* remember which second it is, for next time */
}
#ifdef HAVE_SYSTEMD_209
@@ -2379,70 +2640,6 @@ systemd_watchdog_callback(periodic_timer_t *timer, void *arg)
}
#endif /* defined(HAVE_SYSTEMD_209) */
-/** Timer: used to invoke refill_callback(). */
-static periodic_timer_t *refill_timer = NULL;
-
-/** Libevent callback: invoked periodically to refill token buckets
- * and count r/w bytes. */
-static void
-refill_callback(periodic_timer_t *timer, void *arg)
-{
- static struct timeval current_millisecond;
- struct timeval now;
-
- size_t bytes_written;
- size_t bytes_read;
- int milliseconds_elapsed = 0;
- int seconds_rolled_over = 0;
-
- const or_options_t *options = get_options();
-
- (void)timer;
- (void)arg;
-
- tor_gettimeofday(&now);
-
- /* If this is our first time, no time has passed. */
- if (current_millisecond.tv_sec) {
- long mdiff = tv_mdiff(&current_millisecond, &now);
- if (mdiff > INT_MAX)
- mdiff = INT_MAX;
- milliseconds_elapsed = (int)mdiff;
- seconds_rolled_over = (int)(now.tv_sec - current_millisecond.tv_sec);
- }
-
- bytes_written = stats_prev_global_write_bucket - global_write_bucket;
- bytes_read = stats_prev_global_read_bucket - global_read_bucket;
-
- stats_n_bytes_read += bytes_read;
- stats_n_bytes_written += bytes_written;
- if (accounting_is_enabled(options) && milliseconds_elapsed >= 0)
- accounting_add_bytes(bytes_read, bytes_written, seconds_rolled_over);
-
- if (milliseconds_elapsed > 0)
- connection_bucket_refill(milliseconds_elapsed, (time_t)now.tv_sec);
-
- stats_prev_global_read_bucket = global_read_bucket;
- stats_prev_global_write_bucket = global_write_bucket;
-
- current_millisecond = now; /* remember what time it is, for next time */
-}
-
-#ifndef _WIN32
-/** Called when a possibly ignorable libevent error occurs; ensures that we
- * don't get into an infinite loop by ignoring too many errors from
- * libevent. */
-static int
-got_libevent_error(void)
-{
- if (++n_libevent_errors > 8) {
- log_err(LD_NET, "Too many libevent errors in one second; dying");
- return -1;
- }
- return 0;
-}
-#endif /* !defined(_WIN32) */
-
#define UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST (6*60*60)
/** Called when our IP address seems to have changed. <b>at_interface</b>
@@ -2464,9 +2661,9 @@ ip_address_changed(int at_interface)
}
} else {
if (server) {
- if (stats_n_seconds_working > UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST)
+ if (get_uptime() > UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST)
reset_bandwidth_test();
- stats_n_seconds_working = 0;
+ reset_uptime();
router_reset_reachability();
}
}
@@ -2587,6 +2784,20 @@ do_hup(void)
return 0;
}
+/** Initialize some mainloop_event_t objects that we require. */
+STATIC void
+initialize_mainloop_events(void)
+{
+ if (!schedule_active_linked_connections_event) {
+ schedule_active_linked_connections_event =
+ mainloop_event_postloop_new(schedule_active_linked_connections_cb, NULL);
+ }
+ if (!postloop_cleanup_ev) {
+ postloop_cleanup_ev =
+ mainloop_event_postloop_new(postloop_cleanup_cb, NULL);
+ }
+}
+
/** Tor main loop. */
int
do_main_loop(void)
@@ -2600,6 +2811,8 @@ do_main_loop(void)
initialize_periodic_events();
}
+ initialize_mainloop_events();
+
/* initialize dns resolve map, spawn workers if needed */
if (dns_init() < 0) {
if (get_options()->ServerDNSAllowBrokenConfig)
@@ -2626,8 +2839,6 @@ do_main_loop(void)
/* Set up our buckets */
connection_bucket_init();
- stats_prev_global_read_bucket = global_read_bucket;
- stats_prev_global_write_bucket = global_write_bucket;
/* initialize the bootstrap status events to know we're starting up */
control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
@@ -2691,17 +2902,7 @@ do_main_loop(void)
}
/* set up once-a-second callback. */
- if (! second_timer) {
- struct timeval one_second;
- one_second.tv_sec = 1;
- one_second.tv_usec = 0;
-
- second_timer = periodic_timer_new(tor_libevent_get_base(),
- &one_second,
- second_elapsed_callback,
- NULL);
- tor_assert(second_timer);
- }
+ reschedule_per_second_timer();
#ifdef HAVE_SYSTEMD_209
uint64_t watchdog_delay;
@@ -2725,20 +2926,6 @@ do_main_loop(void)
}
#endif /* defined(HAVE_SYSTEMD_209) */
- if (!refill_timer) {
- struct timeval refill_interval;
- int msecs = get_options()->TokenBucketRefillInterval;
-
- refill_interval.tv_sec = msecs/1000;
- refill_interval.tv_usec = (msecs%1000)*1000;
-
- refill_timer = periodic_timer_new(tor_libevent_get_base(),
- &refill_interval,
- refill_callback,
- NULL);
- tor_assert(refill_timer);
- }
-
#ifdef HAVE_SYSTEMD
{
const int r = sd_notify(0, "READY=1");
@@ -2784,6 +2971,11 @@ do_main_loop(void)
return run_main_loop_until_done();
}
+#ifndef _WIN32
+/** Rate-limiter for EINVAL-type libevent warnings. */
+static ratelim_t libevent_error_ratelim = RATELIM_INIT(10);
+#endif
+
/**
* Run the main loop a single time. Return 0 for "exit"; -1 for "exit with
* error", and 1 for "run this again."
@@ -2804,17 +2996,12 @@ run_main_loop_once(void)
errno = 0;
#endif
- /* All active linked conns should get their read events activated,
- * so that libevent knows to run their callbacks. */
- SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn,
- event_active(conn->read_event, EV_READ, 1));
-
if (get_options()->MainloopStats) {
/* We always enforce that EVLOOP_ONCE is passed to event_base_loop() if we
* are collecting main loop statistics. */
called_loop_once = 1;
} else {
- called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0;
+ called_loop_once = 0;
}
/* Make sure we know (about) what time it is. */
@@ -2824,8 +3011,8 @@ run_main_loop_once(void)
* an event, or the second ends, or until we have some active linked
* connections to trigger events for. Libevent will wait till one
* of these happens, then run all the appropriate callbacks. */
- loop_result = event_base_loop(tor_libevent_get_base(),
- called_loop_once ? EVLOOP_ONCE : 0);
+ loop_result = tor_libevent_run_event_loop(tor_libevent_get_base(),
+ called_loop_once);
if (get_options()->MainloopStats) {
/* Update our main loop counters. */
@@ -2854,9 +3041,12 @@ run_main_loop_once(void)
return -1;
#ifndef _WIN32
} else if (e == EINVAL) {
- log_warn(LD_NET, "EINVAL from libevent: should you upgrade libevent?");
- if (got_libevent_error())
+ log_fn_ratelim(&libevent_error_ratelim, LOG_WARN, LD_NET,
+ "EINVAL from libevent: should you upgrade libevent?");
+ if (libevent_error_ratelim.n_calls_since_last_time > 8) {
+ log_err(LD_NET, "Too many libevent errors, too fast: dying");
return -1;
+ }
#endif /* !defined(_WIN32) */
} else {
tor_assert_nonfatal_once(! ERRNO_IS_EINPROGRESS(e));
@@ -2870,19 +3060,6 @@ run_main_loop_once(void)
if (main_loop_should_exit)
return 0;
- /* And here is where we put callbacks that happen "every time the event loop
- * runs." They must be very fast, or else the whole Tor process will get
- * slowed down.
- *
- * Note that this gets called once per libevent loop, which will make it
- * happen once per group of events that fire, or once per second. */
-
- /* If there are any pending client connections, try attaching them to
- * circuits (if we can.) This will be pretty fast if nothing new is
- * pending.
- */
- connection_ap_attach_pending(0);
-
return 1;
}
@@ -2915,6 +3092,7 @@ signal_callback(evutil_socket_t fd, short events, void *arg)
(void)fd;
(void)events;
+ update_current_time(time(NULL));
process_signal(sig);
}
@@ -2977,10 +3155,20 @@ process_signal(int sig)
case SIGNEWNYM: {
time_t now = time(NULL);
if (time_of_last_signewnym + MAX_SIGNEWNYM_RATE > now) {
- signewnym_is_pending = 1;
+ const time_t delay_sec =
+ time_of_last_signewnym + MAX_SIGNEWNYM_RATE - now;
+ if (! signewnym_is_pending) {
+ signewnym_is_pending = 1;
+ if (!handle_deferred_signewnym_ev) {
+ handle_deferred_signewnym_ev =
+ mainloop_event_postloop_new(handle_deferred_signewnym_cb, NULL);
+ }
+ const struct timeval delay_tv = { delay_sec, 0 };
+ mainloop_event_schedule(handle_deferred_signewnym_ev, &delay_tv);
+ }
log_notice(LD_CONTROL,
- "Rate limiting NEWNYM request: delaying by %d second(s)",
- (int)(MAX_SIGNEWNYM_RATE+time_of_last_signewnym-now));
+ "Rate limiting NEWNYM request: delaying by %d second(s)",
+ (int)(delay_sec));
} else {
signewnym_impl(now);
}
@@ -3004,6 +3192,13 @@ get_uptime,(void))
return stats_n_seconds_working;
}
+/** Reset Tor's uptime. */
+MOCK_IMPL(void,
+reset_uptime,(void))
+{
+ stats_n_seconds_working = 0;
+}
+
/**
* Write current memory usage information to the log.
*/
@@ -3047,13 +3242,13 @@ dumpstats(int severity)
i,
(int)connection_get_inbuf_len(conn),
(int)buf_allocation(conn->inbuf),
- (int)(now - conn->timestamp_lastread));
+ (int)(now - conn->timestamp_last_read_allowed));
tor_log(severity,LD_GENERAL,
"Conn %d: %d bytes waiting on outbuf "
"(len %d, last written %d secs ago)",i,
(int)connection_get_outbuf_len(conn),
(int)buf_allocation(conn->outbuf),
- (int)(now - conn->timestamp_lastwritten));
+ (int)(now - conn->timestamp_last_write_allowed));
if (conn->type == CONN_TYPE_OR) {
or_connection_t *or_conn = TO_OR_CONN(conn);
if (or_conn->tls) {
@@ -3323,14 +3518,12 @@ tor_init(int argc, char *argv[])
if (strstr(version, "alpha") || strstr(version, "beta"))
log_notice(LD_GENERAL, "This version is not a stable Tor release. "
"Expect more bugs than usual.");
+
+ tor_compress_log_init_warnings();
}
#ifdef HAVE_RUST
- char *rust_str = rust_welcome_string();
- if (rust_str != NULL && strlen(rust_str) > 0) {
- log_notice(LD_GENERAL, "%s", rust_str);
- }
- tor_free(rust_str);
+ rust_log_welcome_string();
#endif /* defined(HAVE_RUST) */
if (network_init()<0) {
@@ -3488,6 +3681,9 @@ tor_free_all(int postfork)
consdiffmgr_free_all();
hs_free_all();
dos_free_all();
+ circuitmux_ewma_free_all();
+ accounting_free_all();
+
if (!postfork) {
config_free_all();
or_state_free_all();
@@ -3508,9 +3704,35 @@ tor_free_all(int postfork)
smartlist_free(active_linked_connection_lst);
periodic_timer_free(second_timer);
teardown_periodic_events();
- periodic_timer_free(refill_timer);
tor_event_free(shutdown_did_not_work_event);
tor_event_free(initialize_periodic_events_event);
+ mainloop_event_free(directory_all_unreachable_cb_event);
+ mainloop_event_free(schedule_active_linked_connections_event);
+ mainloop_event_free(postloop_cleanup_ev);
+ mainloop_event_free(handle_deferred_signewnym_ev);
+
+#ifdef HAVE_SYSTEMD_209
+ periodic_timer_free(systemd_watchdog_timer);
+#endif
+
+ memset(&global_bucket, 0, sizeof(global_bucket));
+ memset(&global_relayed_bucket, 0, sizeof(global_relayed_bucket));
+ stats_n_bytes_read = stats_n_bytes_written = 0;
+ time_of_process_start = 0;
+ time_of_last_signewnym = 0;
+ signewnym_is_pending = 0;
+ newnym_epoch = 0;
+ called_loop_once = 0;
+ main_loop_should_exit = 0;
+ main_loop_exit_value = 0;
+ can_complete_circuits = 0;
+ quiet_level = 0;
+ should_init_bridge_stats = 1;
+ dns_honesty_first_time = 1;
+ heartbeat_callback_first_time = 1;
+ current_second = 0;
+ memset(&current_second_last_changed, 0,
+ sizeof(current_second_last_changed));
if (!postfork) {
release_lockfile();
diff --git a/src/or/main.h b/src/or/main.h
index c49d216f4e..9dbbc6e5ee 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -28,6 +28,7 @@ int connection_is_on_closeable_list(connection_t *conn);
MOCK_DECL(smartlist_t *, get_connection_array, (void));
MOCK_DECL(uint64_t,get_bytes_read,(void));
MOCK_DECL(uint64_t,get_bytes_written,(void));
+void stats_increment_bytes_read_and_written(uint64_t r, uint64_t w);
/** Bitmask for events that we can turn on and off with
* connection_watch_events. */
@@ -45,7 +46,6 @@ int connection_is_writing(connection_t *conn);
MOCK_DECL(void,connection_stop_writing,(connection_t *conn));
MOCK_DECL(void,connection_start_writing,(connection_t *conn));
-void tell_event_loop_to_run_external_code(void);
void tor_shutdown_event_loop_and_exit(int exitcode);
int tor_event_loop_shutdown_is_pending(void);
@@ -61,8 +61,15 @@ void dns_servers_relaunch_checks(void);
void reset_all_main_loop_timers(void);
void reschedule_descriptor_update_check(void);
void reschedule_directory_downloads(void);
+void reschedule_or_state_save(void);
+void reschedule_dirvote(const or_options_t *options);
+void mainloop_schedule_postloop_cleanup(void);
+void rescan_periodic_events(const or_options_t *options);
+
+void update_current_time(time_t now);
MOCK_DECL(long,get_uptime,(void));
+MOCK_DECL(void,reset_uptime,(void));
unsigned get_signewnym_epoch(void);
@@ -86,21 +93,27 @@ uint64_t get_main_loop_success_count(void);
uint64_t get_main_loop_error_count(void);
uint64_t get_main_loop_idle_count(void);
+void periodic_events_on_new_options(const or_options_t *options);
+void reschedule_per_second_timer(void);
+
extern time_t time_of_process_start;
-extern long stats_n_seconds_working;
extern int quiet_level;
-extern int global_read_bucket;
-extern int global_write_bucket;
-extern int global_relayed_read_bucket;
-extern int global_relayed_write_bucket;
+extern token_bucket_rw_t global_bucket;
+extern token_bucket_rw_t global_relayed_bucket;
#ifdef MAIN_PRIVATE
STATIC void init_connection_lists(void);
+STATIC void initialize_mainloop_events(void);
STATIC void close_closeable_connections(void);
STATIC void initialize_periodic_events(void);
STATIC void teardown_periodic_events(void);
+STATIC int get_my_roles(const or_options_t *options);
#ifdef TOR_UNIT_TESTS
extern smartlist_t *connection_array;
+
+/* We need the periodic_event_item_t definition. */
+#include "periodic.h"
+extern periodic_event_item_t periodic_events[];
#endif
#endif /* defined(MAIN_PRIVATE) */
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 040405555c..998eaf74e6 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -48,9 +48,10 @@
#include "connection_or.h"
#include "consdiffmgr.h"
#include "control.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "directory.h"
#include "dirserv.h"
-#include "dirvote.h"
#include "dos.h"
#include "entrynodes.h"
#include "hibernate.h"
@@ -64,10 +65,14 @@
#include "routerlist.h"
#include "routerparse.h"
#include "scheduler.h"
-#include "shared_random.h"
#include "transports.h"
#include "torcert.h"
#include "channelpadding.h"
+#include "voting_schedule.h"
+
+#include "dirauth/dirvote.h"
+#include "dirauth/mode.h"
+#include "dirauth/shared_random.h"
/** Most recently received and validated v3 "ns"-flavored consensus network
* status. */
@@ -237,7 +242,7 @@ router_reload_consensus_networkstatus(void)
s = networkstatus_read_cached_consensus_impl(flav, flavor, 1);
if (s) {
if (networkstatus_set_current_consensus(s, flavor,
- flags|NSSET_WAS_WAITING_FOR_CERTS,
+ flags | NSSET_WAS_WAITING_FOR_CERTS,
NULL)) {
log_info(LD_FS, "Couldn't load unverified consensus %s networkstatus "
"from cache", flavor);
@@ -365,9 +370,7 @@ networkstatus_vote_free_(networkstatus_t *ns)
digestmap_free(ns->desc_digest_map, NULL);
if (ns->sr_info.commits) {
- SMARTLIST_FOREACH(ns->sr_info.commits, sr_commit_t *, c,
- sr_commit_free(c));
- smartlist_free(ns->sr_info.commits);
+ dirvote_clear_commits(ns);
}
tor_free(ns->sr_info.previous_srv);
tor_free(ns->sr_info.current_srv);
@@ -391,6 +394,20 @@ networkstatus_get_voter_by_id(networkstatus_t *vote,
return NULL;
}
+/** Return the signature made by <b>voter</b> using the algorithm
+ * <b>alg</b>, or NULL if none is found. */
+document_signature_t *
+networkstatus_get_voter_sig_by_alg(const networkstatus_voter_info_t *voter,
+ digest_algorithm_t alg)
+{
+ if (!voter->sigs)
+ return NULL;
+ SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
+ if (sig->alg == alg)
+ return sig);
+ return NULL;
+}
+
/** Check whether the signature <b>sig</b> is correctly signed with the
* signing key in <b>cert</b>. Return -1 if <b>cert</b> doesn't match the
* signing key; otherwise set the good_signature or bad_signature flag on
@@ -951,9 +968,12 @@ update_consensus_networkstatus_downloads(time_t now)
continue;
}
- /* Check if we're waiting for certificates to download */
- if (check_consensus_waiting_for_certs(i, now, &consensus_dl_status[i]))
+ /** Check if we're waiting for certificates to download. If we are,
+ * launch download for missing directory authority certificates. */
+ if (check_consensus_waiting_for_certs(i, now, &consensus_dl_status[i])) {
+ update_certificate_downloads(now);
continue;
+ }
/* Try the requested attempt */
log_info(LD_DIR, "Launching %s standard networkstatus consensus "
@@ -1230,16 +1250,20 @@ should_delay_dir_fetches(const or_options_t *options, const char **msg_out)
return 0;
}
-/** Launch requests for networkstatus documents and authority certificates as
- * appropriate. */
+/** Launch requests for networkstatus documents as appropriate. This is called
+ * when we retry all the connections on a SIGHUP and periodically by a Periodic
+ * event which checks whether we want to download any networkstatus documents.
+ */
void
update_networkstatus_downloads(time_t now)
{
const or_options_t *options = get_options();
if (should_delay_dir_fetches(options, NULL))
return;
+ /** Launch a consensus download request, we will wait for the consensus to
+ * download and when it completes we will launch a certificate download
+ * request. */
update_consensus_networkstatus_downloads(now);
- update_certificate_downloads(now);
}
/** Launch requests as appropriate for missing directory authority
@@ -1521,7 +1545,7 @@ networkstatus_consensus_has_ipv6(const or_options_t* options)
return
cons->consensus_method >= MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS;
} else {
- return cons->consensus_method >= MIN_METHOD_FOR_A_LINES;
+ return 1;
}
}
@@ -1667,23 +1691,6 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
#endif /* defined(TOR_UNIT_TESTS) */
/**
- * Return true if any option is set in <b>options</b> to make us behave
- * as a client.
- *
- * XXXX If we need this elsewhere at any point, we should make it nonstatic
- * XXXX and move it into another file.
- */
-static int
-any_client_port_set(const or_options_t *options)
-{
- return (options->SocksPort_set ||
- options->TransPort_set ||
- options->NATDPort_set ||
- options->DNSPort_set ||
- options->HTTPTunnelPort_set);
-}
-
-/**
* Helper for handle_missing_protocol_warning: handles either the
* client case (if <b>is_client</b> is set) or the server case otherwise.
*/
@@ -1718,7 +1725,7 @@ handle_missing_protocol_warning(const networkstatus_t *c,
const or_options_t *options)
{
const int is_server = server_mode(options);
- const int is_client = any_client_port_set(options) || !is_server;
+ const int is_client = options_any_client_port_set(options) || !is_server;
if (is_server)
handle_missing_protocol_warning_impl(c, 0);
@@ -1726,6 +1733,57 @@ handle_missing_protocol_warning(const networkstatus_t *c,
handle_missing_protocol_warning_impl(c, 1);
}
+/**
+ * Check whether we received a consensus that appears to be coming
+ * from the future. Because we implicitly trust the directory
+ * authorities' idea of the current time, we produce a warning if we
+ * get an early consensus.
+ *
+ * If we got a consensus that is time stamped far in the past, that
+ * could simply have come from a stale cache. Possible ways to get a
+ * consensus from the future can include:
+ *
+ * - enough directory authorities have wrong clocks
+ * - directory authorities collude to produce misleading time stamps
+ * - our own clock is wrong (this is by far the most likely)
+ *
+ * We neglect highly improbable scenarios that involve actual time
+ * travel.
+ */
+STATIC void
+warn_early_consensus(const networkstatus_t *c, const char *flavor,
+ time_t now)
+{
+ char tbuf[ISO_TIME_LEN+1];
+ char dbuf[64];
+ long delta = now - c->valid_after;
+ char *flavormsg = NULL;
+
+/** If a consensus appears more than this many seconds before it could
+ * possibly be a sufficiently-signed consensus, declare that our clock
+ * is skewed. */
+#define EARLY_CONSENSUS_NOTICE_SKEW 60
+
+ /* We assume that if a majority of dirauths have accurate clocks,
+ * the earliest that a dirauth with a skewed clock could possibly
+ * publish a sufficiently-signed consensus is (valid_after -
+ * dist_seconds). Before that time, the skewed dirauth would be
+ * unable to obtain enough authority signatures for the consensus to
+ * be valid. */
+ if (now >= c->valid_after - c->dist_seconds - EARLY_CONSENSUS_NOTICE_SKEW)
+ return;
+
+ 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);
+ tor_asprintf(&flavormsg, "%s flavor consensus", flavor);
+ clock_skew_warning(NULL, delta, 1, LD_GENERAL, flavormsg, "CONSENSUS");
+ tor_free(flavormsg);
+}
+
/** Try to replace the current cached v3 networkstatus with the one in
* <b>consensus</b>. If we don't have enough certificates to validate it,
* store it in consensus_waiting_for_certs and launch a certificate fetch.
@@ -1768,7 +1826,6 @@ networkstatus_set_current_consensus(const char *consensus,
consensus_waiting_for_certs_t *waiting = NULL;
time_t current_valid_after = 0;
int free_consensus = 1; /* Free 'c' at the end of the function */
- int old_ewma_enabled;
int checked_protocols_already = 0;
if (flav < 0) {
@@ -1834,17 +1891,9 @@ networkstatus_set_current_consensus(const char *consensus,
current_valid_after = current_md_consensus->valid_after;
}
} else {
- cached_dir_t *cur;
- char buf[128];
- tor_snprintf(buf, sizeof(buf), "cached-%s-consensus", flavor);
- consensus_fname = get_cachedir_fname(buf);
- tor_snprintf(buf, sizeof(buf), "unverified-%s-consensus", flavor);
- unverified_fname = get_cachedir_fname(buf);
- cur = dirserv_get_consensus(flavor);
- if (cur) {
- current_digests = &cur->digests;
- current_valid_after = cur->published;
- }
+ tor_assert_nonfatal_unreached();
+ result = -2;
+ goto done;
}
if (current_digests &&
@@ -1917,6 +1966,15 @@ networkstatus_set_current_consensus(const char *consensus,
}
}
+ /* Signatures from the consensus are verified */
+ if (from_cache && was_waiting_for_certs) {
+ /* We check if the consensus is loaded from disk cache and that it
+ * it is an unverified consensus. If it is unverified, rename it to
+ * cached-*-consensus since it has been verified. */
+ log_info(LD_DIR, "Unverified consensus signatures verified.");
+ tor_rename(unverified_fname, consensus_fname);
+ }
+
if (!from_cache && flav == usable_consensus_flavor())
control_event_client_status(LOG_NOTICE, "CONSENSUS_ARRIVED");
@@ -1985,24 +2043,16 @@ networkstatus_set_current_consensus(const char *consensus,
* the first thing we need to do is recalculate the voting schedule static
* object so we can use the timings in there needed by some subsystems
* such as hidden service and shared random. */
- dirvote_recalculate_timing(options, now);
+ voting_schedule_recalculate_timing(options, now);
+ reschedule_dirvote(options);
nodelist_set_consensus(c);
/* XXXXNM Microdescs: needs a non-ns variant. ???? NM*/
update_consensus_networkstatus_fetch_time(now);
- /* Update ewma and adjust policy if needed; first cache the old value */
- old_ewma_enabled = cell_ewma_enabled();
/* Change the cell EWMA settings */
- cell_ewma_set_scale_factor(options, c);
- /* If we just enabled ewma, set the cmux policy on all active channels */
- if (cell_ewma_enabled() && !old_ewma_enabled) {
- channel_set_cmux_policy_everywhere(&ewma_policy);
- } else if (!cell_ewma_enabled() && old_ewma_enabled) {
- /* Turn it off everywhere */
- channel_set_cmux_policy_everywhere(NULL);
- }
+ cmux_ewma_set_options(options, c);
/* XXXX this call might be unnecessary here: can changing the
* current consensus really alter our view of any OR's rate limits? */
@@ -2036,25 +2086,7 @@ networkstatus_set_current_consensus(const char *consensus,
write_str_to_file(consensus_fname, consensus, 0);
}
-/** If a consensus appears more than this many seconds before its declared
- * valid-after time, declare that our clock is skewed. */
-#define EARLY_CONSENSUS_NOTICE_SKEW 60
-
- if (now < c->valid_after - EARLY_CONSENSUS_NOTICE_SKEW) {
- 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);
- tor_asprintf(&flavormsg, "%s flavor consensus", flavor);
- clock_skew_warning(NULL, delta, 1, LD_GENERAL, flavormsg, "CONSENSUS");
- tor_free(flavormsg);
- }
+ warn_early_consensus(c, flavor, now);
/* We got a new consesus. Reset our md fetch fail cache */
microdesc_reset_outdated_dirservers_list();
@@ -2634,6 +2666,25 @@ networkstatus_check_required_protocols(const networkstatus_t *ns,
return 0;
}
+/** Release all storage held in <b>s</b>. */
+void
+ns_detached_signatures_free_(ns_detached_signatures_t *s)
+{
+ if (!s)
+ return;
+ if (s->signatures) {
+ STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) {
+ SMARTLIST_FOREACH(sigs, document_signature_t *, sig,
+ document_signature_free(sig));
+ smartlist_free(sigs);
+ } STRMAP_FOREACH_END;
+ strmap_free(s->signatures, NULL);
+ strmap_free(s->digests, tor_free_);
+ }
+
+ tor_free(s);
+}
+
/** Free all storage held locally in this module. */
void
networkstatus_free_all(void)
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index 1851a55e82..94f85c3c29 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -24,9 +24,16 @@ void routerstatus_free_(routerstatus_t *rs);
void networkstatus_vote_free_(networkstatus_t *ns);
#define networkstatus_vote_free(ns) \
FREE_AND_NULL(networkstatus_t, networkstatus_vote_free_, (ns))
+void ns_detached_signatures_free_(ns_detached_signatures_t *s);
+#define ns_detached_signatures_free(s) \
+ FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s))
networkstatus_voter_info_t *networkstatus_get_voter_by_id(
networkstatus_t *vote,
const char *identity);
+document_signature_t *networkstatus_get_voter_sig_by_alg(
+ const networkstatus_voter_info_t *voter,
+ digest_algorithm_t alg);
+
int networkstatus_check_consensus_signature(networkstatus_t *consensus,
int warn);
int networkstatus_check_document_signature(const networkstatus_t *consensus,
@@ -144,6 +151,8 @@ void vote_routerstatus_free_(vote_routerstatus_t *rs);
#ifdef TOR_UNIT_TESTS
STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
const char *flavor);
+STATIC void warn_early_consensus(const networkstatus_t *c, const char *flavor,
+ time_t now);
extern networkstatus_t *current_ns_consensus;
extern networkstatus_t *current_md_consensus;
#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 212606d2f7..032e8d669f 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -43,6 +43,7 @@
#include "or.h"
#include "address.h"
#include "address_set.h"
+#include "backtrace.h"
#include "bridges.h"
#include "config.h"
#include "control.h"
@@ -63,9 +64,12 @@
#include "routerparse.h"
#include "routerset.h"
#include "torcert.h"
+#include "util_format.h"
#include <string.h>
+#include "dirauth/mode.h"
+
static void nodelist_drop_node(node_t *node, int remove_from_ht);
#define node_free(val) \
FREE_AND_NULL(node_t, node_free_, (val))
@@ -74,10 +78,17 @@ static void node_free_(node_t *node);
/** count_usable_descriptors counts descriptors with these flag(s)
*/
typedef enum {
- /* All descriptors regardless of flags */
- USABLE_DESCRIPTOR_ALL = 0,
- /* Only descriptors with the Exit flag */
- USABLE_DESCRIPTOR_EXIT_ONLY = 1
+ /* All descriptors regardless of flags or exit policies */
+ USABLE_DESCRIPTOR_ALL = 0U,
+ /* Only count descriptors with an exit policy that allows at least one port
+ */
+ USABLE_DESCRIPTOR_EXIT_POLICY = 1U << 0,
+ /* Only count descriptors for relays that have the exit flag in the
+ * consensus */
+ USABLE_DESCRIPTOR_EXIT_FLAG = 1U << 1,
+ /* Only count descriptors for relays that have the policy and the flag */
+ USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG = (USABLE_DESCRIPTOR_EXIT_POLICY |
+ USABLE_DESCRIPTOR_EXIT_FLAG)
} usable_descriptor_t;
static void count_usable_descriptors(int *num_present,
int *num_usable,
@@ -111,6 +122,11 @@ typedef struct nodelist_t {
/* Set of addresses that belong to nodes we believe in. */
address_set_t *node_addrs;
+
+ /* The valid-after time of the last live consensus that initialized the
+ * nodelist. We use this to detect outdated nodelists that need to be
+ * rebuilt using a newer consensus. */
+ time_t live_consensus_valid_after;
} nodelist_t;
static inline unsigned int
@@ -225,7 +241,6 @@ node_get_or_create(const char *identity_digest)
smartlist_add(the_nodelist->nodes, node);
node->nodelist_idx = smartlist_len(the_nodelist->nodes) - 1;
- node->hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t));
node->country = -1;
@@ -260,6 +275,20 @@ node_remove_from_ed25519_map(node_t *node)
return rv;
}
+/** Helper function to log details of duplicated ed2559_ids */
+static void
+node_log_dup_ed_id(const node_t *old, const node_t *node, const char *ed_id)
+{
+ char *s;
+ char *olddesc = tor_strdup(node_describe(old));
+
+ tor_asprintf(&s, "Reused ed25519_id %s: old %s new %s", ed_id,
+ olddesc, node_describe(node));
+ log_backtrace(LOG_NOTICE, LD_DIR, s);
+ tor_free(olddesc);
+ tor_free(s);
+}
+
/** 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.
*/
@@ -281,11 +310,24 @@ node_add_to_ed25519_map(node_t *node)
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)
+ if (old) {
+ char ed_id[BASE32_BUFSIZE(sizeof(key->pubkey))];
+
+ base32_encode(ed_id, sizeof(ed_id), (const char *)key->pubkey,
+ sizeof(key->pubkey));
+ if (BUG(old == node)) {
+ /* Actual bug: all callers of this function call
+ * node_remove_from_ed25519_map first. */
+ log_err(LD_BUG,
+ "Unexpectedly found deleted node with ed25519_id %s", ed_id);
+ } else {
+ /* Distinct nodes sharing a ed25519 id, possibly due to relay
+ * misconfiguration. The key pinning might not catch this,
+ * possibly due to downloading a missing descriptor during
+ * consensus voting. */
+ node_log_dup_ed_id(old, node, ed_id);
memset(&node->ed25519_id, 0, sizeof(node->ed25519_id));
+ }
return 0;
}
@@ -350,26 +392,26 @@ node_set_hsdir_index(node_t *node, const networkstatus_t *ns)
/* Build the fetch index. */
hs_build_hsdir_index(node_identity_pk, fetch_srv, fetch_tp,
- node->hsdir_index->fetch);
+ 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));
+ memcpy(node->hsdir_index.store_first, node->hsdir_index.fetch,
+ sizeof(node->hsdir_index.store_first));
} else {
hs_build_hsdir_index(node_identity_pk, store_first_srv, store_first_tp,
- node->hsdir_index->store_first);
+ 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));
+ memcpy(node->hsdir_index.store_second, node->hsdir_index.fetch,
+ sizeof(node->hsdir_index.store_second));
} else {
hs_build_hsdir_index(node_identity_pk, store_second_srv, store_second_tp,
- node->hsdir_index->store_second);
+ node->hsdir_index.store_second);
}
done:
@@ -629,6 +671,12 @@ nodelist_set_consensus(networkstatus_t *ns)
}
} SMARTLIST_FOREACH_END(node);
}
+
+ /* If the consensus is live, note down the consensus valid-after that formed
+ * the nodelist. */
+ if (networkstatus_is_live(ns, approx_time())) {
+ the_nodelist->live_consensus_valid_after = ns->valid_after;
+ }
}
/** Helper: return true iff a node has a usable amount of information*/
@@ -720,7 +768,6 @@ node_free_(node_t *node)
if (node->md)
node->md->held_by_nodes--;
tor_assert(node->nodelist_idx == -1);
- tor_free(node->hsdir_index);
tor_free(node);
}
@@ -854,6 +901,25 @@ nodelist_assert_ok(void)
digestmap_free(dm, NULL);
}
+/** Ensure that the nodelist has been created with the most recent consensus.
+ * If that's not the case, make it so. */
+void
+nodelist_ensure_freshness(networkstatus_t *ns)
+{
+ tor_assert(ns);
+
+ /* We don't even have a nodelist: this is a NOP. */
+ if (!the_nodelist) {
+ return;
+ }
+
+ if (the_nodelist->live_consensus_valid_after != ns->valid_after) {
+ log_info(LD_GENERAL, "Nodelist was not fresh: rebuilding. (%d / %d)",
+ (int) the_nodelist->live_consensus_valid_after,
+ (int) ns->valid_after);
+ nodelist_set_consensus(ns);
+ }
+}
/** Return a list of a node_t * for every node we know about. The caller
* MUST NOT modify the list. (You can set and clear flags in the nodes if
* you must, but you must not add or remove nodes.) */
@@ -1087,6 +1153,11 @@ node_supports_v3_rendezvous_point(const node_t *node)
{
tor_assert(node);
+ /* We can't use a v3 rendezvous point without the curve25519 onion pk. */
+ if (!node_get_curve25519_onion_key(node)) {
+ return 0;
+ }
+
return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point;
}
@@ -1486,9 +1557,11 @@ node_ipv6_or_preferred(const node_t *node)
/* XX/teor - node->ipv6_preferred is set from
* fascist_firewall_prefer_ipv6_orport() each time the consensus is loaded.
*/
+ node_get_prim_orport(node, &ipv4_addr);
if (!fascist_firewall_use_ipv6(options)) {
return 0;
- } else if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) {
+ } else if (node->ipv6_preferred ||
+ !tor_addr_port_is_valid_ap(&ipv4_addr, 0)) {
return node_has_ipv6_orport(node);
}
return 0;
@@ -1499,14 +1572,12 @@ node_ipv6_or_preferred(const node_t *node)
if (r && tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \
tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \
(ap_out)->port = (r)->port_field; \
- return 0; \
} \
STMT_END
-/** Copy the primary (IPv4) OR port (IP address and TCP port) for
- * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
- * port was copied, else return non-zero.*/
-int
+/** Copy the primary (IPv4) OR port (IP address and TCP port) for <b>node</b>
+ * into *<b>ap_out</b>. */
+void
node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
{
node_assert_ok(node);
@@ -1523,8 +1594,6 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
RETURN_IPV4_AP(node->ri, or_port, ap_out);
RETURN_IPV4_AP(node->rs, or_port, ap_out);
/* Microdescriptors only have an IPv6 address */
-
- return -1;
}
/** Copy the preferred OR port (IP address and TCP port) for
@@ -1549,6 +1618,7 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
{
node_assert_ok(node);
tor_assert(ap_out);
+ memset(ap_out, 0, sizeof(*ap_out));
/* Check ri first, because rewrite_node_address_for_bridge() updates
* node->ri with the configured bridge address.
@@ -1596,32 +1666,35 @@ node_ipv6_dir_preferred(const node_t *node)
* so we can't use it to determine DirPort IPv6 preference.
* This means that bridge clients will use IPv4 DirPorts by default.
*/
+ node_get_prim_dirport(node, &ipv4_addr);
if (!fascist_firewall_use_ipv6(options)) {
return 0;
- } else if (node_get_prim_dirport(node, &ipv4_addr)
+ } else if (!tor_addr_port_is_valid_ap(&ipv4_addr, 0)
|| fascist_firewall_prefer_ipv6_dirport(get_options())) {
return node_has_ipv6_dirport(node);
}
return 0;
}
-/** Copy the primary (IPv4) Dir port (IP address and TCP port) for
- * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
- * port was copied, else return non-zero.*/
-int
+/** Copy the primary (IPv4) Dir port (IP address and TCP port) for <b>node</b>
+ * into *<b>ap_out</b>. */
+void
node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out)
{
node_assert_ok(node);
tor_assert(ap_out);
+ /* Clear the address, as a safety precaution if calling functions ignore the
+ * return value */
+ tor_addr_make_null(&ap_out->addr, AF_INET);
+ ap_out->port = 0;
+
/* Check ri first, because rewrite_node_address_for_bridge() updates
* node->ri with the configured bridge address. */
RETURN_IPV4_AP(node->ri, dir_port, ap_out);
RETURN_IPV4_AP(node->rs, dir_port, ap_out);
/* Microdescriptors only have an IPv6 address */
-
- return -1;
}
#undef RETURN_IPV4_AP
@@ -2078,8 +2151,11 @@ get_dir_info_status_string(void)
* *<b>num_present</b>).
*
* If <b>in_set</b> is non-NULL, only consider those routers in <b>in_set</b>.
- * If <b>exit_only</b> is USABLE_DESCRIPTOR_EXIT_ONLY, only consider nodes
- * with the Exit flag.
+ * If <b>exit_only</b> & USABLE_DESCRIPTOR_EXIT_POLICY, only consider nodes
+ * present if they have an exit policy that accepts at least one port.
+ * If <b>exit_only</b> & USABLE_DESCRIPTOR_EXIT_FLAG, only consider nodes
+ * usable if they have the exit flag in the consensus.
+ *
* If *<b>descs_out</b> is present, add a node_t for each usable descriptor
* to it.
*/
@@ -2100,7 +2176,7 @@ count_usable_descriptors(int *num_present, int *num_usable,
if (!node)
continue; /* This would be a bug: every entry in the consensus is
* supposed to have a node. */
- if (exit_only == USABLE_DESCRIPTOR_EXIT_ONLY && ! rs->is_exit)
+ if ((exit_only & USABLE_DESCRIPTOR_EXIT_FLAG) && ! rs->is_exit)
continue;
if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
continue;
@@ -2113,7 +2189,14 @@ count_usable_descriptors(int *num_present, int *num_usable,
else
present = NULL != router_get_by_descriptor_digest(digest);
if (present) {
- /* we have the descriptor listed in the consensus. */
+ /* Do the policy check last, because it requires a descriptor,
+ * and is potentially expensive */
+ if ((exit_only & USABLE_DESCRIPTOR_EXIT_POLICY) &&
+ node_exit_policy_rejects_all(node)) {
+ continue;
+ }
+ /* we have the descriptor listed in the consensus, and it
+ * satisfies our exit constraints (if any) */
++*num_present;
}
if (descs_out)
@@ -2122,10 +2205,17 @@ count_usable_descriptors(int *num_present, int *num_usable,
}
SMARTLIST_FOREACH_END(rs);
- log_debug(LD_DIR, "%d usable, %d present (%s%s).",
+ log_debug(LD_DIR, "%d usable, %d present (%s%s%s%s%s).",
*num_usable, *num_present,
md ? "microdesc" : "desc",
- exit_only == USABLE_DESCRIPTOR_EXIT_ONLY ? " exits" : "s");
+ (exit_only & USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG) ?
+ " exit" : "s",
+ (exit_only & USABLE_DESCRIPTOR_EXIT_POLICY) ?
+ " policies" : "" ,
+ (exit_only == USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG) ?
+ " and" : "" ,
+ (exit_only & USABLE_DESCRIPTOR_EXIT_FLAG) ?
+ " flags" : "" );
}
/** Return an estimate of which fraction of usable paths through the Tor
@@ -2160,9 +2250,20 @@ compute_frac_paths_available(const networkstatus_t *consensus,
count_usable_descriptors(num_present_out, num_usable_out,
mid, consensus, now, NULL,
USABLE_DESCRIPTOR_ALL);
+ log_debug(LD_NET,
+ "%s: %d present, %d usable",
+ "mid",
+ np,
+ nu);
+
if (options->EntryNodes) {
count_usable_descriptors(&np, &nu, guards, consensus, now,
options->EntryNodes, USABLE_DESCRIPTOR_ALL);
+ log_debug(LD_NET,
+ "%s: %d present, %d usable",
+ "guard",
+ np,
+ nu);
} else {
SMARTLIST_FOREACH(mid, const node_t *, node, {
if (authdir) {
@@ -2173,42 +2274,45 @@ compute_frac_paths_available(const networkstatus_t *consensus,
smartlist_add(guards, (node_t*)node);
}
});
+ log_debug(LD_NET,
+ "%s: %d possible",
+ "guard",
+ smartlist_len(guards));
}
- /* All nodes with exit flag
- * If we're in a network with TestingDirAuthVoteExit set,
- * this can cause false positives on have_consensus_path,
- * incorrectly setting it to CONSENSUS_PATH_EXIT. This is
- * an unavoidable feature of forcing authorities to declare
- * certain nodes as exits.
- */
+ /* All nodes with exit policy and flag */
count_usable_descriptors(&np, &nu, exits, consensus, now,
- NULL, USABLE_DESCRIPTOR_EXIT_ONLY);
+ NULL, USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG);
log_debug(LD_NET,
"%s: %d present, %d usable",
"exits",
np,
nu);
- /* We need at least 1 exit present in the consensus to consider
+ /* We need at least 1 exit (flag and policy) in the consensus to consider
* building exit paths */
/* Update our understanding of whether the consensus has exits */
consensus_path_type_t old_have_consensus_path = have_consensus_path;
- have_consensus_path = ((nu > 0) ?
+ have_consensus_path = ((np > 0) ?
CONSENSUS_PATH_EXIT :
CONSENSUS_PATH_INTERNAL);
- if (have_consensus_path == CONSENSUS_PATH_INTERNAL
- && old_have_consensus_path != have_consensus_path) {
- log_notice(LD_NET,
- "The current consensus has no exit nodes. "
- "Tor can only build internal paths, "
- "such as paths to hidden services.");
-
- /* However, exit nodes can reachability self-test using this consensus,
- * join the network, and appear in a later consensus. This will allow
- * the network to build exit paths, such as paths for world wide web
- * browsing (as distinct from hidden service web browsing). */
+ if (old_have_consensus_path != have_consensus_path) {
+ if (have_consensus_path == CONSENSUS_PATH_INTERNAL) {
+ log_notice(LD_NET,
+ "The current consensus has no exit nodes. "
+ "Tor can only build internal paths, "
+ "such as paths to onion services.");
+
+ /* However, exit nodes can reachability self-test using this consensus,
+ * join the network, and appear in a later consensus. This will allow
+ * the network to build exit paths, such as paths for world wide web
+ * browsing (as distinct from hidden service web browsing). */
+ } else if (old_have_consensus_path == CONSENSUS_PATH_INTERNAL) {
+ log_notice(LD_NET,
+ "The current consensus contains exit nodes. "
+ "Tor can build exit and internal paths.");
+ }
}
f_guard = frac_nodes_with_descriptors(guards, WEIGHT_FOR_GUARD);
@@ -2230,43 +2334,28 @@ compute_frac_paths_available(const networkstatus_t *consensus,
smartlist_t *myexits= smartlist_new();
smartlist_t *myexits_unflagged = smartlist_new();
- /* All nodes with exit flag in ExitNodes option */
+ /* All nodes with exit policy and flag in ExitNodes option */
count_usable_descriptors(&np, &nu, myexits, consensus, now,
- options->ExitNodes, USABLE_DESCRIPTOR_EXIT_ONLY);
+ options->ExitNodes,
+ USABLE_DESCRIPTOR_EXIT_POLICY_AND_FLAG);
log_debug(LD_NET,
"%s: %d present, %d usable",
"myexits",
np,
nu);
- /* Now compute the nodes in the ExitNodes option where which we don't know
- * what their exit policy is, or we know it permits something. */
+ /* Now compute the nodes in the ExitNodes option where we know their exit
+ * policy permits something. */
count_usable_descriptors(&np, &nu, myexits_unflagged,
consensus, now,
- options->ExitNodes, USABLE_DESCRIPTOR_ALL);
+ options->ExitNodes,
+ USABLE_DESCRIPTOR_EXIT_POLICY);
log_debug(LD_NET,
"%s: %d present, %d usable",
"myexits_unflagged (initial)",
np,
nu);
- SMARTLIST_FOREACH_BEGIN(myexits_unflagged, const node_t *, node) {
- if (node_has_preferred_descriptor(node, 0) &&
- node_exit_policy_rejects_all(node)) {
- SMARTLIST_DEL_CURRENT(myexits_unflagged, node);
- /* this node is not actually an exit */
- np--;
- /* this node is unusable as an exit */
- nu--;
- }
- } SMARTLIST_FOREACH_END(node);
-
- log_debug(LD_NET,
- "%s: %d present, %d usable",
- "myexits_unflagged (final)",
- np,
- nu);
-
f_myexit= frac_nodes_with_descriptors(myexits,WEIGHT_FOR_EXIT);
f_myexit_unflagged=
frac_nodes_with_descriptors(myexits_unflagged,WEIGHT_FOR_EXIT);
@@ -2307,14 +2396,14 @@ compute_frac_paths_available(const networkstatus_t *consensus,
tor_asprintf(status_out,
"%d%% of guards bw, "
"%d%% of midpoint bw, and "
- "%d%% of exit bw%s = "
+ "%d%% of %s = "
"%d%% of path bw",
(int)(f_guard*100),
(int)(f_mid*100),
(int)(f_exit*100),
(router_have_consensus_path() == CONSENSUS_PATH_EXIT ?
- "" :
- " (no exits in consensus)"),
+ "exit bw" :
+ "end bw (no exits in consensus)"),
(int)(f_path*100));
return f_path;
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 00f12ca1e4..dbe9ad18ff 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -29,6 +29,7 @@ const node_t *node_get_by_hex_id(const char *identity_digest,
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);
+void nodelist_ensure_freshness(networkstatus_t *ns);
int nodelist_probably_contains_address(const tor_addr_t *addr);
void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md);
@@ -79,11 +80,11 @@ int node_has_ipv6_dirport(const node_t *node);
/* Deprecated - use node_ipv6_or_preferred or node_ipv6_dir_preferred */
#define node_ipv6_preferred(node) node_ipv6_or_preferred(node)
int node_ipv6_or_preferred(const node_t *node);
-int node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out);
+void node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out);
int node_ipv6_dir_preferred(const node_t *node);
-int node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out);
+void node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out);
int node_has_curve25519_onion_key(const node_t *node);
diff --git a/src/or/ntmain.c b/src/or/ntmain.c
index ebbe0018bd..e9a299807a 100644
--- a/src/or/ntmain.c
+++ b/src/or/ntmain.c
@@ -24,8 +24,6 @@
#include "main.h"
#include "ntmain.h"
-#include <event2/event.h>
-
#include <windows.h>
#define GENSRV_SERVICENAME "tor"
#define GENSRV_DISPLAYNAME "Tor Win32 Service"
@@ -245,7 +243,8 @@ nt_service_control(DWORD request)
log_notice(LD_GENERAL,
"Got stop/shutdown request; shutting down cleanly.");
service_status.dwCurrentState = SERVICE_STOP_PENDING;
- event_base_loopexit(tor_libevent_get_base(), &exit_now);
+ tor_libevent_exit_loop_after_delay(tor_libevent_get_base(),
+ &exit_now);
return;
}
service_fns.SetServiceStatus_fn(hStatus, &service_status);
diff --git a/src/or/onion.c b/src/or/onion.c
index bd80c2f503..829be12bae 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -67,6 +67,7 @@
#include "circuitlist.h"
#include "config.h"
#include "cpuworker.h"
+#include "crypto_util.h"
#include "networkstatus.h"
#include "onion.h"
#include "onion_fast.h"
@@ -521,6 +522,11 @@ onion_skin_create(int type,
return r;
}
+/* This is the maximum value for keys_out_len passed to
+ * onion_skin_server_handshake, plus 16. We can make it bigger if needed:
+ * It just defines how many bytes to stack-allocate. */
+#define MAX_KEYS_TMP_LEN 128
+
/** Perform the second (server-side) step of a circuit-creation handshake of
* type <b>type</b>, responding to the client request in <b>onion_skin</b>
* using the keys in <b>keys</b>. On success, write our response into
@@ -563,20 +569,21 @@ onion_skin_server_handshake(int type,
return -1;
{
size_t keys_tmp_len = keys_out_len + DIGEST_LEN;
- uint8_t *keys_tmp = tor_malloc(keys_out_len + DIGEST_LEN);
+ tor_assert(keys_tmp_len <= MAX_KEYS_TMP_LEN);
+ uint8_t keys_tmp[MAX_KEYS_TMP_LEN];
if (onion_skin_ntor_server_handshake(
onion_skin, keys->curve25519_key_map,
keys->junk_keypair,
keys->my_identity,
reply_out, keys_tmp, keys_tmp_len)<0) {
- tor_free(keys_tmp);
+ /* no need to memwipe here, since the output will never be used */
return -1;
}
+
memcpy(keys_out, keys_tmp, keys_out_len);
memcpy(rend_nonce_out, keys_tmp+keys_out_len, DIGEST_LEN);
- memwipe(keys_tmp, 0, keys_tmp_len);
- tor_free(keys_tmp);
+ memwipe(keys_tmp, 0, sizeof(keys_tmp));
r = NTOR_REPLY_LEN;
}
break;
diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c
index de9103b1f5..9f9b2199d4 100644
--- a/src/or/onion_fast.c
+++ b/src/or/onion_fast.c
@@ -29,6 +29,8 @@
#include "or.h"
#include "onion_fast.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
/** Release all state held in <b>victim</b>. */
void
diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c
index b167cb61fb..02d43cb722 100644
--- a/src/or/onion_ntor.c
+++ b/src/or/onion_ntor.c
@@ -22,6 +22,8 @@
#define ONION_NTOR_PRIVATE
#include "crypto.h"
+#include "crypto_digest.h"
+#include "crypto_util.h"
#include "onion_ntor.h"
#include "torlog.h"
#include "util.h"
diff --git a/src/or/onion_tap.c b/src/or/onion_tap.c
index c71fa236ed..44737034f4 100644
--- a/src/or/onion_tap.c
+++ b/src/or/onion_tap.c
@@ -29,6 +29,8 @@
#include "or.h"
#include "config.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "onion_tap.h"
#include "rephist.h"
diff --git a/src/or/or.h b/src/or/or.h
index b9abd6c136..0de07ed7ab 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -80,6 +80,7 @@
#include "crypto_curve25519.h"
#include "crypto_ed25519.h"
#include "tor_queue.h"
+#include "token_bucket.h"
#include "util_format.h"
#include "hs_circuitmap.h"
@@ -727,8 +728,8 @@ typedef enum {
/** Catch-all "other" reason for closing origin circuits. */
#define END_CIRC_AT_ORIGIN -1
-/* Reasons why we (or a remote OR) might close a circuit. See tor-spec.txt for
- * documentation of these. */
+/* Reasons why we (or a remote OR) might close a circuit. See tor-spec.txt
+ * section 5.4 for documentation of these. */
#define END_CIRC_REASON_MIN_ 0
#define END_CIRC_REASON_NONE 0
#define END_CIRC_REASON_TORPROTOCOL 1
@@ -893,8 +894,19 @@ rend_data_v2_t *TO_REND_DATA_V2(const rend_data_t *d)
struct hs_ident_edge_conn_t;
struct hs_ident_dir_conn_t;
struct hs_ident_circuit_t;
-/* Stub because we can't include hs_common.h. */
-struct hsdir_index_t;
+
+/* Hidden service directory index used in a node_t which is set once we set
+ * the consensus. */
+typedef struct hsdir_index_t {
+ /* 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;
/** Time interval for tracking replays of DH public keys received in
* INTRODUCE2 cells. Used only to avoid launching multiple
@@ -917,6 +929,7 @@ typedef enum {
/** Initial value on both sides of a stream transmission window when the
* stream is initialized. Measured in cells. */
#define STREAMWINDOW_START 500
+#define STREAMWINDOW_START_MAX 500
/** Amount to increment a stream window when we get a stream SENDME. */
#define STREAMWINDOW_INCREMENT 50
@@ -1369,10 +1382,10 @@ typedef struct connection_t {
* connection. */
size_t outbuf_flushlen; /**< How much data should we try to flush from the
* outbuf? */
- time_t timestamp_lastread; /**< When was the last time libevent said we could
- * read? */
- time_t timestamp_lastwritten; /**< When was the last time libevent said we
- * could write? */
+ time_t timestamp_last_read_allowed; /**< When was the last time libevent said
+ * we could read? */
+ time_t timestamp_last_write_allowed; /**< When was the last time libevent
+ * said we could write? */
time_t timestamp_created; /**< When was this connection_t created? */
@@ -1660,20 +1673,8 @@ typedef struct or_connection_t {
time_t timestamp_lastempty; /**< When was the outbuf last completely empty?*/
- /* bandwidth* and *_bucket only used by ORs in OPEN state: */
- int bandwidthrate; /**< Bytes/s added to the bucket. (OPEN ORs only.) */
- int bandwidthburst; /**< Max bucket size for this conn. (OPEN ORs only.) */
- int read_bucket; /**< When this hits 0, stop receiving. Every second we
- * add 'bandwidthrate' to this, capping it at
- * bandwidthburst. (OPEN ORs only) */
- int write_bucket; /**< When this hits 0, stop writing. Like read_bucket. */
-
- /** Last emptied read token bucket in msec since midnight; only used if
- * TB_EMPTY events are enabled. */
- uint32_t read_emptied_time;
- /** Last emptied write token bucket in msec since midnight; only used if
- * TB_EMPTY events are enabled. */
- uint32_t write_emptied_time;
+ token_bucket_rw_t bucket; /**< Used for rate limiting when the connection is
+ * in state CONN_OPEN. */
/*
* Count the number of bytes flushed out on this orconn, and the number of
@@ -1744,6 +1745,27 @@ typedef struct edge_connection_t {
uint64_t dirreq_id;
} edge_connection_t;
+/**
+ * Struct to track a connection that we closed that the other end
+ * still thinks is open. Exists in origin_circuit_t.half_streams until
+ * we get an end cell or a resolved cell for this stream id.
+ */
+typedef struct half_edge_t {
+ /** stream_id for the half-closed connection */
+ streamid_t stream_id;
+
+ /** How many sendme's can the other end still send, based on how
+ * much data we had sent at the time of close */
+ int sendmes_pending;
+
+ /** How much more data can the other end still send, based on
+ * our deliver window */
+ int data_pending;
+
+ /** Is there a connected cell pending? */
+ int connected_pending : 1;
+} half_edge_t;
+
/** Subtype of edge_connection_t for an "entry connection" -- that is, a SOCKS
* connection, a DNS request, a TransPort connection or a NATD connection */
typedef struct entry_connection_t {
@@ -2351,10 +2373,10 @@ typedef struct routerstatus_t {
* If it's a descriptor, we only use the first DIGEST_LEN bytes. */
char descriptor_digest[DIGEST256_LEN];
uint32_t addr; /**< IPv4 address for this router, in host order. */
- uint16_t or_port; /**< OR port for this router. */
+ uint16_t or_port; /**< IPv4 OR port for this router. */
uint16_t dir_port; /**< Directory port for this router. */
tor_addr_t ipv6_addr; /**< IPv6 address for this router. */
- uint16_t ipv6_orport; /**<IPV6 OR port for this router. */
+ uint16_t ipv6_orport; /**< IPv6 OR port for this router. */
unsigned int is_authority:1; /**< True iff this router is an authority. */
unsigned int is_exit:1; /**< True iff this router is a good exit. */
unsigned int is_stable:1; /**< True iff this router stays up a long time. */
@@ -2572,7 +2594,7 @@ typedef struct node_t {
/* Hidden service directory index data. This is used by a service or client
* in order to know what's the hs directory index for this node at the time
* the consensus is set. */
- struct hsdir_index_t *hsdir_index;
+ struct hsdir_index_t hsdir_index;
} node_t;
/** Linked list of microdesc hash lines for a single router in a directory
@@ -2907,11 +2929,7 @@ typedef struct {
} u;
} onion_handshake_state_t;
-/** Holds accounting information for a single step in the layered encryption
- * performed by a circuit. Used only at the client edge of a circuit. */
-typedef struct crypt_path_t {
- uint32_t magic;
-
+typedef struct relay_crypto_t {
/* crypto environments */
/** Encryption key and counter for cells heading towards the OR at this
* step. */
@@ -2925,6 +2943,17 @@ typedef struct crypt_path_t {
/** Digest state for cells heading away from the OR at this step. */
crypto_digest_t *b_digest;
+} relay_crypto_t;
+
+/** Holds accounting information for a single step in the layered encryption
+ * performed by a circuit. Used only at the client edge of a circuit. */
+typedef struct crypt_path_t {
+ uint32_t magic;
+
+ /** Cryptographic state used for encrypting and authenticating relay
+ * cells to and from this hop. */
+ relay_crypto_t crypto;
+
/** Current state of the handshake as performed with the OR at this
* step. */
onion_handshake_state_t handshake_state;
@@ -3182,15 +3211,6 @@ typedef struct circuit_t {
/** Index in smartlist of all circuits (global_circuitlist). */
int global_circuitlist_idx;
- /** Next circuit in the doubly-linked ring of circuits waiting to add
- * cells to n_conn. NULL if we have no cells pending, or if we're not
- * linked to an OR connection. */
- struct circuit_t *next_active_on_n_chan;
- /** Previous circuit in the doubly-linked ring of circuits waiting to add
- * cells to n_conn. NULL if we have no cells pending, or if we're not
- * linked to an OR connection. */
- struct circuit_t *prev_active_on_n_chan;
-
/** Various statistics about cells being added to or removed from this
* circuit's queues; used only if CELL_STATS events are enabled and
* cleared after being sent to control port. */
@@ -3274,16 +3294,40 @@ typedef struct origin_circuit_t {
* associated with this circuit. */
edge_connection_t *p_streams;
- /** Bytes read from any attached stream since last call to
+ /** Smartlist of half-closed streams (half_edge_t*) that still
+ * have pending activity */
+ smartlist_t *half_streams;
+
+ /** Bytes read on this circuit since last call to
* control_event_circ_bandwidth_used(). Only used if we're configured
* to emit CIRC_BW events. */
uint32_t n_read_circ_bw;
- /** Bytes written to any attached stream since last call to
+ /** Bytes written to on this circuit since last call to
* control_event_circ_bandwidth_used(). Only used if we're configured
* to emit CIRC_BW events. */
uint32_t n_written_circ_bw;
+ /** Total known-valid relay cell bytes since last call to
+ * control_event_circ_bandwidth_used(). Only used if we're configured
+ * to emit CIRC_BW events. */
+ uint32_t n_delivered_read_circ_bw;
+
+ /** Total written relay cell bytes since last call to
+ * control_event_circ_bandwidth_used(). Only used if we're configured
+ * to emit CIRC_BW events. */
+ uint32_t n_delivered_written_circ_bw;
+
+ /** Total overhead data in all known-valid relay data cells since last
+ * call to control_event_circ_bandwidth_used(). Only used if we're
+ * configured to emit CIRC_BW events. */
+ uint32_t n_overhead_read_circ_bw;
+
+ /** Total written overhead data in all relay data cells since last call to
+ * control_event_circ_bandwidth_used(). Only used if we're configured
+ * to emit CIRC_BW events. */
+ uint32_t n_overhead_written_circ_bw;
+
/** Build state for this circuit. It includes the intended path
* length, the chosen exit router, rendezvous information, etc.
*/
@@ -3470,14 +3514,6 @@ struct onion_queue_t;
typedef struct or_circuit_t {
circuit_t base_;
- /** Next circuit in the doubly-linked ring of circuits waiting to add
- * cells to p_chan. NULL if we have no cells pending, or if we're not
- * linked to an OR connection. */
- struct circuit_t *next_active_on_p_chan;
- /** Previous circuit in the doubly-linked ring of circuits waiting to add
- * cells to p_chan. NULL if we have no cells pending, or if we're not
- * linked to an OR connection. */
- struct circuit_t *prev_active_on_p_chan;
/** Pointer to an entry on the onion queue, if this circuit is waiting for a
* chance to give an onionskin to a cpuworker. Used only in onion.c */
struct onion_queue_t *onionqueue_entry;
@@ -3502,21 +3538,10 @@ typedef struct or_circuit_t {
/** Linked list of Exit streams associated with this circuit that are
* still being resolved. */
edge_connection_t *resolving_streams;
- /** The cipher used by intermediate hops for cells heading toward the
- * OP. */
- crypto_cipher_t *p_crypto;
- /** The cipher used by intermediate hops for cells heading away from
- * the OP. */
- crypto_cipher_t *n_crypto;
-
- /** The integrity-checking digest used by intermediate hops, for
- * cells packaged here and heading towards the OP.
- */
- crypto_digest_t *p_digest;
- /** The integrity-checking digest used by intermediate hops, for
- * cells packaged at the OP and arriving here.
- */
- crypto_digest_t *n_digest;
+
+ /** Cryptographic state used for encrypting and authenticating relay
+ * cells to and from this hop. */
+ relay_crypto_t crypto;
/** Points to spliced circuit if purpose is REND_ESTABLISHED, and circuit
* is not marked for close. */
@@ -4192,6 +4217,8 @@ typedef struct {
int NumDirectoryGuards; /**< How many dir guards do we try to establish?
* If 0, use value from NumEntryGuards. */
+ int NumPrimaryGuards; /**< How many primary guards do we want? */
+
int RephistTrackTime; /**< How many seconds do we keep rephist info? */
/** Should we always fetch our dir info on the mirror schedule (which
* means directly from the authorities) no matter our other config? */
@@ -4235,10 +4262,6 @@ typedef struct {
* testing our DNS server. */
int EnforceDistinctSubnets; /**< If true, don't allow multiple routers in the
* same network zone in the same circuit. */
- int PortForwarding; /**< If true, use NAT-PMP or UPnP to automatically
- * forward the DirPort and ORPort on the NAT device */
- char *PortForwardingHelper; /** < Filename or full path of the port
- forwarding helper executable */
int AllowNonRFC953Hostnames; /**< If true, we allow connections to hostnames
* with weird characters. */
/** If true, we try resolving hostnames with weird characters. */
@@ -4364,19 +4387,19 @@ typedef struct {
/** Schedule for when servers should download things in general. Only
* altered on testing networks. */
- smartlist_t *TestingServerDownloadSchedule;
+ int TestingServerDownloadInitialDelay;
/** Schedule for when clients should download things in general. Only
* altered on testing networks. */
- smartlist_t *TestingClientDownloadSchedule;
+ int TestingClientDownloadInitialDelay;
/** Schedule for when servers should download consensuses. Only altered
* on testing networks. */
- smartlist_t *TestingServerConsensusDownloadSchedule;
+ int TestingServerConsensusDownloadInitialDelay;
/** Schedule for when clients should download consensuses. Only altered
* on testing networks. */
- smartlist_t *TestingClientConsensusDownloadSchedule;
+ int TestingClientConsensusDownloadInitialDelay;
/** Schedule for when clients should download consensuses from authorities
* if they are bootstrapping (that is, they don't have a usable, reasonably
@@ -4386,7 +4409,7 @@ typedef struct {
* This schedule is incremented by (potentially concurrent) connection
* attempts, unlike other schedules, which are incremented by connection
* failures. Only altered on testing networks. */
- smartlist_t *ClientBootstrapConsensusAuthorityDownloadSchedule;
+ int ClientBootstrapConsensusAuthorityDownloadInitialDelay;
/** Schedule for when clients should download consensuses from fallback
* directory mirrors if they are bootstrapping (that is, they don't have a
@@ -4396,7 +4419,7 @@ typedef struct {
* This schedule is incremented by (potentially concurrent) connection
* attempts, unlike other schedules, which are incremented by connection
* failures. Only altered on testing networks. */
- smartlist_t *ClientBootstrapConsensusFallbackDownloadSchedule;
+ int ClientBootstrapConsensusFallbackDownloadInitialDelay;
/** Schedule for when clients should download consensuses from authorities
* if they are bootstrapping (that is, they don't have a usable, reasonably
@@ -4406,15 +4429,15 @@ typedef struct {
* This schedule is incremented by (potentially concurrent) connection
* attempts, unlike other schedules, which are incremented by connection
* failures. Only altered on testing networks. */
- smartlist_t *ClientBootstrapConsensusAuthorityOnlyDownloadSchedule;
+ int ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay;
/** Schedule for when clients should download bridge descriptors. Only
* altered on testing networks. */
- smartlist_t *TestingBridgeDownloadSchedule;
+ int TestingBridgeDownloadInitialDelay;
/** Schedule for when clients should download bridge descriptors when they
* have no running bridges. Only altered on testing networks. */
- smartlist_t *TestingBridgeBootstrapDownloadSchedule;
+ int TestingBridgeBootstrapDownloadInitialDelay;
/** When directory clients have only a few descriptors to request, they
* batch them until they have more, or until this amount of time has
@@ -4462,9 +4485,6 @@ typedef struct {
/** Enable CELL_STATS events. Only altered on testing networks. */
int TestingEnableCellStatsEvent;
- /** Enable TB_EMPTY events. Only altered on testing networks. */
- int TestingEnableTbEmptyEvent;
-
/** If true, and we have GeoIP data, and we're a bridge, keep a per-country
* count of how many client addresses have contacted us so that we can help
* the bridge authority guess which countries have blocked access to us. */
@@ -4795,15 +4815,6 @@ typedef struct {
time_t LastRotatedOnionKey;
} or_state_t;
-/** Change the next_write time of <b>state</b> to <b>when</b>, unless the
- * state is already scheduled to be written to disk earlier than <b>when</b>.
- */
-static inline void or_state_mark_dirty(or_state_t *state, time_t when)
-{
- if (state->next_write > when)
- state->next_write = when;
-}
-
#define MAX_SOCKS_REPLY_LEN 1024
#define MAX_SOCKS_ADDR_LEN 256
#define SOCKS_NO_AUTH 0x00
diff --git a/src/or/parsecommon.c b/src/or/parsecommon.c
index 6c3dd3100e..cd1a0c2521 100644
--- a/src/or/parsecommon.c
+++ b/src/or/parsecommon.c
@@ -345,7 +345,7 @@ get_next_token(memarea_t *area,
goto check_object;
obstart = *s; /* Set obstart to start of object spec */
- if (*s+16 >= eol || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */
+ if (eol - *s <= 16 || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */
strcmp_len(eol-5, "-----", 5) || /* nuls or invalid endings */
(eol-*s) > MAX_UNPARSED_OBJECT_SIZE) { /* name too long */
RET_ERR("Malformed object: bad begin line");
@@ -426,7 +426,7 @@ find_by_keyword_(smartlist_t *s, directory_keyword keyword,
* NULL if no such keyword is found.
*/
directory_token_t *
-find_opt_by_keyword(smartlist_t *s, directory_keyword keyword)
+find_opt_by_keyword(const smartlist_t *s, directory_keyword keyword)
{
SMARTLIST_FOREACH(s, directory_token_t *, t, if (t->tp == keyword) return t);
return NULL;
@@ -448,4 +448,3 @@ find_all_by_keyword(const smartlist_t *s, directory_keyword k)
});
return out;
}
-
diff --git a/src/or/parsecommon.h b/src/or/parsecommon.h
index 903d94478b..d33faf8ec7 100644
--- a/src/or/parsecommon.h
+++ b/src/or/parsecommon.h
@@ -314,7 +314,7 @@ directory_token_t *find_by_keyword_(smartlist_t *s,
#define find_by_keyword(s, keyword) \
find_by_keyword_((s), (keyword), #keyword)
-directory_token_t *find_opt_by_keyword(smartlist_t *s,
+directory_token_t *find_opt_by_keyword(const smartlist_t *s,
directory_keyword keyword);
smartlist_t * find_all_by_keyword(const smartlist_t *s, directory_keyword k);
diff --git a/src/or/periodic.c b/src/or/periodic.c
index 6896b41c86..9470376d06 100644
--- a/src/or/periodic.c
+++ b/src/or/periodic.c
@@ -14,10 +14,9 @@
#include "or.h"
#include "compat_libevent.h"
#include "config.h"
+#include "main.h"
#include "periodic.h"
-#include <event2/event.h>
-
/** We disable any interval greater than this number of seconds, on the
* grounds that it is probably an absolute time mistakenly passed in as a
* relative time.
@@ -34,24 +33,34 @@ periodic_event_set_interval(periodic_event_item_t *event,
struct timeval tv;
tv.tv_sec = next_interval;
tv.tv_usec = 0;
- event_add(event->ev, &tv);
+ mainloop_event_schedule(event->ev, &tv);
}
/** Wraps dispatches for periodic events, <b>data</b> will be a pointer to the
* event that needs to be called */
static void
-periodic_event_dispatch(evutil_socket_t fd, short what, void *data)
+periodic_event_dispatch(mainloop_event_t *ev, void *data)
{
- (void)fd;
- (void)what;
periodic_event_item_t *event = data;
+ tor_assert(ev == event->ev);
+
+ if (BUG(!periodic_event_is_enabled(event))) {
+ return;
+ }
time_t now = time(NULL);
+ update_current_time(now);
const or_options_t *options = get_options();
// log_debug(LD_GENERAL, "Dispatching %s", event->name);
int r = event->fn(now, options);
int next_interval = 0;
+ if (!periodic_event_is_enabled(event)) {
+ /* The event got disabled from inside its callback; no need to
+ * reschedule. */
+ return;
+ }
+
/* update the last run time if action was taken */
if (r==0) {
log_err(LD_BUG, "Invalid return value for periodic event from %s.",
@@ -74,14 +83,17 @@ periodic_event_dispatch(evutil_socket_t fd, short what, void *data)
// log_debug(LD_GENERAL, "Scheduling %s for %d seconds", event->name,
// next_interval);
struct timeval tv = { next_interval , 0 };
- event_add(event->ev, &tv);
+ mainloop_event_schedule(ev, &tv);
}
/** Schedules <b>event</b> to run as soon as possible from now. */
void
periodic_event_reschedule(periodic_event_item_t *event)
{
- periodic_event_set_interval(event, 1);
+ /* Don't reschedule a disabled event. */
+ if (periodic_event_is_enabled(event)) {
+ periodic_event_set_interval(event, 1);
+ }
}
/** Initializes the libevent backend for a periodic event. */
@@ -93,10 +105,8 @@ periodic_event_setup(periodic_event_item_t *event)
tor_assert(0);
}
- event->ev = tor_event_new(tor_libevent_get_base(),
- -1, 0,
- periodic_event_dispatch,
- event);
+ event->ev = mainloop_event_new(periodic_event_dispatch,
+ event);
tor_assert(event->ev);
}
@@ -109,9 +119,15 @@ periodic_event_launch(periodic_event_item_t *event)
log_err(LD_BUG, "periodic_event_launch without periodic_event_setup");
tor_assert(0);
}
+ /* Event already enabled? This is a bug */
+ if (periodic_event_is_enabled(event)) {
+ log_err(LD_BUG, "periodic_event_launch on an already enabled event");
+ tor_assert(0);
+ }
// Initial dispatch
- periodic_event_dispatch(-1, EV_TIMEOUT, event);
+ event->enabled = 1;
+ periodic_event_dispatch(event->ev, event);
}
/** Release all storage associated with <b>event</b> */
@@ -120,7 +136,38 @@ periodic_event_destroy(periodic_event_item_t *event)
{
if (!event)
return;
- tor_event_free(event->ev);
+ mainloop_event_free(event->ev);
event->last_action_time = 0;
}
+/** Enable the given event by setting its "enabled" flag and scheduling it to
+ * run immediately in the event loop. This can be called for an event that is
+ * already enabled. */
+void
+periodic_event_enable(periodic_event_item_t *event)
+{
+ tor_assert(event);
+ /* Safely and silently ignore if this event is already enabled. */
+ if (periodic_event_is_enabled(event)) {
+ return;
+ }
+
+ tor_assert(event->ev);
+ event->enabled = 1;
+ mainloop_event_activate(event->ev);
+}
+
+/** Disable the given event which means the event is destroyed and then the
+ * event's enabled flag is unset. This can be called for an event that is
+ * already disabled. */
+void
+periodic_event_disable(periodic_event_item_t *event)
+{
+ tor_assert(event);
+ /* Safely and silently ignore if this event is already disabled. */
+ if (!periodic_event_is_enabled(event)) {
+ return;
+ }
+ mainloop_event_cancel(event->ev);
+ event->enabled = 0;
+}
diff --git a/src/or/periodic.h b/src/or/periodic.h
index 8baf3994eb..e8208b2475 100644
--- a/src/or/periodic.h
+++ b/src/or/periodic.h
@@ -6,6 +6,39 @@
#define PERIODIC_EVENT_NO_UPDATE (-1)
+/* Tor roles for which a periodic event item is for. An event can be for
+ * multiple roles, they can be combined. */
+#define PERIODIC_EVENT_ROLE_CLIENT (1U << 0)
+#define PERIODIC_EVENT_ROLE_RELAY (1U << 1)
+#define PERIODIC_EVENT_ROLE_BRIDGE (1U << 2)
+#define PERIODIC_EVENT_ROLE_DIRAUTH (1U << 3)
+#define PERIODIC_EVENT_ROLE_BRIDGEAUTH (1U << 4)
+#define PERIODIC_EVENT_ROLE_HS_SERVICE (1U << 5)
+#define PERIODIC_EVENT_ROLE_DIRSERVER (1U << 6)
+
+/* Helper macro to make it a bit less annoying to defined groups of roles that
+ * are often used. */
+
+/* Router that is a Bridge or Relay. */
+#define PERIODIC_EVENT_ROLE_ROUTER \
+ (PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_RELAY)
+/* Authorities that is both bridge and directory. */
+#define PERIODIC_EVENT_ROLE_AUTHORITIES \
+ (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_DIRAUTH)
+/* All roles. */
+#define PERIODIC_EVENT_ROLE_ALL \
+ (PERIODIC_EVENT_ROLE_AUTHORITIES | PERIODIC_EVENT_ROLE_CLIENT | \
+ PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_ROUTER)
+
+/*
+ * Event flags which can change the behavior of an event.
+ */
+
+/* Indicate that the event needs the network meaning that if we are in
+ * DisableNetwork or hibernation mode, the event won't be enabled. This obey
+ * the net_is_disabled() check. */
+#define PERIODIC_EVENT_FLAG_NEED_NET (1U << 0)
+
/** Callback function for a periodic event to take action. The return value
* influences the next time the function will get called. Return
* PERIODIC_EVENT_NO_UPDATE to not update <b>last_action_time</b> and be polled
@@ -14,24 +47,42 @@
typedef int (*periodic_event_helper_t)(time_t now,
const or_options_t *options);
-struct event;
+struct mainloop_event_t;
/** A single item for the periodic-events-function table. */
typedef struct periodic_event_item_t {
periodic_event_helper_t fn; /**< The function to run the event */
time_t last_action_time; /**< The last time the function did something */
- struct event *ev; /**< Libevent callback we're using to implement this */
+ struct mainloop_event_t *ev; /**< Libevent callback we're using to implement
+ * this */
const char *name; /**< Name of the function -- for debug */
+
+ /* Bitmask of roles define above for which this event applies. */
+ uint32_t roles;
+ /* Bitmask of flags which can change the behavior of the event. */
+ uint32_t flags;
+ /* Indicate that this event has been enabled that is scheduled. */
+ unsigned int enabled : 1;
} periodic_event_item_t;
/** events will get their interval from first execution */
-#define PERIODIC_EVENT(fn) { fn##_callback, 0, NULL, #fn }
-#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL }
+#define PERIODIC_EVENT(fn, r, f) { fn##_callback, 0, NULL, #fn, r, f, 0 }
+#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL, 0, 0, 0 }
+
+/* Return true iff the given event was setup before thus is enabled to be
+ * scheduled. */
+static inline int
+periodic_event_is_enabled(const periodic_event_item_t *item)
+{
+ return item->enabled;
+}
void periodic_event_launch(periodic_event_item_t *event);
void periodic_event_setup(periodic_event_item_t *event);
void periodic_event_destroy(periodic_event_item_t *event);
void periodic_event_reschedule(periodic_event_item_t *event);
+void periodic_event_enable(periodic_event_item_t *event);
+void periodic_event_disable(periodic_event_item_t *event);
#endif /* !defined(TOR_PERIODIC_H) */
diff --git a/src/or/policies.c b/src/or/policies.c
index f718ded326..1210ca687d 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -825,9 +825,8 @@ fascist_firewall_choose_address(const tor_addr_port_t *a,
* If pref_only, only choose preferred addresses. In either case, choose
* a preferred address before an address that's not preferred.
* If both addresses could be chosen (they are both preferred or both allowed)
- * choose IPv6 if pref_ipv6 is true, otherwise choose IPv4.
- * If neither address is chosen, return 0, else return 1. */
-static int
+ * choose IPv6 if pref_ipv6 is true, otherwise choose IPv4. */
+static void
fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr,
uint16_t ipv4_orport,
uint16_t ipv4_dirport,
@@ -845,6 +844,9 @@ fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr,
tor_assert(ipv6_addr);
tor_assert(ap);
+ tor_addr_make_null(&ap->addr, AF_UNSPEC);
+ ap->port = 0;
+
tor_addr_port_t ipv4_ap;
tor_addr_copy(&ipv4_ap.addr, ipv4_addr);
ipv4_ap.port = (fw_connection == FIREWALL_OR_CONNECTION
@@ -865,17 +867,12 @@ fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr,
if (result) {
tor_addr_copy(&ap->addr, &result->addr);
ap->port = result->port;
- return 1;
- } else {
- tor_addr_make_null(&ap->addr, AF_UNSPEC);
- ap->port = 0;
- return 0;
}
}
/** Like fascist_firewall_choose_address_base(), but takes a host-order IPv4
* address as the first parameter. */
-static int
+static void
fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr,
uint16_t ipv4_orport,
uint16_t ipv4_dirport,
@@ -889,11 +886,16 @@ fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr,
{
tor_addr_t ipv4_addr;
tor_addr_from_ipv4h(&ipv4_addr, ipv4h_addr);
- return fascist_firewall_choose_address_base(&ipv4_addr, ipv4_orport,
- ipv4_dirport, ipv6_addr,
- ipv6_orport, ipv6_dirport,
- fw_connection, pref_only,
- pref_ipv6, ap);
+ tor_assert(ap);
+
+ tor_addr_make_null(&ap->addr, AF_UNSPEC);
+ ap->port = 0;
+
+ fascist_firewall_choose_address_base(&ipv4_addr, ipv4_orport,
+ ipv4_dirport, ipv6_addr,
+ ipv6_orport, ipv6_dirport,
+ fw_connection, pref_only,
+ pref_ipv6, ap);
}
/* Some microdescriptor consensus methods have no IPv6 addresses in rs: they
@@ -944,23 +946,25 @@ node_awaiting_ipv6(const or_options_t* options, const node_t *node)
* This should only happen when there's no valid consensus, and rs doesn't
* correspond to a bridge client's bridge.
*/
-int
+void
fascist_firewall_choose_address_rs(const routerstatus_t *rs,
firewall_connection_t fw_connection,
int pref_only, tor_addr_port_t* ap)
{
+ tor_assert(ap);
+
+ tor_addr_make_null(&ap->addr, AF_UNSPEC);
+ ap->port = 0;
+
if (!rs) {
- return 0;
+ return;
}
- tor_assert(ap);
-
const or_options_t *options = get_options();
const node_t *node = node_get_by_id(rs->identity_digest);
if (node && !node_awaiting_ipv6(options, node)) {
- return fascist_firewall_choose_address_node(node, fw_connection, pref_only,
- ap);
+ fascist_firewall_choose_address_node(node, fw_connection, pref_only, ap);
} else {
/* There's no node-specific IPv6 preference, so use the generic IPv6
* preference instead. */
@@ -970,33 +974,31 @@ fascist_firewall_choose_address_rs(const routerstatus_t *rs,
/* Assume IPv4 and IPv6 DirPorts are the same.
* Assume the IPv6 OR and Dir addresses are the same. */
- return fascist_firewall_choose_address_ipv4h(rs->addr,
- rs->or_port,
- rs->dir_port,
- &rs->ipv6_addr,
- rs->ipv6_orport,
- rs->dir_port,
- fw_connection,
- pref_only,
- pref_ipv6,
- ap);
+ fascist_firewall_choose_address_ipv4h(rs->addr, rs->or_port, rs->dir_port,
+ &rs->ipv6_addr, rs->ipv6_orport,
+ rs->dir_port, fw_connection,
+ pref_only, pref_ipv6, ap);
}
}
/** Like fascist_firewall_choose_address_base(), but takes <b>node</b>, and
* looks up the node's IPv6 preference rather than taking an argument
* for pref_ipv6. */
-int
+void
fascist_firewall_choose_address_node(const node_t *node,
firewall_connection_t fw_connection,
int pref_only, tor_addr_port_t *ap)
{
+ tor_assert(ap);
+
+ tor_addr_make_null(&ap->addr, AF_UNSPEC);
+ ap->port = 0;
+
if (!node) {
- return 0;
+ return;
}
node_assert_ok(node);
-
/* Calling fascist_firewall_choose_address_node() when the node is missing
* IPv6 information breaks IPv6-only clients.
* If the node is a hard-coded fallback directory or authority, call
@@ -1006,7 +1008,7 @@ fascist_firewall_choose_address_node(const node_t *node,
* descriptor (routerinfo), or is one of our configured bridges before
* calling this function. */
if (BUG(node_awaiting_ipv6(get_options(), node))) {
- return 0;
+ return;
}
const int pref_ipv6_node = (fw_connection == FIREWALL_OR_CONNECTION
@@ -1024,27 +1026,27 @@ fascist_firewall_choose_address_node(const node_t *node,
node_get_pref_ipv6_dirport(node, &ipv6_dir_ap);
/* Assume the IPv6 OR and Dir addresses are the same. */
- return fascist_firewall_choose_address_base(&ipv4_or_ap.addr,
- ipv4_or_ap.port,
- ipv4_dir_ap.port,
- &ipv6_or_ap.addr,
- ipv6_or_ap.port,
- ipv6_dir_ap.port,
- fw_connection,
- pref_only,
- pref_ipv6_node,
- ap);
+ fascist_firewall_choose_address_base(&ipv4_or_ap.addr, ipv4_or_ap.port,
+ ipv4_dir_ap.port, &ipv6_or_ap.addr,
+ ipv6_or_ap.port, ipv6_dir_ap.port,
+ fw_connection, pref_only,
+ pref_ipv6_node, ap);
}
/** Like fascist_firewall_choose_address_rs(), but takes <b>ds</b>. */
-int
+void
fascist_firewall_choose_address_dir_server(const dir_server_t *ds,
firewall_connection_t fw_connection,
int pref_only,
tor_addr_port_t *ap)
{
+ tor_assert(ap);
+
+ tor_addr_make_null(&ap->addr, AF_UNSPEC);
+ ap->port = 0;
+
if (!ds) {
- return 0;
+ return;
}
/* A dir_server_t always has a fake_status. As long as it has the same
@@ -1052,8 +1054,8 @@ fascist_firewall_choose_address_dir_server(const dir_server_t *ds,
* (See #17867.)
* This function relies on fascist_firewall_choose_address_rs looking up the
* node if it can, because that will get the latest info for the relay. */
- return fascist_firewall_choose_address_rs(&ds->fake_status, fw_connection,
- pref_only, ap);
+ fascist_firewall_choose_address_rs(&ds->fake_status, fw_connection,
+ pref_only, ap);
}
/** Return 1 if <b>addr</b> is permitted to connect to our dir port,
@@ -2997,11 +2999,12 @@ getinfo_helper_policies(control_connection_t *conn,
smartlist_free(private_policy_strings);
} else if (!strcmp(question, "exit-policy/reject-private/relay")) {
const or_options_t *options = get_options();
- const routerinfo_t *me = router_get_my_routerinfo();
+ int err = 0;
+ const routerinfo_t *me = router_get_my_routerinfo_with_err(&err);
if (!me) {
- *errmsg = "router_get_my_routerinfo returned NULL";
- return -1;
+ *errmsg = routerinfo_err_to_string(err);
+ return routerinfo_err_is_transient(err) ? -1 : 0;
}
if (!options->ExitPolicyRejectPrivate &&
@@ -3036,11 +3039,17 @@ getinfo_helper_policies(control_connection_t *conn,
SMARTLIST_FOREACH(configured_addresses, tor_addr_t *, a, tor_free(a));
smartlist_free(configured_addresses);
} else if (!strcmpstart(question, "exit-policy/")) {
- const routerinfo_t *me = router_get_my_routerinfo();
-
int include_ipv4 = 0;
int include_ipv6 = 0;
+ int err = 0;
+ const routerinfo_t *me = router_get_my_routerinfo_with_err(&err);
+
+ if (!me) {
+ *errmsg = routerinfo_err_to_string(err);
+ return routerinfo_err_is_transient(err) ? -1 : 0;
+ }
+
if (!strcmp(question, "exit-policy/ipv4")) {
include_ipv4 = 1;
} else if (!strcmp(question, "exit-policy/ipv6")) {
@@ -3051,13 +3060,10 @@ getinfo_helper_policies(control_connection_t *conn,
return 0; /* No such key. */
}
- if (!me) {
- *errmsg = "router_get_my_routerinfo returned NULL";
- return -1;
- }
-
- *answer = router_dump_exit_policy_to_string(me,include_ipv4,include_ipv6);
+ *answer = router_dump_exit_policy_to_string(me,include_ipv4,
+ include_ipv6);
}
+
return 0;
}
diff --git a/src/or/policies.h b/src/or/policies.h
index 35220a812f..4879acdd8d 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -55,13 +55,13 @@ int fascist_firewall_allows_dir_server(const dir_server_t *ds,
firewall_connection_t fw_connection,
int pref_only);
-int fascist_firewall_choose_address_rs(const routerstatus_t *rs,
- firewall_connection_t fw_connection,
- int pref_only, tor_addr_port_t* ap);
-int fascist_firewall_choose_address_node(const node_t *node,
- firewall_connection_t fw_connection,
- int pref_only, tor_addr_port_t* ap);
-int fascist_firewall_choose_address_dir_server(const dir_server_t *ds,
+void fascist_firewall_choose_address_rs(const routerstatus_t *rs,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap);
+void fascist_firewall_choose_address_node(const node_t *node,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap);
+void fascist_firewall_choose_address_dir_server(const dir_server_t *ds,
firewall_connection_t fw_connection,
int pref_only, tor_addr_port_t* ap);
diff --git a/src/or/proto_socks.c b/src/or/proto_socks.c
index 8700fe1269..57a7d1cd64 100644
--- a/src/or/proto_socks.c
+++ b/src/or/proto_socks.c
@@ -9,6 +9,7 @@
#include "buffers.h"
#include "control.h"
#include "config.h"
+#include "crypto_util.h"
#include "ext_orport.h"
#include "proto_socks.h"
#include "reasons.h"
diff --git a/src/or/protover.c b/src/or/protover.c
index 5145881ba9..c973660980 100644
--- a/src/or/protover.c
+++ b/src/or/protover.c
@@ -23,6 +23,7 @@
#define PROTOVER_PRIVATE
+#include "compat.h"
#include "or.h"
#include "protover.h"
#include "routerparse.h"
@@ -178,6 +179,16 @@ parse_version_range(const char *s, const char *end_of_range,
return -1;
}
+static int
+is_valid_keyword(const char *s, size_t n)
+{
+ for (size_t i = 0; i < n; i++) {
+ if (!TOR_ISALNUM(s[i]) && s[i] != '-')
+ return 0;
+ }
+ return 1;
+}
+
/** Parse a single protocol entry from <b>s</b> up to an optional
* <b>end_of_entry</b> pointer, and return that protocol entry. Return NULL
* on error.
@@ -212,6 +223,11 @@ parse_single_entry(const char *s, const char *end_of_entry)
MAX_PROTOCOL_NAME_LENGTH, escaped(out->name));
goto error;
}
+
+ /* The name must contain only alphanumeric characters and hyphens. */
+ if (!is_valid_keyword(s, equals-s))
+ goto error;
+
out->name = tor_strndup(s, equals-s);
tor_assert(equals < end_of_entry);
@@ -765,7 +781,7 @@ protover_all_supported(const char *s, char **missing_out)
versions->high = i;
}
/* If the last one to be unsupported is one less than the current
- * one, we're in a continous range, so set the high field. */
+ * one, we're in a continuous range, so set the high field. */
if ((versions->high && versions->high == i - 1) ||
/* Similarly, if the last high wasn't set and we're currently
* one higher than the low, add current index as the highest
@@ -920,4 +936,3 @@ protover_free_all(void)
}
#endif /* !defined(HAVE_RUST) */
-
diff --git a/src/or/protover.h b/src/or/protover.h
index b94ebab15b..c46a13de66 100644
--- a/src/or/protover.h
+++ b/src/or/protover.h
@@ -10,7 +10,7 @@
#define TOR_PROTOVER_H
#include "container.h"
-#include <stdbool.h>
+
/** The first version of Tor that included "proto" entries in its
* descriptors. Authorities should use this to decide whether to
* guess proto lines. */
diff --git a/src/or/relay.c b/src/or/relay.c
index ae231b89b9..b6886e2bb8 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -61,6 +61,8 @@
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "geoip.h"
#include "hs_cache.h"
#include "main.h"
@@ -70,6 +72,7 @@
#include "policies.h"
#include "reasons.h"
#include "relay.h"
+#include "relay_crypto.h"
#include "rendcache.h"
#include "rendcommon.h"
#include "router.h"
@@ -82,9 +85,6 @@ static edge_connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell,
cell_direction_t cell_direction,
crypt_path_t *layer_hint);
-static int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
- edge_connection_t *conn,
- crypt_path_t *layer_hint);
static void circuit_consider_sending_sendme(circuit_t *circ,
crypt_path_t *layer_hint);
static void circuit_resume_edge_reading(circuit_t *circ,
@@ -122,77 +122,6 @@ uint64_t stats_n_circ_max_cell_reached = 0;
/** Used to tell which stream to read from first on a circuit. */
static tor_weak_rng_t stream_choice_rng = TOR_WEAK_RNG_INIT;
-/** Update digest from the payload of cell. Assign integrity part to
- * cell.
- */
-static void
-relay_set_digest(crypto_digest_t *digest, cell_t *cell)
-{
- char integrity[4];
- relay_header_t rh;
-
- crypto_digest_add_bytes(digest, (char*)cell->payload, CELL_PAYLOAD_SIZE);
- crypto_digest_get_digest(digest, integrity, 4);
-// log_fn(LOG_DEBUG,"Putting digest of %u %u %u %u into relay cell.",
-// integrity[0], integrity[1], integrity[2], integrity[3]);
- relay_header_unpack(&rh, cell->payload);
- memcpy(rh.integrity, integrity, 4);
- relay_header_pack(cell->payload, &rh);
-}
-
-/** Does the digest for this circuit indicate that this cell is for us?
- *
- * Update digest from the payload of cell (with the integrity part set
- * to 0). If the integrity part is valid, return 1, else restore digest
- * and cell to their original state and return 0.
- */
-static int
-relay_digest_matches(crypto_digest_t *digest, cell_t *cell)
-{
- uint32_t received_integrity, calculated_integrity;
- relay_header_t rh;
- crypto_digest_t *backup_digest=NULL;
-
- backup_digest = crypto_digest_dup(digest);
-
- relay_header_unpack(&rh, cell->payload);
- memcpy(&received_integrity, rh.integrity, 4);
- memset(rh.integrity, 0, 4);
- relay_header_pack(cell->payload, &rh);
-
-// log_fn(LOG_DEBUG,"Reading digest of %u %u %u %u from relay cell.",
-// received_integrity[0], received_integrity[1],
-// received_integrity[2], received_integrity[3]);
-
- crypto_digest_add_bytes(digest, (char*) cell->payload, CELL_PAYLOAD_SIZE);
- crypto_digest_get_digest(digest, (char*) &calculated_integrity, 4);
-
- if (calculated_integrity != received_integrity) {
-// log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing.");
-// (%d vs %d).", received_integrity, calculated_integrity);
- /* restore digest to its old form */
- crypto_digest_assign(digest, backup_digest);
- /* restore the relay header */
- memcpy(rh.integrity, &received_integrity, 4);
- relay_header_pack(cell->payload, &rh);
- crypto_digest_free(backup_digest);
- return 0;
- }
- crypto_digest_free(backup_digest);
- return 1;
-}
-
-/** Apply <b>cipher</b> to CELL_PAYLOAD_SIZE bytes of <b>in</b>
- * (in place).
- *
- * Note that we use the same operation for encrypting and for decrypting.
- */
-static void
-relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in)
-{
- crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
-}
-
/**
* Update channel usage state based on the type of relay cell and
* circuit properties.
@@ -297,7 +226,8 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
if (circ->marked_for_close)
return 0;
- if (relay_crypt(circ, cell, cell_direction, &layer_hint, &recognized) < 0) {
+ if (relay_decrypt_cell(circ, cell, cell_direction, &layer_hint, &recognized)
+ < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"relay crypt failed. Dropping connection.");
return -END_CIRC_REASON_INTERNAL;
@@ -309,7 +239,9 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
edge_connection_t *conn = NULL;
if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING) {
- pathbias_check_probe_response(circ, cell);
+ if (pathbias_check_probe_response(circ, cell) == -1) {
+ pathbias_count_valid_cells(circ, cell);
+ }
/* We need to drop this cell no matter what to avoid code that expects
* a certain purpose (such as the hidserv code). */
@@ -402,87 +334,6 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
return 0;
}
-/** Do the appropriate en/decryptions for <b>cell</b> arriving on
- * <b>circ</b> in direction <b>cell_direction</b>.
- *
- * If cell_direction == CELL_DIRECTION_IN:
- * - If we're at the origin (we're the OP), for hops 1..N,
- * decrypt cell. If recognized, stop.
- * - Else (we're not the OP), encrypt one hop. Cell is not recognized.
- *
- * If cell_direction == CELL_DIRECTION_OUT:
- * - decrypt one hop. Check if recognized.
- *
- * If cell is recognized, set *recognized to 1, and set
- * *layer_hint to the hop that recognized it.
- *
- * Return -1 to indicate that we should mark the circuit for close,
- * else return 0.
- */
-int
-relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
- crypt_path_t **layer_hint, char *recognized)
-{
- relay_header_t rh;
-
- tor_assert(circ);
- tor_assert(cell);
- tor_assert(recognized);
- tor_assert(cell_direction == CELL_DIRECTION_IN ||
- cell_direction == CELL_DIRECTION_OUT);
-
- if (cell_direction == CELL_DIRECTION_IN) {
- if (CIRCUIT_IS_ORIGIN(circ)) { /* We're at the beginning of the circuit.
- * We'll want to do layered decrypts. */
- crypt_path_t *thishop, *cpath = TO_ORIGIN_CIRCUIT(circ)->cpath;
- thishop = cpath;
- if (thishop->state != CPATH_STATE_OPEN) {
- log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "Relay cell before first created cell? Closing.");
- return -1;
- }
- do { /* Remember: cpath is in forward order, that is, first hop first. */
- tor_assert(thishop);
-
- /* decrypt one layer */
- relay_crypt_one_payload(thishop->b_crypto, cell->payload);
-
- relay_header_unpack(&rh, cell->payload);
- if (rh.recognized == 0) {
- /* it's possibly recognized. have to check digest to be sure. */
- if (relay_digest_matches(thishop->b_digest, cell)) {
- *recognized = 1;
- *layer_hint = thishop;
- return 0;
- }
- }
-
- thishop = thishop->next;
- } while (thishop != cpath && thishop->state == CPATH_STATE_OPEN);
- log_fn(LOG_PROTOCOL_WARN, LD_OR,
- "Incoming cell at client not recognized. Closing.");
- return -1;
- } else {
- /* We're in the middle. Encrypt one layer. */
- relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->p_crypto, cell->payload);
- }
- } else /* cell_direction == CELL_DIRECTION_OUT */ {
- /* We're in the middle. Decrypt one layer. */
-
- relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->n_crypto, cell->payload);
-
- relay_header_unpack(&rh, cell->payload);
- if (rh.recognized == 0) {
- /* it's possibly recognized. have to check digest to be sure. */
- if (relay_digest_matches(TO_OR_CIRCUIT(circ)->n_digest, cell)) {
- *recognized = 1;
- return 0;
- }
- }
- }
- return 0;
-}
-
/** Package a relay cell from an edge:
* - Encrypt it to the right layer
* - Append it to the appropriate cell_queue on <b>circ</b>.
@@ -501,7 +352,6 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
}
if (cell_direction == CELL_DIRECTION_OUT) {
- crypt_path_t *thishop; /* counter for repeated crypts */
chan = circ->n_chan;
if (!chan) {
log_warn(LD_BUG,"outgoing relay cell sent from %s:%d has n_chan==NULL."
@@ -524,20 +374,14 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
return 0; /* just drop it */
}
- relay_set_digest(layer_hint->f_digest, cell);
+ relay_encrypt_cell_outbound(cell, TO_ORIGIN_CIRCUIT(circ), layer_hint);
- thishop = layer_hint;
- /* moving from farthest to nearest hop */
- do {
- tor_assert(thishop);
- log_debug(LD_OR,"encrypting a layer of the relay cell.");
- relay_crypt_one_payload(thishop->f_crypto, cell->payload);
-
- thishop = thishop->prev;
- } while (thishop != TO_ORIGIN_CIRCUIT(circ)->cpath->prev);
+ /* Update circ written totals for control port */
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ ocirc->n_written_circ_bw = tor_add_u32_nowrap(ocirc->n_written_circ_bw,
+ CELL_PAYLOAD_SIZE);
} else { /* incoming cell */
- or_circuit_t *or_circ;
if (CIRCUIT_IS_ORIGIN(circ)) {
/* We should never package an _incoming_ cell from the circuit
* origin; that means we messed up somewhere. */
@@ -545,11 +389,9 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
assert_circuit_ok(circ);
return 0; /* just drop it */
}
- or_circ = TO_OR_CIRCUIT(circ);
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ relay_encrypt_cell_inbound(cell, or_circ);
chan = or_circ->p_chan;
- relay_set_digest(or_circ->p_digest, cell);
- /* encrypt one layer */
- relay_crypt_one_payload(or_circ->p_crypto, cell->payload);
}
++stats_n_relay_cells_relayed;
@@ -770,6 +612,10 @@ relay_send_command_from_edge_,(streamid_t stream_id, circuit_t *circ,
tor_free(commands);
smartlist_free(commands_list);
}
+
+ /* Let's assume we're well-behaved: Anything that we decide to send is
+ * valid, delivered data. */
+ circuit_sent_valid_data(origin_circ, rh.length);
}
if (circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer,
@@ -899,6 +745,9 @@ connection_ap_process_end_not_open(
}
}
+ /* This end cell is now valid. */
+ circuit_read_valid_data(circ, rh->length);
+
if (rh->length == 0) {
reason = END_STREAM_REASON_MISC;
}
@@ -1118,7 +967,12 @@ remap_event_helper(entry_connection_t *conn, const tor_addr_t *new_addr)
* header has already been parsed into <b>rh</b>. On success, set
* <b>addr_out</b> to the address we're connected to, and <b>ttl_out</b> to
* the ttl of that address, in seconds, and return 0. On failure, return
- * -1. */
+ * -1.
+ *
+ * Note that the resulting address can be UNSPEC if the connected cell had no
+ * address (as for a stream to an union service or a tunneled directory
+ * connection), and that the ttl can be absent (in which case <b>ttl_out</b>
+ * is set to -1). */
STATIC int
connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
tor_addr_t *addr_out, int *ttl_out)
@@ -1389,6 +1243,12 @@ connection_edge_process_resolved_cell(edge_connection_t *conn,
}
}
+ /* This is valid data at this point. Count it */
+ if (conn->on_circuit && CIRCUIT_IS_ORIGIN(conn->on_circuit)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(conn->on_circuit),
+ rh->length);
+ }
+
connection_ap_handshake_socks_got_resolved_cell(entry_conn,
errcode,
resolved_addresses);
@@ -1449,7 +1309,7 @@ connection_edge_process_relay_cell_not_open(
"after %d seconds.",
(unsigned)circ->n_circ_id,
rh->stream_id,
- (int)(time(NULL) - conn->base_.timestamp_lastread));
+ (int)(time(NULL) - conn->base_.timestamp_last_read_allowed));
if (connected_cell_parse(rh, cell, &addr, &ttl) < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_APP,
"Got a badly formatted connected cell. Closing.");
@@ -1458,6 +1318,9 @@ connection_edge_process_relay_cell_not_open(
return 0;
}
if (tor_addr_family(&addr) != AF_UNSPEC) {
+ /* The family is not UNSPEC: so we were given an address in the
+ * connected cell. (This is normal, except for BEGINDIR and onion
+ * service streams.) */
const sa_family_t family = tor_addr_family(&addr);
if (tor_addr_is_null(&addr) ||
(get_options()->ClientDNSRejectInternalAddresses &&
@@ -1524,6 +1387,9 @@ connection_edge_process_relay_cell_not_open(
entry_conn->pending_optimistic_data = NULL;
}
+ /* This is valid data at this point. Count it */
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh->length);
+
/* handle anything that might have queued */
if (connection_edge_package_raw_inbuf(conn, 1, NULL) < 0) {
/* (We already sent an end cell if possible) */
@@ -1556,7 +1422,7 @@ connection_edge_process_relay_cell_not_open(
*
* Return -reason if you want to warn and tear down the circuit, else 0.
*/
-static int
+STATIC int
connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
edge_connection_t *conn,
crypt_path_t *layer_hint)
@@ -1656,7 +1522,6 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
circ->dirreq_id = ++next_id;
TO_OR_CIRCUIT(circ)->p_chan->dirreq_id = circ->dirreq_id;
}
-
return connection_exit_begin_conn(cell, circ);
case RELAY_COMMAND_DATA:
++stats_n_data_cells_received;
@@ -1682,6 +1547,17 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
"stream_id. Dropping.");
return 0;
} else if (!conn) {
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ if (connection_half_edge_is_valid_data(ocirc->half_streams,
+ rh.stream_id)) {
+ circuit_read_valid_data(ocirc, rh.length);
+ log_info(domain,
+ "data cell on circ %u valid on half-closed "
+ "stream id %d", ocirc->global_identifier, rh.stream_id);
+ }
+ }
+
log_info(domain,"data cell dropped, unknown stream (streamid %d).",
rh.stream_id);
return 0;
@@ -1692,6 +1568,10 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
"(relay data) conn deliver_window below 0. Killing.");
return -END_CIRC_REASON_TORPROTOCOL;
}
+ /* Total all valid application bytes delivered */
+ if (CIRCUIT_IS_ORIGIN(circ) && rh.length > 0) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
+ }
stats_n_data_bytes_received += rh.length;
connection_buf_add((char*)(cell->payload + RELAY_HEADER_SIZE),
@@ -1719,6 +1599,20 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
reason = rh.length > 0 ?
get_uint8(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC;
if (!conn) {
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ if (connection_half_edge_is_valid_end(ocirc->half_streams,
+ rh.stream_id)) {
+
+ circuit_read_valid_data(ocirc, rh.length);
+ log_info(domain,
+ "end cell (%s) on circ %u valid on half-closed "
+ "stream id %d",
+ stream_end_reason_to_string(reason),
+ ocirc->global_identifier, rh.stream_id);
+ return 0;
+ }
+ }
log_info(domain,"end cell (%s) dropped, unknown stream.",
stream_end_reason_to_string(reason));
return 0;
@@ -1744,6 +1638,11 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
/* only mark it if not already marked. it's possible to
* get the 'end' right around when the client hangs up on us. */
connection_mark_and_flush(TO_CONN(conn));
+
+ /* Total all valid application bytes delivered */
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
+ }
}
return 0;
case RELAY_COMMAND_EXTEND:
@@ -1809,6 +1708,10 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
log_info(domain,"circuit_send_next_onion_skin() failed.");
return reason;
}
+ /* Total all valid bytes delivered. */
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
+ }
return 0;
case RELAY_COMMAND_TRUNCATE:
if (layer_hint) {
@@ -1846,7 +1749,14 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
"'truncated' unsupported at non-origin. Dropping.");
return 0;
}
- circuit_truncated(TO_ORIGIN_CIRCUIT(circ), layer_hint,
+
+ /* Count the truncated as valid, for completeness. The
+ * circuit is being torn down anyway, though. */
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ),
+ rh.length);
+ }
+ circuit_truncated(TO_ORIGIN_CIRCUIT(circ),
get_uint8(cell->payload + RELAY_HEADER_SIZE));
return 0;
case RELAY_COMMAND_CONNECTED:
@@ -1855,6 +1765,19 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
"'connected' unsupported while open. Closing circ.");
return -END_CIRC_REASON_TORPROTOCOL;
}
+
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ if (connection_half_edge_is_valid_connected(ocirc->half_streams,
+ rh.stream_id)) {
+ circuit_read_valid_data(ocirc, rh.length);
+ log_info(domain,
+ "connected cell on circ %u valid on half-closed "
+ "stream id %d", ocirc->global_identifier, rh.stream_id);
+ return 0;
+ }
+ }
+
log_info(domain,
"'connected' received on circid %u for streamid %d, "
"no conn attached anymore. Ignoring.",
@@ -1875,6 +1798,15 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
log_debug(LD_APP,"circ-level sendme at origin, packagewindow %d.",
layer_hint->package_window);
circuit_resume_edge_reading(circ, layer_hint);
+
+ /* We count circuit-level sendme's as valid delivered data because
+ * they are rate limited.
+ */
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ),
+ rh.length);
+ }
+
} else {
if (circ->package_window + CIRCWINDOW_INCREMENT >
CIRCWINDOW_START_MAX) {
@@ -1894,10 +1826,42 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
return 0;
}
if (!conn) {
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ if (connection_half_edge_is_valid_sendme(ocirc->half_streams,
+ rh.stream_id)) {
+ circuit_read_valid_data(ocirc, rh.length);
+ log_info(domain,
+ "sendme cell on circ %u valid on half-closed "
+ "stream id %d", ocirc->global_identifier, rh.stream_id);
+ }
+ }
+
log_info(domain,"sendme cell dropped, unknown stream (streamid %d).",
rh.stream_id);
return 0;
}
+
+ /* Don't allow the other endpoint to request more than our maximim
+ * (ie initial) stream SENDME window worth of data. Well-behaved
+ * stock clients will not request more than this max (as per the check
+ * in the while loop of connection_edge_consider_sending_sendme()).
+ */
+ if (conn->package_window + STREAMWINDOW_INCREMENT >
+ STREAMWINDOW_START_MAX) {
+ static struct ratelim_t stream_warn_ratelim = RATELIM_INIT(600);
+ log_fn_ratelim(&stream_warn_ratelim,LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Unexpected stream sendme cell. Closing circ (window %d).",
+ conn->package_window);
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+
+ /* At this point, the stream sendme is valid */
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ),
+ rh.length);
+ }
+
conn->package_window += STREAMWINDOW_INCREMENT;
log_debug(domain,"stream-level sendme, packagewindow now %d.",
conn->package_window);
@@ -1936,6 +1900,19 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
"'resolved' unsupported while open. Closing circ.");
return -END_CIRC_REASON_TORPROTOCOL;
}
+
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ if (connection_half_edge_is_valid_resolved(ocirc->half_streams,
+ rh.stream_id)) {
+ circuit_read_valid_data(ocirc, rh.length);
+ log_info(domain,
+ "resolved cell on circ %u valid on half-closed "
+ "stream id %d", ocirc->global_identifier, rh.stream_id);
+ return 0;
+ }
+ }
+
log_info(domain,
"'resolved' received, no conn attached anymore. Ignoring.");
return 0;
@@ -2399,13 +2376,6 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint)
}
}
-#ifdef ACTIVE_CIRCUITS_PARANOIA
-#define assert_cmux_ok_paranoid(chan) \
- assert_circuit_mux_okay(chan)
-#else
-#define assert_cmux_ok_paranoid(chan)
-#endif /* defined(ACTIVE_CIRCUITS_PARANOIA) */
-
/** The total number of cells we have allocated. */
static size_t total_cells_allocated = 0;
@@ -2617,6 +2587,7 @@ cell_queues_check_size(void)
{
time_t now = time(NULL);
size_t alloc = cell_queues_get_total_allocation();
+ alloc += half_streams_get_total_allocation();
alloc += buf_get_total_allocation();
alloc += tor_compress_get_total_allocation();
const size_t rend_cache_total = rend_cache_get_total_allocation();
@@ -2693,16 +2664,12 @@ update_circuit_on_cmux_(circuit_t *circ, cell_direction_t direction,
}
tor_assert(circuitmux_attached_circuit_direction(cmux, circ) == direction);
- assert_cmux_ok_paranoid(chan);
-
/* Update the number of cells we have for the circuit mux */
if (direction == CELL_DIRECTION_OUT) {
circuitmux_set_num_cells(cmux, circ, circ->n_chan_cells.n);
} else {
circuitmux_set_num_cells(cmux, circ, or_circ->p_chan_cells.n);
}
-
- assert_cmux_ok_paranoid(chan);
}
/** Remove all circuits from the cmux on <b>chan</b>.
@@ -2847,7 +2814,6 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max))
}
/* If it returns NULL, no cells left to send */
if (!circ) break;
- assert_cmux_ok_paranoid(chan);
if (circ->n_chan == chan) {
queue = &circ->n_chan_cells;
@@ -2951,8 +2917,6 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max))
}
/* Okay, we're done sending now */
- assert_cmux_ok_paranoid(chan);
-
return n_flushed;
}
@@ -3013,7 +2977,7 @@ relay_consensus_has_changed(const networkstatus_t *ns)
/** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b>
* transmitting in <b>direction</b>.
*
- * The given <b>cell</b> is copied over the circuit queue so the caller must
+ * The given <b>cell</b> is copied onto the circuit queue so the caller must
* cleanup the memory.
*
* This function is part of the fast path. */
@@ -3168,17 +3132,6 @@ circuit_clear_cell_queue(circuit_t *circ, channel_t *chan)
update_circuit_on_cmux(circ, direction);
}
-/** Fail with an assert if the circuit mux on chan is corrupt
- */
-void
-assert_circuit_mux_okay(channel_t *chan)
-{
- tor_assert(chan);
- tor_assert(chan->cmux);
-
- circuitmux_assert_okay(chan->cmux);
-}
-
/** Return 1 if we shouldn't restart reading on this circuit, even if
* we get a SENDME. Else return 0.
*/
@@ -3191,4 +3144,3 @@ circuit_queue_streams_are_blocked(circuit_t *circ)
return circ->streams_blocked_on_p_chan;
}
}
-
diff --git a/src/or/relay.h b/src/or/relay.h
index e96639170c..ce0969b46c 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -78,7 +78,6 @@ void destroy_cell_queue_append(destroy_cell_queue_t *queue,
void channel_unlink_all_circuits(channel_t *chan, smartlist_t *detached_out);
MOCK_DECL(int, channel_flush_from_first_active_circuit,
(channel_t *chan, int max));
-void assert_circuit_mux_okay(channel_t *chan);
void update_circuit_on_cmux_(circuit_t *circ, cell_direction_t direction,
const char *file, int lineno);
#define update_circuit_on_cmux(circ, direction) \
@@ -92,9 +91,6 @@ void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan);
void stream_choice_seed_weak_rng(void);
-int relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
- crypt_path_t **layer_hint, char *recognized);
-
circid_t packed_cell_get_circid(const packed_cell_t *cell, int wide_circ_ids);
#ifdef RELAY_PRIVATE
@@ -118,6 +114,10 @@ STATIC packed_cell_t *packed_cell_new(void);
STATIC packed_cell_t *cell_queue_pop(cell_queue_t *queue);
STATIC destroy_cell_t *destroy_cell_queue_pop(destroy_cell_queue_t *queue);
STATIC int cell_queues_check_size(void);
+STATIC int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
+ edge_connection_t *conn,
+ crypt_path_t *layer_hint);
+
#endif /* defined(RELAY_PRIVATE) */
#endif /* !defined(TOR_RELAY_H) */
diff --git a/src/or/relay_crypto.c b/src/or/relay_crypto.c
new file mode 100644
index 0000000000..530c8e5828
--- /dev/null
+++ b/src/or/relay_crypto.c
@@ -0,0 +1,327 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "config.h"
+#include "crypto_util.h"
+#include "hs_ntor.h" // for HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN
+#include "relay.h"
+#include "relay_crypto.h"
+
+/** Update digest from the payload of cell. Assign integrity part to
+ * cell.
+ */
+static void
+relay_set_digest(crypto_digest_t *digest, cell_t *cell)
+{
+ char integrity[4];
+ relay_header_t rh;
+
+ crypto_digest_add_bytes(digest, (char*)cell->payload, CELL_PAYLOAD_SIZE);
+ crypto_digest_get_digest(digest, integrity, 4);
+// log_fn(LOG_DEBUG,"Putting digest of %u %u %u %u into relay cell.",
+// integrity[0], integrity[1], integrity[2], integrity[3]);
+ relay_header_unpack(&rh, cell->payload);
+ memcpy(rh.integrity, integrity, 4);
+ relay_header_pack(cell->payload, &rh);
+}
+
+/** Does the digest for this circuit indicate that this cell is for us?
+ *
+ * Update digest from the payload of cell (with the integrity part set
+ * to 0). If the integrity part is valid, return 1, else restore digest
+ * and cell to their original state and return 0.
+ */
+static int
+relay_digest_matches(crypto_digest_t *digest, cell_t *cell)
+{
+ uint32_t received_integrity, calculated_integrity;
+ relay_header_t rh;
+ crypto_digest_checkpoint_t backup_digest;
+
+ crypto_digest_checkpoint(&backup_digest, digest);
+
+ relay_header_unpack(&rh, cell->payload);
+ memcpy(&received_integrity, rh.integrity, 4);
+ memset(rh.integrity, 0, 4);
+ relay_header_pack(cell->payload, &rh);
+
+// log_fn(LOG_DEBUG,"Reading digest of %u %u %u %u from relay cell.",
+// received_integrity[0], received_integrity[1],
+// received_integrity[2], received_integrity[3]);
+
+ crypto_digest_add_bytes(digest, (char*) cell->payload, CELL_PAYLOAD_SIZE);
+ crypto_digest_get_digest(digest, (char*) &calculated_integrity, 4);
+
+ int rv = 1;
+
+ if (calculated_integrity != received_integrity) {
+// log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing.");
+// (%d vs %d).", received_integrity, calculated_integrity);
+ /* restore digest to its old form */
+ crypto_digest_restore(digest, &backup_digest);
+ /* restore the relay header */
+ memcpy(rh.integrity, &received_integrity, 4);
+ relay_header_pack(cell->payload, &rh);
+ rv = 0;
+ }
+
+ memwipe(&backup_digest, 0, sizeof(backup_digest));
+ return rv;
+}
+
+/** Apply <b>cipher</b> to CELL_PAYLOAD_SIZE bytes of <b>in</b>
+ * (in place).
+ *
+ * Note that we use the same operation for encrypting and for decrypting.
+ */
+static void
+relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in)
+{
+ crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
+}
+
+/** Do the appropriate en/decryptions for <b>cell</b> arriving on
+ * <b>circ</b> in direction <b>cell_direction</b>.
+ *
+ * If cell_direction == CELL_DIRECTION_IN:
+ * - If we're at the origin (we're the OP), for hops 1..N,
+ * decrypt cell. If recognized, stop.
+ * - Else (we're not the OP), encrypt one hop. Cell is not recognized.
+ *
+ * If cell_direction == CELL_DIRECTION_OUT:
+ * - decrypt one hop. Check if recognized.
+ *
+ * If cell is recognized, set *recognized to 1, and set
+ * *layer_hint to the hop that recognized it.
+ *
+ * Return -1 to indicate that we should mark the circuit for close,
+ * else return 0.
+ */
+int
+relay_decrypt_cell(circuit_t *circ, cell_t *cell,
+ cell_direction_t cell_direction,
+ crypt_path_t **layer_hint, char *recognized)
+{
+ relay_header_t rh;
+
+ tor_assert(circ);
+ tor_assert(cell);
+ tor_assert(recognized);
+ tor_assert(cell_direction == CELL_DIRECTION_IN ||
+ cell_direction == CELL_DIRECTION_OUT);
+
+ if (cell_direction == CELL_DIRECTION_IN) {
+ if (CIRCUIT_IS_ORIGIN(circ)) { /* We're at the beginning of the circuit.
+ * We'll want to do layered decrypts. */
+ crypt_path_t *thishop, *cpath = TO_ORIGIN_CIRCUIT(circ)->cpath;
+ thishop = cpath;
+ if (thishop->state != CPATH_STATE_OPEN) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Relay cell before first created cell? Closing.");
+ return -1;
+ }
+ do { /* Remember: cpath is in forward order, that is, first hop first. */
+ tor_assert(thishop);
+
+ /* decrypt one layer */
+ relay_crypt_one_payload(thishop->crypto.b_crypto, cell->payload);
+
+ relay_header_unpack(&rh, cell->payload);
+ if (rh.recognized == 0) {
+ /* it's possibly recognized. have to check digest to be sure. */
+ if (relay_digest_matches(thishop->crypto.b_digest, cell)) {
+ *recognized = 1;
+ *layer_hint = thishop;
+ return 0;
+ }
+ }
+
+ thishop = thishop->next;
+ } while (thishop != cpath && thishop->state == CPATH_STATE_OPEN);
+ log_fn(LOG_PROTOCOL_WARN, LD_OR,
+ "Incoming cell at client not recognized. Closing.");
+ return -1;
+ } else {
+ relay_crypto_t *crypto = &TO_OR_CIRCUIT(circ)->crypto;
+ /* We're in the middle. Encrypt one layer. */
+ relay_crypt_one_payload(crypto->b_crypto, cell->payload);
+ }
+ } else /* cell_direction == CELL_DIRECTION_OUT */ {
+ /* We're in the middle. Decrypt one layer. */
+ relay_crypto_t *crypto = &TO_OR_CIRCUIT(circ)->crypto;
+
+ relay_crypt_one_payload(crypto->f_crypto, cell->payload);
+
+ relay_header_unpack(&rh, cell->payload);
+ if (rh.recognized == 0) {
+ /* it's possibly recognized. have to check digest to be sure. */
+ if (relay_digest_matches(crypto->f_digest, cell)) {
+ *recognized = 1;
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * Encrypt a cell <b>cell</b> that we are creating, and sending outbound on
+ * <b>circ</b> until the hop corresponding to <b>layer_hint</b>.
+ *
+ * The integrity field and recognized field of <b>cell</b>'s relay headers
+ * must be set to zero.
+ */
+void
+relay_encrypt_cell_outbound(cell_t *cell,
+ origin_circuit_t *circ,
+ crypt_path_t *layer_hint)
+{
+ crypt_path_t *thishop; /* counter for repeated crypts */
+ relay_set_digest(layer_hint->crypto.f_digest, cell);
+
+ thishop = layer_hint;
+ /* moving from farthest to nearest hop */
+ do {
+ tor_assert(thishop);
+ log_debug(LD_OR,"encrypting a layer of the relay cell.");
+ relay_crypt_one_payload(thishop->crypto.f_crypto, cell->payload);
+
+ thishop = thishop->prev;
+ } while (thishop != circ->cpath->prev);
+}
+
+/**
+ * Encrypt a cell <b>cell</b> that we are creating, and sending on
+ * <b>circuit</b> to the origin.
+ *
+ * The integrity field and recognized field of <b>cell</b>'s relay headers
+ * must be set to zero.
+ */
+void
+relay_encrypt_cell_inbound(cell_t *cell,
+ or_circuit_t *or_circ)
+{
+ relay_set_digest(or_circ->crypto.b_digest, cell);
+ /* encrypt one layer */
+ relay_crypt_one_payload(or_circ->crypto.b_crypto, cell->payload);
+}
+
+/**
+ * Release all storage held inside <b>crypto</b>, but do not free
+ * <b>crypto</b> itself: it lives inside another object.
+ */
+void
+relay_crypto_clear(relay_crypto_t *crypto)
+{
+ if (BUG(!crypto))
+ return;
+ crypto_cipher_free(crypto->f_crypto);
+ crypto_cipher_free(crypto->b_crypto);
+ crypto_digest_free(crypto->f_digest);
+ crypto_digest_free(crypto->b_digest);
+}
+
+/** Initialize <b>crypto</b> from the key material in key_data.
+ *
+ * If <b>is_hs_v3</b> is set, this cpath will be used for next gen hidden
+ * service circuits and <b>key_data</b> must be at least
+ * HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN bytes in length.
+ *
+ * If <b>is_hs_v3</b> is not set, key_data must contain CPATH_KEY_MATERIAL_LEN
+ * bytes, which are used as follows:
+ * - 20 to initialize f_digest
+ * - 20 to initialize b_digest
+ * - 16 to key f_crypto
+ * - 16 to key b_crypto
+ *
+ * (If 'reverse' is true, then f_XX and b_XX are swapped.)
+ *
+ * Return 0 if init was successful, else -1 if it failed.
+ */
+int
+relay_crypto_init(relay_crypto_t *crypto,
+ const char *key_data, size_t key_data_len,
+ int reverse, int is_hs_v3)
+{
+ crypto_digest_t *tmp_digest;
+ crypto_cipher_t *tmp_crypto;
+ size_t digest_len = 0;
+ size_t cipher_key_len = 0;
+
+ tor_assert(crypto);
+ tor_assert(key_data);
+ tor_assert(!(crypto->f_crypto || crypto->b_crypto ||
+ crypto->f_digest || crypto->b_digest));
+
+ /* Basic key size validation */
+ if (is_hs_v3 && BUG(key_data_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
+ goto err;
+ } else if (!is_hs_v3 && BUG(key_data_len != CPATH_KEY_MATERIAL_LEN)) {
+ goto err;
+ }
+
+ /* If we are using this crypto for next gen onion services use SHA3-256,
+ otherwise use good ol' SHA1 */
+ if (is_hs_v3) {
+ digest_len = DIGEST256_LEN;
+ cipher_key_len = CIPHER256_KEY_LEN;
+ crypto->f_digest = crypto_digest256_new(DIGEST_SHA3_256);
+ crypto->b_digest = crypto_digest256_new(DIGEST_SHA3_256);
+ } else {
+ digest_len = DIGEST_LEN;
+ cipher_key_len = CIPHER_KEY_LEN;
+ crypto->f_digest = crypto_digest_new();
+ crypto->b_digest = crypto_digest_new();
+ }
+
+ tor_assert(digest_len != 0);
+ tor_assert(cipher_key_len != 0);
+ const int cipher_key_bits = (int) cipher_key_len * 8;
+
+ crypto_digest_add_bytes(crypto->f_digest, key_data, digest_len);
+ crypto_digest_add_bytes(crypto->b_digest, key_data+digest_len, digest_len);
+
+ crypto->f_crypto = crypto_cipher_new_with_bits(key_data+(2*digest_len),
+ cipher_key_bits);
+ if (!crypto->f_crypto) {
+ log_warn(LD_BUG,"Forward cipher initialization failed.");
+ goto err;
+ }
+
+ crypto->b_crypto = crypto_cipher_new_with_bits(
+ key_data+(2*digest_len)+cipher_key_len,
+ cipher_key_bits);
+ if (!crypto->b_crypto) {
+ log_warn(LD_BUG,"Backward cipher initialization failed.");
+ goto err;
+ }
+
+ if (reverse) {
+ tmp_digest = crypto->f_digest;
+ crypto->f_digest = crypto->b_digest;
+ crypto->b_digest = tmp_digest;
+ tmp_crypto = crypto->f_crypto;
+ crypto->f_crypto = crypto->b_crypto;
+ crypto->b_crypto = tmp_crypto;
+ }
+
+ return 0;
+ err:
+ relay_crypto_clear(crypto);
+ return -1;
+}
+
+/** Assert that <b>crypto</b> is valid and set. */
+void
+relay_crypto_assert_ok(const relay_crypto_t *crypto)
+{
+ tor_assert(crypto->f_crypto);
+ tor_assert(crypto->b_crypto);
+ tor_assert(crypto->f_digest);
+ tor_assert(crypto->b_digest);
+}
+
diff --git a/src/or/relay_crypto.h b/src/or/relay_crypto.h
new file mode 100644
index 0000000000..66ae02cee9
--- /dev/null
+++ b/src/or/relay_crypto.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file relay.h
+ * \brief Header file for relay.c.
+ **/
+
+#ifndef TOR_RELAY_CRYPTO_H
+#define TOR_RELAY_CRYPTO_H
+
+int relay_crypto_init(relay_crypto_t *crypto,
+ const char *key_data, size_t key_data_len,
+ int reverse, int is_hs_v3);
+
+int relay_decrypt_cell(circuit_t *circ, cell_t *cell,
+ cell_direction_t cell_direction,
+ crypt_path_t **layer_hint, char *recognized);
+void relay_encrypt_cell_outbound(cell_t *cell, origin_circuit_t *or_circ,
+ crypt_path_t *layer_hint);
+void relay_encrypt_cell_inbound(cell_t *cell, or_circuit_t *or_circ);
+
+void relay_crypto_clear(relay_crypto_t *crypto);
+
+void relay_crypto_assert_ok(const relay_crypto_t *crypto);
+
+#endif /* !defined(TOR_RELAY_CRYPTO_H) */
+
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index e61207f2ee..74118f8f37 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -15,10 +15,13 @@
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
+#include "control.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "directory.h"
-#include "hs_common.h"
#include "hs_circuit.h"
#include "hs_client.h"
+#include "hs_common.h"
#include "main.h"
#include "networkstatus.h"
#include "nodelist.h"
@@ -29,7 +32,6 @@
#include "router.h"
#include "routerlist.h"
#include "routerset.h"
-#include "control.h"
static extend_info_t *rend_client_get_random_intro_impl(
const rend_cache_entry_t *rend_query,
@@ -924,8 +926,8 @@ rend_client_desc_trynow(const char *query)
/* restart their timeout values, so they get a fair shake at
* connecting to the hidden service. */
base_conn->timestamp_created = now;
- base_conn->timestamp_lastread = now;
- base_conn->timestamp_lastwritten = now;
+ base_conn->timestamp_last_read_allowed = now;
+ base_conn->timestamp_last_write_allowed = now;
connection_ap_mark_as_pending_circuit(conn);
} else { /* 404, or fetch didn't get that far */
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 230da4be5c..f3fa2f64d1 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -12,20 +12,23 @@
#include "or.h"
#include "circuitbuild.h"
+#include "circuituse.h"
#include "config.h"
#include "control.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
+#include "hs_client.h"
#include "hs_common.h"
+#include "hs_intropoint.h"
+#include "networkstatus.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendmid.h"
-#include "hs_intropoint.h"
-#include "hs_client.h"
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
-#include "networkstatus.h"
/** Return 0 if one and two are the same service ids, else -1 or 1 */
int
@@ -807,6 +810,11 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
tor_fragile_assert();
}
+ if (r == 0 && origin_circ) {
+ /* This was a valid cell. Count it as delivered + overhead. */
+ circuit_read_valid_data(origin_circ, length);
+ }
+
if (r == -2)
log_info(LD_PROTOCOL, "Dropping cell (type %d) for wrong circuit type.",
command);
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index c4a34ca62c..521023179a 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -227,9 +227,9 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request,
(unsigned)circ->p_circ_id);
if (circ->base_.purpose != CIRCUIT_PURPOSE_OR) {
- log_warn(LD_PROTOCOL,
- "Tried to establish rendezvous on non-OR circuit with purpose %s",
- circuit_purpose_to_string(circ->base_.purpose));
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Tried to establish rendezvous on non-OR circuit with purpose %s",
+ circuit_purpose_to_string(circ->base_.purpose));
goto err;
}
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index ac86c143d1..92c323b10d 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -16,6 +16,8 @@
#include "circuituse.h"
#include "config.h"
#include "control.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "directory.h"
#include "hs_common.h"
#include "hs_config.h"
@@ -348,6 +350,13 @@ rend_add_service(smartlist_t *service_list, rend_service_t *service)
/* The service passed all the checks */
tor_assert(s_list);
smartlist_add(s_list, service);
+
+ /* Notify that our global service list has changed only if this new service
+ * went into our global list. If not, when we move service from the staging
+ * list to the new list, a notify is triggered. */
+ if (s_list == rend_service_list) {
+ hs_service_map_has_changed();
+ }
return 0;
}
@@ -609,6 +618,8 @@ rend_service_prune_list_impl_(void)
circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED);
}
smartlist_free(surviving_services);
+ /* Notify that our global service list has changed. */
+ hs_service_map_has_changed();
}
/* Try to prune our main service list using the temporary one that we just
@@ -959,6 +970,8 @@ rend_service_del_ephemeral(const char *service_id)
}
} SMARTLIST_FOREACH_END(circ);
smartlist_remove(rend_service_list, s);
+ /* Notify that we just removed a service from our global list. */
+ hs_service_map_has_changed();
rend_service_free(s);
log_debug(LD_CONFIG, "Removed ephemeral Onion Service: %s", service_id);
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 9d7baf0765..aa31ef973c 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2017, The Tor Project, Inc. */
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -78,6 +78,7 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
+#include "crypto_rand.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "rephist.h"
@@ -85,9 +86,8 @@
#include "routerlist.h"
#include "ht.h"
#include "channelpadding.h"
-
-#include "channelpadding.h"
#include "connection_or.h"
+#include "statefile.h"
static void bw_arrays_init(void);
static void predicted_ports_alloc(void);
@@ -1208,8 +1208,8 @@ find_largest_max(bw_array_t *b)
*
* Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE.
*/
-int
-rep_hist_bandwidth_assess(void)
+MOCK_IMPL(int,
+rep_hist_bandwidth_assess,(void))
{
uint64_t w,r;
r = find_largest_max(read_array);
diff --git a/src/or/rephist.h b/src/or/rephist.h
index 37a91fd9b6..9ef49d19c0 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2017, The Tor Project, Inc. */
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -22,7 +22,7 @@ void rep_hist_make_router_pessimal(const char *id, time_t when);
void rep_hist_note_dir_bytes_read(uint64_t num_bytes, time_t when);
void rep_hist_note_dir_bytes_written(uint64_t num_bytes, time_t when);
-int rep_hist_bandwidth_assess(void);
+MOCK_DECL(int, rep_hist_bandwidth_assess, (void));
char *rep_hist_get_bandwidth_lines(void);
void rep_hist_update_state(or_state_t *state);
int rep_hist_load_state(or_state_t *state, char **err);
@@ -139,4 +139,3 @@ void rep_hist_prep_published_padding_counts(time_t now);
void rep_hist_padding_count_timers(uint64_t num_timers);
#endif /* !defined(TOR_REPHIST_H) */
-
diff --git a/src/or/router.c b/src/or/router.c
index a3d7cd373c..03959e907d 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -13,6 +13,8 @@
#include "config.h"
#include "connection.h"
#include "control.h"
+#include "crypto_rand.h"
+#include "crypto_util.h"
#include "crypto_curve25519.h"
#include "directory.h"
#include "dirserv.h"
@@ -35,6 +37,8 @@
#include "transports.h"
#include "routerset.h"
+#include "dirauth/mode.h"
+
/**
* \file router.c
* \brief Miscellaneous relay functionality, including RSA key maintenance,
@@ -103,6 +107,64 @@ static authority_cert_t *legacy_key_certificate = NULL;
* used by tor-gencert to sign new signing keys and make new key
* certificates. */
+const char *format_node_description(char *buf,
+ const char *id_digest,
+ int is_named,
+ const char *nickname,
+ const tor_addr_t *addr,
+ uint32_t addr32h);
+
+/** Return a readonly string with human readable description
+ * of <b>err</b>.
+ */
+const char *
+routerinfo_err_to_string(int err)
+{
+ switch (err) {
+ case TOR_ROUTERINFO_ERROR_NO_EXT_ADDR:
+ return "No known exit address yet";
+ case TOR_ROUTERINFO_ERROR_CANNOT_PARSE:
+ return "Cannot parse descriptor";
+ case TOR_ROUTERINFO_ERROR_NOT_A_SERVER:
+ return "Not running in server mode";
+ case TOR_ROUTERINFO_ERROR_DIGEST_FAILED:
+ return "Key digest failed";
+ case TOR_ROUTERINFO_ERROR_CANNOT_GENERATE:
+ return "Cannot generate descriptor";
+ case TOR_ROUTERINFO_ERROR_DESC_REBUILDING:
+ return "Descriptor still rebuilding - not ready yet";
+ }
+
+ log_warn(LD_BUG, "unknown routerinfo error %d - shouldn't happen", err);
+ tor_assert_unreached();
+
+ return "Unknown error";
+}
+
+/** Return true if we expect given error to be transient.
+ * Return false otherwise.
+ */
+int
+routerinfo_err_is_transient(int err)
+{
+ switch (err) {
+ case TOR_ROUTERINFO_ERROR_NO_EXT_ADDR:
+ return 1;
+ case TOR_ROUTERINFO_ERROR_CANNOT_PARSE:
+ return 1;
+ case TOR_ROUTERINFO_ERROR_NOT_A_SERVER:
+ return 0;
+ case TOR_ROUTERINFO_ERROR_DIGEST_FAILED:
+ return 0; // XXX: bug?
+ case TOR_ROUTERINFO_ERROR_CANNOT_GENERATE:
+ return 1;
+ case TOR_ROUTERINFO_ERROR_DESC_REBUILDING:
+ return 1;
+ }
+
+ return 0;
+}
+
/** Replace the current onion key with <b>k</b>. Does not affect
* lastonionkey; to update lastonionkey correctly, call rotate_onion_key().
*/
@@ -1234,7 +1296,8 @@ check_whether_dirport_reachable(const or_options_t *options)
/* XXX Should this be increased? */
#define MIN_BW_TO_ADVERTISE_DIRSERVER 51200
-/** Return true iff we have enough configured bandwidth to cache directory
+/** Return true iff we have enough configured bandwidth to advertise or
+ * automatically provide directory services from cache directory
* information. */
static int
router_has_bandwidth_to_be_dirserver(const or_options_t *options)
@@ -1257,7 +1320,7 @@ router_has_bandwidth_to_be_dirserver(const or_options_t *options)
* MIN_BW_TO_ADVERTISE_DIRSERVER, don't bother trying to serve requests.
*/
static int
-router_should_be_directory_server(const or_options_t *options, int dir_port)
+router_should_be_dirserver(const or_options_t *options, int dir_port)
{
static int advertising=1; /* start out assuming we will advertise */
int new_choice=1;
@@ -1362,7 +1425,7 @@ decide_to_advertise_dir_impl(const or_options_t *options,
/* Part two: consider config options that could make us choose to
* publish or not publish that the user might find surprising. */
- return router_should_be_directory_server(options, dir_port);
+ return router_should_be_dirserver(options, dir_port);
}
/** Front-end to decide_to_advertise_dir_impl(): return 0 if we don't want to
@@ -1370,7 +1433,7 @@ decide_to_advertise_dir_impl(const or_options_t *options,
* DirPort we want to advertise.
*/
static int
-decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
+router_should_advertise_dirport(const or_options_t *options, uint16_t dir_port)
{
/* supports_tunnelled_dir_requests is not relevant, pass 0 */
return decide_to_advertise_dir_impl(options, dir_port, 0) ? dir_port : 0;
@@ -1380,7 +1443,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
* advertise the fact that we support begindir requests, else return 1.
*/
static int
-decide_to_advertise_begindir(const or_options_t *options,
+router_should_advertise_begindir(const or_options_t *options,
int supports_tunnelled_dir_requests)
{
/* dir_port is not relevant, pass 0 */
@@ -1413,26 +1476,17 @@ extend_info_from_router(const routerinfo_t *r)
&ap.addr, ap.port);
}
-/** Some time has passed, or we just got new directory information.
- * See if we currently believe our ORPort or DirPort to be
- * unreachable. If so, launch a new test for it.
- *
- * For ORPort, we simply try making a circuit that ends at ourselves.
- * Success is noticed in onionskin_answer().
- *
- * For DirPort, we make a connection via Tor to our DirPort and ask
- * for our own server descriptor.
- * Success is noticed in connection_dir_client_reached_eof().
+/**See if we currently believe our ORPort or DirPort to be
+ * unreachable. If so, return 1 else return 0.
*/
-void
-consider_testing_reachability(int test_or, int test_dir)
+static int
+router_should_check_reachability(int test_or, int test_dir)
{
const routerinfo_t *me = router_get_my_routerinfo();
const or_options_t *options = get_options();
- int orport_reachable = check_whether_orport_reachable(options);
- tor_addr_t addr;
+
if (!me)
- return;
+ return 0;
if (routerset_contains_router(options->ExcludeNodes, me, -1) &&
options->StrictNodes) {
@@ -1447,43 +1501,66 @@ consider_testing_reachability(int test_or, int test_dir)
"We cannot learn whether we are usable, and will not "
"be able to advertise ourself.");
}
- return;
+ return 0;
}
+ return 1;
+}
+
+/** Some time has passed, or we just got new directory information.
+ * See if we currently believe our ORPort or DirPort to be
+ * unreachable. If so, launch a new test for it.
+ *
+ * For ORPort, we simply try making a circuit that ends at ourselves.
+ * Success is noticed in onionskin_answer().
+ *
+ * For DirPort, we make a connection via Tor to our DirPort and ask
+ * for our own server descriptor.
+ * Success is noticed in connection_dir_client_reached_eof().
+ */
+void
+router_do_reachability_checks(int test_or, int test_dir)
+{
+ const routerinfo_t *me = router_get_my_routerinfo();
+ const or_options_t *options = get_options();
+ int orport_reachable = check_whether_orport_reachable(options);
+ tor_addr_t addr;
+
+ if (router_should_check_reachability(test_or, test_dir)) {
+ if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) {
+ extend_info_t *ei = extend_info_from_router(me);
+ /* XXX IPv6 self testing */
+ log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
+ !orport_reachable ? "reachability" : "bandwidth",
+ fmt_addr32(me->addr), me->or_port);
+ circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
+ CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
+ extend_info_free(ei);
+ }
- if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) {
- extend_info_t *ei = extend_info_from_router(me);
/* XXX IPv6 self testing */
- log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
- !orport_reachable ? "reachability" : "bandwidth",
- fmt_addr32(me->addr), me->or_port);
- circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
- CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
- extend_info_free(ei);
- }
-
- /* XXX IPv6 self testing */
- tor_addr_from_ipv4h(&addr, me->addr);
- if (test_dir && !check_whether_dirport_reachable(options) &&
- !connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, me->dir_port,
- DIR_PURPOSE_FETCH_SERVERDESC)) {
- tor_addr_port_t my_orport, my_dirport;
- memcpy(&my_orport.addr, &addr, sizeof(addr));
- memcpy(&my_dirport.addr, &addr, sizeof(addr));
- my_orport.port = me->or_port;
- my_dirport.port = me->dir_port;
- /* ask myself, via tor, for my server descriptor. */
- directory_request_t *req =
- directory_request_new(DIR_PURPOSE_FETCH_SERVERDESC);
- directory_request_set_or_addr_port(req, &my_orport);
- directory_request_set_dir_addr_port(req, &my_dirport);
- directory_request_set_directory_id_digest(req,
+ tor_addr_from_ipv4h(&addr, me->addr);
+ if (test_dir && !check_whether_dirport_reachable(options) &&
+ !connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &addr, me->dir_port,
+ DIR_PURPOSE_FETCH_SERVERDESC)) {
+ tor_addr_port_t my_orport, my_dirport;
+ memcpy(&my_orport.addr, &addr, sizeof(addr));
+ memcpy(&my_dirport.addr, &addr, sizeof(addr));
+ my_orport.port = me->or_port;
+ my_dirport.port = me->dir_port;
+ /* ask myself, via tor, for my server descriptor. */
+ directory_request_t *req =
+ directory_request_new(DIR_PURPOSE_FETCH_SERVERDESC);
+ directory_request_set_or_addr_port(req, &my_orport);
+ directory_request_set_dir_addr_port(req, &my_dirport);
+ directory_request_set_directory_id_digest(req,
me->cache_info.identity_digest);
- // ask via an anon circuit, connecting to our dirport.
- directory_request_set_indirection(req, DIRIND_ANON_DIRPORT);
- directory_request_set_resource(req, "authority.z");
- directory_initiate_request(req);
- directory_request_free(req);
+ // ask via an anon circuit, connecting to our dirport.
+ directory_request_set_indirection(req, DIRIND_ANON_DIRPORT);
+ directory_request_set_resource(req, "authority.z");
+ directory_initiate_request(req);
+ directory_request_free(req);
+ }
}
}
@@ -1528,7 +1605,7 @@ router_dirport_found_reachable(void)
&& check_whether_orport_reachable(options) ?
" Publishing server descriptor." : "");
can_reach_dir_port = 1;
- if (decide_to_advertise_dirport(options, me->dir_port)) {
+ if (router_should_advertise_dirport(options, me->dir_port)) {
mark_my_descriptor_dirty("DirPort found reachable");
/* This is a significant enough change to upload immediately,
* at least in a test network */
@@ -1573,30 +1650,31 @@ router_perform_bandwidth_test(int num_circs, time_t now)
}
}
-/** Return true iff our network is in some sense disabled: either we're
- * hibernating, entering hibernation, or the network is turned off with
- * DisableNetwork. */
+/** Return true iff our network is in some sense disabled or shutting down:
+ * either we're hibernating, entering hibernation, or the network is turned
+ * off with DisableNetwork. */
int
net_is_disabled(void)
{
return get_options()->DisableNetwork || we_are_hibernating();
}
-/** Return true iff we believe ourselves to be an authoritative
- * directory server.
- */
+/** Return true iff our network is in some sense "completely disabled" either
+ * we're fully hibernating or the network is turned off with
+ * DisableNetwork. */
int
-authdir_mode(const or_options_t *options)
+net_is_completely_disabled(void)
{
- return options->AuthoritativeDir != 0;
+ return get_options()->DisableNetwork || we_are_fully_hibernating();
}
-/** Return true iff we believe ourselves to be a v3 authoritative
+
+/** Return true iff we believe ourselves to be an authoritative
* directory server.
*/
int
-authdir_mode_v3(const or_options_t *options)
+authdir_mode(const or_options_t *options)
{
- return authdir_mode(options) && options->V3AuthoritativeDir != 0;
+ return options->AuthoritativeDir != 0;
}
/** Return true iff we are an authoritative directory server that is
* authoritative about receiving and serving descriptors of type
@@ -1999,10 +2077,43 @@ router_is_me(const routerinfo_t *router)
MOCK_IMPL(const routerinfo_t *,
router_get_my_routerinfo,(void))
{
- if (!server_mode(get_options()))
+ return router_get_my_routerinfo_with_err(NULL);
+}
+
+/** Return routerinfo of this OR. Rebuild it from
+ * scratch if needed. Set <b>*err</b> to 0 on success or to
+ * appropriate TOR_ROUTERINFO_ERROR_* value on failure.
+ */
+MOCK_IMPL(const routerinfo_t *,
+router_get_my_routerinfo_with_err,(int *err))
+{
+ if (!server_mode(get_options())) {
+ if (err)
+ *err = TOR_ROUTERINFO_ERROR_NOT_A_SERVER;
+
return NULL;
- if (router_rebuild_descriptor(0))
+ }
+
+ if (!desc_clean_since) {
+ int rebuild_err = router_rebuild_descriptor(0);
+ if (rebuild_err < 0) {
+ if (err)
+ *err = rebuild_err;
+
+ return NULL;
+ }
+ }
+
+ if (!desc_routerinfo) {
+ if (err)
+ *err = TOR_ROUTERINFO_ERROR_DESC_REBUILDING;
+
return NULL;
+ }
+
+ if (err)
+ *err = 0;
+
return desc_routerinfo;
}
@@ -2179,7 +2290,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
if (router_pick_published_address(options, &addr, 0) < 0) {
log_warn(LD_CONFIG, "Don't know my address while generating descriptor");
- return -1;
+ return TOR_ROUTERINFO_ERROR_NO_EXT_ADDR;
}
/* Log a message if the address in the descriptor doesn't match the ORPort
@@ -2235,7 +2346,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
if (crypto_pk_get_digest(ri->identity_pkey,
ri->cache_info.identity_digest)<0) {
routerinfo_free(ri);
- return -1;
+ return TOR_ROUTERINFO_ERROR_DIGEST_FAILED;
}
ri->cache_info.signing_key_cert =
tor_cert_dup(get_master_signing_key_cert());
@@ -2251,6 +2362,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
/* and compute ri->bandwidthburst similarly */
ri->bandwidthburst = get_effective_bwburst(options);
+ /* Report bandwidth, unless we're hibernating or shutting down */
ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess();
if (dns_seems_to_be_broken() || has_dns_init_failed()) {
@@ -2368,7 +2480,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
log_warn(LD_BUG, "Couldn't generate router descriptor.");
routerinfo_free(ri);
extrainfo_free(ei);
- return -1;
+ return TOR_ROUTERINFO_ERROR_CANNOT_GENERATE;
}
ri->cache_info.signed_descriptor_len =
strlen(ri->cache_info.signed_descriptor_body);
@@ -2411,6 +2523,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
int
router_rebuild_descriptor(int force)
{
+ int err = 0;
routerinfo_t *ri;
extrainfo_t *ei;
uint32_t addr;
@@ -2425,13 +2538,14 @@ router_rebuild_descriptor(int force)
* learn that it's time to try again when ip_address_changed()
* marks it dirty. */
desc_clean_since = time(NULL);
- return -1;
+ return TOR_ROUTERINFO_ERROR_DESC_REBUILDING;
}
log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : "");
- if (router_build_fresh_descriptor(&ri, &ei) < 0) {
- return -1;
+ err = router_build_fresh_descriptor(&ri, &ei);
+ if (err < 0) {
+ return err;
}
routerinfo_free(desc_routerinfo);
@@ -2516,22 +2630,41 @@ mark_my_descriptor_dirty(const char *reason)
* if our previous bandwidth estimate was exactly 0. */
#define MAX_BANDWIDTH_CHANGE_FREQ (3*60*60)
+/** Maximum uptime to republish our descriptor because of large shifts in
+ * estimated bandwidth. */
+#define MAX_UPTIME_BANDWIDTH_CHANGE (24*60*60)
+
+/** By which factor bandwidth shifts have to change to be considered large. */
+#define BANDWIDTH_CHANGE_FACTOR 2
+
/** Check whether bandwidth has changed a lot since the last time we announced
- * bandwidth. If so, mark our descriptor dirty. */
+ * bandwidth while the uptime is smaller than MAX_UPTIME_BANDWIDTH_CHANGE.
+ * If so, mark our descriptor dirty. */
void
check_descriptor_bandwidth_changed(time_t now)
{
static time_t last_changed = 0;
uint64_t prev, cur;
const routerinfo_t *my_ri = router_get_my_routerinfo();
+
+ int hibernating = we_are_hibernating();
+
+ /* If the relay uptime is bigger than MAX_UPTIME_BANDWIDTH_CHANGE,
+ * the next regularly scheduled descriptor update (18h) will be enough */
+ if (get_uptime() > MAX_UPTIME_BANDWIDTH_CHANGE && !hibernating)
+ return;
+
if (!my_ri) /* make sure routerinfo exists */
return;
prev = my_ri->bandwidthcapacity;
- cur = we_are_hibernating() ? 0 : rep_hist_bandwidth_assess();
+
+ /* Consider ourselves to have zero bandwidth if we're hibernating or
+ * shutting down. */
+ cur = hibernating ? 0 : rep_hist_bandwidth_assess();
if ((prev != cur && (!prev || !cur)) ||
- cur > prev*2 ||
- cur < prev/2) {
+ cur > (prev * BANDWIDTH_CHANGE_FACTOR) ||
+ cur < (prev / BANDWIDTH_CHANGE_FACTOR) ) {
if (last_changed+MAX_BANDWIDTH_CHANGE_FREQ < now || !prev) {
log_info(LD_GENERAL,
"Measured bandwidth has changed; rebuilding descriptor.");
@@ -2931,14 +3064,14 @@ router_dump_router_to_string(routerinfo_t *router,
router->nickname,
address,
router->or_port,
- decide_to_advertise_dirport(options, router->dir_port),
+ router_should_advertise_dirport(options, router->dir_port),
ed_cert_line ? ed_cert_line : "",
extra_or_address ? extra_or_address : "",
router->platform,
proto_line,
published,
fingerprint,
- stats_n_seconds_working,
+ get_uptime(),
(int) router->bandwidthrate,
(int) router->bandwidthburst,
(int) router->bandwidthcapacity,
@@ -3005,7 +3138,7 @@ router_dump_router_to_string(routerinfo_t *router,
tor_free(p6);
}
- if (decide_to_advertise_begindir(options,
+ if (router_should_advertise_begindir(options,
router->supports_tunnelled_dir_requests)) {
smartlist_add_strdup(chunks, "tunnelled-dir-server\n");
}
@@ -3454,6 +3587,15 @@ is_legal_hexdigest(const char *s)
strspn(s,HEX_CHARACTERS)==HEX_DIGEST_LEN);
}
+/**
+ * Longest allowed output of format_node_description, plus 1 character for
+ * NUL. This allows space for:
+ * "$FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~xxxxxxxxxxxxxxxxxxx at"
+ * " [ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255]"
+ * plus a terminating NUL.
+ */
+#define NODE_DESC_BUF_LEN (MAX_VERBOSE_NICKNAME_LEN+4+TOR_ADDR_BUF_LEN)
+
/** Use <b>buf</b> (which must be at least NODE_DESC_BUF_LEN bytes long) to
* hold a human-readable description of a node with identity digest
* <b>id_digest</b>, named-status <b>is_named</b>, nickname <b>nickname</b>,
@@ -3499,15 +3641,16 @@ format_node_description(char *buf,
return buf;
}
-/** Use <b>buf</b> (which must be at least NODE_DESC_BUF_LEN bytes long) to
- * hold a human-readable description of <b>ri</b>.
- *
+/** Return a human-readable description of the routerinfo_t <b>ri</b>.
*
- * Return a pointer to the front of <b>buf</b>.
+ * This function is not thread-safe. Each call to this function invalidates
+ * previous values returned by this function.
*/
const char *
-router_get_description(char *buf, const routerinfo_t *ri)
+router_describe(const routerinfo_t *ri)
{
+ static char buf[NODE_DESC_BUF_LEN];
+
if (!ri)
return "<null>";
return format_node_description(buf,
@@ -3518,14 +3661,15 @@ router_get_description(char *buf, const routerinfo_t *ri)
ri->addr);
}
-/** Use <b>buf</b> (which must be at least NODE_DESC_BUF_LEN bytes long) to
- * hold a human-readable description of <b>node</b>.
+/** Return a human-readable description of the node_t <b>node</b>.
*
- * Return a pointer to the front of <b>buf</b>.
+ * This function is not thread-safe. Each call to this function invalidates
+ * previous values returned by this function.
*/
const char *
-node_get_description(char *buf, const node_t *node)
+node_describe(const node_t *node)
{
+ static char buf[NODE_DESC_BUF_LEN];
const char *nickname = NULL;
uint32_t addr32h = 0;
int is_named = 0;
@@ -3550,14 +3694,16 @@ node_get_description(char *buf, const node_t *node)
addr32h);
}
-/** Use <b>buf</b> (which must be at least NODE_DESC_BUF_LEN bytes long) to
- * hold a human-readable description of <b>rs</b>.
+/** Return a human-readable description of the routerstatus_t <b>rs</b>.
*
- * Return a pointer to the front of <b>buf</b>.
+ * This function is not thread-safe. Each call to this function invalidates
+ * previous values returned by this function.
*/
const char *
-routerstatus_get_description(char *buf, const routerstatus_t *rs)
+routerstatus_describe(const routerstatus_t *rs)
{
+ static char buf[NODE_DESC_BUF_LEN];
+
if (!rs)
return "<null>";
return format_node_description(buf,
@@ -3568,14 +3714,16 @@ routerstatus_get_description(char *buf, const routerstatus_t *rs)
rs->addr);
}
-/** Use <b>buf</b> (which must be at least NODE_DESC_BUF_LEN bytes long) to
- * hold a human-readable description of <b>ei</b>.
+/** Return a human-readable description of the extend_info_t <b>ei</b>.
*
- * Return a pointer to the front of <b>buf</b>.
+ * This function is not thread-safe. Each call to this function invalidates
+ * previous values returned by this function.
*/
const char *
-extend_info_get_description(char *buf, const extend_info_t *ei)
+extend_info_describe(const extend_info_t *ei)
{
+ static char buf[NODE_DESC_BUF_LEN];
+
if (!ei)
return "<null>";
return format_node_description(buf,
@@ -3586,54 +3734,6 @@ extend_info_get_description(char *buf, const extend_info_t *ei)
0);
}
-/** Return a human-readable description of the routerinfo_t <b>ri</b>.
- *
- * This function is not thread-safe. Each call to this function invalidates
- * previous values returned by this function.
- */
-const char *
-router_describe(const routerinfo_t *ri)
-{
- static char buf[NODE_DESC_BUF_LEN];
- return router_get_description(buf, ri);
-}
-
-/** Return a human-readable description of the node_t <b>node</b>.
- *
- * This function is not thread-safe. Each call to this function invalidates
- * previous values returned by this function.
- */
-const char *
-node_describe(const node_t *node)
-{
- static char buf[NODE_DESC_BUF_LEN];
- return node_get_description(buf, node);
-}
-
-/** Return a human-readable description of the routerstatus_t <b>rs</b>.
- *
- * This function is not thread-safe. Each call to this function invalidates
- * previous values returned by this function.
- */
-const char *
-routerstatus_describe(const routerstatus_t *rs)
-{
- static char buf[NODE_DESC_BUF_LEN];
- return routerstatus_get_description(buf, rs);
-}
-
-/** 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.
- */
-const char *
-extend_info_describe(const extend_info_t *ei)
-{
- static char buf[NODE_DESC_BUF_LEN];
- return extend_info_get_description(buf, ei);
-}
-
/** Set <b>buf</b> (which must have MAX_VERBOSE_NICKNAME_LEN+1 bytes) to the
* verbose representation of the identity of <b>router</b>. The format is:
* A dollar sign.
@@ -3733,4 +3833,3 @@ router_get_all_orports(const routerinfo_t *ri)
fake_node.ri = (routerinfo_t *)ri;
return node_get_all_orports(&fake_node);
}
-
diff --git a/src/or/router.h b/src/or/router.h
index 696e983662..752f2f2dbe 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -14,6 +14,13 @@
#include "testsupport.h"
+#define TOR_ROUTERINFO_ERROR_NO_EXT_ADDR (-1)
+#define TOR_ROUTERINFO_ERROR_CANNOT_PARSE (-2)
+#define TOR_ROUTERINFO_ERROR_NOT_A_SERVER (-3)
+#define TOR_ROUTERINFO_ERROR_DIGEST_FAILED (-4)
+#define TOR_ROUTERINFO_ERROR_CANNOT_GENERATE (-5)
+#define TOR_ROUTERINFO_ERROR_DESC_REBUILDING (-6)
+
crypto_pk_t *get_onion_key(void);
time_t get_onion_key_set_at(void);
void set_server_identity_key(crypto_pk_t *k);
@@ -47,15 +54,15 @@ int init_keys_client(void);
int check_whether_orport_reachable(const or_options_t *options);
int check_whether_dirport_reachable(const or_options_t *options);
int dir_server_mode(const or_options_t *options);
-void consider_testing_reachability(int test_or, int test_dir);
+void router_do_reachability_checks(int test_or, int test_dir);
void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now);
int net_is_disabled(void);
+int net_is_completely_disabled(void);
int authdir_mode(const or_options_t *options);
-int authdir_mode_v3(const or_options_t *options);
int authdir_mode_handles_descs(const or_options_t *options, int purpose);
int authdir_mode_publishes_statuses(const or_options_t *options);
int authdir_mode_tests_reachability(const or_options_t *options);
@@ -86,6 +93,7 @@ void router_new_address_suggestion(const char *suggestion,
int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port);
MOCK_DECL(int, router_my_exit_policy_is_reject_star,(void));
MOCK_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+MOCK_DECL(const routerinfo_t *, router_get_my_routerinfo_with_err,(int *err));
extrainfo_t *router_get_my_extrainfo(void);
const char *router_get_my_descriptor(void);
const char *router_get_descriptor_gen_reason(void);
@@ -123,29 +131,14 @@ int is_legal_nickname(const char *s);
int is_legal_nickname_or_hexdigest(const char *s);
int is_legal_hexdigest(const char *s);
-/**
- * Longest allowed output of format_node_description, plus 1 character for
- * NUL. This allows space for:
- * "$FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~xxxxxxxxxxxxxxxxxxx at"
- * " [ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255]"
- * plus a terminating NUL.
- */
-#define NODE_DESC_BUF_LEN (MAX_VERBOSE_NICKNAME_LEN+4+TOR_ADDR_BUF_LEN)
-const char *format_node_description(char *buf,
- const char *id_digest,
- int is_named,
- const char *nickname,
- const tor_addr_t *addr,
- uint32_t addr32h);
-const char *router_get_description(char *buf, const routerinfo_t *ri);
-const char *node_get_description(char *buf, const node_t *node);
-const char *routerstatus_get_description(char *buf, const routerstatus_t *rs);
-const char *extend_info_get_description(char *buf, const extend_info_t *ei);
const char *router_describe(const routerinfo_t *ri);
const char *node_describe(const node_t *node);
const char *routerstatus_describe(const routerstatus_t *ri);
const char *extend_info_describe(const extend_info_t *ei);
+const char *routerinfo_err_to_string(int err);
+int routerinfo_err_is_transient(int err);
+
void router_get_verbose_nickname(char *buf, const routerinfo_t *router);
void router_reset_warnings(void);
void router_reset_reachability(void);
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index 1933aaf4b6..43460da8cc 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -16,6 +16,7 @@
#include "or.h"
#include "config.h"
+#include "crypto_util.h"
#include "router.h"
#include "crypto_pwbox.h"
#include "routerkeys.h"
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 203895c867..2f27af7f06 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -99,9 +99,9 @@
#include "config.h"
#include "connection.h"
#include "control.h"
+#include "crypto_rand.h"
#include "directory.h"
#include "dirserv.h"
-#include "dirvote.h"
#include "entrynodes.h"
#include "fp_pair.h"
#include "geoip.h"
@@ -122,6 +122,9 @@
#include "sandbox.h"
#include "torcert.h"
+#include "dirauth/dirvote.h"
+#include "dirauth/mode.h"
+
// #define DEBUG_ROUTERLIST
/****************************************************************************/
@@ -2761,9 +2764,8 @@ frac_nodes_with_descriptors(const smartlist_t *sl,
if (node_has_any_descriptor(node))
n_with_descs++;
});
-
tor_free(bandwidths);
- return ((double)n_with_descs) / (double)smartlist_len(sl);
+ return ((double)n_with_descs) / smartlist_len(sl);
}
present = 0.0;
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 79499f2e6f..e82ecec5b7 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -56,30 +56,34 @@
#define ROUTERPARSE_PRIVATE
#include "or.h"
-#include "config.h"
#include "circuitstats.h"
+#include "config.h"
+#include "crypto_util.h"
+#include "dirauth/shared_random.h"
#include "dirserv.h"
-#include "dirvote.h"
+#include "entrynodes.h"
+#include "memarea.h"
+#include "microdesc.h"
+#include "networkstatus.h"
#include "parsecommon.h"
#include "policies.h"
#include "protover.h"
#include "rendcommon.h"
-#include "router.h"
-#include "routerlist.h"
-#include "memarea.h"
-#include "microdesc.h"
-#include "networkstatus.h"
#include "rephist.h"
+#include "router.h"
#include "routerkeys.h"
+#include "routerlist.h"
#include "routerparse.h"
-#include "entrynodes.h"
-#include "torcert.h"
#include "sandbox.h"
-#include "shared_random.h"
+#include "shared_random_client.h"
+#include "torcert.h"
+#include "voting_schedule.h"
#undef log
#include <math.h>
+#include "dirauth/dirvote.h"
+
/****************************************************************************/
/** List of tokens recognized in router descriptors */
@@ -2743,8 +2747,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
/* These are implied true by having been included in a consensus made
* with a given method */
rs->is_flagged_running = 1; /* Starting with consensus method 4. */
- if (consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES)
- rs->is_valid = 1;
+ rs->is_valid = 1; /* Starting with consensus method 24. */
}
{
const char *protocols = NULL, *version = NULL;
@@ -3283,60 +3286,6 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
return valid;
}
-/** Parse and extract all SR commits from <b>tokens</b> and place them in
- * <b>ns</b>. */
-static void
-extract_shared_random_commits(networkstatus_t *ns, smartlist_t *tokens)
-{
- smartlist_t *chunks = NULL;
-
- tor_assert(ns);
- tor_assert(tokens);
- /* Commits are only present in a vote. */
- tor_assert(ns->type == NS_TYPE_VOTE);
-
- ns->sr_info.commits = smartlist_new();
-
- smartlist_t *commits = find_all_by_keyword(tokens, K_COMMIT);
- /* It's normal that a vote might contain no commits even if it participates
- * in the SR protocol. Don't treat it as an error. */
- if (commits == NULL) {
- goto end;
- }
-
- /* Parse the commit. We do NO validation of number of arguments or ordering
- * for forward compatibility, it's the parse commit job to inform us if it's
- * supported or not. */
- chunks = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(commits, directory_token_t *, tok) {
- /* Extract all arguments and put them in the chunks list. */
- for (int i = 0; i < tok->n_args; i++) {
- smartlist_add(chunks, tok->args[i]);
- }
- sr_commit_t *commit = sr_parse_commit(chunks);
- smartlist_clear(chunks);
- if (commit == NULL) {
- /* Get voter identity so we can warn that this dirauth vote contains
- * commit we can't parse. */
- networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0);
- tor_assert(voter);
- log_warn(LD_DIR, "SR: Unable to parse commit %s from vote of voter %s.",
- escaped(tok->object_body),
- hex_str(voter->identity_digest,
- sizeof(voter->identity_digest)));
- /* Commitment couldn't be parsed. Continue onto the next commit because
- * this one could be unsupported for instance. */
- continue;
- }
- /* Add newly created commit object to the vote. */
- smartlist_add(ns->sr_info.commits, commit);
- } SMARTLIST_FOREACH_END(tok);
-
- end:
- smartlist_free(chunks);
- smartlist_free(commits);
-}
-
/** Check if a shared random value of type <b>srv_type</b> is in
* <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return
* -1 on failure, 0 on success. The resulting srv is allocated on the heap and
@@ -3774,13 +3723,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
/* If this is a vote document, check if information about the shared
randomness protocol is included, and extract it. */
if (ns->type == NS_TYPE_VOTE) {
- /* Does this authority participates in the SR protocol? */
- tok = find_opt_by_keyword(tokens, K_SR_FLAG);
- if (tok) {
- ns->sr_info.participate = 1;
- /* Get the SR commitments and reveals from the vote. */
- extract_shared_random_commits(ns, tokens);
- }
+ dirvote_parse_sr_commits(ns, tokens);
}
/* For both a vote and consensus, extract the shared random values. */
if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) {
@@ -3970,7 +3913,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
}
}
- if (voter_get_sig_by_algorithm(v, sig->alg)) {
+ if (networkstatus_get_voter_sig_by_alg(v, sig->alg)) {
/* We already parsed a vote with this algorithm from this voter. Use the
first one. */
log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
@@ -4659,13 +4602,13 @@ find_start_of_next_microdesc(const char *s, const char *eos)
return NULL;
#define CHECK_LENGTH() STMT_BEGIN \
- if (s+32 > eos) \
+ if (eos - s < 32) \
return NULL; \
STMT_END
#define NEXT_LINE() STMT_BEGIN \
s = memchr(s, '\n', eos-s); \
- if (!s || s+1 >= eos) \
+ if (!s || eos - s <= 1) \
return NULL; \
s++; \
STMT_END
@@ -4689,7 +4632,7 @@ find_start_of_next_microdesc(const char *s, const char *eos)
/* Okay, now we're pointed at the first line of the microdescriptor which is
not an annotation or onion-key. The next line that _is_ an annotation or
onion-key is the start of the next microdescriptor. */
- while (s+32 < eos) {
+ while (eos - s > 32) {
if (*s == '@' || !strcmpstart(s, "onion-key"))
return s;
NEXT_LINE();
@@ -5724,4 +5667,3 @@ routerparse_free_all(void)
{
dump_desc_fifo_cleanup();
}
-
diff --git a/src/or/scheduler.c b/src/or/scheduler.c
index 382b3e3ca9..da894294bf 100644
--- a/src/or/scheduler.c
+++ b/src/or/scheduler.c
@@ -13,8 +13,6 @@
#define TOR_CHANNEL_INTERNAL_
#include "channeltls.h"
-#include <event2/event.h>
-
/**
* \file scheduler.c
* \brief Channel scheduling system: decides which channels should send and
@@ -169,7 +167,7 @@ STATIC smartlist_t *channels_pending = NULL;
* This event runs the scheduler from its callback, and is manually
* activated whenever a channel enters open for writes/cells to send.
*/
-STATIC struct event *run_sched_ev = NULL;
+STATIC struct mainloop_event_t *run_sched_ev = NULL;
static int have_logged_kist_suddenly_disabled = 0;
@@ -203,10 +201,9 @@ get_scheduler_type_string(scheduler_types_t type)
* if any scheduling work was created during the event loop.
*/
static void
-scheduler_evt_callback(evutil_socket_t fd, short events, void *arg)
+scheduler_evt_callback(mainloop_event_t *event, void *arg)
{
- (void) fd;
- (void) events;
+ (void) event;
(void) arg;
log_debug(LD_SCHED, "Scheduler event callback called");
@@ -487,10 +484,7 @@ scheduler_free_all(void)
log_debug(LD_SCHED, "Shutting down scheduler");
if (run_sched_ev) {
- if (event_del(run_sched_ev) < 0) {
- log_warn(LD_BUG, "Problem deleting run_sched_ev");
- }
- tor_event_free(run_sched_ev);
+ mainloop_event_free(run_sched_ev);
run_sched_ev = NULL;
}
@@ -589,7 +583,7 @@ scheduler_ev_add(const struct timeval *next_run)
{
tor_assert(run_sched_ev);
tor_assert(next_run);
- if (BUG(event_add(run_sched_ev, next_run) < 0)) {
+ if (BUG(mainloop_event_schedule(run_sched_ev, next_run) < 0)) {
log_warn(LD_SCHED, "Adding to libevent failed. Next run time was set to: "
"%ld.%06ld", next_run->tv_sec, (long)next_run->tv_usec);
return;
@@ -598,10 +592,10 @@ scheduler_ev_add(const struct timeval *next_run)
/** Make the scheduler event active with the given flags. */
void
-scheduler_ev_active(int flags)
+scheduler_ev_active(void)
{
tor_assert(run_sched_ev);
- event_active(run_sched_ev, flags, 1);
+ mainloop_event_activate(run_sched_ev);
}
/*
@@ -618,11 +612,10 @@ scheduler_init(void)
IF_BUG_ONCE(!!run_sched_ev) {
log_warn(LD_SCHED, "We should not already have a libevent scheduler event."
"I'll clean the old one up, but this is odd.");
- tor_event_free(run_sched_ev);
+ mainloop_event_free(run_sched_ev);
run_sched_ev = NULL;
}
- run_sched_ev = tor_event_new(tor_libevent_get_base(), -1,
- 0, scheduler_evt_callback, NULL);
+ run_sched_ev = mainloop_event_new(scheduler_evt_callback, NULL);
channels_pending = smartlist_new();
set_scheduler();
diff --git a/src/or/scheduler.h b/src/or/scheduler.h
index aeba9e2b75..08b02e286f 100644
--- a/src/or/scheduler.h
+++ b/src/or/scheduler.h
@@ -155,12 +155,12 @@ void scheduler_bug_occurred(const channel_t *chan);
smartlist_t *get_channels_pending(void);
MOCK_DECL(int, scheduler_compare_channels,
(const void *c1_v, const void *c2_v));
-void scheduler_ev_active(int flags);
+void scheduler_ev_active(void);
void scheduler_ev_add(const struct timeval *next_run);
#ifdef TOR_UNIT_TESTS
extern smartlist_t *channels_pending;
-extern struct event *run_sched_ev;
+extern struct mainloop_event_t *run_sched_ev;
extern const scheduler_t *the_scheduler;
void scheduler_touch_channel(channel_t *chan);
#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/or/scheduler_kist.c b/src/or/scheduler_kist.c
index a4668ef33e..e941626371 100644
--- a/src/or/scheduler_kist.c
+++ b/src/or/scheduler_kist.c
@@ -3,8 +3,6 @@
#define SCHEDULER_KIST_PRIVATE
-#include <event2/event.h>
-
#include "or.h"
#include "buffers.h"
#include "config.h"
@@ -278,7 +276,7 @@ update_socket_info_impl, (socket_table_ent_t *ent))
extra_space =
clamp_double_to_int64(
(ent->cwnd * (int64_t)ent->mss) * sock_buf_size_factor) -
- ent->notsent;
+ ent->notsent - (int64_t)channel_outbuf_length((channel_t *) ent->chan);
if ((tcp_space + extra_space) < 0) {
/* This means that the "notsent" queue is just too big so we shouldn't put
* more in the kernel for now. */
@@ -551,7 +549,7 @@ kist_scheduler_schedule(void)
/* Re-adding an event reschedules it. It does not duplicate it. */
scheduler_ev_add(&next_run);
} else {
- scheduler_ev_active(EV_TIMEOUT);
+ scheduler_ev_active();
}
}
diff --git a/src/or/scheduler_vanilla.c b/src/or/scheduler_vanilla.c
index 7a83b9da18..b674d8256c 100644
--- a/src/or/scheduler_vanilla.c
+++ b/src/or/scheduler_vanilla.c
@@ -1,8 +1,6 @@
/* Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#include <event2/event.h>
-
#include "or.h"
#include "config.h"
#define TOR_CHANNEL_INTERNAL_
@@ -42,7 +40,7 @@ vanilla_scheduler_schedule(void)
}
/* Activate our event so it can process channels. */
- scheduler_ev_active(EV_TIMEOUT);
+ scheduler_ev_active();
}
static void
diff --git a/src/or/shared_random_client.c b/src/or/shared_random_client.c
new file mode 100644
index 0000000000..3aef83cef4
--- /dev/null
+++ b/src/or/shared_random_client.c
@@ -0,0 +1,259 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random_client.c
+ * \brief This file contains functions that are from the shared random
+ * subsystem but used by many part of tor. The full feature is built
+ * as part of the dirauth module.
+ **/
+
+#define SHARED_RANDOM_CLIENT_PRIVATE
+#include "shared_random_client.h"
+
+#include "config.h"
+#include "voting_schedule.h"
+#include "networkstatus.h"
+#include "util.h"
+#include "util_format.h"
+
+/* Convert a given srv object to a string for the control port. This doesn't
+ * fail and the srv object MUST be valid. */
+static char *
+srv_to_control_string(const sr_srv_t *srv)
+{
+ char *srv_str;
+ char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+ tor_assert(srv);
+
+ sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
+ tor_asprintf(&srv_str, "%s", srv_hash_encoded);
+ return srv_str;
+}
+
+/* Return the voting interval of the tor vote subsystem. */
+int
+get_voting_interval(void)
+{
+ int interval;
+ networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL));
+
+ if (consensus) {
+ interval = (int)(consensus->fresh_until - consensus->valid_after);
+ } else {
+ /* Same for both a testing and real network. We voluntarily ignore the
+ * InitialVotingInterval since it complexifies things and it doesn't
+ * affect the SR protocol. */
+ interval = get_options()->V3AuthVotingInterval;
+ }
+ tor_assert(interval > 0);
+ return interval;
+}
+
+/* Given the time <b>now</b>, return the start time of the current round of
+ * the SR protocol. For example, if it's 23:47:08, the current round thus
+ * started at 23:47:00 for a voting interval of 10 seconds. */
+time_t
+get_start_time_of_current_round(void)
+{
+ const or_options_t *options = get_options();
+ int voting_interval = get_voting_interval();
+ /* First, get the start time of the next round */
+ time_t next_start = voting_schedule_get_next_valid_after_time();
+ /* Now roll back next_start by a voting interval to find the start time of
+ the current round. */
+ time_t curr_start = voting_schedule_get_start_of_next_interval(
+ next_start - voting_interval - 1,
+ voting_interval,
+ options->TestingV3AuthVotingStartOffset);
+ return curr_start;
+}
+
+/*
+ * Public API
+ */
+
+/* Encode the given shared random value and put it in dst. Destination
+ * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
+void
+sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
+{
+ int ret;
+ /* Extra byte for the NULL terminated char. */
+ char buf[SR_SRV_VALUE_BASE64_LEN + 1];
+
+ tor_assert(dst);
+ tor_assert(srv);
+ tor_assert(dst_len >= sizeof(buf));
+
+ ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
+ sizeof(srv->value), 0);
+ /* Always expect the full length without the NULL byte. */
+ tor_assert(ret == (sizeof(buf) - 1));
+ tor_assert(ret <= (int) dst_len);
+ strlcpy(dst, buf, dst_len);
+}
+
+/* Return the current SRV string representation for the control port. Return a
+ * newly allocated string on success containing the value else "" if not found
+ * or if we don't have a valid consensus yet. */
+char *
+sr_get_current_for_control(void)
+{
+ char *srv_str;
+ const networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c && c->sr_info.current_srv) {
+ srv_str = srv_to_control_string(c->sr_info.current_srv);
+ } else {
+ srv_str = tor_strdup("");
+ }
+ return srv_str;
+}
+
+/* Return the previous SRV string representation for the control port. Return
+ * a newly allocated string on success containing the value else "" if not
+ * found or if we don't have a valid consensus yet. */
+char *
+sr_get_previous_for_control(void)
+{
+ char *srv_str;
+ const networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c && c->sr_info.previous_srv) {
+ srv_str = srv_to_control_string(c->sr_info.previous_srv);
+ } else {
+ srv_str = tor_strdup("");
+ }
+ return srv_str;
+}
+
+/* Return current shared random value from the latest consensus. Caller can
+ * NOT keep a reference to the returned pointer. Return NULL if none. */
+const sr_srv_t *
+sr_get_current(const networkstatus_t *ns)
+{
+ const networkstatus_t *consensus;
+
+ /* Use provided ns else get a live one */
+ if (ns) {
+ consensus = ns;
+ } else {
+ consensus = networkstatus_get_live_consensus(approx_time());
+ }
+ /* Ideally we would never be asked for an SRV without a live consensus. Make
+ * sure this assumption is correct. */
+ tor_assert_nonfatal(consensus);
+
+ if (consensus) {
+ return consensus->sr_info.current_srv;
+ }
+ return NULL;
+}
+
+/* Return previous shared random value from the latest consensus. Caller can
+ * NOT keep a reference to the returned pointer. Return NULL if none. */
+const sr_srv_t *
+sr_get_previous(const networkstatus_t *ns)
+{
+ const networkstatus_t *consensus;
+
+ /* Use provided ns else get a live one */
+ if (ns) {
+ consensus = ns;
+ } else {
+ consensus = networkstatus_get_live_consensus(approx_time());
+ }
+ /* Ideally we would never be asked for an SRV without a live consensus. Make
+ * sure this assumption is correct. */
+ tor_assert_nonfatal(consensus);
+
+ if (consensus) {
+ return consensus->sr_info.previous_srv;
+ }
+ return NULL;
+}
+
+/* Parse a list of arguments from a SRV value either from a vote, consensus
+ * or from our disk state and return a newly allocated srv object. NULL is
+ * returned on error.
+ *
+ * The arguments' order:
+ * num_reveals, value
+ */
+sr_srv_t *
+sr_parse_srv(const smartlist_t *args)
+{
+ char *value;
+ int ok, ret;
+ uint64_t num_reveals;
+ sr_srv_t *srv = NULL;
+
+ tor_assert(args);
+
+ if (smartlist_len(args) < 2) {
+ goto end;
+ }
+
+ /* First argument is the number of reveal values */
+ num_reveals = tor_parse_uint64(smartlist_get(args, 0),
+ 10, 0, UINT64_MAX, &ok, NULL);
+ if (!ok) {
+ goto end;
+ }
+ /* Second and last argument is the shared random value it self. */
+ value = smartlist_get(args, 1);
+ if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) {
+ goto end;
+ }
+
+ srv = tor_malloc_zero(sizeof(*srv));
+ srv->num_reveals = num_reveals;
+ /* We subtract one byte from the srclen because the function ignores the
+ * '=' character in the given buffer. This is broken but it's a documented
+ * behavior of the implementation. */
+ ret = base64_decode((char *) srv->value, sizeof(srv->value), value,
+ SR_SRV_VALUE_BASE64_LEN - 1);
+ if (ret != sizeof(srv->value)) {
+ tor_free(srv);
+ srv = NULL;
+ goto end;
+ }
+ end:
+ return srv;
+}
+
+/** Return the start time of the current SR protocol run. For example, if the
+ * time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this
+ * function should return 23/06/2017 00:00:00. */
+time_t
+sr_state_get_start_time_of_current_protocol_run(time_t now)
+{
+ int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+ int voting_interval = get_voting_interval();
+ /* Find the time the current round started. */
+ time_t beginning_of_current_round = get_start_time_of_current_round();
+
+ /* Get current SR protocol round */
+ int current_round = (now / voting_interval) % total_rounds;
+
+ /* Get start time by subtracting the time elapsed from the beginning of the
+ protocol run */
+ time_t time_elapsed_since_start_of_run = current_round * voting_interval;
+ return beginning_of_current_round - time_elapsed_since_start_of_run;
+}
+
+/** Return the time (in seconds) it takes to complete a full SR protocol phase
+ * (e.g. the commit phase). */
+unsigned int
+sr_state_get_phase_duration(void)
+{
+ return SHARED_RANDOM_N_ROUNDS * get_voting_interval();
+}
+
+/** Return the time (in seconds) it takes to complete a full SR protocol run */
+unsigned int
+sr_state_get_protocol_run_duration(void)
+{
+ int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+ return total_protocol_rounds * get_voting_interval();
+}
+
diff --git a/src/or/shared_random_client.h b/src/or/shared_random_client.h
new file mode 100644
index 0000000000..89c608d45f
--- /dev/null
+++ b/src/or/shared_random_client.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random_client.h
+ * \brief Header file for shared_random_client.c.
+ **/
+
+#ifndef TOR_SHARED_RANDOM_CLIENT_H
+#define TOR_SHARED_RANDOM_CLIENT_H
+
+/* Dirauth module. */
+#include "dirauth/shared_random.h"
+
+/* Helper functions. */
+void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv);
+int get_voting_interval(void);
+
+/* Control port functions. */
+char *sr_get_current_for_control(void);
+char *sr_get_previous_for_control(void);
+
+/* SRV functions. */
+const sr_srv_t *sr_get_current(const networkstatus_t *ns);
+const sr_srv_t *sr_get_previous(const networkstatus_t *ns);
+sr_srv_t *sr_parse_srv(const smartlist_t *args);
+
+/*
+ * Shared Random State API
+ */
+
+/* Each protocol phase has 12 rounds */
+#define SHARED_RANDOM_N_ROUNDS 12
+/* Number of phase we have in a protocol. */
+#define SHARED_RANDOM_N_PHASES 2
+
+time_t sr_state_get_start_time_of_current_protocol_run(time_t now);
+unsigned int sr_state_get_phase_duration(void);
+unsigned int sr_state_get_protocol_run_duration(void);
+time_t get_start_time_of_current_round(void);
+
+#ifdef TOR_UNIT_TESTS
+
+#endif /* TOR_UNIT_TESTS */
+
+#endif /* TOR_SHARED_RANDOM_CLIENT_H */
+
diff --git a/src/or/statefile.c b/src/or/statefile.c
index cc114f0a2b..c81ea44e06 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -37,6 +37,7 @@
#include "control.h"
#include "entrynodes.h"
#include "hibernate.h"
+#include "main.h"
#include "rephist.h"
#include "router.h"
#include "sandbox.h"
@@ -680,6 +681,18 @@ save_transport_to_state(const char *transport,
tor_free(transport_addrport);
}
+/** Change the next_write time of <b>state</b> to <b>when</b>, unless the
+ * state is already scheduled to be written to disk earlier than <b>when</b>.
+ */
+void
+or_state_mark_dirty(or_state_t *state, time_t when)
+{
+ if (state->next_write > when) {
+ state->next_write = when;
+ reschedule_or_state_save();
+ }
+}
+
STATIC void
or_state_free_(or_state_t *state)
{
diff --git a/src/or/statefile.h b/src/or/statefile.h
index b4cc4d1dc6..5aa2ca9320 100644
--- a/src/or/statefile.h
+++ b/src/or/statefile.h
@@ -17,6 +17,7 @@ char *get_stored_bindaddr_for_server_transport(const char *transport);
int or_state_load(void);
int or_state_loaded(void);
void or_state_free_all(void);
+void or_state_mark_dirty(or_state_t *state, time_t when);
#ifdef STATEFILE_PRIVATE
STATIC config_line_t *get_transport_in_state_by_name(const char *transport);
diff --git a/src/or/status.c b/src/or/status.c
index 4f7be164b1..4b8033d114 100644
--- a/src/or/status.c
+++ b/src/or/status.c
@@ -25,7 +25,6 @@
#include "main.h"
#include "rephist.h"
#include "hibernate.h"
-#include "rephist.h"
#include "statefile.h"
#include "hs_stats.h"
#include "hs_service.h"
@@ -88,19 +87,19 @@ bytes_to_usage(uint64_t bytes)
return bw_string;
}
-/** Log some usage info about our hidden service */
+/** Log some usage info about our onion service(s). */
static void
log_onion_service_stats(void)
{
unsigned int num_services = hs_service_get_num_services();
- /* If there are no active hidden services, no need to print logs */
+ /* If there are no active onion services, no need to print logs */
if (num_services == 0) {
return;
}
log_notice(LD_HEARTBEAT,
- "Our hidden service%s received %u v2 and %u v3 INTRODUCE2 cells "
+ "Our onion service%s received %u v2 and %u v3 INTRODUCE2 cells "
"and attempted to launch %d rendezvous circuits.",
num_services == 1 ? "" : "s",
hs_stats_get_n_introduce2_v2_cells(),
diff --git a/src/or/torcert.c b/src/or/torcert.c
index 51935ddf72..1c5afd965a 100644
--- a/src/or/torcert.c
+++ b/src/or/torcert.c
@@ -27,7 +27,7 @@
#include "or.h"
#include "config.h"
-#include "crypto.h"
+#include "crypto_util.h"
#include "torcert.h"
#include "ed25519_cert.h"
#include "torlog.h"
diff --git a/src/or/transports.c b/src/or/transports.c
index b08dcd1613..614fc81da8 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -135,7 +135,7 @@ static smartlist_t *transport_list = NULL;
/** Returns a transport_t struct for a transport proxy supporting the
protocol <b>name</b> listening at <b>addr</b>:<b>port</b> using
SOCKS version <b>socks_ver</b>. */
-static transport_t *
+STATIC transport_t *
transport_new(const tor_addr_t *addr, uint16_t port,
const char *name, int socks_ver,
const char *extra_info_args)
@@ -222,8 +222,8 @@ transport_copy(const transport_t *transport)
/** Returns the transport in our transport list that has the name <b>name</b>.
* Else returns NULL. */
-transport_t *
-transport_get_by_name(const char *name)
+MOCK_IMPL(transport_t *,
+transport_get_by_name,(const char *name))
{
tor_assert(name);
@@ -1025,48 +1025,71 @@ parse_method_error(const char *line, int is_server)
line+strlen(error)+1);
}
-/** Parses an SMETHOD <b>line</b> and if well-formed it registers the
- * new transport in <b>mp</b>. */
-STATIC int
-parse_smethod_line(const char *line, managed_proxy_t *mp)
+/** A helper for parse_{c,s}method_line(), bootstraps its
+ * functionalities. If <b>is_smethod</b> is true then the
+ * the line to parse is a SMETHOD line otherwise it is a
+ * CMETHOD line*/
+static int
+parse_method_line_helper(const char *line,
+ managed_proxy_t *mp,
+ int is_smethod)
{
+ int item_index = 0;
int r;
- smartlist_t *items = NULL;
- char *method_name=NULL;
+ char *transport_name=NULL;
char *args_string=NULL;
char *addrport=NULL;
- tor_addr_t tor_addr;
+ int socks_ver=PROXY_NONE;
char *address=NULL;
uint16_t port = 0;
+ const char *method_str = is_smethod ? PROTO_SMETHOD : PROTO_CMETHOD;
+ const int min_args_count = is_smethod ? 3 : 4;
+
+ tor_addr_t tor_addr;
transport_t *transport=NULL;
+ smartlist_t *items= smartlist_new();
- items = smartlist_new();
smartlist_split_string(items, line, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
- if (smartlist_len(items) < 3) {
- log_warn(LD_CONFIG, "Server managed proxy sent us a SMETHOD line "
- "with too few arguments.");
+ if (smartlist_len(items) < min_args_count) {
+ log_warn(LD_CONFIG, "Managed proxy sent us a %s line "
+ "with too few arguments.", method_str);
goto err;
}
- /* Example of legit SMETHOD line:
- SMETHOD obfs2 0.0.0.0:25612 ARGS:secret=supersekrit,key=superkey */
-
- tor_assert(!strcmp(smartlist_get(items,0),PROTO_SMETHOD));
+ tor_assert(!strcmp(smartlist_get(items, item_index),method_str));
+ ++item_index;
- method_name = smartlist_get(items,1);
- if (!string_is_C_identifier(method_name)) {
+ transport_name = smartlist_get(items,item_index);
+ ++item_index;
+ if (!string_is_C_identifier(transport_name)) {
log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
- method_name);
+ transport_name);
goto err;
}
- addrport = smartlist_get(items, 2);
+ /** Check for the proxy method sent to us in CMETHOD line. */
+ if (!is_smethod) {
+ const char *socks_ver_str = smartlist_get(items,item_index);
+ ++item_index;
+
+ if (!strcmp(socks_ver_str,"socks4")) {
+ socks_ver = PROXY_SOCKS4;
+ } else if (!strcmp(socks_ver_str,"socks5")) {
+ socks_ver = PROXY_SOCKS5;
+ } else {
+ log_warn(LD_CONFIG, "Client managed proxy sent us a proxy protocol "
+ "we don't recognize. (%s)", socks_ver_str);
+ goto err;
+ }
+ }
+
+ addrport = smartlist_get(items, item_index);
+ ++item_index;
if (tor_addr_port_split(LOG_WARN, addrport, &address, &port)<0) {
- log_warn(LD_CONFIG, "Error parsing transport "
- "address '%s'", addrport);
+ log_warn(LD_CONFIG, "Error parsing transport address '%s'", addrport);
goto err;
}
@@ -1081,10 +1104,11 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
goto err;
}
- if (smartlist_len(items) > 3) {
+ /** Check for options in the SMETHOD line. */
+ if (is_smethod && smartlist_len(items) > min_args_count) {
/* Seems like there are also some [options] in the SMETHOD line.
Let's see if we can parse them. */
- char *options_string = smartlist_get(items, 3);
+ char *options_string = smartlist_get(items, item_index);
log_debug(LD_CONFIG, "Got options_string: %s", options_string);
if (!strcmpstart(options_string, "ARGS:")) {
args_string = options_string+strlen("ARGS:");
@@ -1092,15 +1116,20 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
}
}
- transport = transport_new(&tor_addr, port, method_name,
- PROXY_NONE, args_string);
+ transport = transport_new(&tor_addr, port, transport_name,
+ socks_ver, args_string);
smartlist_add(mp->transports, transport);
- /* For now, notify the user so that they know where the server
- transport is listening. */
- log_info(LD_CONFIG, "Server transport %s at %s:%d.",
- method_name, address, (int)port);
+ /** Logs info about line parsing success for client or server */
+ if (is_smethod) {
+ log_info(LD_CONFIG, "Server transport %s at %s:%d.",
+ transport_name, address, (int)port);
+ } else {
+ log_info(LD_CONFIG, "Transport %s at %s:%d with SOCKS %d. "
+ "Attached to managed proxy.",
+ transport_name, address, (int)port, socks_ver);
+ }
r=0;
goto done;
@@ -1115,93 +1144,24 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
return r;
}
+/** Parses an SMETHOD <b>line</b> and if well-formed it registers the
+ * new transport in <b>mp</b>. */
+STATIC int
+parse_smethod_line(const char *line, managed_proxy_t *mp)
+{
+ /* Example of legit SMETHOD line:
+ SMETHOD obfs2 0.0.0.0:25612 ARGS:secret=supersekrit,key=superkey */
+ return parse_method_line_helper(line, mp, 1);
+}
+
/** Parses a CMETHOD <b>line</b>, and if well-formed it registers
* the new transport in <b>mp</b>. */
STATIC int
parse_cmethod_line(const char *line, managed_proxy_t *mp)
{
- int r;
- smartlist_t *items = NULL;
-
- char *method_name=NULL;
-
- char *socks_ver_str=NULL;
- int socks_ver=PROXY_NONE;
-
- char *addrport=NULL;
- tor_addr_t tor_addr;
- char *address=NULL;
- uint16_t port = 0;
-
- transport_t *transport=NULL;
-
- items = smartlist_new();
- smartlist_split_string(items, line, NULL,
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
- if (smartlist_len(items) < 4) {
- log_warn(LD_CONFIG, "Client managed proxy sent us a CMETHOD line "
- "with too few arguments.");
- goto err;
- }
-
- tor_assert(!strcmp(smartlist_get(items,0),PROTO_CMETHOD));
-
- method_name = smartlist_get(items,1);
- if (!string_is_C_identifier(method_name)) {
- log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
- method_name);
- goto err;
- }
-
- socks_ver_str = smartlist_get(items,2);
-
- if (!strcmp(socks_ver_str,"socks4")) {
- socks_ver = PROXY_SOCKS4;
- } else if (!strcmp(socks_ver_str,"socks5")) {
- socks_ver = PROXY_SOCKS5;
- } else {
- log_warn(LD_CONFIG, "Client managed proxy sent us a proxy protocol "
- "we don't recognize. (%s)", socks_ver_str);
- goto err;
- }
-
- addrport = smartlist_get(items, 3);
- if (tor_addr_port_split(LOG_WARN, addrport, &address, &port)<0) {
- log_warn(LD_CONFIG, "Error parsing transport "
- "address '%s'", addrport);
- goto err;
- }
-
- if (!port) {
- log_warn(LD_CONFIG,
- "Transport address '%s' has no port.", addrport);
- goto err;
- }
-
- if (tor_addr_parse(&tor_addr, address) < 0) {
- log_warn(LD_CONFIG, "Error parsing transport address '%s'", address);
- goto err;
- }
-
- transport = transport_new(&tor_addr, port, method_name, socks_ver, NULL);
-
- smartlist_add(mp->transports, transport);
-
- log_info(LD_CONFIG, "Transport %s at %s:%d with SOCKS %d. "
- "Attached to managed proxy.",
- method_name, address, (int)port, socks_ver);
-
- r=0;
- goto done;
-
- err:
- r = -1;
-
- done:
- SMARTLIST_FOREACH(items, char*, s, tor_free(s));
- smartlist_free(items);
- tor_free(address);
- return r;
+ /* Example of legit CMETHOD line:
+ CMETHOD obfs2 socks5 127.0.0.1:35713 */
+ return parse_method_line_helper(line, mp, 0);
}
/** Parses an PROXY-ERROR <b>line</b> and warns the user accordingly. */
diff --git a/src/or/transports.h b/src/or/transports.h
index 1b2786472c..022b926a03 100644
--- a/src/or/transports.h
+++ b/src/or/transports.h
@@ -38,7 +38,7 @@ MOCK_DECL(int, transport_add_from_config,
void transport_free_(transport_t *transport);
#define transport_free(tr) FREE_AND_NULL(transport_t, transport_free_, (tr))
-transport_t *transport_get_by_name(const char *name);
+MOCK_DECL(transport_t*, transport_get_by_name, (const char *name));
MOCK_DECL(void, pt_kickstart_proxy,
(const smartlist_t *transport_list, char **proxy_argv,
@@ -113,6 +113,9 @@ typedef struct {
smartlist_t *transports;
} managed_proxy_t;
+STATIC transport_t *transport_new(const tor_addr_t *addr, uint16_t port,
+ const char *name, int socks_ver,
+ const char *extra_info_args);
STATIC int parse_cmethod_line(const char *line, managed_proxy_t *mp);
STATIC int parse_smethod_line(const char *line, managed_proxy_t *mp);
diff --git a/src/or/voting_schedule.c b/src/or/voting_schedule.c
new file mode 100644
index 0000000000..d230a6dbcd
--- /dev/null
+++ b/src/or/voting_schedule.c
@@ -0,0 +1,192 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file voting_schedule.c
+ * \brief This file contains functions that are from the directory authority
+ * subsystem related to voting specifically but used by many part of
+ * tor. The full feature is built as part of the dirauth module.
+ **/
+
+#define VOTING_SCHEDULE_PRIVATE
+#include "voting_schedule.h"
+
+#include "or.h"
+#include "config.h"
+#include "networkstatus.h"
+
+/* =====
+ * Vote scheduling
+ * ===== */
+
+/** Return the start of the next interval of size <b>interval</b> (in
+ * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always
+ * starts a fresh interval, and if the last interval of a day would be
+ * truncated to less than half its size, it is rolled into the
+ * previous interval. */
+time_t
+voting_schedule_get_start_of_next_interval(time_t now, int interval,
+ int offset)
+{
+ struct tm tm;
+ time_t midnight_today=0;
+ time_t midnight_tomorrow;
+ time_t next;
+
+ tor_gmtime_r(&now, &tm);
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+
+ if (tor_timegm(&tm, &midnight_today) < 0) {
+ // LCOV_EXCL_START
+ log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight.");
+ // LCOV_EXCL_STOP
+ }
+ midnight_tomorrow = midnight_today + (24*60*60);
+
+ next = midnight_today + ((now-midnight_today)/interval + 1)*interval;
+
+ /* Intervals never cross midnight. */
+ if (next > midnight_tomorrow)
+ next = midnight_tomorrow;
+
+ /* If the interval would only last half as long as it's supposed to, then
+ * skip over to the next day. */
+ if (next + interval/2 > midnight_tomorrow)
+ next = midnight_tomorrow;
+
+ next += offset;
+ if (next - interval > now)
+ next -= interval;
+
+ return next;
+}
+
+/* Populate and return a new voting_schedule_t that can be used to schedule
+ * voting. The object is allocated on the heap and it's the responsibility of
+ * the caller to free it. Can't fail. */
+static voting_schedule_t *
+get_voting_schedule(const or_options_t *options, time_t now, int severity)
+{
+ int interval, vote_delay, dist_delay;
+ time_t start;
+ time_t end;
+ networkstatus_t *consensus;
+ voting_schedule_t *new_voting_schedule;
+
+ new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t));
+
+ consensus = networkstatus_get_live_consensus(now);
+
+ if (consensus) {
+ interval = (int)( consensus->fresh_until - consensus->valid_after );
+ vote_delay = consensus->vote_seconds;
+ dist_delay = consensus->dist_seconds;
+
+ /* Note down the consensus valid after, so that we detect outdated voting
+ * schedules in case of skewed clocks etc. */
+ new_voting_schedule->live_consensus_valid_after = consensus->valid_after;
+ } else {
+ interval = options->TestingV3AuthInitialVotingInterval;
+ vote_delay = options->TestingV3AuthInitialVoteDelay;
+ dist_delay = options->TestingV3AuthInitialDistDelay;
+ }
+
+ tor_assert(interval > 0);
+
+ if (vote_delay + dist_delay > interval/2)
+ vote_delay = dist_delay = interval / 4;
+
+ start = new_voting_schedule->interval_starts =
+ voting_schedule_get_start_of_next_interval(now,interval,
+ options->TestingV3AuthVotingStartOffset);
+ end = voting_schedule_get_start_of_next_interval(start+1, interval,
+ options->TestingV3AuthVotingStartOffset);
+
+ tor_assert(end > start);
+
+ new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2);
+ new_voting_schedule->voting_ends = start - dist_delay;
+ new_voting_schedule->fetch_missing_votes =
+ start - dist_delay - (vote_delay/2);
+ new_voting_schedule->voting_starts = start - dist_delay - vote_delay;
+
+ {
+ char tbuf[ISO_TIME_LEN+1];
+ format_iso_time(tbuf, new_voting_schedule->interval_starts);
+ tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: "
+ "consensus_set=%d, interval=%d",
+ tbuf, consensus?1:0, interval);
+ }
+
+ return new_voting_schedule;
+}
+
+#define voting_schedule_free(s) \
+ FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s))
+
+/** Frees a voting_schedule_t. This should be used instead of the generic
+ * tor_free. */
+static void
+voting_schedule_free_(voting_schedule_t *voting_schedule_to_free)
+{
+ if (!voting_schedule_to_free)
+ return;
+ tor_free(voting_schedule_to_free);
+}
+
+voting_schedule_t voting_schedule;
+
+/* Using the time <b>now</b>, return the next voting valid-after time. */
+time_t
+voting_schedule_get_next_valid_after_time(void)
+{
+ time_t now = approx_time();
+ bool need_to_recalculate_voting_schedule = false;
+
+ /* This is a safe guard in order to make sure that the voting schedule
+ * static object is at least initialized. Using this function with a zeroed
+ * voting schedule can lead to bugs. */
+ if (tor_mem_is_zero((const char *) &voting_schedule,
+ sizeof(voting_schedule))) {
+ need_to_recalculate_voting_schedule = true;
+ goto done; /* no need for next check if we have to recalculate anyway */
+ }
+
+ /* Also make sure we are not using an outdated voting schedule. If we have a
+ * newer consensus, make sure we recalculate the voting schedule. */
+ const networkstatus_t *ns = networkstatus_get_live_consensus(now);
+ if (ns && ns->valid_after != voting_schedule.live_consensus_valid_after) {
+ log_info(LD_DIR, "Voting schedule is outdated: recalculating (%d/%d)",
+ (int) ns->valid_after,
+ (int) voting_schedule.live_consensus_valid_after);
+ need_to_recalculate_voting_schedule = true;
+ }
+
+ done:
+ if (need_to_recalculate_voting_schedule) {
+ voting_schedule_recalculate_timing(get_options(), now);
+ voting_schedule.created_on_demand = 1;
+ }
+
+ return voting_schedule.interval_starts;
+}
+
+/** Set voting_schedule to hold the timing for the next vote we should be
+ * doing. All type of tor do that because HS subsystem needs the timing as
+ * well to function properly. */
+void
+voting_schedule_recalculate_timing(const or_options_t *options, time_t now)
+{
+ voting_schedule_t *new_voting_schedule;
+
+ /* get the new voting schedule */
+ new_voting_schedule = get_voting_schedule(options, now, LOG_INFO);
+ tor_assert(new_voting_schedule);
+
+ /* Fill in the global static struct now */
+ memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule));
+ voting_schedule_free(new_voting_schedule);
+}
+
diff --git a/src/or/voting_schedule.h b/src/or/voting_schedule.h
new file mode 100644
index 0000000000..087701408e
--- /dev/null
+++ b/src/or/voting_schedule.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file voting_schedule.h
+ * \brief Header file for voting_schedule.c.
+ **/
+
+#ifndef TOR_VOTING_SCHEDULE_H
+#define TOR_VOTING_SCHEDULE_H
+
+#include "or.h"
+
+/** Scheduling information for a voting interval. */
+typedef struct {
+ /** When do we generate and distribute our vote for this interval? */
+ time_t voting_starts;
+ /** When do we send an HTTP request for any votes that we haven't
+ * been posted yet?*/
+ time_t fetch_missing_votes;
+ /** When do we give up on getting more votes and generate a consensus? */
+ time_t voting_ends;
+ /** When do we send an HTTP request for any signatures we're expecting to
+ * see on the consensus? */
+ time_t fetch_missing_signatures;
+ /** When do we publish the consensus? */
+ time_t interval_starts;
+
+ /* True iff we have generated and distributed our vote. */
+ int have_voted;
+ /* True iff we've requested missing votes. */
+ int have_fetched_missing_votes;
+ /* True iff we have built a consensus and sent the signatures around. */
+ int have_built_consensus;
+ /* True iff we've fetched missing signatures. */
+ int have_fetched_missing_signatures;
+ /* True iff we have published our consensus. */
+ int have_published_consensus;
+
+ /* True iff this voting schedule was set on demand meaning not through the
+ * normal vote operation of a dirauth or when a consensus is set. This only
+ * applies to a directory authority that needs to recalculate the voting
+ * timings only for the first vote even though this object was initilized
+ * prior to voting. */
+ int created_on_demand;
+
+ /** The valid-after time of the last live consensus that filled this voting
+ * schedule. It's used to detect outdated voting schedules. */
+ time_t live_consensus_valid_after;
+} voting_schedule_t;
+
+/* Public API. */
+
+extern voting_schedule_t voting_schedule;
+
+void voting_schedule_recalculate_timing(const or_options_t *options,
+ time_t now);
+
+time_t voting_schedule_get_start_of_next_interval(time_t now,
+ int interval,
+ int offset);
+time_t voting_schedule_get_next_valid_after_time(void);
+
+#endif /* TOR_VOTING_SCHEDULE_H */
+