summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/compat_time.c4
-rw-r--r--src/common/compat_time.h2
-rw-r--r--src/common/crypto_rsa.h2
-rw-r--r--src/common/sandbox.c3
-rw-r--r--src/common/token_bucket.c13
-rw-r--r--src/common/token_bucket.h4
-rw-r--r--src/common/torint.h2
-rw-r--r--src/common/util.c22
-rw-r--r--src/common/util.h3
-rw-r--r--src/or/channel.c4
-rw-r--r--src/or/circuitbuild.c67
-rw-r--r--src/or/circuitlist.c1
-rw-r--r--src/or/circuitmux_ewma.c2
-rw-r--r--src/or/circuituse.c4
-rw-r--r--src/or/command.c11
-rw-r--r--src/or/config.c25
-rw-r--r--src/or/connection.c274
-rw-r--r--src/or/connection.h8
-rw-r--r--src/or/consdiffmgr.c33
-rw-r--r--src/or/control.c45
-rw-r--r--src/or/control.h4
-rw-r--r--src/or/dircollate.c38
-rw-r--r--src/or/dirvote.c72
-rw-r--r--src/or/dirvote.h48
-rw-r--r--src/or/entrynodes.c23
-rw-r--r--src/or/hibernate.c12
-rw-r--r--src/or/hs_common.c15
-rw-r--r--src/or/hs_service.c32
-rw-r--r--src/or/hs_service.h1
-rw-r--r--src/or/main.c407
-rw-r--r--src/or/main.h11
-rw-r--r--src/or/networkstatus.c4
-rw-r--r--src/or/networkstatus.h2
-rw-r--r--src/or/nodelist.c39
-rw-r--r--src/or/nodelist.h4
-rw-r--r--src/or/or.h2
-rw-r--r--src/or/periodic.c51
-rw-r--r--src/or/periodic.h53
-rw-r--r--src/or/protover.c2
-rw-r--r--src/or/relay.c6
-rw-r--r--src/or/rendservice.c13
-rw-r--r--src/or/router.c21
-rw-r--r--src/or/routerlist.c6
-rw-r--r--src/or/routerparse.c3
-rw-r--r--src/test/geoip_dummy0
-rw-r--r--src/test/include.am2
-rw-r--r--src/test/test.c1
-rw-r--r--src/test/test.h1
-rw-r--r--src/test/test_controller.c56
-rw-r--r--src/test/test_dir.c48
-rw-r--r--src/test/test_entrynodes.c19
-rw-r--r--src/test/test_geoip.c84
-rw-r--r--src/test/test_hs.c1
-rw-r--r--src/test/test_hs_common.c3
-rw-r--r--src/test/test_microdesc.c45
-rw-r--r--src/test/test_periodic_event.c323
-rw-r--r--src/test/test_util.c18
-rw-r--r--src/test/testing_common.c3
58 files changed, 1422 insertions, 580 deletions
diff --git a/src/common/compat_time.c b/src/common/compat_time.c
index b940447b67..966216768f 100644
--- a/src/common/compat_time.c
+++ b/src/common/compat_time.c
@@ -71,8 +71,8 @@ tor_sleep_msec(int msec)
/** Set *timeval to the current time of day. On error, log and terminate.
* (Same as gettimeofday(timeval,NULL), but never returns -1.)
*/
-void
-tor_gettimeofday(struct timeval *timeval)
+MOCK_IMPL(void,
+tor_gettimeofday, (struct timeval *timeval))
{
#ifdef _WIN32
/* Epoch bias copied from perl: number of units between windows epoch and
diff --git a/src/common/compat_time.h b/src/common/compat_time.h
index 75b57f6f24..09dd6add3d 100644
--- a/src/common/compat_time.h
+++ b/src/common/compat_time.h
@@ -173,7 +173,7 @@ void monotime_coarse_add_msec(monotime_coarse_t *out,
#define monotime_coarse_add_msec monotime_add_msec
#endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */
-void tor_gettimeofday(struct timeval *timeval);
+MOCK_DECL(void, tor_gettimeofday, (struct timeval *timeval));
#ifdef TOR_UNIT_TESTS
void tor_sleep_msec(int msec);
diff --git a/src/common/crypto_rsa.h b/src/common/crypto_rsa.h
index 2f5442a5d2..e952089318 100644
--- a/src/common/crypto_rsa.h
+++ b/src/common/crypto_rsa.h
@@ -35,7 +35,7 @@
/** A public key, or a public/private key-pair. */
typedef struct crypto_pk_t crypto_pk_t;
-/* RSA enviroment setup */
+/* RSA environment setup */
MOCK_DECL(crypto_pk_t *,crypto_pk_new,(void));
void crypto_pk_free_(crypto_pk_t *env);
#define crypto_pk_free(pk) FREE_AND_NULL(crypto_pk_t, crypto_pk_free_, (pk))
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index 3588f60dec..440f8722f2 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -197,6 +197,9 @@ static int filter_nopar_gen[] = {
SCMP_SYS(mmap),
#endif
SCMP_SYS(munmap),
+#ifdef __NR_nanosleep
+ SCMP_SYS(nanosleep),
+#endif
#ifdef __NR_prlimit
SCMP_SYS(prlimit),
#endif
diff --git a/src/common/token_bucket.c b/src/common/token_bucket.c
index 747189e751..f2396ec58a 100644
--- a/src/common/token_bucket.c
+++ b/src/common/token_bucket.c
@@ -238,13 +238,18 @@ token_bucket_rw_dec_write(token_bucket_rw_t *bucket,
/**
* As token_bucket_rw_dec_read and token_bucket_rw_dec_write, in a single
- * operation.
+ * operation. Return a bitmask of TB_READ and TB_WRITE to indicate
+ * which buckets became empty.
*/
-void
+int
token_bucket_rw_dec(token_bucket_rw_t *bucket,
ssize_t n_read, ssize_t n_written)
{
- token_bucket_rw_dec_read(bucket, n_read);
- token_bucket_rw_dec_write(bucket, n_written);
+ int flags = 0;
+ if (token_bucket_rw_dec_read(bucket, n_read))
+ flags |= TB_READ;
+ if (token_bucket_rw_dec_write(bucket, n_written))
+ flags |= TB_WRITE;
+ return flags;
}
diff --git a/src/common/token_bucket.h b/src/common/token_bucket.h
index fb5d9fc60a..0e7832e838 100644
--- a/src/common/token_bucket.h
+++ b/src/common/token_bucket.h
@@ -85,8 +85,8 @@ int token_bucket_rw_dec_read(token_bucket_rw_t *bucket,
int token_bucket_rw_dec_write(token_bucket_rw_t *bucket,
ssize_t n);
-void token_bucket_rw_dec(token_bucket_rw_t *bucket,
- ssize_t n_read, ssize_t n_written);
+int token_bucket_rw_dec(token_bucket_rw_t *bucket,
+ ssize_t n_read, ssize_t n_written);
static inline size_t token_bucket_rw_get_read(const token_bucket_rw_t *bucket);
static inline size_t
diff --git a/src/common/torint.h b/src/common/torint.h
index 0b8061d24f..fc7818fe2c 100644
--- a/src/common/torint.h
+++ b/src/common/torint.h
@@ -40,6 +40,8 @@
#include <inttypes.h>
#endif
+#include <stdbool.h>
+
#if (SIZEOF_INT8_T != 0)
#define HAVE_INT8_T
#endif
diff --git a/src/common/util.c b/src/common/util.c
index 041e7aee3d..53e4507f1f 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -572,6 +572,19 @@ add_laplace_noise(int64_t signal_, double random_, double delta_f,
return signal_ + noise;
}
+/* Helper: safely add two uint32_t's, capping at UINT32_MAX rather
+ * than overflow */
+uint32_t
+tor_add_u32_nowrap(uint32_t a, uint32_t b)
+{
+ /* a+b > UINT32_MAX check, without overflow */
+ if (PREDICT_UNLIKELY(a > UINT32_MAX - b)) {
+ return UINT32_MAX;
+ } else {
+ return a+b;
+ }
+}
+
/* Helper: return greatest common divisor of a,b */
static uint64_t
gcd64(uint64_t a, uint64_t b)
@@ -1821,6 +1834,15 @@ format_iso_time(char *buf, time_t t)
strftime(buf, ISO_TIME_LEN+1, "%Y-%m-%d %H:%M:%S", tor_gmtime_r(&t, &tm));
}
+/** As format_local_iso_time, but use the yyyy-mm-ddThh:mm:ss format to avoid
+ * embedding an internal space. */
+void
+format_local_iso_time_nospace(char *buf, time_t t)
+{
+ format_local_iso_time(buf, t);
+ buf[10] = 'T';
+}
+
/** As format_iso_time, but use the yyyy-mm-ddThh:mm:ss format to avoid
* embedding an internal space. */
void
diff --git a/src/common/util.h b/src/common/util.h
index ae27e5f016..7172b7da08 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -176,6 +176,8 @@ int n_bits_set_u8(uint8_t v);
int64_t clamp_double_to_int64(double number);
void simplify_fraction64(uint64_t *numer, uint64_t *denom);
+uint32_t tor_add_u32_nowrap(uint32_t a, uint32_t b);
+
/* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b>
* and positive <b>b</b>. Works on integer types only. Not defined if a+(b-1)
* can overflow. */
@@ -269,6 +271,7 @@ int parse_rfc1123_time(const char *buf, time_t *t);
#define ISO_TIME_USEC_LEN (ISO_TIME_LEN+7)
void format_local_iso_time(char *buf, time_t t);
void format_iso_time(char *buf, time_t t);
+void format_local_iso_time_nospace(char *buf, time_t t);
void format_iso_time_nospace(char *buf, time_t t);
void format_iso_time_nospace_usec(char *buf, const struct timeval *tv);
int parse_iso_time_(const char *cp, time_t *t, int strict, int nospace);
diff --git a/src/or/channel.c b/src/or/channel.c
index 68245db497..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) {
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 54446bb01d..c33dbbeb2d 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -418,7 +418,7 @@ onion_populate_cpath(origin_circuit_t *circ)
circ->cpath->extend_info->identity_digest);
/* If we don't know the node and its descriptor, we must be bootstrapping.
*/
- if (!node || !node_has_descriptor(node)) {
+ if (!node || !node_has_preferred_descriptor(node, 1)) {
return 0;
}
}
@@ -1758,7 +1758,7 @@ ap_stream_wants_exit_attention(connection_t *conn)
* Return NULL if we can't find any suitable routers.
*/
static const node_t *
-choose_good_exit_server_general(int need_uptime, int need_capacity)
+choose_good_exit_server_general(router_crn_flags_t flags)
{
int *n_supported;
int n_pending_connections = 0;
@@ -1768,6 +1768,9 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
const or_options_t *options = get_options();
const smartlist_t *the_nodes;
const node_t *selected_node=NULL;
+ const int need_uptime = (flags & CRN_NEED_UPTIME) != 0;
+ const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0;
+ const int direct_conn = (flags & CRN_DIRECT_CONN) != 0;
connections = get_connection_array();
@@ -1800,7 +1803,7 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
*/
continue;
}
- if (!node_has_descriptor(node)) {
+ if (!node_has_preferred_descriptor(node, direct_conn)) {
n_supported[i] = -1;
continue;
}
@@ -1913,7 +1916,8 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
need_capacity?", fast":"",
need_uptime?", stable":"");
tor_free(n_supported);
- return choose_good_exit_server_general(0, 0);
+ flags &= ~(CRN_NEED_UPTIME|CRN_NEED_CAPACITY);
+ return choose_good_exit_server_general(flags);
}
log_notice(LD_CIRC, "All routers are down or won't exit%s -- "
"choosing a doomed exit at random.",
@@ -2160,17 +2164,11 @@ pick_restricted_middle_node(router_crn_flags_t flags,
* toward the preferences in 'options'.
*/
static const node_t *
-choose_good_exit_server(origin_circuit_t *circ, int need_uptime,
- int need_capacity, int is_internal, int need_hs_v3)
+choose_good_exit_server(origin_circuit_t *circ,
+ router_crn_flags_t flags, int is_internal)
{
const or_options_t *options = get_options();
- router_crn_flags_t flags = CRN_NEED_DESC;
- if (need_uptime)
- flags |= CRN_NEED_UPTIME;
- if (need_capacity)
- flags |= CRN_NEED_CAPACITY;
- if (need_hs_v3)
- flags |= CRN_RENDEZVOUS_V3;
+ flags |= CRN_NEED_DESC;
switch (TO_CIRCUIT(circ)->purpose) {
case CIRCUIT_PURPOSE_C_HSDIR_GET:
@@ -2184,7 +2182,7 @@ choose_good_exit_server(origin_circuit_t *circ, int need_uptime,
if (is_internal) /* pick it like a middle hop */
return router_choose_random_node(NULL, options->ExcludeNodes, flags);
else
- return choose_good_exit_server_general(need_uptime,need_capacity);
+ return choose_good_exit_server_general(flags);
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
{
/* Pick a new RP */
@@ -2309,15 +2307,22 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
extend_info_describe(exit_ei));
exit_ei = extend_info_dup(exit_ei);
} else { /* we have to decide one */
+ router_crn_flags_t flags = CRN_NEED_DESC;
+ if (state->need_uptime)
+ flags |= CRN_NEED_UPTIME;
+ if (state->need_capacity)
+ flags |= CRN_NEED_CAPACITY;
+ if (is_hs_v3_rp_circuit)
+ flags |= CRN_RENDEZVOUS_V3;
+ if (state->onehop_tunnel)
+ flags |= CRN_DIRECT_CONN;
const node_t *node =
- choose_good_exit_server(circ, state->need_uptime,
- state->need_capacity, state->is_internal,
- is_hs_v3_rp_circuit);
+ choose_good_exit_server(circ, flags, state->is_internal);
if (!node) {
log_warn(LD_CIRC,"Failed to choose an exit server");
return -1;
}
- exit_ei = extend_info_from_node(node, 0);
+ exit_ei = extend_info_from_node(node, state->onehop_tunnel);
if (BUG(exit_ei == NULL))
return -1;
}
@@ -2374,6 +2379,10 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
/** Return the number of routers in <b>routers</b> that are currently up
* and available for building circuits through.
+ *
+ * (Note that this function may overcount or undercount, if we have
+ * descriptors that are not the type we would prefer to use for some
+ * particular router. See bug #25885.)
*/
MOCK_IMPL(STATIC int,
count_acceptable_nodes, (smartlist_t *nodes))
@@ -2390,7 +2399,7 @@ count_acceptable_nodes, (smartlist_t *nodes))
if (! node->is_valid)
// log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
continue;
- if (! node_has_descriptor(node))
+ if (! node_has_any_descriptor(node))
continue;
/* The node has a descriptor, so we can just check the ntor key directly */
if (!node_has_curve25519_onion_key(node))
@@ -2778,9 +2787,10 @@ extend_info_new(const char *nickname,
* of the node (i.e. its IPv4 address) unless
* <b>for_direct_connect</b> is true, in which case the preferred
* address is used instead. May return NULL if there is not enough
- * info about <b>node</b> to extend to it--for example, if there is no
- * routerinfo_t or microdesc_t, or if for_direct_connect is true and none of
- * the node's addresses are allowed by tor's firewall and IP version config.
+ * info about <b>node</b> to extend to it--for example, if the preferred
+ * routerinfo_t or microdesc_t is missing, or if for_direct_connect is
+ * true and none of the node's addresses is allowed by tor's firewall
+ * and IP version config.
**/
extend_info_t *
extend_info_from_node(const node_t *node, int for_direct_connect)
@@ -2788,17 +2798,8 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
tor_addr_port_t ap;
int valid_addr = 0;
- const int is_bridge = node_is_a_configured_bridge(node);
- const int we_use_mds = we_use_microdescriptors_for_circuits(get_options());
-
- if ((is_bridge && for_direct_connect) || !we_use_mds) {
- /* We need an ri in this case. */
- if (!node->ri)
- return NULL;
- } else {
- /* Otherwise we need an md. */
- if (node->rs == NULL || node->md == NULL)
- return NULL;
+ if (!node_has_preferred_descriptor(node, for_direct_connect)) {
+ return NULL;
}
/* Choose a preferred address first, but fall back to an allowed address.
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 9a82713cbe..ea2f2c15c5 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -2067,6 +2067,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)",
diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c
index b2ace8a9fa..4b80124a77 100644
--- a/src/or/circuitmux_ewma.c
+++ b/src/or/circuitmux_ewma.c
@@ -643,7 +643,7 @@ get_circuit_priority_halflife(const or_options_t *options,
((double) CMUX_PRIORITY_HALFLIFE_MSEC_DEFAULT) / 1000.0;
/* Try to get it from configuration file first. */
- if (options && options->CircuitPriorityHalflife < EPSILON) {
+ if (options && options->CircuitPriorityHalflife >= -EPSILON) {
halflife = options->CircuitPriorityHalflife;
*source_msg = "CircuitPriorityHalflife in configuration";
goto end;
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index fe28dd9a1a..47e29c28dd 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -2383,7 +2383,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
const node_t *r;
int opt = conn->chosen_exit_optional;
r = node_get_by_nickname(conn->chosen_exit_name, 0);
- if (r && node_has_descriptor(r)) {
+ if (r && node_has_preferred_descriptor(r, conn->want_onehop ? 1 : 0)) {
/* We might want to connect to an IPv6 bridge for loading
descriptors so we use the preferred address rather than
the primary. */
@@ -2393,7 +2393,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
"Discarding this circuit.", conn->chosen_exit_name);
return -1;
}
- } else { /* ! (r && node_has_descriptor(r)) */
+ } else { /* ! (r && node_has_preferred_descriptor(...)) */
log_debug(LD_DIR, "considering %d, %s",
want_onehop, conn->chosen_exit_name);
if (want_onehop && conn->chosen_exit_name[0] == '$') {
diff --git a/src/or/command.c b/src/or/command.c
index 4f99462f38..4fa05a18b4 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -495,6 +495,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 9c0b321b56..5d22365703 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -457,6 +457,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),
@@ -903,8 +904,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;
}
@@ -2169,9 +2175,16 @@ options_act(const or_options_t *old_options)
if (transition_affects_workers) {
log_info(LD_GENERAL,
"Worker-related options changed. Rotating workers.");
+ const int server_mode_turned_on =
+ server_mode(options) && !server_mode(old_options);
+ const int dir_server_mode_turned_on =
+ dir_server_mode(options) && !dir_server_mode(old_options);
- if (server_mode(options) && !server_mode(old_options)) {
+ if (server_mode_turned_on || dir_server_mode_turned_on) {
cpu_init();
+ }
+
+ if (server_mode_turned_on) {
ip_address_changed(0);
if (have_completed_a_circuit() || !any_predicted_circuits(time(NULL)))
inform_testing_reachability();
@@ -3763,6 +3776,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) &&
@@ -4733,7 +4751,8 @@ options_transition_affects_workers(const or_options_t *old_options,
YES_IF_CHANGED_LINELIST(Logs);
if (server_mode(old_options) != server_mode(new_options) ||
- public_server_mode(old_options) != public_server_mode(new_options))
+ public_server_mode(old_options) != public_server_mode(new_options) ||
+ dir_server_mode(old_options) != dir_server_mode(new_options))
return 1;
/* Nothing that changed matters. */
diff --git a/src/or/connection.c b/src/or/connection.c
index 9573989854..de0f0485b0 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. */
@@ -85,6 +85,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"
@@ -137,6 +138,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.
@@ -772,8 +775,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);
@@ -2814,10 +2817,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? */
@@ -2845,14 +2848,14 @@ 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_val, 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_val / 8;
@@ -2899,8 +2902,8 @@ connection_bucket_read_limit(connection_t *conn, time_t now)
global_bucket_val = MIN(global_bucket_val, relayed);
}
- return connection_bucket_round_robin(base, priority,
- global_bucket_val, 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? */
@@ -2931,8 +2934,8 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
global_bucket_val = MIN(global_bucket_val, relayed);
}
- return connection_bucket_round_robin(base, priority,
- global_bucket_val, 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
@@ -2969,8 +2972,11 @@ global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
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? */
@@ -2986,6 +2992,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>. */
@@ -3016,6 +3026,20 @@ 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);
+
+ /* Remember these bytes towards statistics. */
+ stats_increment_bytes_read_and_written(num_read, num_written);
+
+ /* 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;
+ }
}
/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes
@@ -3042,19 +3066,54 @@ connection_buckets_decrement(connection_t *conn, time_t now,
if (!connection_is_rate_limited(conn))
return; /* local IPs are free */
+ unsigned flags = 0;
if (connection_counts_as_relayed_traffic(conn, now)) {
- token_bucket_rw_dec(&global_relayed_bucket, num_read, num_written);
+ flags = token_bucket_rw_dec(&global_relayed_bucket, num_read, num_written);
+ }
+ flags |= token_bucket_rw_dec(&global_bucket, num_read, num_written);
+
+ if (flags & TB_WRITE) {
+ write_buckets_last_empty_at = now;
}
- token_bucket_rw_dec(&global_bucket, num_read, num_written);
if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
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_reading(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;
@@ -3062,6 +3121,8 @@ connection_consider_empty_read_buckets(connection_t *conn)
if (!connection_is_rate_limited(conn))
return; /* Always okay. */
+ 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()) &&
@@ -3071,17 +3132,17 @@ connection_consider_empty_read_buckets(connection_t *conn)
conn->state == OR_CONN_STATE_OPEN &&
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;
@@ -3089,6 +3150,7 @@ connection_consider_empty_write_buckets(connection_t *conn)
if (!connection_is_rate_limited(conn))
return; /* Always okay. */
+ 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()) &&
@@ -3098,12 +3160,12 @@ connection_consider_empty_write_buckets(connection_t *conn)
conn->state == OR_CONN_STATE_OPEN &&
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 buckets to the values configured in the
@@ -3128,6 +3190,8 @@ connection_bucket_init(void)
(int32_t)options->BandwidthBurst,
now_ts);
}
+
+ reenable_blocked_connection_init(options);
}
/** Update the global connection bucket settings to a new value. */
@@ -3148,57 +3212,104 @@ connection_bucket_adjust(const or_options_t *options)
}
}
-/** Time has passed; increment buckets appropriately. */
-void
-connection_bucket_refill(time_t now, uint32_t now_ts)
+/**
+ * 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)
{
- smartlist_t *conns = get_connection_array();
+ /* 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;
+ }
- write_buckets_empty_last_second =
- token_bucket_rw_get_write(&global_bucket) <= 0 ||
- token_bucket_rw_get_write(&global_relayed_bucket) <= 0;
+ 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);
+ }
+}
- /* refill the global buckets */
- token_bucket_rw_refill(&global_bucket, now_ts);
- token_bucket_rw_refill(&global_relayed_bucket, now_ts);
+/**
+ * Event to re-enable all connections that were previously blocked on read or
+ * write.
+ */
+static mainloop_event_t *reenable_blocked_connections_ev = NULL;
- /* 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);
+/** True iff reenable_blocked_connections_ev is currently scheduled. */
+static int reenable_blocked_connections_is_scheduled = 0;
- if (conn->state == OR_CONN_STATE_OPEN) {
- token_bucket_rw_refill(&or_conn->bucket, now_ts);
- }
- }
+/** 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 */
- && token_bucket_rw_get_read(&global_bucket) > 0 /* and we can read */
- && (!connection_counts_as_relayed_traffic(conn, now) ||
- token_bucket_rw_get_read(&global_relayed_bucket) > 0)
- && (!connection_speaks_cells(conn) ||
- conn->state != OR_CONN_STATE_OPEN ||
- token_bucket_rw_get_read(&TO_OR_CONN(conn)->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
- && token_bucket_rw_get_write(&global_bucket) > 0 /* and we can write */
- && (!connection_counts_as_relayed_traffic(conn, now) ||
- token_bucket_rw_get_write(&global_relayed_bucket) > 0)
- && (!connection_speaks_cells(conn) ||
- conn->state != OR_CONN_STATE_OPEN ||
- token_bucket_rw_get_write(&TO_OR_CONN(conn)->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;
+}
+
+/**
+ * Initialize the mainloop event that we use to wake up connections that
+ * find themselves blocked on bandwidth.
+ */
+static void
+reenable_blocked_connection_init(const or_options_t *options)
+{
+ 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;
+}
+
+/**
+ * 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.
@@ -3221,6 +3332,8 @@ connection_handle_read_impl(connection_t *conn)
conn->timestamp_last_read_allowed = approx_time();
+ connection_bucket_refill_single(conn, monotime_coarse_get_stamp());
+
switch (conn->type) {
case CONN_TYPE_OR_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_OR);
@@ -3479,25 +3592,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
@@ -3583,7 +3686,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
@@ -3655,6 +3757,8 @@ connection_handle_write_impl(connection_t *conn, int force)
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)) {
if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) {
@@ -3768,8 +3872,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.
*/
@@ -3815,22 +3918,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
@@ -5129,6 +5222,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
diff --git a/src/or/connection.h b/src/or/connection.h
index cfe31c3727..a2dce2435f 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -123,8 +123,12 @@ 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_adjust(const or_options_t *options);
-void connection_bucket_refill(time_t now,
- uint32_t now_ts);
+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);
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 0539ddaca3..dda8872182 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1931,6 +1931,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
@@ -3073,6 +3098,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",
@@ -3487,17 +3515,19 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
smartlist_free(args);
nodes = smartlist_new();
+ int first_node = zero_circ;
SMARTLIST_FOREACH_BEGIN(router_nicknames, const char *, n) {
const node_t *node = node_get_by_nickname(n, 0);
if (!node) {
connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n);
goto done;
}
- if (!node_has_descriptor(node)) {
+ if (!node_has_preferred_descriptor(node, first_node)) {
connection_printf_to_buf(conn, "552 No descriptor for \"%s\"\r\n", n);
goto done;
}
smartlist_add(nodes, (void*)node);
+ first_node = 0;
} SMARTLIST_FOREACH_END(n);
if (!smartlist_len(nodes)) {
connection_write_str_to_buf("512 No router names provided\r\n", conn);
@@ -3510,14 +3540,15 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
}
/* now circ refers to something that is ready to be extended */
- int first_node = zero_circ;
+ first_node = zero_circ;
SMARTLIST_FOREACH(nodes, const node_t *, node,
{
extend_info_t *info = extend_info_from_node(node, first_node);
if (!info) {
tor_assert_nonfatal(first_node);
log_warn(LD_CONTROL,
- "controller tried to connect to a node that doesn't have any "
+ "controller tried to connect to a node that lacks a suitable "
+ "descriptor, or which doesn't have any "
"addresses that are allowed by the firewall configuration; "
"circuit marked for closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED);
@@ -5800,8 +5831,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)) {
@@ -5817,12 +5846,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;
}
diff --git a/src/or/control.h b/src/or/control.h
index 2fd3c553f3..2f312a6638 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -311,6 +311,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/dircollate.c b/src/or/dircollate.c
index ce4534ff6c..dec6f75154 100644
--- a/src/or/dircollate.c
+++ b/src/or/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/dirvote.c b/src/or/dirvote.c
index c3cd0d3cd1..f3b8a19f00 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -549,12 +549,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 +664,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);
}
@@ -1455,19 +1448,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);
@@ -1516,7 +1504,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) {
@@ -1536,7 +1524,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
@@ -1601,7 +1589,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) {
@@ -1863,7 +1851,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);
@@ -1890,10 +1877,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;
@@ -1923,8 +1908,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 {
@@ -1951,8 +1935,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. */
@@ -1973,8 +1956,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);
@@ -1991,8 +1973,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;
@@ -2132,8 +2113,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) {
@@ -3835,8 +3815,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,
@@ -3846,8 +3825,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));
@@ -3858,8 +3836,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.*/
@@ -3869,11 +3846,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,
@@ -3951,13 +3927,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}
};
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index deeb27bfe1..8a317deb47 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -56,57 +56,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
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 88d1b94deb..2c2bf99925 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -185,14 +185,14 @@ should_apply_guardfraction(const networkstatus_t *ns)
return options->UseGuardFraction;
}
-/** Return true iff we know a descriptor for <b>guard</b> */
+/** Return true iff we know a preferred descriptor for <b>guard</b> */
static int
guard_has_descriptor(const entry_guard_t *guard)
{
const node_t *node = node_get_by_id(guard->identity);
if (!node)
return 0;
- return node_has_descriptor(node);
+ return node_has_preferred_descriptor(node, 1);
}
/**
@@ -432,14 +432,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 +455,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";
@@ -2269,7 +2273,8 @@ entry_guard_pick_for_circuit(guard_selection_t *gs,
// XXXX #20827 check Ed ID.
if (! node)
goto fail;
- if (BUG(usage != GUARD_USAGE_DIRGUARD && !node_has_descriptor(node)))
+ if (BUG(usage != GUARD_USAGE_DIRGUARD &&
+ !node_has_preferred_descriptor(node, 1)))
goto fail;
*chosen_node_out = node;
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 7261cf8002..c4ae63acc4 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -297,7 +297,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
@@ -1111,10 +1111,18 @@ 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());
+ }
}
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index aa34b0e8fb..24eb7a104a 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -1279,8 +1279,10 @@ node_has_hsdir_index(const node_t *node)
tor_assert(node_supports_v3_hsdir(node));
/* A node can't have an HSDir index without a descriptor since we need desc
- * to get its ed25519 key */
- if (!node_has_descriptor(node)) {
+ * to get its ed25519 key. for_direct_connect should be zero, since we
+ * always use the consensus-indexed node's keys to build the hash ring, even
+ * if some of the consensus-indexed nodes are also bridges. */
+ if (!node_has_preferred_descriptor(node, 0)) {
return 0;
}
@@ -1611,12 +1613,17 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
hs_clean_last_hid_serv_requests(now);
/* Only select those hidden service directories to which we did not send a
- * request recently and for which we have a router descriptor here. */
+ * request recently and for which we have a router descriptor here.
+ *
+ * Use for_direct_connect==0 even if we will be connecting to the node
+ * directly, since we always use the key information in the
+ * consensus-indexed node descriptors for building the index.
+ **/
SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) {
time_t last = hs_lookup_last_hid_serv_request(dir, req_key_str, 0, 0);
const node_t *node = node_get_by_id(dir->identity_digest);
if (last + hs_hsdir_requery_period(options) >= now ||
- !node || !node_has_descriptor(node)) {
+ !node || !node_has_preferred_descriptor(node, 0)) {
SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
continue;
}
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index ba8abc4237..7af14373d4 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -80,6 +80,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);
@@ -152,6 +153,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;
}
@@ -178,6 +185,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>. */
@@ -841,6 +853,10 @@ move_hs_state(hs_service_t *src_service, hs_service_t *dst_service)
/* Let's do a shallow copy */
dst->intro_circ_retry_started_time = src->intro_circ_retry_started_time;
dst->num_intro_circ_launched = src->num_intro_circ_launched;
+ /* Freeing a NULL replaycache triggers an info LD_BUG. */
+ if (dst->replay_cache_rend_cookie != NULL) {
+ replaycache_free(dst->replay_cache_rend_cookie);
+ }
dst->replay_cache_rend_cookie = src->replay_cache_rend_cookie;
src->replay_cache_rend_cookie = NULL; /* steal pointer reference */
@@ -912,6 +928,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
@@ -2932,6 +2953,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
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index d163eeef28..2e27d8a899 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -260,6 +260,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/main.c b/src/or/main.c
index a852d3273d..10f26c954e 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -159,13 +159,6 @@ token_bucket_rw_t global_bucket;
/* Token bucket for relayed traffic. */
token_bucket_rw_t global_relayed_bucket;
-/** What was the read/write bucket before the last second_elapsed_callback()
- * call? (used to determine how many bytes we've read). */
-static size_t 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 size_t stats_prev_global_write_bucket;
-
/* DOCDOC stats_prev_n_read */
static uint64_t stats_prev_n_read = 0;
/* DOCDOC stats_prev_n_written */
@@ -452,6 +445,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. */
@@ -479,21 +473,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)
*/
@@ -1025,19 +1035,22 @@ 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);
+ connection_consider_empty_read_buckets(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);
}
- if (connection_is_reading(conn)) {
+ if (BUG(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_read_bw_exhausted(conn, true/* kludge. */);
}
}
return 0;
@@ -1316,69 +1329,97 @@ 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(downrate_stability);
+CALLBACK(expire_old_ciruits_serverside);
+CALLBACK(fetch_networkstatus);
+CALLBACK(heartbeat);
+CALLBACK(hs_service);
+CALLBACK(launch_descriptor_fetches);
+CALLBACK(launch_reachability_tests);
+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(write_bridge_ns);
-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(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(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(clean_consdiffmgr, PERIODIC_EVENT_ROLE_ROUTER, 0),
+ CALLBACK(expire_old_ciruits_serverside, 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),
+
+ /* 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),
+
END_OF_PERIODIC_EVENTS
};
#undef CALLBACK
@@ -1420,6 +1461,32 @@ 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_client = any_client_port_set(options);
+ 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();
+
+ 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;
+
+ return roles;
+}
+
/** Event to run initialize_periodic_events_cb */
static struct event *initialize_periodic_events_event = NULL;
@@ -1434,11 +1501,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
@@ -1449,6 +1515,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]);
@@ -1479,6 +1546,57 @@ teardown_periodic_events(void)
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);
+ }
+}
+
/**
* Update our schedule so that we'll check whether we need to update our
* descriptor immediately, rather than after up to CHECK_DESCRIPTOR_INTERVAL
@@ -1505,6 +1623,30 @@ 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)
+{
+ 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]. */
@@ -1603,12 +1745,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;
@@ -1616,32 +1752,13 @@ 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
@@ -2358,63 +2475,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;
-
-/** Millisecond when refall_callback was last invoked. */
-static struct timeval refill_timer_current_millisecond;
-
-/** Libevent callback: invoked periodically to refill token buckets
- * and count r/w bytes. */
-static void
-refill_callback(periodic_timer_t *timer, void *arg)
-{
- 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 (refill_timer_current_millisecond.tv_sec) {
- long mdiff = tv_mdiff(&refill_timer_current_millisecond, &now);
- if (mdiff > INT_MAX)
- mdiff = INT_MAX;
- milliseconds_elapsed = (int)mdiff;
- seconds_rolled_over = (int)(now.tv_sec -
- refill_timer_current_millisecond.tv_sec);
- }
-
- bytes_written = stats_prev_global_write_bucket -
- token_bucket_rw_get_write(&global_bucket);
- bytes_read = stats_prev_global_read_bucket -
- token_bucket_rw_get_read(&global_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((time_t)now.tv_sec,
- monotime_coarse_get_stamp());
- }
-
- stats_prev_global_read_bucket = token_bucket_rw_get_read(&global_bucket);
- stats_prev_global_write_bucket = token_bucket_rw_get_write(&global_bucket);
-
- /* remember what time it is, for next time */
- refill_timer_current_millisecond = now;
-}
-
#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
@@ -2574,6 +2634,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)
@@ -2587,10 +2661,7 @@ do_main_loop(void)
initialize_periodic_events();
}
- if (!schedule_active_linked_connections_event) {
- schedule_active_linked_connections_event =
- mainloop_event_postloop_new(schedule_active_linked_connections_cb, NULL);
- }
+ initialize_mainloop_events();
/* initialize dns resolve map, spawn workers if needed */
if (dns_init() < 0) {
@@ -2618,8 +2689,6 @@ do_main_loop(void)
/* Set up our buckets */
connection_bucket_init();
- stats_prev_global_read_bucket = token_bucket_rw_get_read(&global_bucket);
- stats_prev_global_write_bucket = token_bucket_rw_get_write(&global_bucket);
/* initialize the bootstrap status events to know we're starting up */
control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
@@ -2669,7 +2738,7 @@ do_main_loop(void)
now = time(NULL);
directory_info_has_arrived(now, 1, 0);
- if (server_mode(get_options())) {
+ if (server_mode(get_options()) || dir_server_mode(get_options())) {
/* launch cpuworkers. Need to do this *after* we've read the onion key. */
cpu_init();
}
@@ -2717,20 +2786,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");
@@ -3487,11 +3542,11 @@ 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);
#ifdef HAVE_SYSTEMD_209
periodic_timer_free(systemd_watchdog_timer);
@@ -3499,7 +3554,6 @@ tor_free_all(int postfork)
memset(&global_bucket, 0, sizeof(global_bucket));
memset(&global_relayed_bucket, 0, sizeof(global_relayed_bucket));
- stats_prev_global_read_bucket = stats_prev_global_write_bucket = 0;
stats_prev_n_read = stats_prev_n_written = 0;
stats_n_bytes_read = stats_n_bytes_written = 0;
time_of_process_start = 0;
@@ -3516,7 +3570,6 @@ tor_free_all(int postfork)
heartbeat_callback_first_time = 1;
n_libevent_errors = 0;
current_second = 0;
- memset(&refill_timer_current_millisecond, 0, sizeof(struct timeval));
if (!postfork) {
release_lockfile();
diff --git a/src/or/main.h b/src/or/main.h
index e50d14d4d9..2447339fb5 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. */
@@ -60,6 +61,8 @@ 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 mainloop_schedule_postloop_cleanup(void);
+void rescan_periodic_events(const or_options_t *options);
MOCK_DECL(long,get_uptime,(void));
MOCK_DECL(void,reset_uptime,(void));
@@ -86,6 +89,8 @@ 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);
+
extern time_t time_of_process_start;
extern int quiet_level;
extern token_bucket_rw_t global_bucket;
@@ -93,11 +98,17 @@ 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 e03d9e6fe5..b0db0cecbc 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -1528,7 +1528,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;
}
}
@@ -1680,7 +1680,7 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
* XXXX If we need this elsewhere at any point, we should make it nonstatic
* XXXX and move it into another file.
*/
-static int
+int
any_client_port_set(const or_options_t *options)
{
return (options->SocksPort_set ||
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index 1851a55e82..04cf277d2f 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -140,6 +140,8 @@ void vote_routerstatus_free_(vote_routerstatus_t *rs);
#define vote_routerstatus_free(rs) \
FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs))
+int any_client_port_set(const or_options_t *options);
+
#ifdef NETWORKSTATUS_PRIVATE
#ifdef TOR_UNIT_TESTS
STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index bbcfb6cfff..e7342f9799 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 "bridges.h"
#include "config.h"
#include "control.h"
#include "dirserv.h"
@@ -1130,15 +1131,44 @@ node_is_dir(const node_t *node)
}
}
-/** Return true iff <b>node</b> has either kind of usable descriptor -- that
- * is, a routerdescriptor or a microdescriptor. */
+/** Return true iff <b>node</b> has either kind of descriptor -- that
+ * is, a routerdescriptor or a microdescriptor.
+ *
+ * You should probably use node_has_preferred_descriptor() instead.
+ **/
int
-node_has_descriptor(const node_t *node)
+node_has_any_descriptor(const node_t *node)
{
return (node->ri ||
(node->rs && node->md));
}
+/** Return true iff <b>node</b> has the kind of descriptor we would prefer to
+ * use for it, given our configuration and how we intend to use the node.
+ *
+ * If <b>for_direct_connect</b> is true, we intend to connect to the node
+ * directly, as the first hop of a circuit; otherwise, we intend to connect to
+ * it indirectly, or use it as if we were connecting to it indirectly. */
+int
+node_has_preferred_descriptor(const node_t *node,
+ int for_direct_connect)
+{
+ const int is_bridge = node_is_a_configured_bridge(node);
+ const int we_use_mds = we_use_microdescriptors_for_circuits(get_options());
+
+ if ((is_bridge && for_direct_connect) || !we_use_mds) {
+ /* We need an ri in this case. */
+ if (!node->ri)
+ return 0;
+ } else {
+ /* Otherwise we need an rs and an md. */
+ if (node->rs == NULL || node->md == NULL)
+ return 0;
+ }
+
+ return 1;
+}
+
/** Return the router_purpose of <b>node</b>. */
int
node_get_purpose(const node_t *node)
@@ -2223,7 +2253,8 @@ compute_frac_paths_available(const networkstatus_t *consensus,
nu);
SMARTLIST_FOREACH_BEGIN(myexits_unflagged, const node_t *, node) {
- if (node_has_descriptor(node) && node_exit_policy_rejects_all(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--;
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 53b18ab48a..1ffba2e8df 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -46,7 +46,9 @@ void node_get_verbose_nickname(const node_t *node,
void node_get_verbose_nickname_by_id(const char *id_digest,
char *verbose_name_out);
int node_is_dir(const node_t *node);
-int node_has_descriptor(const node_t *node);
+int node_has_any_descriptor(const node_t *node);
+int node_has_preferred_descriptor(const node_t *node,
+ int for_direct_connect);
int node_get_purpose(const node_t *node);
#define node_is_bridge(node) \
(node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE)
diff --git a/src/or/or.h b/src/or/or.h
index c5a039e939..4752743408 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -4148,6 +4148,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? */
diff --git a/src/or/periodic.c b/src/or/periodic.c
index fa40965de1..76aa418b35 100644
--- a/src/or/periodic.c
+++ b/src/or/periodic.c
@@ -43,12 +43,22 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data)
periodic_event_item_t *event = data;
tor_assert(ev == event->ev);
+ if (BUG(!periodic_event_is_enabled(event))) {
+ return;
+ }
+
time_t now = time(NULL);
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.",
@@ -78,7 +88,10 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data)
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. */
@@ -104,8 +117,14 @@ 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
+ event->enabled = 1;
periodic_event_dispatch(event->ev, event);
}
@@ -119,3 +138,33 @@ periodic_event_destroy(periodic_event_item_t *event)
event->last_action_time = 0;
}
+/** Enable the given event which means the event is launched and then the
+ * event's enabled flag is set. 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;
+ }
+
+ periodic_event_launch(event);
+}
+
+/** 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 285400b8bb..a044b34fdd 100644
--- a/src/or/periodic.h
+++ b/src/or/periodic.h
@@ -6,6 +6,38 @@
#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)
+
+/* 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
@@ -23,16 +55,33 @@ typedef struct periodic_event_item_t {
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/protover.c b/src/or/protover.c
index 6532f09c2f..18382ba7c9 100644
--- a/src/or/protover.c
+++ b/src/or/protover.c
@@ -715,7 +715,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
diff --git a/src/or/relay.c b/src/or/relay.c
index a33e0d1f36..8c248e6d98 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -374,6 +374,12 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
}
relay_encrypt_cell_outbound(cell, TO_ORIGIN_CIRCUIT(circ), layer_hint);
+
+ /* 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 */
if (CIRCUIT_IS_ORIGIN(circ)) {
/* We should never package an _incoming_ cell from the circuit
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index cc22429777..afaeabe5dc 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -348,6 +348,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 +616,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
@@ -958,6 +967,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);
@@ -3596,7 +3607,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
/* Don't upload descriptor if we succeeded in doing so last time. */
continue;
node = node_get_by_id(hs_dir->identity_digest);
- if (!node || !node_has_descriptor(node)) {
+ if (!node || !node_has_preferred_descriptor(node,0)) {
log_info(LD_REND, "Not launching upload for for v2 descriptor to "
"hidden service directory %s; we don't have its "
"router descriptor. Queuing for later upload.",
diff --git a/src/or/router.c b/src/or/router.c
index e5996f665e..93b61b69ef 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -138,7 +138,8 @@ get_onion_key(void)
}
/** Store a full copy of the current onion key into *<b>key</b>, and a full
- * copy of the most recent onion key into *<b>last</b>.
+ * copy of the most recent onion key into *<b>last</b>. Store NULL into
+ * a pointer if the corresponding key does not exist.
*/
void
dup_onion_keys(crypto_pk_t **key, crypto_pk_t **last)
@@ -146,8 +147,10 @@ dup_onion_keys(crypto_pk_t **key, crypto_pk_t **last)
tor_assert(key);
tor_assert(last);
tor_mutex_acquire(key_lock);
- tor_assert(onionkey);
- *key = crypto_pk_copy_full(onionkey);
+ if (onionkey)
+ *key = crypto_pk_copy_full(onionkey);
+ else
+ *key = NULL;
if (lastonionkey)
*last = crypto_pk_copy_full(lastonionkey);
else
@@ -214,10 +217,14 @@ construct_ntor_key_map(void)
{
di_digest256_map_t *m = NULL;
- dimap_add_entry(&m,
- curve25519_onion_key.pubkey.public_key,
- tor_memdup(&curve25519_onion_key,
- sizeof(curve25519_keypair_t)));
+ if (!tor_mem_is_zero((const char*)
+ curve25519_onion_key.pubkey.public_key,
+ CURVE25519_PUBKEY_LEN)) {
+ dimap_add_entry(&m,
+ curve25519_onion_key.pubkey.public_key,
+ tor_memdup(&curve25519_onion_key,
+ sizeof(curve25519_keypair_t)));
+ }
if (!tor_mem_is_zero((const char*)
last_curve25519_onion_key.pubkey.public_key,
CURVE25519_PUBKEY_LEN)) {
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index bc3abb236f..1bfbd9f670 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -2335,7 +2335,7 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int need_uptime,
SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
if (!node->is_running || !node->is_valid)
continue;
- if (need_desc && !(node->ri || (node->rs && node->md)))
+ if (need_desc && !node_has_preferred_descriptor(node, direct_conn))
continue;
if (node->ri && node->ri->purpose != ROUTER_PURPOSE_GENERAL)
continue;
@@ -2758,7 +2758,7 @@ frac_nodes_with_descriptors(const smartlist_t *sl,
total <= 0.0) {
int n_with_descs = 0;
SMARTLIST_FOREACH(sl, const node_t *, node, {
- if (node_has_descriptor(node))
+ if (node_has_any_descriptor(node))
n_with_descs++;
});
return ((double)n_with_descs) / (double)smartlist_len(sl);
@@ -2766,7 +2766,7 @@ frac_nodes_with_descriptors(const smartlist_t *sl,
present = 0.0;
SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
- if (node_has_descriptor(node))
+ if (node_has_any_descriptor(node))
present += bandwidths[node_sl_idx];
} SMARTLIST_FOREACH_END(node);
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 79499f2e6f..1834cfad24 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -2743,8 +2743,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;
diff --git a/src/test/geoip_dummy b/src/test/geoip_dummy
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/test/geoip_dummy
diff --git a/src/test/include.am b/src/test/include.am
index 474da3f880..eb61d74893 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -144,6 +144,7 @@ src_test_test_SOURCES = \
src/test/test_oom.c \
src/test/test_oos.c \
src/test/test_options.c \
+ src/test/test_periodic_event.c \
src/test/test_policy.c \
src/test/test_procmon.c \
src/test/test_proto_http.c \
@@ -342,6 +343,7 @@ src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS)
EXTRA_DIST += \
src/test/bt_test.py \
+ src/test/geoip_dummy \
src/test/ntor_ref.py \
src/test/hs_ntor_ref.py \
src/test/hs_build_address.py \
diff --git a/src/test/test.c b/src/test/test.c
index 422e181b94..2963f169cf 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -863,6 +863,7 @@ struct testgroup_t testgroups[] = {
{ "oom/", oom_tests },
{ "oos/", oos_tests },
{ "options/", options_tests },
+ { "periodic-event/" , periodic_event_tests },
{ "policy/" , policy_tests },
{ "procmon/", procmon_tests },
{ "proto/http/", proto_http_tests },
diff --git a/src/test/test.h b/src/test/test.h
index 1728831ed0..15965ccaf4 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -239,6 +239,7 @@ extern struct testcase_t nodelist_tests[];
extern struct testcase_t oom_tests[];
extern struct testcase_t oos_tests[];
extern struct testcase_t options_tests[];
+extern struct testcase_t periodic_event_tests[];
extern struct testcase_t policy_tests[];
extern struct testcase_t procmon_tests[];
extern struct testcase_t proto_http_tests[];
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 1c285bb3a2..3b58a78e1d 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -1470,6 +1470,61 @@ test_download_status_bridge(void *arg)
return;
}
+/** Set timeval to a mock date and time. This is neccessary
+ * to make tor_gettimeofday() mockable. */
+static void
+mock_tor_gettimeofday(struct timeval *timeval)
+{
+ timeval->tv_sec = 1523405073;
+ timeval->tv_usec = 271645;
+}
+
+static void
+test_current_time(void *arg)
+{
+ /* We just need one of these to pass, it doesn't matter what's in it */
+ control_connection_t dummy;
+ /* Get results out */
+ char *answer = NULL;
+ const char *errmsg = NULL;
+
+ (void)arg;
+
+ /* We need these for storing the (mock) time. */
+ MOCK(tor_gettimeofday, mock_tor_gettimeofday);
+ struct timeval now;
+ tor_gettimeofday(&now);
+ char timebuf[ISO_TIME_LEN+1];
+
+ /* Case 1 - local time */
+ format_local_iso_time_nospace(timebuf, (time_t)now.tv_sec);
+ getinfo_helper_current_time(&dummy,
+ "current-time/local",
+ &answer, &errmsg);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
+ tt_str_op(answer, OP_EQ, timebuf);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 2 - UTC time */
+ format_iso_time_nospace(timebuf, (time_t)now.tv_sec);
+ getinfo_helper_current_time(&dummy,
+ "current-time/utc",
+ &answer, &errmsg);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
+ tt_str_op(answer, OP_EQ, timebuf);
+ tor_free(answer);
+ errmsg = NULL;
+
+ done:
+ UNMOCK(tor_gettimeofday);
+ tor_free(answer);
+
+ return;
+}
+
struct testcase_t controller_tests[] = {
{ "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0,
NULL, NULL },
@@ -1486,6 +1541,7 @@ struct testcase_t controller_tests[] = {
NULL },
{ "download_status_desc", test_download_status_desc, 0, NULL, NULL },
{ "download_status_bridge", test_download_status_bridge, 0, NULL, NULL },
+ { "current_time", test_current_time, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 5fac045b26..df5ae2d594 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -2917,8 +2917,9 @@ gen_routerstatus_for_umbw(int idx, time_t now)
rs->addr = 0x99008801;
rs->or_port = 443;
rs->dir_port = 8000;
- /* all flags but running cleared */
+ /* all flags but running and valid cleared */
rs->is_flagged_running = 1;
+ rs->is_valid = 1;
/*
* This one has measured bandwidth below the clip cutoff, and
* so shouldn't be clipped; we'll have to test that it isn't
@@ -2991,8 +2992,9 @@ gen_routerstatus_for_umbw(int idx, time_t now)
rs->addr = 0xC0000203;
rs->or_port = 500;
rs->dir_port = 1999;
- /* all flags but running cleared */
+ /* all flags but running and valid cleared */
rs->is_flagged_running = 1;
+ rs->is_valid = 1;
/*
* This one has unmeasured bandwidth below the clip cutoff, and
* so shouldn't be clipped; we'll have to test that it isn't
@@ -3014,7 +3016,7 @@ gen_routerstatus_for_umbw(int idx, time_t now)
if (vrs) {
vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
tor_asprintf(&vrs->microdesc->microdesc_hash_line,
- "m 9,10,11,12,13,14,15,16,17 "
+ "m 25,26,27,28 "
"sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa%d\n",
idx);
}
@@ -3040,7 +3042,7 @@ vote_tweaks_for_umbw(networkstatus_t *v, int voter, time_t now)
smartlist_clear(v->supported_methods);
/* Method 17 is MIN_METHOD_TO_CLIP_UNMEASURED_BW_KB */
smartlist_split_string(v->supported_methods,
- "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17",
+ "25 26 27 28",
NULL, 0, -1);
/* If we're using a non-default clip bandwidth, add it to net_params */
if (alternate_clip_bw > 0) {
@@ -3202,9 +3204,9 @@ test_routerstatus_for_umbw(routerstatus_t *rs, time_t now)
tt_assert(!rs->is_fast);
tt_assert(!rs->is_possible_guard);
tt_assert(!rs->is_stable);
- /* (If it wasn't running it wouldn't be here) */
+ /* (If it wasn't running and valid it wouldn't be here) */
tt_assert(rs->is_flagged_running);
- tt_assert(!rs->is_valid);
+ tt_assert(rs->is_valid);
tt_assert(!rs->is_named);
/* This one should have measured bandwidth below the clip cutoff */
tt_assert(rs->has_bandwidth);
@@ -5615,9 +5617,8 @@ test_dir_assumed_flags(void *arg)
memarea_t *area = memarea_new();
routerstatus_t *rs = NULL;
- /* First, we should always assume that the Running flag is set, even
- * when it isn't listed, since the consensus method is always
- * higher than 4. */
+ /* We can assume that consensus method is higher than 24, so Running and
+ * Valid are always implicitly set */
const char *str1 =
"r example hereiswhereyouridentitygoes 2015-08-30 12:00:00 "
"192.168.0.1 9001 0\n"
@@ -5626,17 +5627,6 @@ test_dir_assumed_flags(void *arg)
const char *cp = str1;
rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL,
- 23, FLAV_MICRODESC);
- tt_assert(rs);
- tt_assert(rs->is_flagged_running);
- tt_assert(! rs->is_valid);
- tt_assert(! rs->is_exit);
- tt_assert(rs->is_fast);
- routerstatus_free(rs);
-
- /* With method 24 or later, we can assume "valid" is set. */
- cp = str1;
- rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL,
24, FLAV_MICRODESC);
tt_assert(rs);
tt_assert(rs->is_flagged_running);
@@ -5769,22 +5759,10 @@ test_dir_networkstatus_consensus_has_ipv6(void *arg)
/* Test the bounds for A lines in the NS consensus */
mock_options->UseMicrodescriptors = 0;
- mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES;
+ mock_networkstatus->consensus_method = MIN_SUPPORTED_CONSENSUS_METHOD;
has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
tt_assert(has_ipv6);
- mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES + 1;
- has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
- tt_assert(has_ipv6);
-
- mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES + 20;
- has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
- tt_assert(has_ipv6);
-
- mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES - 1;
- has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
- tt_assert(!has_ipv6);
-
/* Test the bounds for A lines in the microdesc consensus */
mock_options->UseMicrodescriptors = 1;
@@ -5793,6 +5771,10 @@ test_dir_networkstatus_consensus_has_ipv6(void *arg)
has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
tt_assert(has_ipv6);
+ mock_networkstatus->consensus_method = MAX_SUPPORTED_CONSENSUS_METHOD + 20;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(has_ipv6);
+
mock_networkstatus->consensus_method =
MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS + 1;
has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index 92a860360d..f55e9f0173 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -2679,6 +2679,23 @@ test_enty_guard_should_expire_waiting(void *arg)
tor_free(fake_state);
}
+/** Test that the number of primary guards can be controlled using torrc */
+static void
+test_entry_guard_number_of_primaries(void *arg)
+{
+ (void) arg;
+
+ /* Get default value */
+ tt_int_op(get_n_primary_guards(), OP_EQ, DFLT_N_PRIMARY_GUARDS);
+
+ /* Set number of primaries using torrc */
+ get_options_mutable()->NumPrimaryGuards = 42;
+ tt_int_op(get_n_primary_guards(), OP_EQ, 42);
+
+ done:
+ ;
+}
+
static void
mock_directory_initiate_request(directory_request_t *req)
{
@@ -2826,6 +2843,8 @@ struct testcase_t entrynodes_tests[] = {
test_entry_guard_parse_from_state_broken, TT_FORK, NULL, NULL },
{ "get_guard_selection_by_name",
test_entry_guard_get_guard_selection_by_name, TT_FORK, NULL, NULL },
+ { "number_of_primaries",
+ test_entry_guard_number_of_primaries, TT_FORK, NULL, NULL },
BFN_TEST(choose_selection_initial),
BFN_TEST(add_single_guard),
BFN_TEST(node_filter),
diff --git a/src/test/test_geoip.c b/src/test/test_geoip.c
index 20f6114dc0..0711a113eb 100644
--- a/src/test/test_geoip.c
+++ b/src/test/test_geoip.c
@@ -444,6 +444,86 @@ test_geoip_load_file(void *arg)
tor_free(dhex);
}
+static void
+test_geoip6_load_file(void *arg)
+{
+ (void)arg;
+ struct in6_addr iaddr6;
+ char *contents = NULL;
+ char *dhex = NULL;
+
+ /* A nonexistant filename should fail. */
+ tt_int_op(-1, OP_EQ,
+ geoip_load_file(AF_INET6, "/you/did/not/put/a/file/here/I/hope"));
+
+ /* Any lookup attempt should say "-1" because we have no info */
+ tor_inet_pton(AF_INET6, "2001:4860:4860::8888", &iaddr6);
+ tt_int_op(-1, OP_EQ, geoip_get_country_by_ipv6(&iaddr6));
+
+ /* Load geiop6 file */
+ const char FNAME6[] = SRCDIR "/src/config/geoip6";
+ tt_int_op(0, OP_EQ, geoip_load_file(AF_INET6, FNAME6));
+
+ /* Check that we loaded some countries; this will fail if there are ever
+ * fewer than 50 countries in the world. */
+ tt_int_op(geoip_get_n_countries(), OP_GE, 50);
+
+ /* Let's see where 2001:4860:4860::8888 (google dns) is. */
+ const char *caddr6 = "2001:4860:4860::8888";
+ tor_inet_pton(AF_INET6, caddr6, &iaddr6);
+ int country6 = geoip_get_country_by_ipv6(&iaddr6);
+ tt_int_op(country6, OP_GE, 1);
+
+ const char *cc6 = geoip_get_country_name(country6);
+ tt_int_op(strlen(cc6), OP_EQ, 2);
+
+ /* The digest should be set.... */
+ tt_str_op("0000000000000000000000000000000000000000", OP_NE,
+ geoip_db_digest(AF_INET6));
+
+ /* And it should be set correctly */
+ contents = read_file_to_str(FNAME6, RFTS_BIN, NULL);
+ uint8_t d[DIGEST_LEN];
+ crypto_digest((char*)d, contents, strlen(contents));
+ dhex = tor_strdup(hex_str((char*)d, DIGEST_LEN));
+ tt_str_op(dhex, OP_EQ, geoip_db_digest(AF_INET6));
+
+ /* Make sure geoip_free_all() works. */
+ geoip_free_all();
+ tt_int_op(1, OP_EQ, geoip_get_n_countries());
+ tt_str_op("??", OP_EQ, geoip_get_country_name(0));
+ tor_inet_pton(AF_INET6, "::1:2:3:4", &iaddr6);
+ tt_int_op(-1, OP_EQ, geoip_get_country_by_ipv6(&iaddr6));
+ tt_str_op("0000000000000000000000000000000000000000", OP_EQ,
+ geoip_db_digest(AF_INET6));
+
+ done:
+ tor_free(contents);
+ tor_free(dhex);
+}
+
+static void
+test_geoip_load_2nd_file(void *arg)
+{
+ (void)arg;
+
+ /* Load 1st geoip file */
+ const char FNAME[] = SRCDIR "/src/config/geoip";
+ tt_int_op(0, OP_EQ, geoip_load_file(AF_INET, FNAME));
+
+ /* Load 2nd geoip (empty) file */
+ /* It has to be the same IP address family */
+ const char FNAME2[] = SRCDIR "/src/test/geoip_dummy";
+ tt_int_op(0, OP_EQ, geoip_load_file(AF_INET, FNAME2));
+
+ /* Check that there is no geoip information for 8.8.8.8, */
+ /* since loading the empty 2nd file should have delete it. */
+ int country = geoip_get_country_by_ipv4(0x08080808);
+ tt_int_op(country, OP_EQ, 0);
+
+ done: ;
+}
+
#define ENT(name) \
{ #name, test_ ## name , 0, NULL, NULL }
#define FORK(name) \
@@ -459,6 +539,10 @@ struct testcase_t geoip_tests[] = {
{ "geoip", test_geoip, TT_FORK, NULL, NULL },
{ "geoip_with_pt", test_geoip_with_pt, TT_FORK, NULL, NULL },
{ "load_file", test_geoip_load_file, TT_FORK|SKIP_ON_WINDOWS, NULL, NULL },
+ { "load_file6", test_geoip6_load_file, TT_FORK | SKIP_ON_WINDOWS,
+ NULL, NULL },
+ { "load_2nd_file", test_geoip_load_2nd_file, TT_FORK | SKIP_ON_WINDOWS,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index 9189bb65be..64448de510 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -361,6 +361,7 @@ test_pick_tor2web_rendezvous_node(void *arg)
/* Parse Tor2webRendezvousPoints as a routerset. */
options->Tor2webRendezvousPoints = routerset_new();
+ options->UseMicrodescriptors = 0;
retval = routerset_parse(options->Tor2webRendezvousPoints,
tor2web_rendezvous_str,
"test_tor2web_rp");
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index 8c273c9639..17ba11ca7d 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -305,7 +305,8 @@ helper_add_hsdir_to_networkstatus(networkstatus_t *ns,
node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
tt_assert(node);
node->rs = rs;
- /* We need this to exist for node_has_descriptor() to return true. */
+ /* We need this to exist for node_has_preferred_descriptor() to return
+ * true. */
node->md = tor_malloc_zero(sizeof(microdesc_t));
/* Do this now the nodelist_set_routerinfo() function needs a "rs" to set
* the indexes which it doesn't have when it is called. */
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
index 59b28f7580..3d02fc1a59 100644
--- a/src/test/test_microdesc.c
+++ b/src/test/test_microdesc.c
@@ -385,25 +385,6 @@ static const char test_ri2[] =
"cf34GXHv61XReJF3AlzNHFpbrPOYmowmhrTULKyMqow=\n"
"-----END SIGNATURE-----\n";
-static const char test_md_8[] =
- "onion-key\n"
- "-----BEGIN RSA PUBLIC KEY-----\n"
- "MIGJAoGBANBJz8Vldl12aFeSMPLiA4nOetLDN0oxU8bB1SDhO7Uu2zdWYVYAF5J0\n"
- "st7WvrVy/jA9v/fsezNAPskBanecHRSkdMTpkcgRPMHE7CTGEwIy1Yp1X4bPgDlC\n"
- "VCnbs5Pcts5HnWEYNK7qHDAUn+IlmjOO+pTUY8uyq+GQVz6H9wFlAgMBAAE=\n"
- "-----END RSA PUBLIC KEY-----\n"
- "p reject 25,119,135-139,445,563,1214,4661-4666,6346-6429,6699,6881-6999\n";
-
-static const char test_md_16[] =
- "onion-key\n"
- "-----BEGIN RSA PUBLIC KEY-----\n"
- "MIGJAoGBANBJz8Vldl12aFeSMPLiA4nOetLDN0oxU8bB1SDhO7Uu2zdWYVYAF5J0\n"
- "st7WvrVy/jA9v/fsezNAPskBanecHRSkdMTpkcgRPMHE7CTGEwIy1Yp1X4bPgDlC\n"
- "VCnbs5Pcts5HnWEYNK7qHDAUn+IlmjOO+pTUY8uyq+GQVz6H9wFlAgMBAAE=\n"
- "-----END RSA PUBLIC KEY-----\n"
- "ntor-onion-key Gg73xH7+kTfT6bi1uNVx9gwQdQas9pROIfmc4NpAdC4=\n"
- "p reject 25,119,135-139,445,563,1214,4661-4666,6346-6429,6699,6881-6999\n";
-
static const char test_md_18[] =
"onion-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
@@ -415,16 +396,6 @@ static const char test_md_18[] =
"p reject 25,119,135-139,445,563,1214,4661-4666,6346-6429,6699,6881-6999\n"
"id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4\n";
-static const char test_md2_18[] =
- "onion-key\n"
- "-----BEGIN RSA PUBLIC KEY-----\n"
- "MIGJAoGBAL2R8EfubUcahxha4u02P4VAR0llQIMwFAmrHPjzcK7apcQgDOf2ovOA\n"
- "+YQnJFxlpBmCoCZC6ssCi+9G0mqo650lFuTMP5I90BdtjotfzESfTykHLiChyvhd\n"
- "l0dlqclb2SU/GKem/fLRXH16aNi72CdSUu/1slKs/70ILi34QixRAgMBAAE=\n"
- "-----END RSA PUBLIC KEY-----\n"
- "ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n"
- "id rsa1024 t+J/EEITw28T5+mCkYKEXklZl6A\n";
-
static const char test_md2_21[] =
"onion-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
@@ -444,17 +415,6 @@ test_md_generate(void *arg)
ri = router_parse_entry_from_string(test_ri, NULL, 0, 0, NULL, NULL);
tt_assert(ri);
- md = dirvote_create_microdescriptor(ri, 8);
- tt_str_op(md->body, OP_EQ, test_md_8);
-
- /* XXXX test family lines. */
- /* XXXX test method 14 for A lines. */
- /* XXXX test method 15 for P6 lines. */
-
- microdesc_free(md);
- md = NULL;
- md = dirvote_create_microdescriptor(ri, 16);
- tt_str_op(md->body, OP_EQ, test_md_16);
microdesc_free(md);
md = NULL;
@@ -471,11 +431,6 @@ test_md_generate(void *arg)
microdesc_free(md);
md = NULL;
- md = dirvote_create_microdescriptor(ri, 18);
- tt_str_op(md->body, OP_EQ, test_md2_18);
-
- microdesc_free(md);
- md = NULL;
md = dirvote_create_microdescriptor(ri, 21);
tt_str_op(md->body, OP_EQ, test_md2_21);
tt_assert(ed25519_pubkey_eq(md->ed25519_identity_pkey,
diff --git a/src/test/test_periodic_event.c b/src/test/test_periodic_event.c
new file mode 100644
index 0000000000..74e799360e
--- /dev/null
+++ b/src/test/test_periodic_event.c
@@ -0,0 +1,323 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_periodic_event.c
+ * \brief Test the periodic events that Tor uses for different roles. They are
+ * part of the libevent mainloop
+ */
+
+#define CONFIG_PRIVATE
+#define HS_SERVICE_PRIVATE
+#define MAIN_PRIVATE
+
+#include "test.h"
+#include "test_helpers.h"
+
+#include "or.h"
+#include "config.h"
+#include "hibernate.h"
+#include "hs_service.h"
+#include "main.h"
+#include "periodic.h"
+
+/** Helper function: This is replaced in some tests for the event callbacks so
+ * we don't actually go into the code path of those callbacks. */
+static int
+dumb_event_fn(time_t now, const or_options_t *options)
+{
+ (void) now;
+ (void) options;
+
+ /* Will get rescheduled in 300 seconds. It just can't be 0. */
+ return 300;
+}
+
+static void
+register_dummy_hidden_service(hs_service_t *service)
+{
+ memset(service, 0, sizeof(hs_service_t));
+ memset(&service->keys.identity_pk, 'A', sizeof(service->keys.identity_pk));
+ (void) register_service(get_hs_service_map(), service);
+}
+
+static void
+test_pe_initialize(void *arg)
+{
+ (void) arg;
+
+ /* Initialize the events but the callback won't get called since we would
+ * need to run the main loop and then wait for a second delaying the unit
+ * tests. Instead, we'll test the callback work indepedently elsewhere. */
+ initialize_periodic_events();
+
+ /* Validate that all events have been set up. */
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ tt_assert(item->ev);
+ tt_assert(item->fn);
+ tt_u64_op(item->last_action_time, OP_EQ, 0);
+ /* Every event must have role(s) assign to it. This is done statically. */
+ tt_u64_op(item->roles, OP_NE, 0);
+ tt_uint_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ }
+
+ done:
+ teardown_periodic_events();
+}
+
+static void
+test_pe_launch(void *arg)
+{
+ hs_service_t service, *to_remove = NULL;
+ or_options_t *options;
+
+ (void) arg;
+
+ hs_init();
+ /* We need to put tor in hibernation live state so the events requiring
+ * network gets enabled. */
+ consider_hibernation(time(NULL));
+
+ /* Hack: We'll set a dumb fn() of each events so they don't get called when
+ * dispatching them. We just want to test the state of the callbacks, not
+ * the whole code path. */
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ item->fn = dumb_event_fn;
+ }
+
+ /* Lets make sure that before intialization, we can't scan the periodic
+ * events list and launch them. Lets try by being a Client. */
+ options = get_options_mutable();
+ options->SocksPort_set = 1;
+ periodic_events_on_new_options(options);
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ }
+
+ initialize_periodic_events();
+
+ /* Now that we've initialized, rescan the list to launch. */
+ periodic_events_on_new_options(options);
+
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ if (item->roles & PERIODIC_EVENT_ROLE_CLIENT) {
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
+ tt_u64_op(item->last_action_time, OP_NE, 0);
+ } else {
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ tt_u64_op(item->last_action_time, OP_EQ, 0);
+ }
+ }
+
+ /* Remove Client but become a Relay. */
+ options->SocksPort_set = 0;
+ options->ORPort_set = 1;
+ periodic_events_on_new_options(options);
+
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ /* Only Client role should be disabled. */
+ if (item->roles == PERIODIC_EVENT_ROLE_CLIENT) {
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ /* Was previously enabled so they should never be to 0. */
+ tt_u64_op(item->last_action_time, OP_NE, 0);
+ }
+ if (item->roles & PERIODIC_EVENT_ROLE_RELAY) {
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
+ tt_u64_op(item->last_action_time, OP_NE, 0);
+ }
+ /* Non Relay role should be disabled! */
+ if (!(item->roles & PERIODIC_EVENT_ROLE_RELAY)) {
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ }
+ }
+
+ /* Disable everything and we'll enable them ALL. */
+ options->SocksPort_set = 0;
+ options->ORPort_set = 0;
+ periodic_events_on_new_options(options);
+
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ }
+
+ /* Enable everything. */
+ options->SocksPort_set = 1; options->ORPort_set = 1;
+ options->BridgeRelay = 1; options->AuthoritativeDir = 1;
+ options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1;
+ register_dummy_hidden_service(&service);
+ periodic_events_on_new_options(options);
+ /* Note down the reference because we need to remove this service from the
+ * global list before the hs_free_all() call so it doesn't try to free
+ * memory on the stack. Furthermore, we can't remove it now else it will
+ * trigger a rescan of the event disabling the HS service event. */
+ to_remove = &service;
+
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
+ }
+
+ done:
+ if (to_remove) {
+ remove_service(get_hs_service_map(), to_remove);
+ }
+ hs_free_all();
+}
+
+static void
+test_pe_get_roles(void *arg)
+{
+ int roles;
+
+ (void) arg;
+
+ /* Just so the HS global map exists. */
+ hs_init();
+
+ or_options_t *options = get_options_mutable();
+ tt_assert(options);
+
+ /* Nothing configured, should be no roles. */
+ roles = get_my_roles(options);
+ tt_int_op(roles, OP_EQ, 0);
+
+ /* Indicate we have a SocksPort, roles should be come Client. */
+ options->SocksPort_set = 1;
+ roles = get_my_roles(options);
+ tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT);
+
+ /* Now, we'll add a ORPort so should now be a Relay + Client. */
+ options->ORPort_set = 1;
+ roles = get_my_roles(options);
+ tt_int_op(roles, OP_EQ,
+ (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY));
+
+ /* Now add a Bridge. */
+ options->BridgeRelay = 1;
+ roles = get_my_roles(options);
+ tt_int_op(roles, OP_EQ,
+ (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
+ PERIODIC_EVENT_ROLE_BRIDGE));
+ tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER);
+ /* Unset client so we can solely test Router role. */
+ options->SocksPort_set = 0;
+ roles = get_my_roles(options);
+ tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_ROUTER);
+
+ /* Reset options so we can test authorities. */
+ options->SocksPort_set = 0;
+ options->ORPort_set = 0;
+ options->BridgeRelay = 0;
+ roles = get_my_roles(options);
+ tt_int_op(roles, OP_EQ, 0);
+
+ /* Now upgrade to Dirauth. */
+ options->AuthoritativeDir = 1;
+ options->V3AuthoritativeDir = 1;
+ roles = get_my_roles(options);
+ tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_DIRAUTH);
+ tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
+
+ /* Now Bridge Authority. */
+ options->V3AuthoritativeDir = 0;
+ options->BridgeAuthoritativeDir = 1;
+ roles = get_my_roles(options);
+ tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_BRIDGEAUTH);
+ tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
+
+ /* Move that bridge auth to become a relay. */
+ options->ORPort_set = 1;
+ roles = get_my_roles(options);
+ tt_int_op(roles, OP_EQ,
+ (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY));
+ tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
+
+ /* And now an Hidden service. */
+ hs_service_t service;
+ register_dummy_hidden_service(&service);
+ roles = get_my_roles(options);
+ /* Remove it now so the hs_free_all() doesn't try to free stack memory. */
+ remove_service(get_hs_service_map(), &service);
+ tt_int_op(roles, OP_EQ,
+ (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY |
+ PERIODIC_EVENT_ROLE_HS_SERVICE));
+ tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
+
+ done:
+ hs_free_all();
+}
+
+static void
+test_pe_hs_service(void *arg)
+{
+ hs_service_t service, *to_remove = NULL;
+
+ (void) arg;
+
+ hs_init();
+ /* We need to put tor in hibernation live state so the events requiring
+ * network gets enabled. */
+ consider_hibernation(time(NULL));
+ /* Initialize the events so we can enable them */
+ initialize_periodic_events();
+
+ /* Hack: We'll set a dumb fn() of each events so they don't get called when
+ * dispatching them. We just want to test the state of the callbacks, not
+ * the whole code path. */
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ item->fn = dumb_event_fn;
+ }
+
+ /* This should trigger a rescan of the list and enable the HS service
+ * events. */
+ register_dummy_hidden_service(&service);
+ /* Note down the reference because we need to remove this service from the
+ * global list before the hs_free_all() call so it doesn't try to free
+ * memory on the stack. Furthermore, we can't remove it now else it will
+ * trigger a rescan of the event disabling the HS service event. */
+ to_remove = &service;
+
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
+ }
+ }
+ to_remove = NULL;
+
+ /* Remove the service from the global map, it should trigger a rescan and
+ * disable the HS service events. */
+ remove_service(get_hs_service_map(), &service);
+ for (int i = 0; periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &periodic_events[i];
+ if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ }
+ }
+
+ done:
+ if (to_remove) {
+ remove_service(get_hs_service_map(), to_remove);
+ }
+ hs_free_all();
+}
+
+#define PE_TEST(name) \
+ { #name, test_pe_## name , TT_FORK, NULL, NULL }
+
+struct testcase_t periodic_event_tests[] = {
+ PE_TEST(initialize),
+ PE_TEST(launch),
+ PE_TEST(get_roles),
+ PE_TEST(hs_service),
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 24b43c899b..350273bf4c 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -6050,6 +6050,23 @@ test_util_monotonic_time_add_msec(void *arg)
}
static void
+test_util_nowrap_math(void *arg)
+{
+ (void)arg;
+
+ tt_u64_op(0, OP_EQ, tor_add_u32_nowrap(0, 0));
+ tt_u64_op(1, OP_EQ, tor_add_u32_nowrap(0, 1));
+ tt_u64_op(1, OP_EQ, tor_add_u32_nowrap(1, 0));
+ tt_u64_op(4, OP_EQ, tor_add_u32_nowrap(2, 2));
+ tt_u64_op(UINT32_MAX, OP_EQ, tor_add_u32_nowrap(UINT32_MAX-1, 2));
+ tt_u64_op(UINT32_MAX, OP_EQ, tor_add_u32_nowrap(2, UINT32_MAX-1));
+ tt_u64_op(UINT32_MAX, OP_EQ, tor_add_u32_nowrap(UINT32_MAX, UINT32_MAX));
+
+ done:
+ ;
+}
+
+static void
test_util_htonll(void *arg)
{
(void)arg;
@@ -6243,6 +6260,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(listdir, 0),
UTIL_TEST(parent_dir, 0),
UTIL_TEST(ftruncate, 0),
+ UTIL_TEST(nowrap_math, 0),
UTIL_TEST(num_cpus, 0),
UTIL_TEST_WIN_ONLY(load_win_lib, 0),
UTIL_TEST_NO_WIN(exit_status, 0),
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index b9b36d96d0..312c07471d 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -8,6 +8,7 @@
* \brief Common pieces to implement unit tests.
**/
+#define MAIN_PRIVATE
#include "orconfig.h"
#include "or.h"
#include "control.h"
@@ -16,6 +17,7 @@
#include "backtrace.h"
#include "test.h"
#include "channelpadding.h"
+#include "main.h"
#include <stdio.h>
#ifdef HAVE_FCNTL_H
@@ -290,6 +292,7 @@ main(int c, const char **v)
}
rep_hist_init();
setup_directory();
+ initialize_mainloop_events();
options_init(options);
options->DataDirectory = tor_strdup(temp_dir);
tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys",