summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/aes.c2
-rw-r--r--src/common/compat_libevent.c31
-rw-r--r--src/common/compat_libevent.h2
-rw-r--r--src/common/compat_openssl.h6
-rw-r--r--src/common/crypto.c16
-rw-r--r--src/common/crypto_digest.c15
-rw-r--r--src/common/crypto_rand.c6
-rw-r--r--src/common/tortls.c4
m---------src/ext/rust0
-rw-r--r--src/or/addressmap.c4
-rw-r--r--src/or/circuitbuild.c139
-rw-r--r--src/or/circuitbuild.h9
-rw-r--r--src/or/config.c23
-rw-r--r--src/or/config.h3
-rw-r--r--src/or/connection.c15
-rw-r--r--src/or/connection_edge.c1
-rw-r--r--src/or/control.c89
-rw-r--r--src/or/control.h3
-rw-r--r--src/or/dirserv.c14
-rw-r--r--src/or/entrynodes.c11
-rw-r--r--src/or/entrynodes.h1
-rw-r--r--src/or/hibernate.c90
-rw-r--r--src/or/hibernate.h2
-rw-r--r--src/or/hs_descriptor.c2
-rw-r--r--src/or/hs_service.c5
-rw-r--r--src/or/hs_stats.c6
-rw-r--r--src/or/main.c190
-rw-r--r--src/or/main.h3
-rw-r--r--src/or/networkstatus.c20
-rw-r--r--src/or/networkstatus.h2
-rw-r--r--src/or/periodic.c2
-rw-r--r--src/or/rendservice.c5
-rw-r--r--src/or/router.c18
-rw-r--r--src/or/router.h1
-rw-r--r--src/or/status.c6
-rw-r--r--src/rust/Cargo.lock35
-rw-r--r--src/rust/Cargo.toml11
-rw-r--r--src/rust/crypto/Cargo.toml21
-rw-r--r--src/rust/crypto/digests/mod.rs7
-rw-r--r--src/rust/crypto/digests/sha2.rs213
-rw-r--r--src/rust/crypto/lib.rs36
-rw-r--r--src/rust/external/Cargo.toml3
-rw-r--r--src/rust/external/crypto_digest.rs402
-rw-r--r--src/rust/external/lib.rs4
-rw-r--r--src/rust/include.am5
-rw-r--r--src/test/include.am1
-rw-r--r--src/test/test.c1
-rw-r--r--src/test/test.h1
-rw-r--r--src/test/test_addr.c155
-rw-r--r--src/test/test_dir.c27
-rw-r--r--src/test/test_entrynodes.c201
-rw-r--r--src/test/test_mainloop.c142
-rw-r--r--src/test/test_tortls.c5
-rw-r--r--src/test/test_workqueue.c12
54 files changed, 1825 insertions, 203 deletions
diff --git a/src/common/aes.c b/src/common/aes.c
index a83a654348..f6b933374a 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -115,7 +115,7 @@ aes_cipher_free_(aes_cnt_cipher_t *cipher_)
if (!cipher_)
return;
EVP_CIPHER_CTX *cipher = (EVP_CIPHER_CTX *) cipher_;
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+#ifdef OPENSSL_1_1_API
EVP_CIPHER_CTX_reset(cipher);
#else
EVP_CIPHER_CTX_cleanup(cipher);
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index fa00fb836b..56ee682fde 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -253,10 +253,39 @@ periodic_timer_new(struct event_base *base,
}
timer->cb = cb;
timer->data = data;
- event_add(timer->ev, (struct timeval *)tv); /*drop const for old libevent*/
+ periodic_timer_launch(timer, tv);
return timer;
}
+/**
+ * Launch the timer <b>timer</b> to run at <b>tv</b> from now, and every
+ * <b>tv</b> thereafter.
+ *
+ * If the timer is already enabled, this function does nothing.
+ */
+void
+periodic_timer_launch(periodic_timer_t *timer, const struct timeval *tv)
+{
+ tor_assert(timer);
+ if (event_pending(timer->ev, EV_TIMEOUT, NULL))
+ return;
+ event_add(timer->ev, tv);
+}
+
+/**
+ * Disable the provided <b>timer</b>, but do not free it.
+ *
+ * You can reenable the same timer later with periodic_timer_launch.
+ *
+ * If the timer is already disabled, this function does nothing.
+ */
+void
+periodic_timer_disable(periodic_timer_t *timer)
+{
+ tor_assert(timer);
+ event_del(timer->ev);
+}
+
/** Stop and free a periodic timer */
void
periodic_timer_free_(periodic_timer_t *timer)
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index e2747860a9..286a268122 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -31,6 +31,8 @@ periodic_timer_t *periodic_timer_new(struct event_base *base,
void (*cb)(periodic_timer_t *timer, void *data),
void *data);
void periodic_timer_free_(periodic_timer_t *);
+void periodic_timer_launch(periodic_timer_t *, const struct timeval *tv);
+void periodic_timer_disable(periodic_timer_t *);
#define periodic_timer_free(t) \
FREE_AND_NULL(periodic_timer_t, periodic_timer_free_, (t))
diff --git a/src/common/compat_openssl.h b/src/common/compat_openssl.h
index 1299ac36bb..d1481fb46c 100644
--- a/src/common/compat_openssl.h
+++ b/src/common/compat_openssl.h
@@ -9,6 +9,7 @@
#include <openssl/opensslv.h>
#include "crypto_openssl_mgt.h"
+
/**
* \file compat_openssl.h
*
@@ -27,8 +28,11 @@
#define OPENSSL_1_1_API
#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) && ... */
-#ifndef OPENSSL_1_1_API
+#ifndef OPENSSL_VERSION
#define OPENSSL_VERSION SSLEAY_VERSION
+#endif
+
+#ifndef OPENSSL_1_1_API
#define OpenSSL_version(v) SSLeay_version(v)
#define OpenSSL_version_num() SSLeay()
#define RAND_OpenSSL() RAND_SSLeay()
diff --git a/src/common/crypto.c b/src/common/crypto.c
index e148878be0..d5b7c96916 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -60,12 +60,6 @@ ENABLE_GCC_WARNING(redundant-decls)
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
-#ifdef HAVE_SYS_SYSCALL_H
-#include <sys/syscall.h>
-#endif
-#ifdef HAVE_SYS_RANDOM_H
-#include <sys/random.h>
-#endif
#include "torlog.h"
#include "torint.h"
@@ -175,7 +169,7 @@ crypto_early_init(void)
crypto_early_initialized_ = 1;
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+#ifdef OPENSSL_1_1_API
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
OPENSSL_INIT_ADD_ALL_CIPHERS |
@@ -1068,13 +1062,13 @@ crypto_dh_free_(crypto_dh_t *dh)
int
crypto_global_cleanup(void)
{
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
+#ifndef OPENSSL_1_1_API
EVP_cleanup();
#endif
#ifndef NEW_THREAD_API
ERR_remove_thread_state(NULL);
#endif
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
+#ifndef OPENSSL_1_1_API
ERR_free_strings();
#endif
@@ -1088,13 +1082,13 @@ crypto_global_cleanup(void)
dh_param_p = dh_param_p_tls = dh_param_g = NULL;
#ifndef DISABLE_ENGINES
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
+#ifndef OPENSSL_1_1_API
ENGINE_cleanup();
#endif
#endif
CONF_modules_unload(1);
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
+#ifndef OPENSSL_1_1_API
CRYPTO_cleanup_all_ex_data();
#endif
diff --git a/src/common/crypto_digest.c b/src/common/crypto_digest.c
index f7163de133..9f9a1a1e2c 100644
--- a/src/common/crypto_digest.c
+++ b/src/common/crypto_digest.c
@@ -268,7 +268,11 @@ crypto_digest_new(void)
}
/** Allocate and return a new digest object to compute 256-bit digests
- * using <b>algorithm</b>. */
+ * using <b>algorithm</b>.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest256_new`
+ * C_RUST_COUPLED: `crypto::digest::Sha256::default`
+ */
crypto_digest_t *
crypto_digest256_new(digest_algorithm_t algorithm)
{
@@ -298,6 +302,9 @@ crypto_digest_free_(crypto_digest_t *digest)
}
/** Add <b>len</b> bytes from <b>data</b> to the digest object.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_add_bytess`
+ * C_RUST_COUPLED: `crypto::digest::Sha256::process`
*/
void
crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
@@ -335,6 +342,9 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
/** Compute the hash of the data that has been passed to the digest
* object; write the first out_len bytes of the result to <b>out</b>.
* <b>out_len</b> must be \<= DIGEST512_LEN.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_get_digest`
+ * C_RUST_COUPLED: `impl digest::FixedOutput for Sha256`
*/
void
crypto_digest_get_digest(crypto_digest_t *digest,
@@ -383,6 +393,9 @@ crypto_digest_get_digest(crypto_digest_t *digest,
/** Allocate and return a new digest object with the same state as
* <b>digest</b>
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_dup`
+ * C_RUST_COUPLED: `impl Clone for crypto::digest::Sha256`
*/
crypto_digest_t *
crypto_digest_dup(const crypto_digest_t *digest)
diff --git a/src/common/crypto_rand.c b/src/common/crypto_rand.c
index 5abf0b0968..df2e2f65d3 100644
--- a/src/common/crypto_rand.c
+++ b/src/common/crypto_rand.c
@@ -55,6 +55,12 @@ ENABLE_GCC_WARNING(redundant-decls)
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
/**
* How many bytes of entropy we add at once.
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 10b0319bec..669742c9dd 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -58,7 +58,7 @@ ENABLE_GCC_WARNING(redundant-decls)
#include "container.h"
#include <string.h>
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+#ifdef OPENSSL_1_1_API
#define X509_get_notBefore_const(cert) \
X509_get0_notBefore(cert)
#define X509_get_notAfter_const(cert) \
@@ -372,7 +372,7 @@ tor_tls_init(void)
check_no_tls_errors();
if (!tls_library_is_initialized) {
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+#ifdef OPENSSL_1_1_API
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
#else
SSL_library_init();
diff --git a/src/ext/rust b/src/ext/rust
-Subproject a870933f6b20fc9c47a13e9d980d56b49e30ddb
+Subproject e92c124a41535bd2131b9506a7d95c68c9d8fed
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index 3cd153307d..7f861e4d24 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -960,9 +960,11 @@ addressmap_get_virtual_address(int type)
char tmp[TOR_ADDR_BUF_LEN];
tor_addr_to_str(tmp, &addr, sizeof(tmp), 0);
if (strmap_get(addressmap, tmp)) {
+ // LCOV_EXCL_START
log_warn(LD_BUG, "%s wasn't in the addressmap, but %s was.",
buf, tmp);
continue;
+ // LCOV_EXCL_STOP
}
return tor_strdup(buf);
@@ -971,8 +973,10 @@ addressmap_get_virtual_address(int type)
log_warn(LD_CONFIG, "Ran out of virtual addresses!");
return NULL;
} else {
+ // LCOV_EXCL_START
log_warn(LD_BUG, "Called with unsupported address type (%d)", type);
return NULL;
+ // LCOV_EXCL_STOP
}
}
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index c6a09f8b7f..09102d0c4c 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -72,10 +72,7 @@ static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
static int circuit_deliver_create_cell(circuit_t *circ,
const create_cell_t *create_cell,
int relayed);
-static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit,
- int is_hs_v3_rp_circuit);
static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
-static int onion_extend_cpath(origin_circuit_t *circ);
STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
static int circuit_send_first_onion_skin(origin_circuit_t *circ);
static int circuit_build_no_more_hops(origin_circuit_t *circ);
@@ -1133,19 +1130,29 @@ circuit_send_intermediate_onion_skin(origin_circuit_t *circ,
return 0;
}
-/** Our clock just jumped by <b>seconds_elapsed</b>. Assume
- * something has also gone wrong with our network: notify the user,
- * and abandon all not-yet-used circuits. */
+/** Our clock just jumped by <b>seconds_elapsed</b>. If <b>was_idle</b> is
+ * true, then the monotonic time matches; otherwise it doesn't. Assume
+ * something has also gone wrong with our network: notify the user, and
+ * abandon all not-yet-used circuits. */
void
-circuit_note_clock_jumped(int seconds_elapsed)
+circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle)
{
int severity = server_mode(get_options()) ? LOG_WARN : LOG_NOTICE;
- tor_log(severity, LD_GENERAL, "Your system clock just jumped %d seconds %s; "
- "assuming established circuits no longer work.",
- seconds_elapsed >=0 ? seconds_elapsed : -seconds_elapsed,
- seconds_elapsed >=0 ? "forward" : "backward");
- control_event_general_status(LOG_WARN, "CLOCK_JUMPED TIME=%d",
- seconds_elapsed);
+ if (was_idle) {
+ tor_log(severity, LD_GENERAL, "Tor has been idle for "I64_FORMAT
+ " seconds; assuming established circuits no longer work.",
+ I64_PRINTF_ARG(seconds_elapsed));
+ } else {
+ tor_log(severity, LD_GENERAL,
+ "Your system clock just jumped "I64_FORMAT" seconds %s; "
+ "assuming established circuits no longer work.",
+ I64_PRINTF_ARG(
+ seconds_elapsed >=0 ? seconds_elapsed : -seconds_elapsed),
+ seconds_elapsed >=0 ? "forward" : "backward");
+ }
+ control_event_general_status(LOG_WARN, "CLOCK_JUMPED TIME="I64_FORMAT
+ " IDLE=%d",
+ I64_PRINTF_ARG(seconds_elapsed), was_idle?1:0);
/* so we log when it works again */
note_that_we_maybe_cant_complete_circuits();
control_event_client_status(severity, "CIRCUIT_NOT_ESTABLISHED REASON=%s",
@@ -2283,7 +2290,7 @@ warn_if_last_router_excluded(origin_circuit_t *circ,
* be used as an HS v3 rendezvous point.
*
* Return 0 if ok, -1 if circuit should be closed. */
-static int
+STATIC int
onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
int is_hs_v3_rp_circuit)
{
@@ -2454,12 +2461,71 @@ cpath_get_n_hops(crypt_path_t **head_ptr)
#endif /* defined(TOR_UNIT_TESTS) */
/**
+ * Build the exclude list for vanguard circuits.
+ *
+ * For vanguard circuits we exclude all the already chosen nodes (including the
+ * exit) from being middle hops to prevent the creation of A - B - A subpaths.
+ * We also allow the 4th hop to be the same as the guard node so as to not leak
+ * guard information to RP/IP/HSDirs.
+ *
+ * For vanguard circuits, we don't apply any subnet or family restrictions.
+ * This is to avoid impossible-to-build circuit paths, or just situations where
+ * our earlier guards prevent us from using most of our later ones.
+ *
+ * The alternative is building the circuit in reverse. Reverse calls to
+ * onion_extend_cpath() (ie: select outer hops first) would then have the
+ * property that you don't gain information about inner hops by observing
+ * outer ones. See https://trac.torproject.org/projects/tor/ticket/24487
+ * for this.
+ *
+ * (Note further that we still exclude the exit to prevent A - B - A
+ * at the end of the path. */
+static smartlist_t *
+build_vanguard_middle_exclude_list(uint8_t purpose,
+ cpath_build_state_t *state,
+ crypt_path_t *head,
+ int cur_len)
+{
+ smartlist_t *excluded;
+ const node_t *r;
+ crypt_path_t *cpath;
+ int i;
+
+ (void) purpose;
+
+ excluded = smartlist_new();
+
+ /* Add the exit to the exclude list (note that the exit/last hop is always
+ * chosen first in circuit_establish_circuit()). */
+ if ((r = build_state_get_exit_node(state))) {
+ smartlist_add(excluded, (node_t*)r);
+ }
+
+ /* If we are picking the 4th hop, allow that node to be the guard too.
+ * This prevents us from avoiding the Guard for those hops, which
+ * gives the adversary information about our guard if they control
+ * the RP, IP, or HSDIR. We don't do this check based on purpose
+ * because we also want to allow HS_VANGUARDS pre-build circuits
+ * to use the guard for that last hop.
+ */
+ if (cur_len == DEFAULT_ROUTE_LEN+1) {
+ /* Skip the first hop for the exclude list below */
+ head = head->next;
+ cur_len--;
+ }
+
+ for (i = 0, cpath = head; cpath && i < cur_len; ++i, cpath=cpath->next) {
+ if ((r = node_get_by_id(cpath->extend_info->identity_digest))) {
+ smartlist_add(excluded, (node_t*)r);
+ }
+ }
+
+ return excluded;
+}
+
+/**
* Build a list of nodes to exclude from the choice of this middle
* hop, based on already chosen nodes.
- *
- * XXX: At present, this function does not exclude any nodes from
- * the vanguard circuits. See
- * https://trac.torproject.org/projects/tor/ticket/24487
*/
static smartlist_t *
build_middle_exclude_list(uint8_t purpose,
@@ -2472,32 +2538,21 @@ build_middle_exclude_list(uint8_t purpose,
crypt_path_t *cpath;
int i;
+ /** Vanguard circuits have their own path selection rules */
+ if (circuit_should_use_vanguards(purpose)) {
+ return build_vanguard_middle_exclude_list(purpose, state, head, cur_len);
+ }
+
excluded = smartlist_new();
- /* Add the exit to the exclude list (note that the exit/last hop is always
- * chosen first in circuit_establish_circuit()). */
+ /* For non-vanguard circuits, add the exit and its family to the exclude list
+ * (note that the exit/last hop is always chosen first in
+ * circuit_establish_circuit()). */
if ((r = build_state_get_exit_node(state))) {
nodelist_add_node_and_family(excluded, r);
}
- /* XXX: We don't apply any other previously selected node restrictions for
- * vanguards, and allow nodes to be reused for those hop positions in the
- * same circuit. This is because after many rotations, you get to learn
- * inner guard nodes through the nodes that are not selected for outer
- * hops.
- *
- * The alternative is building the circuit in reverse. Reverse calls to
- * onion_extend_cpath() (ie: select outer hops first) would then have the
- * property that you don't gain information about inner hops by observing
- * outer ones. See https://trac.torproject.org/projects/tor/ticket/24487
- * for this.
- *
- * (Note further that we can and do still exclude the exit in the block
- * above, because it is chosen first in circuit_establish_circuit()..) */
- if (circuit_should_use_vanguards(purpose)) {
- return excluded;
- }
-
+ /* also exclude all other already chosen nodes and their family */
for (i = 0, cpath = head; cpath && i < cur_len; ++i, cpath=cpath->next) {
if ((r = node_get_by_id(cpath->extend_info->identity_digest))) {
nodelist_add_node_and_family(excluded, r);
@@ -2597,7 +2652,9 @@ choose_good_middle_server(uint8_t purpose,
/** If a hidden service circuit wants a specific middle node, pin it. */
if (middle_node_must_be_vanguard(options, purpose, cur_len)) {
log_debug(LD_GENERAL, "Picking a sticky node (cur_len = %d)", cur_len);
- return pick_vanguard_middle_node(options, flags, cur_len, excluded);
+ choice = pick_vanguard_middle_node(options, flags, cur_len, excluded);
+ smartlist_free(excluded);
+ return choice;
}
choice = router_choose_random_node(excluded, options->ExcludeNodes, flags);
@@ -2637,7 +2694,7 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state,
/* This request is for an entry server to use for a regular circuit,
* and we use entry guard nodes. Just return one of the guard nodes. */
tor_assert(guard_state_out);
- return guards_choose_guard(state, guard_state_out);
+ return guards_choose_guard(state, purpose, guard_state_out);
}
excluded = smartlist_new();
@@ -2680,7 +2737,7 @@ onion_next_hop_in_cpath(crypt_path_t *cpath)
* Return 1 if the path is complete, 0 if we successfully added a hop,
* and -1 on error.
*/
-static int
+STATIC int
onion_extend_cpath(origin_circuit_t *circ)
{
uint8_t purpose = circ->base_.purpose;
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index bea31ad0dd..0184898e29 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -29,7 +29,7 @@ void circuit_n_chan_done(channel_t *chan, int status,
int inform_testing_reachability(void);
int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ);
int circuit_send_next_onion_skin(origin_circuit_t *circ);
-void circuit_note_clock_jumped(int seconds_elapsed);
+void circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle);
int circuit_extend(cell_t *cell, circuit_t *circ);
int circuit_init_cpath_crypto(crypt_path_t *cpath,
const char *key_data, size_t key_data_len,
@@ -83,6 +83,13 @@ STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan);
STATIC int new_route_len(uint8_t purpose, extend_info_t *exit_ei,
smartlist_t *nodes);
MOCK_DECL(STATIC int, count_acceptable_nodes, (smartlist_t *nodes));
+
+STATIC int onion_extend_cpath(origin_circuit_t *circ);
+
+STATIC int
+onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
+ int is_hs_v3_rp_circuit);
+
#if defined(ENABLE_TOR2WEB_MODE) || defined(TOR_UNIT_TESTS)
STATIC const node_t *pick_tor2web_rendezvous_node(router_crn_flags_t flags,
const or_options_t *options);
diff --git a/src/or/config.c b/src/or/config.c
index 9af613e931..94a58f3488 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -1449,9 +1449,9 @@ options_act_reversible(const or_options_t *old_options, char **msg)
consider_hibernation(time(NULL));
/* Launch the listeners. (We do this before we setuid, so we can bind to
- * ports under 1024.) We don't want to rebind if we're hibernating. If
- * networking is disabled, this will close all but the control listeners,
- * but disable those. */
+ * ports under 1024.) We don't want to rebind if we're hibernating or
+ * shutting down. If networking is disabled, this will close all but the
+ * control listeners, but disable those. */
if (!we_are_hibernating()) {
if (retry_all_listeners(replaced_listeners, new_listeners,
options->DisableNetwork) < 0) {
@@ -2001,6 +2001,9 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
+ /* See whether we need to enable/disable our once-a-second timer. */
+ reschedule_per_second_timer();
+
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||
@@ -8433,3 +8436,17 @@ init_cookie_authentication(const char *fname, const char *header,
return retval;
}
+/**
+ * Return true if any option is set in <b>options</b> to make us behave
+ * as a client.
+ */
+int
+options_any_client_port_set(const or_options_t *options)
+{
+ return (options->SocksPort_set ||
+ options->TransPort_set ||
+ options->NATDPort_set ||
+ options->DNSPort_set ||
+ options->HTTPTunnelPort_set);
+}
+
diff --git a/src/or/config.h b/src/or/config.h
index 1d3c27217e..4b41274434 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -214,6 +214,9 @@ smartlist_t *get_options_from_transport_options_line(const char *line,
const char *transport);
smartlist_t *get_options_for_server_transport(const char *transport);
+/* Port helper functions. */
+int options_any_client_port_set(const or_options_t *options);
+
#ifdef CONFIG_PRIVATE
#define CL_PORT_NO_STREAM_OPTIONS (1u<<0)
diff --git a/src/or/connection.c b/src/or/connection.c
index 4361e1ca0c..3462dbeac2 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -1762,13 +1762,13 @@ connection_connect_sockaddr,(connection_t *conn,
tor_assert(sa);
tor_assert(socket_error);
- if (get_options()->DisableNetwork) {
- /* We should never even try to connect anyplace if DisableNetwork is set.
- * Warn if we do, and refuse to make the connection.
+ if (net_is_completely_disabled()) {
+ /* We should never even try to connect anyplace if the network is
+ * completely shut off.
*
- * We only check DisableNetwork here, not we_are_hibernating(), since
- * we'll still try to fulfill client requests sometimes in the latter case
- * (if it is soft hibernation) */
+ * (We don't check net_is_disabled() here, since we still sometimes
+ * want to open connections when we're in soft hibernation.)
+ */
static ratelim_t disablenet_violated = RATELIM_INIT(30*60);
*socket_error = SOCK_ERRNO(ENETUNREACH);
log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG,
@@ -3440,7 +3440,7 @@ int
connection_handle_read(connection_t *conn)
{
int res;
-
+ update_current_time(time(NULL));
res = connection_handle_read_impl(conn);
return res;
}
@@ -3983,6 +3983,7 @@ int
connection_handle_write(connection_t *conn, int force)
{
int res;
+ update_current_time(time(NULL));
conn->in_connection_handle_write = 1;
res = connection_handle_write_impl(conn, force);
conn->in_connection_handle_write = 0;
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 5ae1538bfe..28e18aa853 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -3537,6 +3537,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
n_stream->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
/* default to failed, change in dns_resolve if it turns out not to fail */
+ /* If we're hibernating or shutting down, we refuse to open new streams. */
if (we_are_hibernating()) {
relay_send_end_cell_from_edge(rh.stream_id, circ,
END_STREAM_REASON_HIBERNATING, NULL);
diff --git a/src/or/control.c b/src/or/control.c
index 93b204f9a3..237a8572d4 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1,3 +1,4 @@
+
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
@@ -112,6 +113,10 @@ static int disable_log_messages = 0;
#define EVENT_IS_INTERESTING(e) \
(!! (global_event_mask & EVENT_MASK_(e)))
+/** Macro: true if any event from the bitfield 'e' is interesting. */
+#define ANY_EVENT_IS_INTERESTING(e) \
+ EVENT_IS_INTERESTING(e)
+
/** If we're using cookie-type authentication, how long should our cookies be?
*/
#define AUTHENTICATION_COOKIE_LEN 32
@@ -219,6 +224,7 @@ static void set_cached_network_liveness(int liveness);
static void flush_queued_events_cb(mainloop_event_t *event, void *arg);
static char * download_status_to_string(const download_status_t *dl);
+static void control_get_bytes_rw_last_sec(uint64_t *r, uint64_t *w);
/** Given a control event code for a message event, return the corresponding
* log severity. */
@@ -271,6 +277,7 @@ control_update_global_event_mask(void)
smartlist_t *conns = get_connection_array();
event_mask_t old_mask, new_mask;
old_mask = global_event_mask;
+ int any_old_per_sec_events = control_any_per_second_event_enabled();
global_event_mask = 0;
SMARTLIST_FOREACH(conns, connection_t *, _conn,
@@ -288,10 +295,13 @@ control_update_global_event_mask(void)
* we want to hear...*/
control_adjust_event_log_severity();
+ /* Macro: true if ev was false before and is true now. */
+#define NEWLY_ENABLED(ev) \
+ (! (old_mask & (ev)) && (new_mask & (ev)))
+
/* ...then, if we've started logging stream or circ bw, clear the
* appropriate fields. */
- if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) &&
- (new_mask & EVENT_STREAM_BANDWIDTH_USED)) {
+ if (NEWLY_ENABLED(EVENT_STREAM_BANDWIDTH_USED)) {
SMARTLIST_FOREACH(conns, connection_t *, conn,
{
if (conn->type == CONN_TYPE_AP) {
@@ -300,10 +310,18 @@ control_update_global_event_mask(void)
}
});
}
- if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) &&
- (new_mask & EVENT_CIRC_BANDWIDTH_USED)) {
+ if (NEWLY_ENABLED(EVENT_CIRC_BANDWIDTH_USED)) {
clear_circ_bw_fields();
}
+ if (NEWLY_ENABLED(EVENT_BANDWIDTH_USED)) {
+ uint64_t r, w;
+ control_get_bytes_rw_last_sec(&r, &w);
+ }
+ if (any_old_per_sec_events != control_any_per_second_event_enabled()) {
+ reschedule_per_second_timer();
+ }
+
+#undef NEWLY_ENABLED
}
/** Adjust the log severities that result in control_event_logmsg being called
@@ -352,6 +370,65 @@ control_event_is_interesting(int event)
return EVENT_IS_INTERESTING(event);
}
+/** Return true if any event that needs to fire once a second is enabled. */
+int
+control_any_per_second_event_enabled(void)
+{
+ return ANY_EVENT_IS_INTERESTING(
+ EVENT_BANDWIDTH_USED |
+ EVENT_CELL_STATS |
+ EVENT_CIRC_BANDWIDTH_USED |
+ EVENT_CONN_BW |
+ EVENT_STREAM_BANDWIDTH_USED
+ );
+}
+
+/* The value of 'get_bytes_read()' the previous time that
+ * control_get_bytes_rw_last_sec() as called. */
+static uint64_t stats_prev_n_read = 0;
+/* The value of 'get_bytes_written()' the previous time that
+ * control_get_bytes_rw_last_sec() as called. */
+static uint64_t stats_prev_n_written = 0;
+
+/**
+ * Set <b>n_read</b> and <b>n_written</b> to the total number of bytes read
+ * and written by Tor since the last call to this function.
+ *
+ * Call this only from the main thread.
+ */
+static void
+control_get_bytes_rw_last_sec(uint64_t *n_read,
+ uint64_t *n_written)
+{
+ const uint64_t stats_n_bytes_read = get_bytes_read();
+ const uint64_t stats_n_bytes_written = get_bytes_written();
+
+ *n_read = stats_n_bytes_read - stats_prev_n_read;
+ *n_written = stats_n_bytes_written - stats_prev_n_written;
+ stats_prev_n_read = stats_n_bytes_read;
+ stats_prev_n_written = stats_n_bytes_written;
+}
+
+/**
+ * Run all the controller events (if any) that are scheduled to trigger once
+ * per second.
+ */
+void
+control_per_second_events(void)
+{
+ if (!control_any_per_second_event_enabled())
+ return;
+
+ uint64_t bytes_read, bytes_written;
+ control_get_bytes_rw_last_sec(&bytes_read, &bytes_written);
+ control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
+
+ control_event_stream_bandwidth_used();
+ control_event_conn_bandwidth_used();
+ control_event_circ_bandwidth_used();
+ control_event_circuit_cell_stats();
+}
+
/** Append a NUL-terminated string <b>s</b> to the end of
* <b>conn</b>-\>outbuf.
*/
@@ -7035,6 +7112,8 @@ control_event_bootstrap_problem(const char *warn, const char *reason,
if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
dowarn = 1;
+ /* Don't warn about our bootstrapping status if we are hibernating or
+ * shutting down. */
if (we_are_hibernating())
dowarn = 0;
@@ -7606,6 +7685,8 @@ control_free_all(void)
{
smartlist_t *queued_events = NULL;
+ stats_prev_n_read = stats_prev_n_written = 0;
+
if (authentication_cookie) /* Free the auth cookie */
tor_free(authentication_cookie);
if (detached_onion_services) { /* Free the detached onion services */
diff --git a/src/or/control.h b/src/or/control.h
index 7f8a0bdb5f..92cbf866dd 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -40,6 +40,9 @@ int connection_control_process_inbuf(control_connection_t *conn);
#define EVENT_NS 0x000F
int control_event_is_interesting(int event);
+void control_per_second_events(void);
+int control_any_per_second_event_enabled(void);
+
int control_event_circuit_status(origin_circuit_t *circ,
circuit_status_event_t e, int reason);
int control_event_circuit_purpose_changed(origin_circuit_t *circ,
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index e058e04f8b..c01234e0b9 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -955,7 +955,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
tor_assert(node);
if (router_is_me(router)) {
- /* We always know if we are down ourselves. */
+ /* We always know if we are shutting down or hibernating ourselves. */
answer = ! we_are_hibernating();
} else if (router->is_hibernating &&
(router->cache_info.published_on +
@@ -2459,6 +2459,18 @@ measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line)
int got_bw = 0;
int got_node_id = 0;
char *strtok_state; /* lame sauce d'jour */
+
+ if (strlen(line) == 0) {
+ log_warn(LD_DIRSERV, "Empty line in bandwidth file");
+ tor_free(line);
+ return -1;
+ }
+
+ /* Remove end of line character, so that is not part of the token */
+ if (line[strlen(line) - 1] == '\n') {
+ line[strlen(line) - 1] = '\0';
+ }
+
cp = tor_strtok_r(cp, " \t", &strtok_state);
if (!cp) {
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index bc2afb142a..27d760f1a8 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -118,6 +118,7 @@
#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitlist.h"
+#include "circuituse.h"
#include "circuitstats.h"
#include "config.h"
#include "confparse.h"
@@ -3479,12 +3480,18 @@ guards_update_all(void)
used. */
const node_t *
guards_choose_guard(cpath_build_state_t *state,
- circuit_guard_state_t **guard_state_out)
+ uint8_t purpose,
+ circuit_guard_state_t **guard_state_out)
{
const node_t *r = NULL;
const uint8_t *exit_id = NULL;
entry_guard_restriction_t *rst = NULL;
- if (state && (exit_id = build_state_get_exit_rsa_id(state))) {
+
+ /* Only apply restrictions if we have a specific exit node in mind, and only
+ * if we are not doing vanguard circuits: we don't want to apply guard
+ * restrictions to vanguard circuits. */
+ if (state && !circuit_should_use_vanguards(purpose) &&
+ (exit_id = build_state_get_exit_rsa_id(state))) {
/* We're building to a targeted exit node, so that node can't be
* chosen as our guard for this circuit. Remember that fact in a
* restriction. */
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index d562498313..e8c91da41b 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -322,6 +322,7 @@ struct circuit_guard_state_t {
/* Common entry points for old and new guard code */
int guards_update_all(void);
const node_t *guards_choose_guard(cpath_build_state_t *state,
+ uint8_t purpose,
circuit_guard_state_t **guard_state_out);
const node_t *guards_choose_dirguard(uint8_t dir_purpose,
circuit_guard_state_t **guard_state_out);
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 98f32adb1c..d7d259470f 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -52,6 +52,10 @@ static time_t hibernate_end_time = 0;
* we aren't shutting down. */
static time_t shutdown_time = 0;
+/** A timed event that we'll use when it's time to wake up from
+ * hibernation. */
+static mainloop_event_t *wakeup_event = NULL;
+
/** Possible accounting periods. */
typedef enum {
UNIT_MONTH=1, UNIT_WEEK=2, UNIT_DAY=3,
@@ -131,6 +135,8 @@ static time_t start_of_accounting_period_after(time_t now);
static time_t start_of_accounting_period_containing(time_t now);
static void accounting_set_wakeup_time(void);
static void on_hibernate_state_change(hibernate_state_t prev_state);
+static void hibernate_schedule_wakeup_event(time_t now, time_t end_time);
+static void wakeup_event_callback(mainloop_event_t *ev, void *data);
/**
* Return the human-readable name for the hibernation state <b>state</b>
@@ -877,13 +883,26 @@ hibernate_begin_shutdown(void)
hibernate_begin(HIBERNATE_STATE_EXITING, time(NULL));
}
-/** Return true iff we are currently hibernating. */
+/**
+ * Return true iff we are currently hibernating -- that is, if we are in
+ * any non-live state.
+ */
MOCK_IMPL(int,
we_are_hibernating,(void))
{
return hibernate_state != HIBERNATE_STATE_LIVE;
}
+/**
+ * Return true iff we are currently _fully_ hibernating -- that is, if we are
+ * in a state where we expect to handle no network activity at all.
+ */
+MOCK_IMPL(int,
+we_are_fully_hibernating,(void))
+{
+ return hibernate_state == HIBERNATE_STATE_DORMANT;
+}
+
/** If we aren't currently dormant, close all connections and become
* dormant. */
static void
@@ -936,6 +955,63 @@ hibernate_go_dormant(time_t now)
or_state_mark_dirty(get_or_state(),
get_options()->AvoidDiskWrites ? now+600 : 0);
+
+ hibernate_schedule_wakeup_event(now, hibernate_end_time);
+}
+
+/**
+ * Schedule a mainloop event at <b>end_time</b> to wake up from a dormant
+ * state. We can't rely on this happening from second_elapsed_callback,
+ * since second_elapsed_callback will be shut down when we're dormant.
+ *
+ * (Note that We might immediately go back to sleep after we set the next
+ * wakeup time.)
+ */
+static void
+hibernate_schedule_wakeup_event(time_t now, time_t end_time)
+{
+ struct timeval delay = { 0, 0 };
+
+ if (now >= end_time) {
+ // In these cases we always wait at least a second, to avoid running
+ // the callback in a tight loop.
+ delay.tv_sec = 1;
+ } else {
+ delay.tv_sec = (end_time - now);
+ }
+
+ if (!wakeup_event) {
+ wakeup_event = mainloop_event_postloop_new(wakeup_event_callback, NULL);
+ }
+
+ mainloop_event_schedule(wakeup_event, &delay);
+}
+
+/**
+ * Called at the end of the interval, or at the wakeup time of the current
+ * interval, to exit the dormant state.
+ **/
+static void
+wakeup_event_callback(mainloop_event_t *ev, void *data)
+{
+ (void) ev;
+ (void) data;
+
+ const time_t now = time(NULL);
+ accounting_run_housekeeping(now);
+ consider_hibernation(now);
+ if (hibernate_state != HIBERNATE_STATE_DORMANT) {
+ /* We woke up, so everything's great here */
+ return;
+ }
+
+ /* We're still dormant. */
+ if (now < interval_wakeup_time)
+ hibernate_end_time = interval_wakeup_time;
+ else
+ hibernate_end_time = interval_end_time;
+
+ hibernate_schedule_wakeup_event(now, hibernate_end_time);
}
/** Called when hibernate_end_time has arrived. */
@@ -1124,6 +1200,18 @@ on_hibernate_state_change(hibernate_state_t prev_state)
if (prev_state != HIBERNATE_STATE_INITIAL) {
rescan_periodic_events(get_options());
}
+
+ reschedule_per_second_timer();
+}
+
+/** Free all resources held by the accounting module */
+void
+accounting_free_all(void)
+{
+ mainloop_event_free(wakeup_event);
+ hibernate_state = HIBERNATE_STATE_INITIAL;
+ hibernate_end_time = 0;
+ shutdown_time = 0;
}
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/hibernate.h b/src/or/hibernate.h
index 85fb42864b..453969d052 100644
--- a/src/or/hibernate.h
+++ b/src/or/hibernate.h
@@ -25,11 +25,13 @@ void accounting_add_bytes(size_t n_read, size_t n_written, int seconds);
int accounting_record_bandwidth_usage(time_t now, or_state_t *state);
void hibernate_begin_shutdown(void);
MOCK_DECL(int, we_are_hibernating, (void));
+MOCK_DECL(int, we_are_fully_hibernating,(void));
void consider_hibernation(time_t now);
int getinfo_helper_accounting(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
uint64_t get_accounting_max_total(void);
+void accounting_free_all(void);
#ifdef HIBERNATE_PRIVATE
/** Possible values of hibernate_state */
diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c
index eb48cb0601..096122392d 100644
--- a/src/or/hs_descriptor.c
+++ b/src/or/hs_descriptor.c
@@ -1897,7 +1897,7 @@ desc_sig_is_valid(const char *b64_sig,
}
/* Find the start of signature. */
- sig_start = tor_memstr(encoded_desc, encoded_len, "\n" str_signature);
+ sig_start = tor_memstr(encoded_desc, encoded_len, "\n" str_signature " ");
/* Getting here means the token parsing worked for the signature so if we
* can't find the start of the signature, we have a code flow issue. */
if (!sig_start) {
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index b7296ddcf9..9001a521ab 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -878,11 +878,6 @@ register_all_services(void)
tor_assert(hs_service_staging_list);
- /* We'll save us some allocation and computing time. */
- if (smartlist_len(hs_service_staging_list) == 0) {
- return;
- }
-
/* Allocate a new map that will replace the current one. */
new_service_map = tor_malloc_zero(sizeof(*new_service_map));
HT_INIT(hs_service_ht, new_service_map);
diff --git a/src/or/hs_stats.c b/src/or/hs_stats.c
index 3e183a5bfc..1e2a96945b 100644
--- a/src/or/hs_stats.c
+++ b/src/or/hs_stats.c
@@ -3,7 +3,7 @@
/**
* \file hs_stats.c
- * \brief Keeps stats about the activity of our hidden service.
+ * \brief Keeps stats about the activity of our onion service(s).
**/
#include "or.h"
@@ -42,14 +42,14 @@ hs_stats_get_n_introduce2_v2_cells(void)
return n_introduce2_v2;
}
-/** Note that we attempted to launch another circuit to a rendezvous point */
+/** Note that we attempted to launch another circuit to a rendezvous point. */
void
hs_stats_note_service_rendezvous_launch(void)
{
n_rendezvous_launches++;
}
-/** Return the number of rendezvous circuits we have attempted to launch */
+/** Return the number of rendezvous circuits we have attempted to launch. */
uint32_t
hs_stats_get_n_rendezvous_launches(void)
{
diff --git a/src/or/main.c b/src/or/main.c
index 87b57f89e3..9dce158b33 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -163,11 +163,6 @@ token_bucket_rw_t global_bucket;
/* Token bucket for relayed traffic. */
token_bucket_rw_t global_relayed_bucket;
-/* DOCDOC stats_prev_n_read */
-static uint64_t stats_prev_n_read = 0;
-/* DOCDOC stats_prev_n_written */
-static uint64_t stats_prev_n_written = 0;
-
/* XXX we might want to keep stats about global_relayed_*_bucket too. Or not.*/
/** How many bytes have we read since we started the process? */
static uint64_t stats_n_bytes_read = 0;
@@ -1258,7 +1253,8 @@ run_connection_housekeeping(int i, time_t now)
} else if (we_are_hibernating() &&
! have_any_circuits &&
!connection_get_outbuf_len(conn)) {
- /* We're hibernating, there's no circuits, and nothing to flush.*/
+ /* We're hibernating or shutting down, there's no circuits, and nothing to
+ * flush.*/
log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) "
"[Hibernating or exiting].",
(int)conn->s,conn->address, conn->port);
@@ -1497,7 +1493,7 @@ get_my_roles(const or_options_t *options)
int roles = 0;
int is_bridge = options->BridgeRelay;
- int is_client = any_client_port_set(options);
+ int is_client = options_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);
@@ -2500,10 +2496,100 @@ hs_service_callback(time_t now, const or_options_t *options)
/** Timer: used to invoke second_elapsed_callback() once per second. */
static periodic_timer_t *second_timer = NULL;
-/** Number of libevent errors in the last second: we die if we get too many. */
-static int n_libevent_errors = 0;
-/** Last time that second_elapsed_callback was called. */
+
+/**
+ * Enable or disable the per-second timer as appropriate, creating it if
+ * necessary.
+ */
+void
+reschedule_per_second_timer(void)
+{
+ struct timeval one_second;
+ one_second.tv_sec = 1;
+ one_second.tv_usec = 0;
+
+ if (! second_timer) {
+ second_timer = periodic_timer_new(tor_libevent_get_base(),
+ &one_second,
+ second_elapsed_callback,
+ NULL);
+ tor_assert(second_timer);
+ }
+
+ const bool run_per_second_events =
+ control_any_per_second_event_enabled() || ! net_is_completely_disabled();
+
+ if (run_per_second_events) {
+ periodic_timer_launch(second_timer, &one_second);
+ } else {
+ periodic_timer_disable(second_timer);
+ }
+}
+
+/** Last time that update_current_time was called. */
static time_t current_second = 0;
+/** Last time that update_current_time updated current_second. */
+static monotime_coarse_t current_second_last_changed;
+
+/**
+ * Set the current time to "now", which should be the value returned by
+ * time(). Check for clock jumps and track the total number of seconds we
+ * have been running.
+ */
+void
+update_current_time(time_t now)
+{
+ if (PREDICT_LIKELY(now == current_second)) {
+ /* We call this function a lot. Most frequently, the current second
+ * will not have changed, so we just return. */
+ return;
+ }
+
+ const time_t seconds_elapsed = current_second ? (now - current_second) : 0;
+
+ /* Check the wall clock against the monotonic clock, so we can
+ * better tell idleness from clock jumps and/or other shenanigans. */
+ monotime_coarse_t last_updated;
+ memcpy(&last_updated, &current_second_last_changed, sizeof(last_updated));
+ monotime_coarse_get(&current_second_last_changed);
+
+ /** How much clock jumping do we tolerate? */
+#define NUM_JUMPED_SECONDS_BEFORE_WARN 100
+
+ /** How much idleness do we tolerate? */
+#define NUM_IDLE_SECONDS_BEFORE_WARN 3600
+
+ if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN) {
+ // moving back in time is always a bad sign.
+ circuit_note_clock_jumped(seconds_elapsed, false);
+ } else if (seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
+ /* Compare the monotonic clock to the result of time(). */
+ const int32_t monotime_msec_passed =
+ monotime_coarse_diff_msec32(&last_updated,
+ &current_second_last_changed);
+ const int monotime_sec_passed = monotime_msec_passed / 1000;
+ const int discrepancy = monotime_sec_passed - (int)seconds_elapsed;
+ /* If the monotonic clock deviates from time(NULL), we have a couple of
+ * possibilities. On some systems, this means we have been suspended or
+ * sleeping. Everywhere, it can mean that the wall-clock time has
+ * been changed -- for example, with settimeofday().
+ *
+ * On the other hand, if the monotonic time matches with the wall-clock
+ * time, we've probably just been idle for a while, with no events firing.
+ * we tolerate much more of that.
+ */
+ const bool clock_jumped = abs(discrepancy) > 2;
+
+ if (clock_jumped || seconds_elapsed >= NUM_IDLE_SECONDS_BEFORE_WARN) {
+ circuit_note_clock_jumped(seconds_elapsed, ! clock_jumped);
+ }
+ } else if (seconds_elapsed > 0) {
+ stats_n_seconds_working += seconds_elapsed;
+ }
+
+ update_approx_time(now);
+ current_second = now;
+}
/** Libevent callback: invoked once every second. */
static void
@@ -2513,43 +2599,21 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
* could use Libevent's timers for this rather than checking the current
* time against a bunch of timeouts every second. */
time_t now;
- size_t bytes_written;
- size_t bytes_read;
- int seconds_elapsed;
(void)timer;
(void)arg;
- n_libevent_errors = 0;
-
- /* log_notice(LD_GENERAL, "Tick."); */
now = time(NULL);
- update_approx_time(now);
- /* the second has rolled over. check more stuff. */
- seconds_elapsed = current_second ? (int)(now - current_second) : 0;
- bytes_read = (size_t)(stats_n_bytes_read - stats_prev_n_read);
- bytes_written = (size_t)(stats_n_bytes_written - stats_prev_n_written);
- stats_prev_n_read = stats_n_bytes_read;
- stats_prev_n_written = stats_n_bytes_written;
-
- control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
- control_event_stream_bandwidth_used();
- control_event_conn_bandwidth_used();
- control_event_circ_bandwidth_used();
- control_event_circuit_cell_stats();
-
-/** If more than this many seconds have elapsed, probably the clock
- * jumped: doesn't count. */
-#define NUM_JUMPED_SECONDS_BEFORE_WARN 100
- if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN ||
- seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
- circuit_note_clock_jumped(seconds_elapsed);
- } else if (seconds_elapsed > 0)
- stats_n_seconds_working += seconds_elapsed;
+ /* We don't need to do this once-per-second any more: time-updating is
+ * only in this callback _because it is a callback_. It should be fine
+ * to disable this callback, and the time will still get updated.
+ */
+ update_current_time(now);
- run_scheduled_events(now);
+ /* Maybe some controller events are ready to fire */
+ control_per_second_events();
- current_second = now; /* remember which second it is, for next time */
+ run_scheduled_events(now);
}
#ifdef HAVE_SYSTEMD_209
@@ -2565,21 +2629,6 @@ systemd_watchdog_callback(periodic_timer_t *timer, void *arg)
}
#endif /* defined(HAVE_SYSTEMD_209) */
-#ifndef _WIN32
-/** Called when a possibly ignorable libevent error occurs; ensures that we
- * don't get into an infinite loop by ignoring too many errors from
- * libevent. */
-static int
-got_libevent_error(void)
-{
- if (++n_libevent_errors > 8) {
- log_err(LD_NET, "Too many libevent errors in one second; dying");
- return -1;
- }
- return 0;
-}
-#endif /* !defined(_WIN32) */
-
#define UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST (6*60*60)
/** Called when our IP address seems to have changed. <b>at_interface</b>
@@ -2842,17 +2891,7 @@ do_main_loop(void)
}
/* set up once-a-second callback. */
- if (! second_timer) {
- struct timeval one_second;
- one_second.tv_sec = 1;
- one_second.tv_usec = 0;
-
- second_timer = periodic_timer_new(tor_libevent_get_base(),
- &one_second,
- second_elapsed_callback,
- NULL);
- tor_assert(second_timer);
- }
+ reschedule_per_second_timer();
#ifdef HAVE_SYSTEMD_209
uint64_t watchdog_delay;
@@ -2921,6 +2960,11 @@ do_main_loop(void)
return run_main_loop_until_done();
}
+#ifndef _WIN32
+/** Rate-limiter for EINVAL-type libevent warnings. */
+static ratelim_t libevent_error_ratelim = RATELIM_INIT(10);
+#endif
+
/**
* Run the main loop a single time. Return 0 for "exit"; -1 for "exit with
* error", and 1 for "run this again."
@@ -2986,9 +3030,12 @@ run_main_loop_once(void)
return -1;
#ifndef _WIN32
} else if (e == EINVAL) {
- log_warn(LD_NET, "EINVAL from libevent: should you upgrade libevent?");
- if (got_libevent_error())
+ log_fn_ratelim(&libevent_error_ratelim, LOG_WARN, LD_NET,
+ "EINVAL from libevent: should you upgrade libevent?");
+ if (libevent_error_ratelim.n_calls_since_last_time > 8) {
+ log_err(LD_NET, "Too many libevent errors, too fast: dying");
return -1;
+ }
#endif /* !defined(_WIN32) */
} else {
tor_assert_nonfatal_once(! ERRNO_IS_EINPROGRESS(e));
@@ -3034,6 +3081,7 @@ signal_callback(evutil_socket_t fd, short events, void *arg)
(void)fd;
(void)events;
+ update_current_time(time(NULL));
process_signal(sig);
}
@@ -3623,6 +3671,8 @@ tor_free_all(int postfork)
hs_free_all();
dos_free_all();
circuitmux_ewma_free_all();
+ accounting_free_all();
+
if (!postfork) {
config_free_all();
or_state_free_all();
@@ -3656,7 +3706,6 @@ tor_free_all(int postfork)
memset(&global_bucket, 0, sizeof(global_bucket));
memset(&global_relayed_bucket, 0, sizeof(global_relayed_bucket));
- stats_prev_n_read = stats_prev_n_written = 0;
stats_n_bytes_read = stats_n_bytes_written = 0;
time_of_process_start = 0;
time_of_last_signewnym = 0;
@@ -3670,8 +3719,9 @@ tor_free_all(int postfork)
should_init_bridge_stats = 1;
dns_honesty_first_time = 1;
heartbeat_callback_first_time = 1;
- n_libevent_errors = 0;
current_second = 0;
+ memset(&current_second_last_changed, 0,
+ sizeof(current_second_last_changed));
if (!postfork) {
release_lockfile();
diff --git a/src/or/main.h b/src/or/main.h
index a312b51e05..9dbbc6e5ee 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -66,6 +66,8 @@ void reschedule_dirvote(const or_options_t *options);
void mainloop_schedule_postloop_cleanup(void);
void rescan_periodic_events(const or_options_t *options);
+void update_current_time(time_t now);
+
MOCK_DECL(long,get_uptime,(void));
MOCK_DECL(void,reset_uptime,(void));
@@ -92,6 +94,7 @@ uint64_t get_main_loop_error_count(void);
uint64_t get_main_loop_idle_count(void);
void periodic_events_on_new_options(const or_options_t *options);
+void reschedule_per_second_timer(void);
extern time_t time_of_process_start;
extern int quiet_level;
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 44c0638c2b..b7443b4c7a 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -1691,24 +1691,6 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
#endif /* defined(TOR_UNIT_TESTS) */
/**
- * Return true if any option is set in <b>options</b> to make us behave
- * as a client.
- *
- * XXXX If we need this elsewhere at any point, we should make it nonstatic
- * XXXX and move it into another file.
- */
-int
-any_client_port_set(const or_options_t *options)
-{
- return (options->SocksPort_set ||
- options->TransPort_set ||
- options->NATDPort_set ||
- options->ControlPort_set ||
- options->DNSPort_set ||
- options->HTTPTunnelPort_set);
-}
-
-/**
* Helper for handle_missing_protocol_warning: handles either the
* client case (if <b>is_client</b> is set) or the server case otherwise.
*/
@@ -1743,7 +1725,7 @@ handle_missing_protocol_warning(const networkstatus_t *c,
const or_options_t *options)
{
const int is_server = server_mode(options);
- const int is_client = any_client_port_set(options) || !is_server;
+ const int is_client = options_any_client_port_set(options) || !is_server;
if (is_server)
handle_missing_protocol_warning_impl(c, 0);
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index 0c325959d7..6a7a42f911 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -147,8 +147,6 @@ 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/periodic.c b/src/or/periodic.c
index 76aa418b35..92fa677f8f 100644
--- a/src/or/periodic.c
+++ b/src/or/periodic.c
@@ -14,6 +14,7 @@
#include "or.h"
#include "compat_libevent.h"
#include "config.h"
+#include "main.h"
#include "periodic.h"
/** We disable any interval greater than this number of seconds, on the
@@ -48,6 +49,7 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data)
}
time_t now = time(NULL);
+ update_current_time(now);
const or_options_t *options = get_options();
// log_debug(LD_GENERAL, "Dispatching %s", event->name);
int r = event->fn(now, options);
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 43600cd913..92c323b10d 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -629,10 +629,11 @@ void
rend_service_prune_list(void)
{
smartlist_t *old_service_list = rend_service_list;
- /* Don't try to prune anything if we have no staging list. */
+
if (!rend_service_staging_list) {
- return;
+ rend_service_staging_list = smartlist_new();
}
+
rend_service_prune_list_impl_();
if (old_service_list) {
/* Every remaining service in the old list have been removed from the
diff --git a/src/or/router.c b/src/or/router.c
index 996a28a91f..07abf1f8d5 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -1599,15 +1599,24 @@ router_perform_bandwidth_test(int num_circs, time_t now)
}
}
-/** Return true iff our network is in some sense disabled: either we're
- * hibernating, entering hibernation, or the network is turned off with
- * DisableNetwork. */
+/** Return true iff our network is in some sense disabled or shutting down:
+ * either we're hibernating, entering hibernation, or the network is turned
+ * off with DisableNetwork. */
int
net_is_disabled(void)
{
return get_options()->DisableNetwork || we_are_hibernating();
}
+/** Return true iff our network is in some sense "completely disabled" either
+ * we're fully hibernating or the network is turned off with
+ * DisableNetwork. */
+int
+net_is_completely_disabled(void)
+{
+ return get_options()->DisableNetwork || we_are_fully_hibernating();
+}
+
/** Return true iff we believe ourselves to be an authoritative
* directory server.
*/
@@ -2268,6 +2277,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
/* and compute ri->bandwidthburst similarly */
ri->bandwidthburst = get_effective_bwburst(options);
+ /* Report bandwidth, unless we're hibernating or shutting down */
ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess();
if (dns_seems_to_be_broken() || has_dns_init_failed()) {
@@ -2538,6 +2548,8 @@ check_descriptor_bandwidth_changed(time_t now)
return;
prev = router_get_my_routerinfo()->bandwidthcapacity;
+ /* Consider ourselves to have zero bandwidth if we're hibernating or
+ * shutting down. */
cur = we_are_hibernating() ? 0 : rep_hist_bandwidth_assess();
if ((prev != cur && (!prev || !cur)) ||
cur > prev*2 ||
diff --git a/src/or/router.h b/src/or/router.h
index 03eca9c65d..0db2c1cfb2 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -53,6 +53,7 @@ void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now);
int net_is_disabled(void);
+int net_is_completely_disabled(void);
int authdir_mode(const or_options_t *options);
int authdir_mode_handles_descs(const or_options_t *options, int purpose);
diff --git a/src/or/status.c b/src/or/status.c
index 4c497739e8..4b8033d114 100644
--- a/src/or/status.c
+++ b/src/or/status.c
@@ -87,19 +87,19 @@ bytes_to_usage(uint64_t bytes)
return bw_string;
}
-/** Log some usage info about our hidden service */
+/** Log some usage info about our onion service(s). */
static void
log_onion_service_stats(void)
{
unsigned int num_services = hs_service_get_num_services();
- /* If there are no active hidden services, no need to print logs */
+ /* If there are no active onion services, no need to print logs */
if (num_services == 0) {
return;
}
log_notice(LD_HEARTBEAT,
- "Our hidden service%s received %u v2 and %u v3 INTRODUCE2 cells "
+ "Our onion service%s received %u v2 and %u v3 INTRODUCE2 cells "
"and attempted to launch %d rendezvous circuits.",
num_services == 1 ? "" : "s",
hs_stats_get_n_introduce2_v2_cells(),
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index 2ac32809d6..ddbc0ac2b7 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -1,8 +1,35 @@
[[package]]
+name = "crypto"
+version = "0.0.1"
+dependencies = [
+ "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "external 0.0.1",
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smartlist 0.0.1",
+]
+
+[[package]]
+name = "digest"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "external"
version = "0.0.1"
dependencies = [
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smartlist 0.0.1",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -87,7 +114,15 @@ dependencies = [
"tor_log 0.1.0",
]
+[[package]]
+name = "typenum"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[metadata]
+"checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603"
+"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
"checksum rand 0.5.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7d7a7728c20bfd9fcc6e713e748e787c3d00e5ffd139b3ad1b5be92c5dfbaad5"
"checksum rand_core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0224284424a4b818387b58d59336c288f99b48f69681aa60cc681fe038bbca5d"
+"checksum typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a99dc6780ef33c78780b826cf9d2a78840b72cae9474de4bcaf9051e60ebbd"
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index d47cd64223..1aaab0c4f8 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -1,7 +1,14 @@
[workspace]
-members = ["tor_util", "protover", "smartlist", "external", "tor_allocate",
-"tor_rust", "tor_log",
+members = [
+ "crypto",
+ "external",
+ "protover",
"rand",
+ "smartlist",
+ "tor_allocate",
+ "tor_log",
+ "tor_rust",
+ "tor_util",
]
[profile.release]
diff --git a/src/rust/crypto/Cargo.toml b/src/rust/crypto/Cargo.toml
new file mode 100644
index 0000000000..e6a8bffa27
--- /dev/null
+++ b/src/rust/crypto/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+authors = ["The Tor Project",
+ "Isis Lovecruft <isis@torproject.org>"]
+name = "crypto"
+version = "0.0.1"
+publish = false
+
+[lib]
+name = "crypto"
+path = "lib.rs"
+crate_type = ["rlib", "staticlib"]
+
+[dependencies]
+libc = "=0.2.39"
+digest = "=0.7.2"
+
+[dependencies.external]
+path = "../external"
+
+[dependencies.smartlist]
+path = "../smartlist"
diff --git a/src/rust/crypto/digests/mod.rs b/src/rust/crypto/digests/mod.rs
new file mode 100644
index 0000000000..a2463b89eb
--- /dev/null
+++ b/src/rust/crypto/digests/mod.rs
@@ -0,0 +1,7 @@
+// Copyright (c) 2018, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Hash Digests and eXtendible Output Functions (XOFs)
+
+pub mod sha2;
diff --git a/src/rust/crypto/digests/sha2.rs b/src/rust/crypto/digests/sha2.rs
new file mode 100644
index 0000000000..1cbb6c581e
--- /dev/null
+++ b/src/rust/crypto/digests/sha2.rs
@@ -0,0 +1,213 @@
+// Copyright (c) 2018, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Hash Digests and eXtendible Output Functions (XOFs)
+
+pub use digest::Digest;
+
+use digest::BlockInput;
+use digest::FixedOutput;
+use digest::Input;
+use digest::generic_array::GenericArray;
+use digest::generic_array::typenum::U32;
+use digest::generic_array::typenum::U64;
+
+use external::crypto_digest::CryptoDigest;
+use external::crypto_digest::DigestAlgorithm;
+use external::crypto_digest::get_256_bit_digest;
+use external::crypto_digest::get_512_bit_digest;
+
+pub use external::crypto_digest::DIGEST256_LEN;
+pub use external::crypto_digest::DIGEST512_LEN;
+
+/// The block size for both SHA-256 and SHA-512 digests is 512 bits/64 bytes.
+///
+/// Unfortunately, we have to use the generic_array crate currently to express
+/// this at compile time. Later, in the future, when Rust implements const
+/// generics, we'll be able to remove this dependency (actually, it will get
+/// removed from the digest crate, which is currently `pub use`ing it).
+type BlockSize = U64;
+
+/// A SHA2-256 digest.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_dup`
+#[derive(Clone)]
+pub struct Sha256 {
+ engine: CryptoDigest,
+}
+
+/// Construct a new, default instance of a `Sha256` hash digest function.
+///
+/// # Examples
+///
+/// ```
+/// use crypto::digest::Sha256;
+///
+/// let hasher: Sha256 = Sha256::default();
+/// ```
+///
+/// # Returns
+///
+/// A new `Sha256` digest.
+impl Default for Sha256 {
+ fn default() -> Sha256 {
+ Sha256{ engine: CryptoDigest::new(Some(DigestAlgorithm::SHA2_256)) }
+ }
+}
+
+impl BlockInput for Sha256 {
+ type BlockSize = BlockSize;
+}
+
+/// Input `msg` into the digest.
+///
+/// # Examples
+///
+/// ```
+/// use crypto::digest::Sha256;
+///
+/// let hasher: Sha256 = Sha256::default();
+///
+/// hasher.process(b"foo");
+/// hasher.process(b"bar");
+/// ```
+impl Input for Sha256 {
+ fn process(&mut self, msg: &[u8]) {
+ self.engine.add_bytes(&msg);
+ }
+}
+
+/// Retrieve the output hash from everything which has been fed into this
+/// `Sha256` digest thus far.
+///
+//
+// FIXME: Once const generics land in Rust, we should genericise calling
+// crypto_digest_get_digest in external::crypto_digest.
+impl FixedOutput for Sha256 {
+ type OutputSize = U32;
+
+ fn fixed_result(self) -> GenericArray<u8, Self::OutputSize> {
+ let buffer: [u8; DIGEST256_LEN] = get_256_bit_digest(self.engine);
+
+ GenericArray::from(buffer)
+ }
+}
+
+/// A SHA2-512 digest.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_dup`
+#[derive(Clone)]
+pub struct Sha512 {
+ engine: CryptoDigest,
+}
+
+/// Construct a new, default instance of a `Sha512` hash digest function.
+///
+/// # Examples
+///
+/// ```
+/// use crypto::digest::Sha512;
+///
+/// let hasher: Sha256 = Sha512::default();
+/// ```
+///
+/// # Returns
+///
+/// A new `Sha512` digest.
+impl Default for Sha512 {
+ fn default() -> Sha512 {
+ Sha512{ engine: CryptoDigest::new(Some(DigestAlgorithm::SHA2_512)) }
+ }
+}
+
+impl BlockInput for Sha512 {
+ type BlockSize = BlockSize;
+}
+
+/// Input `msg` into the digest.
+///
+/// # Examples
+///
+/// ```
+/// use crypto::digest::Sha512;
+///
+/// let hasher: Sha512 = Sha512::default();
+///
+/// hasher.process(b"foo");
+/// hasher.process(b"bar");
+/// ```
+impl Input for Sha512 {
+ fn process(&mut self, msg: &[u8]) {
+ self.engine.add_bytes(&msg);
+ }
+}
+
+/// Retrieve the output hash from everything which has been fed into this
+/// `Sha512` digest thus far.
+///
+//
+// FIXME: Once const generics land in Rust, we should genericise calling
+// crypto_digest_get_digest in external::crypto_digest.
+impl FixedOutput for Sha512 {
+ type OutputSize = U32;
+
+ fn fixed_result(self) -> GenericArray<u8, Self::OutputSize> {
+ let buffer: [u8; DIGEST512_LEN] = get_512_bit_digest(self.engine);
+
+ GenericArray::clone_from_slice(&buffer)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use digest::Digest;
+
+ use super::*;
+
+ #[test]
+ fn sha256_default() {
+ let _: Sha256 = Sha256::default();
+ }
+
+ #[test]
+ fn sha256_digest() {
+ let mut h: Sha256 = Sha256::new();
+ let mut result: [u8; DIGEST256_LEN] = [0u8; DIGEST256_LEN];
+
+ h.input(b"foo");
+ h.input(b"bar");
+ h.input(b"baz");
+
+ result.copy_from_slice(h.fixed_result().as_slice());
+
+ println!("{:?}", &result[..]);
+
+ assert_eq!(&result[..], &b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"[..]);
+ }
+
+ #[test]
+ fn sha512_default() {
+ let _: Sha512 = Sha512::default();
+ }
+
+ #[test]
+ fn sha512_digest() {
+ let mut h: Sha512 = Sha512::new();
+ let mut result: [u8; DIGEST512_LEN] = [0u8; DIGEST512_LEN];
+
+ h.input(b"foo");
+ h.input(b"bar");
+ h.input(b"baz");
+
+ result.copy_from_slice(h.fixed_result().as_slice());
+
+ println!("{:?}", &result[..]);
+
+ assert_eq!(&result[..], &b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"[..]);
+ }
+}
diff --git a/src/rust/crypto/lib.rs b/src/rust/crypto/lib.rs
new file mode 100644
index 0000000000..d0793a8575
--- /dev/null
+++ b/src/rust/crypto/lib.rs
@@ -0,0 +1,36 @@
+// Copyright (c) 2018, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Common cryptographic functions and utilities.
+//!
+//! # Hash Digests and eXtendable Output Functions (XOFs)
+//!
+//! The `digests` module contains submodules for specific hash digests
+//! and extendable output functions.
+//!
+//! ```
+//! use crypto::digests::sha256::Sha256;
+//!
+//! let hasher: Sha256 = Sha256::default();
+//! let mut result: [u8; 32] = [0u8; 32];
+//!
+//! hasher.input("foo");
+//! hasher.input("bar");
+//! hasher.input("baz");
+//!
+//! result.copy_from_slice(hasher.result().as_bytes());
+//!
+//! assert!(result == "XXX");
+//! ```
+
+#[deny(missing_docs)]
+
+// External crates from cargo or TOR_RUST_DEPENDENCIES.
+extern crate digest;
+extern crate libc;
+
+// Our local crates.
+extern crate external;
+
+mod digests; // Unfortunately named "digests" plural to avoid name conflict with the digest crate
diff --git a/src/rust/external/Cargo.toml b/src/rust/external/Cargo.toml
index b5957b1079..60ec03be40 100644
--- a/src/rust/external/Cargo.toml
+++ b/src/rust/external/Cargo.toml
@@ -6,6 +6,9 @@ name = "external"
[dependencies]
libc = "=0.2.39"
+[dependencies.smartlist]
+path = "../smartlist"
+
[lib]
name = "external"
path = "lib.rs"
diff --git a/src/rust/external/crypto_digest.rs b/src/rust/external/crypto_digest.rs
new file mode 100644
index 0000000000..bc49e6124c
--- /dev/null
+++ b/src/rust/external/crypto_digest.rs
@@ -0,0 +1,402 @@
+// Copyright (c) 2018, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Bindings to external digest and XOF functions which live within
+//! src/common/crypto_digest.[ch].
+//!
+//! We wrap our C implementations in src/common/crypto_digest.[ch] with more
+//! Rusty types and interfaces in src/rust/crypto/digest/.
+
+use std::process::abort;
+
+use libc::c_char;
+use libc::c_int;
+use libc::size_t;
+use libc::uint8_t;
+
+use smartlist::Stringlist;
+
+/// Length of the output of our message digest.
+pub const DIGEST_LEN: usize = 20;
+
+/// Length of the output of our second (improved) message digests. (For now
+/// this is just sha256, but it could be any other 256-bit digest.)
+pub const DIGEST256_LEN: usize = 32;
+
+/// Length of the output of our 64-bit optimized message digests (SHA512).
+pub const DIGEST512_LEN: usize = 64;
+
+/// Length of a sha1 message digest when encoded in base32 with trailing = signs
+/// removed.
+pub const BASE32_DIGEST_LEN: usize = 32;
+
+/// Length of a sha1 message digest when encoded in base64 with trailing = signs
+/// removed.
+pub const BASE64_DIGEST_LEN: usize = 27;
+
+/// Length of a sha256 message digest when encoded in base64 with trailing =
+/// signs removed.
+pub const BASE64_DIGEST256_LEN: usize = 43;
+
+/// Length of a sha512 message digest when encoded in base64 with trailing =
+/// signs removed.
+pub const BASE64_DIGEST512_LEN: usize = 86;
+
+/// Length of hex encoding of SHA1 digest, not including final NUL.
+pub const HEX_DIGEST_LEN: usize = 40;
+
+/// Length of hex encoding of SHA256 digest, not including final NUL.
+pub const HEX_DIGEST256_LEN: usize = 64;
+
+/// Length of hex encoding of SHA512 digest, not including final NUL.
+pub const HEX_DIGEST512_LEN: usize = 128;
+
+/// Our C code uses an enum to declare the digest algorithm types which we know
+/// about. However, because enums are implementation-defined in C, we can
+/// neither work with them directly nor translate them into Rust enums.
+/// Instead, we represent them as a u8 (under the assumption that we'll never
+/// support more than 256 hash functions).
+#[allow(non_camel_case_types)]
+type digest_algorithm_t = u8;
+
+const DIGEST_SHA1: digest_algorithm_t = 0;
+const DIGEST_SHA256: digest_algorithm_t = 1;
+const DIGEST_SHA512: digest_algorithm_t = 2;
+const DIGEST_SHA3_256: digest_algorithm_t = 3;
+const DIGEST_SHA3_512: digest_algorithm_t = 4;
+
+/// The total number of digest algorithms we currently support.
+///
+/// We can't access these from Rust, because their definitions in C require
+/// introspecting the `digest_algorithm_t` typedef, which is an enum, so we have
+/// to redefine them here.
+const N_DIGEST_ALGORITHMS: usize = DIGEST_SHA3_512 as usize + 1;
+
+/// The number of hash digests we produce for a `common_digests_t`.
+///
+/// We can't access these from Rust, because their definitions in C require
+/// introspecting the `digest_algorithm_t` typedef, which is an enum, so we have
+/// to redefine them here.
+const N_COMMON_DIGEST_ALGORITHMS: usize = DIGEST_SHA256 as usize + 1;
+
+/// A digest function.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+#[allow(non_camel_case_types)]
+struct crypto_digest_t {
+ // This private, zero-length field forces the struct to be treated the same
+ // as its opaque C couterpart.
+ _unused: [u8; 0],
+}
+
+/// An eXtendible Output Function (XOF).
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+#[allow(non_camel_case_types)]
+struct crypto_xof_t {
+ // This private, zero-length field forces the struct to be treated the same
+ // as its opaque C couterpart.
+ _unused: [u8; 0],
+}
+
+/// A set of all the digests we commonly compute, taken on a single
+/// string. Any digests that are shorter than 512 bits are right-padded
+/// with 0 bits.
+///
+/// Note that this representation wastes 44 bytes for the SHA1 case, so
+/// don't use it for anything where we need to allocate a whole bunch at
+/// once.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+#[allow(non_camel_case_types)]
+struct common_digests_t {
+ pub d: [[c_char; N_COMMON_DIGEST_ALGORITHMS]; DIGEST256_LEN],
+}
+
+/// A `smartlist_t` is just an alias for the `#[repr(C)]` type `Stringlist`, to
+/// make it more clear that we're working with a smartlist which is owned by C.
+#[allow(non_camel_case_types)]
+type smartlist_t = Stringlist;
+
+/// All of the external functions from `src/common/crypto_digest.h`.
+///
+/// These are kept private because they should be wrapped with Rust to make their usage safer.
+//
+// BINDGEN_GENERATED: These definitions were generated with bindgen and cleaned
+// up manually. As such, there are more bindings than are likely necessary or
+// which are in use.
+#[allow(dead_code)]
+extern "C" {
+ fn crypto_digest(digest: *mut c_char, m: *const c_char, len: size_t) -> c_int;
+ fn crypto_digest256(digest: *mut c_char, m: *const c_char, len: size_t,
+ algorithm: digest_algorithm_t) -> c_int;
+ fn crypto_digest512(digest: *mut c_char, m: *const c_char, len: size_t,
+ algorithm: digest_algorithm_t) -> c_int;
+ fn crypto_common_digests(ds_out: *mut common_digests_t, m: *const c_char, len: size_t) -> c_int;
+ fn crypto_digest_smartlist_prefix(digest_out: *mut c_char, len_out: size_t, prepend: *const c_char,
+ lst: *const smartlist_t, append: *const c_char, alg: digest_algorithm_t);
+ fn crypto_digest_smartlist(digest_out: *mut c_char, len_out: size_t,
+ lst: *const smartlist_t, append: *const c_char, alg: digest_algorithm_t);
+ fn crypto_digest_algorithm_get_name(alg: digest_algorithm_t) -> *const c_char;
+ fn crypto_digest_algorithm_get_length(alg: digest_algorithm_t) -> size_t;
+ fn crypto_digest_algorithm_parse_name(name: *const c_char) -> c_int;
+ fn crypto_digest_new() -> *mut crypto_digest_t;
+ fn crypto_digest256_new(algorithm: digest_algorithm_t) -> *mut crypto_digest_t;
+ fn crypto_digest512_new(algorithm: digest_algorithm_t) -> *mut crypto_digest_t;
+ fn crypto_digest_free(digest: *mut crypto_digest_t);
+ fn crypto_digest_add_bytes(digest: *mut crypto_digest_t, data: *const c_char, len: size_t);
+ fn crypto_digest_get_digest(digest: *mut crypto_digest_t, out: *mut c_char, out_len: size_t);
+ fn crypto_digest_dup(digest: *const crypto_digest_t) -> *mut crypto_digest_t;
+ fn crypto_digest_assign(into: *mut crypto_digest_t, from: *const crypto_digest_t);
+ fn crypto_hmac_sha256(hmac_out: *mut c_char, key: *const c_char, key_len: size_t,
+ msg: *const c_char, msg_len: size_t);
+ fn crypto_mac_sha3_256(mac_out: *mut uint8_t, len_out: size_t,
+ key: *const uint8_t, key_len: size_t,
+ msg: *const uint8_t, msg_len: size_t);
+ fn crypto_xof_new() -> *mut crypto_xof_t;
+ fn crypto_xof_add_bytes(xof: *mut crypto_xof_t, data: *const uint8_t, len: size_t);
+ fn crypto_xof_squeeze_bytes(xof: *mut crypto_xof_t, out: *mut uint8_t, len: size_t);
+ fn crypto_xof_free(xof: *mut crypto_xof_t);
+}
+
+/// A wrapper around a `digest_algorithm_t`.
+pub enum DigestAlgorithm {
+ SHA2_256,
+ SHA2_512,
+ SHA3_256,
+ SHA3_512,
+}
+
+impl From<DigestAlgorithm> for digest_algorithm_t {
+ fn from(digest: DigestAlgorithm) -> digest_algorithm_t {
+ match digest {
+ DigestAlgorithm::SHA2_256 => DIGEST_SHA256,
+ DigestAlgorithm::SHA2_512 => DIGEST_SHA512,
+ DigestAlgorithm::SHA3_256 => DIGEST_SHA3_256,
+ DigestAlgorithm::SHA3_512 => DIGEST_SHA3_512,
+ }
+ }
+}
+
+/// A wrapper around a mutable pointer to a `crypto_digest_t`.
+pub struct CryptoDigest(*mut crypto_digest_t);
+
+/// Explicitly copy the state of a `CryptoDigest` hash digest context.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_dup`
+impl Clone for CryptoDigest {
+ fn clone(&self) -> CryptoDigest {
+ let digest: *mut crypto_digest_t;
+
+ unsafe {
+ digest = crypto_digest_dup(self.0 as *const crypto_digest_t);
+ }
+
+ // See the note in the implementation of CryptoDigest for the
+ // reasoning for `abort()` here.
+ if digest.is_null() {
+ abort();
+ }
+
+ CryptoDigest(digest)
+ }
+}
+
+impl CryptoDigest {
+ /// A wrapper to call one of the C functions `crypto_digest_new`,
+ /// `crypto_digest256_new`, or `crypto_digest512_new`.
+ ///
+ /// # Warnings
+ ///
+ /// This function will `abort()` the entire process in an "abnormal" fashion,
+ /// i.e. not unwinding this or any other thread's stack, running any
+ /// destructors, or calling any panic/exit hooks) if `tor_malloc()` (called in
+ /// `crypto_digest256_new()`) is unable to allocate memory.
+ ///
+ /// # Returns
+ ///
+ /// A new `CryptoDigest`, which is a wrapper around a opaque representation
+ /// of a `crypto_digest_t`. The underlying `crypto_digest_t` _MUST_ only
+ /// ever be handled via a raw pointer, and never introspected.
+ ///
+ /// # C_RUST_COUPLED
+ ///
+ /// * `crypto_digest_new`
+ /// * `crypto_digest256_new`
+ /// * `crypto_digest512_new`
+ /// * `tor_malloc` (called by `crypto_digest256_new`, but we make
+ /// assumptions about its behvaiour and return values here)
+ pub fn new(algorithm: Option<DigestAlgorithm>) -> CryptoDigest {
+ let digest: *mut crypto_digest_t;
+
+ if algorithm.is_none() {
+ unsafe {
+ digest = crypto_digest_new();
+ }
+ } else {
+ let algo: digest_algorithm_t = algorithm.unwrap().into(); // can't fail because it's Some
+
+ unsafe {
+ // XXX This is a pretty awkward API to use from Rust...
+ digest = match algo {
+ DIGEST_SHA1 => crypto_digest_new(),
+ DIGEST_SHA256 => crypto_digest256_new(DIGEST_SHA256),
+ DIGEST_SHA3_256 => crypto_digest256_new(DIGEST_SHA3_256),
+ DIGEST_SHA512 => crypto_digest512_new(DIGEST_SHA512),
+ DIGEST_SHA3_512 => crypto_digest512_new(DIGEST_SHA3_512),
+ _ => abort(),
+ }
+ }
+ }
+
+ // In our C code, `crypto_digest*_new()` allocates memory with
+ // `tor_malloc()`. In `tor_malloc()`, if the underlying malloc
+ // implementation fails to allocate the requested memory and returns a
+ // NULL pointer, we call `exit(1)`. In the case that this `exit(1)` is
+ // called within a worker, be that a process or a thread, the inline
+ // comments within `tor_malloc()` mention "that's ok, since the parent
+ // will run out of memory soon anyway". However, if it takes long
+ // enough for the worker to die, and it manages to return a NULL pointer
+ // to our Rust code, our Rust is now in an irreparably broken state and
+ // may exhibit undefined behaviour. An even worse scenario, if/when we
+ // have parent/child processes/threads controlled by Rust, would be that
+ // the UB contagion in Rust manages to spread to other children before
+ // the entire process (hopefully terminates).
+ //
+ // However, following the assumptions made in `tor_malloc()` that
+ // calling `exit(1)` in a child is okay because the parent will
+ // eventually run into the same errors, and also to stymie any UB
+ // contagion in the meantime, we call abort!() here to terminate the
+ // entire program immediately.
+ if digest.is_null() {
+ abort();
+ }
+
+ CryptoDigest(digest)
+ }
+
+ /// A wrapper to call the C function `crypto_digest_add_bytes`.
+ ///
+ /// # Inputs
+ ///
+ /// * `bytes`: a byte slice of bytes to be added into this digest.
+ ///
+ /// # C_RUST_COUPLED
+ ///
+ /// * `crypto_digest_add_bytes`
+ pub fn add_bytes(&self, bytes: &[u8]) {
+ unsafe {
+ crypto_digest_add_bytes(self.0 as *mut crypto_digest_t,
+ bytes.as_ptr() as *const c_char,
+ bytes.len() as size_t)
+ }
+ }
+}
+
+/// Get the 256-bit digest output of a `crypto_digest_t`.
+///
+/// # Inputs
+///
+/// * `digest`: A `CryptoDigest` which wraps either a `DIGEST_SHA256` or a
+/// `DIGEST_SHA3_256`.
+///
+/// # Warning
+///
+/// Calling this function with a `CryptoDigest` which is neither SHA2-256 or
+/// SHA3-256 is a programming error. Since we cannot introspect the opaque
+/// struct from Rust, however, there is no way for us to check that the correct
+/// one is being passed in. That is up to you, dear programmer. If you mess
+/// up, you will get a incorrectly-sized hash digest in return, and it will be
+/// your fault. Don't do that.
+///
+/// # Returns
+///
+/// A 256-bit hash digest, as a `[u8; 32]`.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_get_digest`
+/// * `DIGEST256_LEN`
+//
+// FIXME: Once const generics land in Rust, we should genericise calling
+// crypto_digest_get_digest w.r.t. output array size.
+pub fn get_256_bit_digest(digest: CryptoDigest) -> [u8; DIGEST256_LEN] {
+ let mut buffer: [u8; DIGEST256_LEN] = [0u8; DIGEST256_LEN];
+
+ unsafe {
+ crypto_digest_get_digest(digest.0,
+ buffer.as_mut_ptr() as *mut c_char,
+ DIGEST256_LEN as size_t);
+
+ if buffer.as_ptr().is_null() {
+ abort();
+ }
+ }
+ buffer
+}
+
+/// Get the 512-bit digest output of a `crypto_digest_t`.
+///
+/// # Inputs
+///
+/// * `digest`: A `CryptoDigest` which wraps either a `DIGEST_SHA512` or a
+/// `DIGEST_SHA3_512`.
+///
+/// # Warning
+///
+/// Calling this function with a `CryptoDigest` which is neither SHA2-512 or
+/// SHA3-512 is a programming error. Since we cannot introspect the opaque
+/// struct from Rust, however, there is no way for us to check that the correct
+/// one is being passed in. That is up to you, dear programmer. If you mess
+/// up, you will get a incorrectly-sized hash digest in return, and it will be
+/// your fault. Don't do that.
+///
+/// # Returns
+///
+/// A 512-bit hash digest, as a `[u8; 64]`.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_get_digest`
+/// * `DIGEST512_LEN`
+//
+// FIXME: Once const generics land in Rust, we should genericise calling
+// crypto_digest_get_digest w.r.t. output array size.
+pub fn get_512_bit_digest(digest: CryptoDigest) -> [u8; DIGEST512_LEN] {
+ let mut buffer: [u8; DIGEST512_LEN] = [0u8; DIGEST512_LEN];
+
+ unsafe {
+ crypto_digest_get_digest(digest.0,
+ buffer.as_mut_ptr() as *mut c_char,
+ DIGEST512_LEN as size_t);
+
+ if buffer.as_ptr().is_null() {
+ abort();
+ }
+ }
+ buffer
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_layout_common_digests_t() {
+ assert_eq!(::std::mem::size_of::<common_digests_t>(), 64usize,
+ concat!("Size of: ", stringify!(common_digests_t)));
+ assert_eq!(::std::mem::align_of::<common_digests_t>(), 1usize,
+ concat!("Alignment of ", stringify!(common_digests_t)));
+ }
+
+ #[test]
+ fn test_layout_crypto_digest_t() {
+ assert_eq!(::std::mem::size_of::<crypto_digest_t>(), 0usize,
+ concat!("Size of: ", stringify!(crypto_digest_t)));
+ assert_eq!(::std::mem::align_of::<crypto_digest_t>(), 1usize,
+ concat!("Alignment of ", stringify!(crypto_digest_t)));
+ }
+}
diff --git a/src/rust/external/lib.rs b/src/rust/external/lib.rs
index 5fd74cf4c2..ffd38ac5da 100644
--- a/src/rust/external/lib.rs
+++ b/src/rust/external/lib.rs
@@ -9,8 +9,10 @@
extern crate libc;
+extern crate smartlist;
+
+pub mod crypto_digest;
mod crypto_rand;
mod external;
-pub use crypto_rand::*;
pub use external::*;
diff --git a/src/rust/include.am b/src/rust/include.am
index 3edc87e1e2..ba652bda0c 100644
--- a/src/rust/include.am
+++ b/src/rust/include.am
@@ -4,7 +4,12 @@ EXTRA_DIST +=\
src/rust/Cargo.toml \
src/rust/Cargo.lock \
src/rust/.cargo/config.in \
+ src/rust/crypto/Cargo.toml \
+ src/rust/crypto/lib.rs \
+ src/rust/crypto/digests/mod.rs \
+ src/rust/crypto/digests/sha2.rs \
src/rust/external/Cargo.toml \
+ src/rust/external/crypto_digest.rs \
src/rust/external/crypto_rand.rs \
src/rust/external/external.rs \
src/rust/external/lib.rs \
diff --git a/src/test/include.am b/src/test/include.am
index 43eaf1e239..9543635c84 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -139,6 +139,7 @@ src_test_test_SOURCES = \
src/test/test_keypin.c \
src/test/test_link_handshake.c \
src/test/test_logging.c \
+ src/test/test_mainloop.c \
src/test/test_microdesc.c \
src/test/test_nodelist.c \
src/test/test_oom.c \
diff --git a/src/test/test.c b/src/test/test.c
index 0ad33ec0c0..f0e8b9b728 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -880,6 +880,7 @@ struct testgroup_t testgroups[] = {
{ "introduce/", introduce_tests },
{ "keypin/", keypin_tests },
{ "link-handshake/", link_handshake_tests },
+ { "mainloop/", mainloop_tests },
{ "nodelist/", nodelist_tests },
{ "oom/", oom_tests },
{ "oos/", oos_tests },
diff --git a/src/test/test.h b/src/test/test.h
index 0cd0304629..3095d54e33 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -234,6 +234,7 @@ extern struct testcase_t introduce_tests[];
extern struct testcase_t keypin_tests[];
extern struct testcase_t link_handshake_tests[];
extern struct testcase_t logging_tests[];
+extern struct testcase_t mainloop_tests[];
extern struct testcase_t microdesc_tests[];
extern struct testcase_t nodelist_tests[];
extern struct testcase_t oom_tests[];
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index e1a40b7e60..40db31320f 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -6,8 +6,10 @@
#define ADDRESSMAP_PRIVATE
#include "orconfig.h"
#include "or.h"
+#include "crypto_rand.h"
#include "test.h"
#include "addressmap.h"
+#include "log_test_helpers.h"
/** Mocking replacement: only handles localhost. */
static int
@@ -941,6 +943,158 @@ test_virtaddrmap(void *data)
;
}
+static const char *canned_data = NULL;
+static size_t canned_data_len = 0;
+
+/* Mock replacement for crypto_rand() that returns canned data from
+ * canned_data above. */
+static void
+crypto_canned(char *ptr, size_t n)
+{
+ if (canned_data_len) {
+ size_t to_copy = MIN(n, canned_data_len);
+ memcpy(ptr, canned_data, to_copy);
+ canned_data += to_copy;
+ canned_data_len -= to_copy;
+ n -= to_copy;
+ ptr += to_copy;
+ }
+ if (n) {
+ crypto_rand_unmocked(ptr, n);
+ }
+}
+
+static void
+test_virtaddrmap_persist(void *data)
+{
+ (void)data;
+ const char *a, *b, *c;
+ tor_addr_t addr;
+ char *ones = NULL;
+
+ addressmap_init();
+
+ // Try a hostname.
+ a = addressmap_register_virtual_address(RESOLVED_TYPE_HOSTNAME,
+ tor_strdup("foobar.baz"));
+ tt_assert(a);
+ tt_assert(!strcmpend(a, ".virtual"));
+
+ // mock crypto_rand to repeat the same result twice; make sure we get
+ // different outcomes. (Because even though the odds for receiving the
+ // same 80-bit address twice is only 1/2^40, it could still happen for
+ // some user -- but running our test through 2^40 iterations isn't
+ // reasonable.)
+ canned_data = "1234567890" // the first call returns this.
+ "1234567890" // the second call returns this.
+ "abcdefghij"; // the third call returns this.
+ canned_data_len = 30;
+ MOCK(crypto_rand, crypto_canned);
+
+ a = addressmap_register_virtual_address(RESOLVED_TYPE_HOSTNAME,
+ tor_strdup("quuxit.baz"));
+ b = addressmap_register_virtual_address(RESOLVED_TYPE_HOSTNAME,
+ tor_strdup("nescio.baz"));
+ tt_assert(a);
+ tt_assert(b);
+ tt_str_op(a, OP_EQ, "gezdgnbvgy3tqojq.virtual");
+ tt_str_op(b, OP_EQ, "mfrggzdfmztwq2lk.virtual");
+
+ // Now try something to get us an ipv4 address
+ UNMOCK(crypto_rand);
+ tt_int_op(0,OP_EQ, parse_virtual_addr_network("192.168.0.0/16",
+ AF_INET, 0, NULL));
+ a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
+ tor_strdup("foobar.baz"));
+ tt_assert(a);
+ tt_assert(!strcmpstart(a, "192.168."));
+ tor_addr_parse(&addr, a);
+ tt_int_op(AF_INET, OP_EQ, tor_addr_family(&addr));
+
+ b = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
+ tor_strdup("quuxit.baz"));
+ tt_str_op(b, OP_NE, a);
+ tt_assert(!strcmpstart(b, "192.168."));
+
+ // Try some canned entropy and verify all the we discard duplicates,
+ // addresses that end with 0, and addresses that end with 255.
+ MOCK(crypto_rand, crypto_canned);
+ canned_data = "\x01\x02\x03\x04" // okay
+ "\x01\x02\x03\x04" // duplicate
+ "\x03\x04\x00\x00" // bad ending 1
+ "\x05\x05\x00\xff" // bad ending 2
+ "\x05\x06\x07\xf0"; // okay
+ canned_data_len = 20;
+ a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
+ tor_strdup("wumble.onion"));
+ b = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
+ tor_strdup("wumpus.onion"));
+ tt_str_op(a, OP_EQ, "192.168.3.4");
+ tt_str_op(b, OP_EQ, "192.168.7.240");
+
+ // Now try IPv6!
+ UNMOCK(crypto_rand);
+ tt_int_op(0,OP_EQ, parse_virtual_addr_network("1010:F000::/20",
+ AF_INET6, 0, NULL));
+ a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
+ tor_strdup("foobar.baz"));
+ tt_assert(a);
+ tt_assert(!strcmpstart(a, "[1010:f"));
+ tor_addr_parse(&addr, a);
+ tt_int_op(AF_INET6, OP_EQ, tor_addr_family(&addr));
+
+ b = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
+ tor_strdup("quuxit.baz"));
+ tt_str_op(b, OP_NE, a);
+ tt_assert(!strcmpstart(b, "[1010:f"));
+
+ // Try IPv6 with canned entropy, to make sure we detect duplicates.
+ MOCK(crypto_rand, crypto_canned);
+ canned_data = "acanthopterygian" // okay
+ "cinematographist" // okay
+ "acanthopterygian" // duplicate
+ "acanthopterygian" // duplicate
+ "acanthopterygian" // duplicate
+ "cinematographist" // duplicate
+ "coadministration"; // okay
+ canned_data_len = 16 * 7;
+ a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
+ tor_strdup("wuffle.baz"));
+ b = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
+ tor_strdup("gribble.baz"));
+ c = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
+ tor_strdup("surprisingly-legible.baz"));
+ tt_str_op(a, OP_EQ, "[1010:f16e:7468:6f70:7465:7279:6769:616e]");
+ tt_str_op(b, OP_EQ, "[1010:fe65:6d61:746f:6772:6170:6869:7374]");
+ tt_str_op(c, OP_EQ, "[1010:f164:6d69:6e69:7374:7261:7469:6f6e]");
+
+ // Try address exhaustion: make sure we can actually fail if we
+ // get too many already-existing addresses.
+ canned_data_len = 128*1024;
+ canned_data = ones = tor_malloc(canned_data_len);
+ memset(ones, 1, canned_data_len);
+ // There is some chance this one will fail if a previous random
+ // allocation gave out the address already.
+ a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
+ tor_strdup("might-work.onion"));
+ if (a) {
+ tt_str_op(a, OP_EQ, "192.168.1.1");
+ }
+ setup_capture_of_logs(LOG_WARN);
+ // This one will definitely fail, since we've set up the RNG to hand
+ // out "1" forever.
+ b = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
+ tor_strdup("wont-work.onion"));
+ tt_assert(b == NULL);
+ expect_single_log_msg_containing("Ran out of virtual addresses!");
+
+ done:
+ UNMOCK(crypto_rand);
+ tor_free(ones);
+ addressmap_free_all();
+ teardown_capture_of_logs();
+}
+
static void
test_addr_localname(void *arg)
{
@@ -1095,6 +1249,7 @@ struct testcase_t addr_tests[] = {
ADDR_LEGACY(ip6_helpers),
ADDR_LEGACY(parse),
{ "virtaddr", test_virtaddrmap, 0, NULL, NULL },
+ { "virtaddr_persist", test_virtaddrmap_persist, TT_FORK, NULL, NULL },
{ "localname", test_addr_localname, 0, NULL, NULL },
{ "dup_ip", test_addr_dup_ip, 0, NULL, NULL },
{ "sockaddr_to_str", test_addr_sockaddr_to_str, 0, NULL, NULL },
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 25b4e42877..0106e40d97 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -1501,6 +1501,13 @@ test_dir_measured_bw_kb(void *arg)
"bw=1024 junk=007\n",
"misc=junk node_id=$557365204145532d32353620696e73746561642e "
"bw=1024 junk=007\n",
+ /* check whether node_id can be at the end */
+ "bw=1024 node_id=$557365204145532d32353620696e73746561642e\n",
+ /* check whether node_id can be at the end and bw has something in front*/
+ "foo=bar bw=1024 node_id=$557365204145532d32353620696e73746561642e\n",
+ /* check whether node_id can be at the end and something in the
+ * in the middle of bw and node_id */
+ "bw=1024 foo=bar node_id=$557365204145532d32353620696e73746561642e\n",
"end"
};
const char *lines_fail[] = {
@@ -1555,6 +1562,25 @@ test_dir_measured_bw_kb(void *arg)
return;
}
+/* Test dirserv_read_measured_bandwidths */
+static void
+test_dir_dirserv_read_measured_bandwidths(void *arg)
+{
+ char *fname=NULL;
+ (void)arg;
+
+ fname = tor_strdup(get_fname("V3BandwidthsFile"));
+ /* Test an empty file */
+ write_str_to_file(fname, "", 0);
+ setup_capture_of_logs(LOG_WARN);
+ tt_int_op(-1, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL));
+ expect_log_msg("Empty bandwidth file\n");
+
+ done:
+ tor_free(fname);
+ teardown_capture_of_logs();
+}
+
#define MBWC_INIT_TIME 1000
/** Do the measured bandwidth cache unit test */
@@ -5823,6 +5849,7 @@ struct testcase_t dir_tests[] = {
DIR_LEGACY(versions),
DIR_LEGACY(fp_pairs),
DIR(split_fps, 0),
+ DIR_LEGACY(dirserv_read_measured_bandwidths),
DIR_LEGACY(measured_bw_kb),
DIR_LEGACY(measured_bw_kb_cache),
DIR_LEGACY(param_voting),
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index 6af18f8f4f..cfcb88a66e 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -4,6 +4,7 @@
#include "orconfig.h"
#define CIRCUITLIST_PRIVATE
+#define CIRCUITBUILD_PRIVATE
#define STATEFILE_PRIVATE
#define ENTRYNODES_PRIVATE
#define ROUTERLIST_PRIVATE
@@ -14,6 +15,7 @@
#include "bridges.h"
#include "circuitlist.h"
+#include "circuitbuild.h"
#include "config.h"
#include "confparse.h"
#include "crypto_rand.h"
@@ -75,6 +77,17 @@ bfn_mock_node_get_by_id(const char *id)
return NULL;
}
+/* Helper function to free a test node. */
+static void
+test_node_free(node_t *n)
+{
+ tor_free(n->rs);
+ tor_free(n->md->onion_curve25519_pkey);
+ short_policy_free(n->md->exit_policy);
+ tor_free(n->md);
+ tor_free(n);
+}
+
/* Unittest cleanup function: Cleanup the fake network. */
static int
big_fake_network_cleanup(const struct testcase_t *testcase, void *ptr)
@@ -84,9 +97,7 @@ big_fake_network_cleanup(const struct testcase_t *testcase, void *ptr)
if (big_fake_net_nodes) {
SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, {
- tor_free(n->rs);
- tor_free(n->md);
- tor_free(n);
+ test_node_free(n);
});
smartlist_free(big_fake_net_nodes);
}
@@ -114,9 +125,18 @@ big_fake_network_setup(const struct testcase_t *testcase)
big_fake_net_nodes = smartlist_new();
for (i = 0; i < N_NODES; ++i) {
+ curve25519_secret_key_t curve25519_secret_key;
+
node_t *n = tor_malloc_zero(sizeof(node_t));
n->md = tor_malloc_zero(sizeof(microdesc_t));
+ /* Generate curve25519 key for this node */
+ n->md->onion_curve25519_pkey =
+ tor_malloc_zero(sizeof(curve25519_public_key_t));
+ curve25519_secret_key_generate(&curve25519_secret_key, 0);
+ curve25519_public_key_generate(n->md->onion_curve25519_pkey,
+ &curve25519_secret_key);
+
crypto_rand(n->identity, sizeof(n->identity));
n->rs = tor_malloc_zero(sizeof(routerstatus_t));
@@ -136,8 +156,8 @@ big_fake_network_setup(const struct testcase_t *testcase)
{
char nickname_binary[8];
crypto_rand(nickname_binary, sizeof(nickname_binary));
- base64_encode(n->rs->nickname, sizeof(n->rs->nickname),
- nickname_binary, sizeof(nickname_binary), 0);
+ base32_encode(n->rs->nickname, sizeof(n->rs->nickname),
+ nickname_binary, sizeof(nickname_binary));
}
/* Call half of the nodes a possible guard. */
@@ -145,6 +165,12 @@ big_fake_network_setup(const struct testcase_t *testcase)
n->is_possible_guard = 1;
n->rs->guardfraction_percentage = 100;
n->rs->has_guardfraction = 1;
+ n->rs->is_possible_guard = 1;
+ }
+
+ /* Make some of these nodes a possible exit */
+ if (i % 7 == 0) {
+ n->md->exit_policy = parse_short_policy("accept 443");
}
smartlist_add(big_fake_net_nodes, n);
@@ -1076,9 +1102,7 @@ test_entry_guard_expand_sample_small_net(void *arg)
/* Fun corner case: not enough guards to make up our whole sample size. */
SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, {
if (n_sl_idx >= 15) {
- tor_free(n->rs);
- tor_free(n->md);
- tor_free(n);
+ test_node_free(n);
SMARTLIST_DEL_CURRENT(big_fake_net_nodes, n);
} else {
n->rs->addr = 0; // make the filter reject this.
@@ -1172,9 +1196,7 @@ test_entry_guard_update_from_consensus_status(void *arg)
entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 5);
node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity);
smartlist_remove(big_fake_net_nodes, n);
- tor_free(n->rs);
- tor_free(n->md);
- tor_free(n);
+ test_node_free(n);
}
update_approx_time(start + 300);
sampled_guards_update_from_consensus(gs);
@@ -2805,6 +2827,161 @@ test_entry_guard_outdated_dirserver_exclusion(void *arg)
}
}
+/** Test helper to extend the <b>oc</b> circuit path <b>n</b> times and then
+ * ensure that the circuit is now complete. */
+static void
+helper_extend_circuit_path_n_times(origin_circuit_t *oc, int n)
+{
+ int retval;
+ int i;
+
+ /* Extend path n times */
+ for (i = 0 ; i < n ; i++) {
+ retval = onion_extend_cpath(oc);
+ tt_int_op(retval, OP_EQ, 0);
+ tt_int_op(circuit_get_cpath_len(oc), OP_EQ, i+1);
+ }
+
+ /* Now do it one last time and see that circ is complete */
+ retval = onion_extend_cpath(oc);
+ tt_int_op(retval, OP_EQ, 1);
+
+ done:
+ ;
+}
+
+/** Test for basic Tor path selection. Makes sure we build 3-hop circuits. */
+static void
+test_entry_guard_basic_path_selection(void *arg)
+{
+ (void) arg;
+
+ int retval;
+
+ /* Enable entry guards */
+ or_options_t *options = get_options_mutable();
+ options->UseEntryGuards = 1;
+
+ /* disables /16 check since all nodes have the same addr... */
+ options->EnforceDistinctSubnets = 0;
+
+ /* Create our circuit */
+ circuit_t *circ = dummy_origin_circuit_new(30);
+ origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
+ oc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
+
+ /* First pick the exit and pin it on the build_state */
+ retval = onion_pick_cpath_exit(oc, NULL, 0);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Extend path 3 times. First we pick guard, then middle, then exit. */
+ helper_extend_circuit_path_n_times(oc, 3);
+
+ done:
+ circuit_free_(circ);
+}
+
+/** Test helper to build an L2 and L3 vanguard list. The vanguard lists
+ * produced should be completely disjoint. */
+static void
+helper_setup_vanguard_list(or_options_t *options)
+{
+ int i = 0;
+
+ /* Add some nodes to the vanguard L2 list */
+ options->HSLayer2Nodes = routerset_new();
+ for (i = 0; i < 10 ; i += 2) {
+ node_t *vanguard_node = smartlist_get(big_fake_net_nodes, i);
+ tt_assert(vanguard_node->is_possible_guard);
+ routerset_parse(options->HSLayer2Nodes, vanguard_node->rs->nickname, "l2");
+ }
+ /* also add some nodes to vanguard L3 list
+ * (L2 list and L3 list should be disjoint for this test to work) */
+ options->HSLayer3Nodes = routerset_new();
+ for (i = 10; i < 20 ; i += 2) {
+ node_t *vanguard_node = smartlist_get(big_fake_net_nodes, i);
+ tt_assert(vanguard_node->is_possible_guard);
+ routerset_parse(options->HSLayer3Nodes, vanguard_node->rs->nickname, "l3");
+ }
+
+ done:
+ ;
+}
+
+/** Test to ensure that vanguard path selection works properly. Ensures that
+ * default vanguard circuits are 4 hops, and that path selection works
+ * correctly given the vanguard settings. */
+static void
+test_entry_guard_vanguard_path_selection(void *arg)
+{
+ (void) arg;
+
+ int retval;
+
+ /* Enable entry guards */
+ or_options_t *options = get_options_mutable();
+ options->UseEntryGuards = 1;
+
+ /* XXX disables /16 check */
+ options->EnforceDistinctSubnets = 0;
+
+ /* Setup our vanguard list */
+ helper_setup_vanguard_list(options);
+
+ /* Create our circuit */
+ circuit_t *circ = dummy_origin_circuit_new(30);
+ origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
+ oc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
+ oc->build_state->is_internal = 1;
+
+ /* Switch circuit purpose to vanguards */
+ circ->purpose = CIRCUIT_PURPOSE_HS_VANGUARDS;
+
+ /* First pick the exit and pin it on the build_state */
+ tt_int_op(oc->build_state->desired_path_len, OP_EQ, 0);
+ retval = onion_pick_cpath_exit(oc, NULL, 0);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Ensure that vanguards make 4-hop circuits by default */
+ tt_int_op(oc->build_state->desired_path_len, OP_EQ, 4);
+
+ /* Extend path as many times as needed to have complete circ. */
+ helper_extend_circuit_path_n_times(oc, oc->build_state->desired_path_len);
+
+ /* Test that the cpath linked list is set correctly. */
+ crypt_path_t *l1_node = oc->cpath;
+ crypt_path_t *l2_node = l1_node->next;
+ crypt_path_t *l3_node = l2_node->next;
+ crypt_path_t *l4_node = l3_node->next;
+ crypt_path_t *l1_node_again = l4_node->next;
+ tt_ptr_op(l1_node, OP_EQ, l1_node_again);
+
+ /* Test that L2 is indeed HSLayer2Node */
+ retval = routerset_contains_extendinfo(options->HSLayer2Nodes,
+ l2_node->extend_info);
+ tt_int_op(retval, OP_EQ, 4);
+ /* test that L3 node is _not_ contained in HSLayer2Node */
+ retval = routerset_contains_extendinfo(options->HSLayer2Nodes,
+ l3_node->extend_info);
+ tt_int_op(retval, OP_LT, 4);
+
+ /* Test that L3 is indeed HSLayer3Node */
+ retval = routerset_contains_extendinfo(options->HSLayer3Nodes,
+ l3_node->extend_info);
+ tt_int_op(retval, OP_EQ, 4);
+ /* test that L2 node is _not_ contained in HSLayer3Node */
+ retval = routerset_contains_extendinfo(options->HSLayer3Nodes,
+ l2_node->extend_info);
+ tt_int_op(retval, OP_LT, 4);
+
+ /* TODO: Test that L1 can be the same as exit. To test this we need start
+ enforcing EnforceDistinctSubnets again, which means that we need to give
+ each test node a different address which currently breaks some tests. */
+
+ done:
+ circuit_free_(circ);
+}
+
static const struct testcase_setup_t big_fake_network = {
big_fake_network_setup, big_fake_network_cleanup
};
@@ -2868,6 +3045,8 @@ struct testcase_t entrynodes_tests[] = {
BFN_TEST(select_and_cancel),
BFN_TEST(drop_guards),
BFN_TEST(outdated_dirserver_exclusion),
+ BFN_TEST(basic_path_selection),
+ BFN_TEST(vanguard_path_selection),
UPGRADE_TEST(upgrade_a_circuit, "c1-done c2-done"),
UPGRADE_TEST(upgrade_blocked_by_live_primary_guards, "c1-done c2-done"),
diff --git a/src/test/test_mainloop.c b/src/test/test_mainloop.c
new file mode 100644
index 0000000000..9da8a039dd
--- /dev/null
+++ b/src/test/test_mainloop.c
@@ -0,0 +1,142 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_mainloop.c
+ * \brief Tests for functions closely related to the Tor main loop
+ */
+
+#include "test.h"
+#include "log_test_helpers.h"
+
+#include "or.h"
+#include "main.h"
+
+static const uint64_t BILLION = 1000000000;
+
+static void
+test_mainloop_update_time_normal(void *arg)
+{
+ (void)arg;
+
+ monotime_enable_test_mocking();
+ /* This is arbitrary */
+ uint64_t mt_now = U64_LITERAL(7493289274986);
+ /* This time is in the past as of when this test was written. */
+ time_t now = 1525272090;
+ monotime_coarse_set_mock_time_nsec(mt_now);
+ reset_uptime();
+ update_current_time(now);
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 0);
+
+ update_current_time(now); // Same time as before is a no-op.
+ tt_int_op(get_uptime(), OP_EQ, 0);
+
+ now += 1;
+ mt_now += BILLION;
+ monotime_coarse_set_mock_time_nsec(mt_now);
+ update_current_time(now);
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 1);
+
+ now += 2; // two-second jump is unremarkable.
+ mt_now += 2*BILLION;
+ update_current_time(now);
+ monotime_coarse_set_mock_time_nsec(mt_now);
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 3);
+
+ now -= 1; // a one-second hop backwards is also unremarkable.
+ update_current_time(now);
+ tt_int_op(approx_time(), OP_EQ, now); // it changes the approx time...
+ tt_int_op(get_uptime(), OP_EQ, 3); // but it doesn't roll back our uptime
+
+ done:
+ monotime_disable_test_mocking();
+}
+
+static void
+test_mainloop_update_time_jumps(void *arg)
+{
+ (void)arg;
+
+ monotime_enable_test_mocking();
+ /* This is arbitrary */
+ uint64_t mt_now = U64_LITERAL(7493289274986);
+ /* This time is in the past as of when this test was written. */
+ time_t now = 220897152;
+ monotime_coarse_set_mock_time_nsec(mt_now);
+ reset_uptime();
+ update_current_time(now);
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 0);
+
+ /* Put some uptime on the clock.. */
+ now += 3;
+ mt_now += 3*BILLION;
+ monotime_coarse_set_mock_time_nsec(mt_now);
+ update_current_time(now);
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 3);
+
+ /* Now try jumping forward and backward, without updating the monotonic
+ * clock. */
+ setup_capture_of_logs(LOG_NOTICE);
+ now += 1800;
+ update_current_time(now);
+ expect_single_log_msg_containing(
+ "Your system clock just jumped 1800 seconds forward");
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 3); // no uptime change.
+ mock_clean_saved_logs();
+
+ now -= 600;
+ update_current_time(now);
+ expect_single_log_msg_containing(
+ "Your system clock just jumped 600 seconds backward");
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 3); // no uptime change.
+ mock_clean_saved_logs();
+
+ /* uptime tracking should go normally now if the clock moves sensibly. */
+ now += 2;
+ mt_now += 2*BILLION;
+ update_current_time(now);
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 5);
+
+ /* If we skip forward by a few minutes but the monotonic clock agrees,
+ * we've just been idle: that counts as not worth warning about. */
+ now += 1800;
+ mt_now += 1800*BILLION;
+ monotime_coarse_set_mock_time_nsec(mt_now);
+ update_current_time(now);
+ expect_no_log_entry();
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 5); // this doesn't count to uptime, though.
+
+ /* If we skip forward by a long time, even if the clock agrees, it's
+ * idnless that counts. */
+ now += 4000;
+ mt_now += 4000*BILLION;
+ monotime_coarse_set_mock_time_nsec(mt_now);
+ update_current_time(now);
+ expect_single_log_msg_containing("Tor has been idle for 4000 seconds");
+ tt_int_op(approx_time(), OP_EQ, now);
+ tt_int_op(get_uptime(), OP_EQ, 5);
+
+ done:
+ teardown_capture_of_logs();
+ monotime_disable_test_mocking();
+}
+
+#define MAINLOOP_TEST(name) \
+ { #name, test_mainloop_## name , TT_FORK, NULL, NULL }
+
+struct testcase_t mainloop_tests[] = {
+ MAINLOOP_TEST(update_time_normal),
+ MAINLOOP_TEST(update_time_jumps),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index ef1be139a6..896e093968 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -205,7 +205,7 @@ test_tortls_tor_tls_get_error(void *data)
static void
library_init(void)
{
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+#ifdef OPENSSL_1_1_API
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
#else
SSL_library_init();
@@ -847,8 +847,10 @@ test_tortls_classify_client_ciphers(void *ignored)
sk_SSL_CIPHER_zero(ciphers);
one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
+ tt_assert(one);
one->id = 0x00ff;
two = get_cipher_by_name("ECDHE-RSA-AES128-GCM-SHA256");
+ tt_assert(two);
two->id = 0x0000;
sk_SSL_CIPHER_push(ciphers, one);
tls->client_cipher_list_type = 0;
@@ -918,6 +920,7 @@ test_tortls_client_is_using_v2_ciphers(void *ignored)
ciphers = sk_SSL_CIPHER_new_null();
SSL_CIPHER *one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
+ tt_assert(one);
one->id = 0x00ff;
sk_SSL_CIPHER_push(ciphers, one);
sess->ciphers = ciphers;
diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c
index 8d29d2062d..cc7073850c 100644
--- a/src/test/test_workqueue.c
+++ b/src/test/test_workqueue.c
@@ -224,7 +224,8 @@ add_n_work_items(threadpool_t *tp, int n)
workqueue_entry_t **to_cancel;
workqueue_entry_t *ent;
- to_cancel = tor_malloc(sizeof(workqueue_entry_t*) * opt_n_cancel);
+ // We'll choose randomly which entries to cancel.
+ to_cancel = tor_calloc(opt_n_cancel, sizeof(workqueue_entry_t*));
while (n_queued++ < n) {
ent = add_work(tp);
@@ -233,9 +234,14 @@ add_n_work_items(threadpool_t *tp, int n)
tor_libevent_exit_loop_after_delay(tor_libevent_get_base(), NULL);
return -1;
}
- if (n_try_cancel < opt_n_cancel &&
- tor_weak_random_range(&weak_rng, n) < opt_n_cancel) {
+
+ if (n_try_cancel < opt_n_cancel) {
to_cancel[n_try_cancel++] = ent;
+ } else {
+ int p = tor_weak_random_range(&weak_rng, n_queued);
+ if (p < n_try_cancel) {
+ to_cancel[p] = ent;
+ }
}
}