diff options
97 files changed, 2708 insertions, 1939 deletions
diff --git a/.travis.yml b/.travis.yml index 18c5b5bdd5..6ea11ce9d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -106,6 +106,8 @@ matrix: env: DISTCHECK="yes" RUST_OPTIONS="" - compiler: gcc env: DISTCHECK="yes" RUST_OPTIONS="--enable-rust --enable-cargo-online-mode" + - compiler: gcc + env: MODULES_OPTIONS="--disable-module-dirauth" ## The "sudo: required" forces non-containerized builds, working ## around a Travis CI environment issue: clang LeakAnalyzer fails ## because it requires ptrace and the containerized environment no @@ -118,6 +120,9 @@ matrix: - compiler: clang sudo: required env: RUST_OPTIONS="" + - compiler: clang + sudo: required + env: MODULES_OPTIONS="--disable-module-dirauth" before_install: ## If we're on OSX, homebrew usually needs to updated first @@ -144,13 +149,11 @@ install: - if [[ "$RUST_OPTIONS" != "" ]]; then rustc --version; fi - if [[ "$RUST_OPTIONS" != "" ]]; then cargo --version; fi ## If we're testing rust builds in offline-mode, then set up our vendored dependencies - - if [[ "$RUST_OPTIONS" == "--enable-rust" ]]; then git submodule init ; fi - - if [[ "$RUST_OPTIONS" == "--enable-rust" ]]; then git submodule update; fi - if [[ "$TOR_RUST_DEPENDENCIES" == "true" ]]; then export TOR_RUST_DEPENDENCIES=$PWD/src/ext/rust/crates; fi script: - ./autogen.sh - - ./configure $RUST_OPTIONS $COVERAGE_OPTIONS --disable-asciidoc --enable-fatal-warnings --disable-silent-rules --enable-fragile-hardening + - ./configure $RUST_OPTIONS $COVERAGE_OPTIONS $MODULES_OPTIONS --disable-asciidoc --enable-fatal-warnings --disable-silent-rules --enable-fragile-hardening ## We run `make check` because that's what https://jenkins.torproject.org does. - if [[ "$DISTCHECK" == "" ]]; then make check; fi - if [[ "$DISTCHECK" != "" ]]; then make distcheck DISTCHECK_CONFIGURE_FLAGS="$RUST_OPTIONS $COVERAGE_OPTIONS --disable-asciidoc --enable-fatal-warnings --disable-silent-rules --enable-fragile-hardening"; fi diff --git a/Makefile.am b/Makefile.am index cccad6c5ed..163b650bb0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,14 +51,14 @@ AM_ETAGSFLAGS=--regex='{c}/MOCK_IMPL([^,]+,\W*\([a-zA-Z0-9_]+\)\W*,/\1/s' if COVERAGE_ENABLED TEST_CFLAGS=-fno-inline -fprofile-arcs -ftest-coverage if DISABLE_ASSERTS_IN_UNIT_TESTS -TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE -DDISABLE_ASSERTS_IN_UNIT_TESTS +TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE -DDISABLE_ASSERTS_IN_UNIT_TESTS @TOR_MODULES_ALL_ENABLED@ else -TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE +TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE @TOR_MODULES_ALL_ENABLED@ endif TEST_NETWORK_FLAGS=--coverage --hs-multi-client 1 else TEST_CFLAGS= -TEST_CPPFLAGS=-DTOR_UNIT_TESTS +TEST_CPPFLAGS=-DTOR_UNIT_TESTS @TOR_MODULES_ALL_ENABLED@ TEST_NETWORK_FLAGS=--hs-multi-client 1 endif TEST_NETWORK_WARNING_FLAGS=--quiet --only-warnings diff --git a/changes/bug23094 b/changes/bug23094 new file mode 100644 index 0000000000..5040ab4e71 --- /dev/null +++ b/changes/bug23094 @@ -0,0 +1,4 @@ + o Code simplification and refactoring: + - Make hsdir_index in node_t a hsdir_index_t rather than a pointer + as hsdir_index is always present. Also, we move hsdir_index_t into + or.h. Closes ticket 23094. Patch by Neel Chauhan. diff --git a/changes/bug23107 b/changes/bug23107 new file mode 100644 index 0000000000..55885e10fd --- /dev/null +++ b/changes/bug23107 @@ -0,0 +1,6 @@ + o Code simplification and refactoring: + - Put a SHA1 public key digest in hs_service_intro_point_t, and use it in + register_intro_circ() and service_intro_point_new(). This prevents the + digest from being re-calculated each time. Closes ticket 23107. Patch by + Neel Chauhan. + diff --git a/changes/bug25843 b/changes/bug25843 new file mode 100644 index 0000000000..ddbbbef787 --- /dev/null +++ b/changes/bug25843 @@ -0,0 +1,3 @@ + o Minor feature (entry guards): + - Introduce torrc option NumPrimaryGuards for controlling the number of + primary guards. Closes ticket 25843.
\ No newline at end of file diff --git a/changes/bug26007 b/changes/bug26007 new file mode 100644 index 0000000000..efcd15084d --- /dev/null +++ b/changes/bug26007 @@ -0,0 +1,5 @@ + o Major bugfixes (directory authorities, security): + - When directory authorities read a zero-byte bandwidth file, they log + a warning with the contents of an uninitialised buffer. Log a warning + about the empty file instead. + Fixes bug 26007; bugfix on 0.2.2.1-alpha. diff --git a/changes/feature19429 b/changes/feature19429 new file mode 100644 index 0000000000..0faa6e84e1 --- /dev/null +++ b/changes/feature19429 @@ -0,0 +1,5 @@ + o Minor features (compatibility): + - Tor now detects versions of OpenSSL 1.1.0 and later compiled with the + no-deprecated option, and builds correctly with them. Closes + tickets 19429, 19981, and 25353. + diff --git a/changes/ticket20522 b/changes/ticket20522 new file mode 100644 index 0000000000..a5e6718e1e --- /dev/null +++ b/changes/ticket20522 @@ -0,0 +1,6 @@ + o Deprecated features: + - As we are not recommending 0.2.5 anymore we require relays that once had + an ed25519 key associated with their RSA key to always have that key + instead of allowing them to drop back to a version that didn't support + ed25519. This means they need to use a new RSA key if the want to + downgrade to an older version of tor without ed25519. Closes ticket 20522. diff --git a/changes/ticket23354 b/changes/ticket23354 new file mode 100644 index 0000000000..f79dfd96ae --- /dev/null +++ b/changes/ticket23354 @@ -0,0 +1,6 @@ + o Minor features (configuration): + - The "DownloadSchedule" options have been renamed to end with + "DownloadInitialDelay". The old names are still allowed, but will + produce a warning. Comma-separated lists are still permitted for + these options, but all values after the first are ignored (as they have + been since 0.2.9). Closes ticket 23354. diff --git a/changes/ticket24734 b/changes/ticket24734 new file mode 100644 index 0000000000..00029ce578 --- /dev/null +++ b/changes/ticket24734 @@ -0,0 +1,6 @@ + o Code simplification and refactoring: + - Remove the return value for fascist_firewall_choose_address_base(), + and sister functions such as fascist_firewall_choose_address_node() + and fascist_firewall_choose_address_rs(). Also, while we're here, + initialize the ap argument as leaving it uninitialized can pose a + security hazard. Closes ticket 24734. Patch by Neel Chauhan. diff --git a/changes/ticket25610 b/changes/ticket25610 new file mode 100644 index 0000000000..887770e6ac --- /dev/null +++ b/changes/ticket25610 @@ -0,0 +1,5 @@ + o Major feature (directory authority, modularization): + - The directory authority subsystem has been modularized. The code is now + located in src/or/dirauth/ which is compiled in by default. To disable the + module, the configure option --disable-module-dirauth has been added. + Closes ticket 25610; diff --git a/changes/ticket25927.1 b/changes/ticket25927.1 new file mode 100644 index 0000000000..84ac86e187 --- /dev/null +++ b/changes/ticket25927.1 @@ -0,0 +1,6 @@ + o Minor features (timekeeping, circuit scheduling): + - When keeping track of how busy each circuit have been recently on + a given connection, use coarse-grained monotonic timers rather than + gettimeofday(). This change should marginally increase accuracy + and performance. Implements part of ticket 25927. + diff --git a/changes/ticket25927.2 b/changes/ticket25927.2 new file mode 100644 index 0000000000..9acb4aaf6f --- /dev/null +++ b/changes/ticket25927.2 @@ -0,0 +1,5 @@ + o Code simplification and refactoring: + - Remove our previous logic for "cached gettimeofday()" -- our coarse + monotonic timers are fast enough for this purpose, and far less + error-prone. Implements part of ticket 25927. + diff --git a/changes/ticket25937 b/changes/ticket25937 new file mode 100644 index 0000000000..7c49fac708 --- /dev/null +++ b/changes/ticket25937 @@ -0,0 +1,9 @@ + o Minor features (mainloop): + - Move responsibility for + consensus voting + from a once-per-second callback to a callback that is only scheduled as + needed. Once enough items are removed from our once-per-second + callback, we can eliminate it entirely to conserve CPU when idle. + Closes ticket + 25937. + diff --git a/changes/ticket25948 b/changes/ticket25948 new file mode 100644 index 0000000000..7851d0b65b --- /dev/null +++ b/changes/ticket25948 @@ -0,0 +1,9 @@ + o Minor features (mainloop): + - Move responsibility for + saving the state file to disk + from a once-per-second callback to a callback that is only scheduled as + needed. Once enough items are removed from our once-per-second + callback, we can eliminate it entirely to conserve CPU when idle. + Closes ticket + 25948. + diff --git a/changes/ticket25996 b/changes/ticket25996 new file mode 100644 index 0000000000..f523bc6304 --- /dev/null +++ b/changes/ticket25996 @@ -0,0 +1,5 @@ + o Minor bugfixes (testing): + - Repeat part of the test in test_client_pick_intro() a number of times, + to give it consistent coverage. Fixes bug 25996; bugfix on + 0.3.2.1-alpha. + diff --git a/changes/ticket25997 b/changes/ticket25997 new file mode 100644 index 0000000000..2c802c856b --- /dev/null +++ b/changes/ticket25997 @@ -0,0 +1,5 @@ + o Minor bugfixes (testing, coverage): + - Remove randomness from the hs_common/responsible_hsdirs test, + so that it always takes the same path through the function it tests. + Fixes bug 25997; bugfix on 0.3.2.1-alpha. + diff --git a/configure.ac b/configure.ac index 878f5a88bb..387fc6fbeb 100644 --- a/configure.ac +++ b/configure.ac @@ -230,6 +230,31 @@ if test "x$enable_android" = "xyes"; then fi +dnl --- +dnl Tor modules options. These options are namespaced with --disable-module-XXX +dnl --- + +dnl All our modules. +m4_define(MODULES, dirauth) + +dnl Directory Authority module. +AC_ARG_ENABLE([module-dirauth], + AS_HELP_STRING([--disable-module-dirauth], + [Do not build tor with the dirauth module]), + [], dnl Action if-given + AC_DEFINE([HAVE_MODULE_DIRAUTH], [1], + [Compile with Directory Authority feature support])) +AM_CONDITIONAL(BUILD_MODULE_DIRAUTH, [test "x$enable_module_dirauth" != "xno"]) + +dnl Helper variables. +TOR_MODULES_ALL_ENABLED= +AC_DEFUN([ADD_MODULE], [ + MODULE=m4_toupper($1) + TOR_MODULES_ALL_ENABLED="${TOR_MODULES_ALL_ENABLED} -DHAVE_MODULE_${MODULE}=1" +]) +m4_foreach_w([module], MODULES, [ADD_MODULE([module])]) +AC_SUBST(TOR_MODULES_ALL_ENABLED) + dnl check for the correct "ar" when cross-compiling. dnl (AM_PROG_AR was new in automake 1.11.2, which we do not yet require, dnl so kludge up a replacement for the case where it isn't there yet.) @@ -788,9 +813,18 @@ AC_ARG_WITH(ssl-dir, AC_MSG_NOTICE([Now, we'll look for OpenSSL >= 1.0.1]) TOR_SEARCH_LIBRARY(openssl, $tryssldir, [-lssl -lcrypto $TOR_LIB_GDI $TOR_LIB_WS32], - [#include <openssl/ssl.h>], - [struct ssl_method_st; const struct ssl_method_st *TLSv1_1_method(void);], - [TLSv1_1_method();], [], + [#include <openssl/ssl.h> + char *getenv(const char *);], + [struct ssl_cipher_st; + unsigned SSL_CIPHER_get_id(const struct ssl_cipher_st *); + char *getenv(const char *);], + dnl This funny-looking test program calls getenv, so that the compiler + dnl will neither make code that call SSL_CIPHER_get_id(NULL) [producing + dnl a crash], nor optimize out the call to SSL_CIPHER_get_id(). + dnl We look for SSL_cipher_get_id() because it is present in + dnl OpenSSL >=1.0.1, because it is not deprecated, and because Tor + dnl depends on it. + [if (getenv("THIS_SHOULDNT_BE_SET_X201803")) SSL_CIPHER_get_id((void *)0);], [], [/usr/local/opt/openssl /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/local /opt/openssl]) dnl XXXX check for OPENSSL_VERSION_NUMBER == SSLeay() diff --git a/doc/tor.1.txt b/doc/tor.1.txt index cf666e9142..9471db9362 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1347,6 +1347,13 @@ The following options are useful only for clients (that is, if number from the guard-n-primary-guards-to-use consensus parameter, and default to 1 if the consensus parameter isn't set. (Default: 0) +[[NumPrimaryGuards]] **NumPrimaryGuards** __NUM__:: + If UseEntryGuards is set to 1, we will try to pick NUM routers for our + primary guard list, which is the set of routers we strongly prefer when + connecting to the Tor network. If NUM is 0, we try to learn the number from + the guard-n-primary-guards consensus parameter, and default to 3 if the + consensus parameter isn't set. (Default: 0) + [[NumDirectoryGuards]] **NumDirectoryGuards** __NUM__:: If UseEntryGuards is set to 1, we try to make sure we have at least NUM routers to use as directory guards. If this option is set to 0, use the @@ -1735,32 +1742,29 @@ The following options are useful only for clients (that is, if directory authorities do not choose a value, Tor will default to 0.6. (Default: -1) -[[ClientBootstrapConsensusAuthorityDownloadSchedule]] **ClientBootstrapConsensusAuthorityDownloadSchedule** __N__,__N__,__...__:: - Schedule for when clients should download consensuses from authorities +[[ClientBootstrapConsensusAuthorityDownloadInitialDelay]] **ClientBootstrapConsensusAuthorityDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download consensuses from authorities if they are bootstrapping (that is, they don't have a usable, reasonably live consensus). Only used by clients fetching from a list of fallback directory mirrors. This schedule is advanced by (potentially concurrent) connection attempts, unlike other schedules, which are advanced by - connection failures. (Default: 6, 11, 3600, 10800, 25200, 54000, 111600, - 262800) + connection failures. (Default: 6) -[[ClientBootstrapConsensusFallbackDownloadSchedule]] **ClientBootstrapConsensusFallbackDownloadSchedule** __N__,__N__,__...__:: - Schedule for when clients should download consensuses from fallback +[[ClientBootstrapConsensusFallbackDownloadInitialDelay]] **ClientBootstrapConsensusFallbackDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download consensuses from fallback directory mirrors if they are bootstrapping (that is, they don't have a usable, reasonably live consensus). Only used by clients fetching from a list of fallback directory mirrors. This schedule is advanced by (potentially concurrent) connection attempts, unlike other schedules, - which are advanced by connection failures. (Default: 0, 1, 4, 11, 3600, - 10800, 25200, 54000, 111600, 262800) + which are advanced by connection failures. (Default: 0) -[[ClientBootstrapConsensusAuthorityOnlyDownloadSchedule]] **ClientBootstrapConsensusAuthorityOnlyDownloadSchedule** __N__,__N__,__...__:: - Schedule for when clients should download consensuses from authorities +[[ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay]] **ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download consensuses from authorities if they are bootstrapping (that is, they don't have a usable, reasonably live consensus). Only used by clients which don't have or won't fetch from a list of fallback directory mirrors. This schedule is advanced by (potentially concurrent) connection attempts, unlike other schedules, - which are advanced by connection failures. (Default: 0, 3, 7, 3600, - 10800, 25200, 54000, 111600, 262800) + which are advanced by connection failures. (Default: 0) [[ClientBootstrapConsensusMaxInProgressTries]] **ClientBootstrapConsensusMaxInProgressTries** __NUM__:: Try this many simultaneous connections to download a consensus before @@ -2891,12 +2895,9 @@ The following options are used for running a testing Tor network. AssumeReachable 1 AuthDirMaxServersPerAddr 0 AuthDirMaxServersPerAuthAddr 0 - ClientBootstrapConsensusAuthorityDownloadSchedule 0, 2, - 4 (for 40 seconds), 8, 16, 32, 60 - ClientBootstrapConsensusFallbackDownloadSchedule 0, 1, - 4 (for 40 seconds), 8, 16, 32, 60 - ClientBootstrapConsensusAuthorityOnlyDownloadSchedule 0, 1, - 4 (for 40 seconds), 8, 16, 32, 60 + ClientBootstrapConsensusAuthorityDownloadInitialDelay 0 + ClientBootstrapConsensusFallbackDownloadInitialDelay 0 + ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay 0 ClientDNSRejectInternalAddresses 0 ClientRejectInternalAddresses 0 CountPrivateBandwidth 1 @@ -2911,12 +2912,12 @@ The following options are used for running a testing Tor network. TestingV3AuthInitialDistDelay 20 seconds TestingAuthDirTimeToLearnReachability 0 minutes TestingEstimatedDescriptorPropagationTime 0 minutes - TestingServerDownloadSchedule 0, 0, 0, 5, 10, 15, 20, 30, 60 - TestingClientDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 - TestingServerConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 - TestingClientConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 - TestingBridgeDownloadSchedule 10, 30, 60 - TestingBridgeBootstrapDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60 + TestingServerDownloadInitialDelay 0 + TestingClientDownloadInitialDelay 0 + TestingServerConsensusDownloadInitialDelay 0 + TestingClientConsensusDownloadInitialDelay 0 + TestingBridgeDownloadInitialDelay 10 + TestingBridgeBootstrapDownloadInitialDelay 0 TestingClientMaxIntervalWithoutRequest 5 seconds TestingDirConnectionMaxStall 30 seconds TestingEnableConnBwEvent 1 @@ -2955,37 +2956,31 @@ The following options are used for running a testing Tor network. Minimum value for the Fast flag. Overrides the ordinary minimum taken from the consensus when TestingTorNetwork is set. (Default: 0.) -[[TestingServerDownloadSchedule]] **TestingServerDownloadSchedule** __N__,__N__,__...__:: - Schedule for when servers should download things in general. Changing this - requires that **TestingTorNetwork** is set. (Default: 0, 0, 0, 60, 60, 120, - 300, 900, 2147483647) +[[TestingServerDownloadInitialDelay]] **TestingServerDownloadInitialDelay** __N__:: + Initial delay in seconds for when servers should download things in general. Changing this + requires that **TestingTorNetwork** is set. (Default: 0) -[[TestingClientDownloadSchedule]] **TestingClientDownloadSchedule** __N__,__N__,__...__:: - Schedule for when clients should download things in general. Changing this - requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600, - 2147483647) +[[TestingClientDownloadInitialDelay]] **TestingClientDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download things in general. Changing this + requires that **TestingTorNetwork** is set. (Default: 0) -[[TestingServerConsensusDownloadSchedule]] **TestingServerConsensusDownloadSchedule** __N__,__N__,__...__:: - Schedule for when servers should download consensuses. Changing this - requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600, - 1800, 1800, 1800, 1800, 1800, 3600, 7200) +[[TestingServerConsensusDownloadInitialDelay]] **TestingServerConsensusDownloadInitialDelay** __N__:: + Initial delay in seconds for when servers should download consensuses. Changing this + requires that **TestingTorNetwork** is set. (Default: 0) -[[TestingClientConsensusDownloadSchedule]] **TestingClientConsensusDownloadSchedule** __N__,__N__,__...__:: - Schedule for when clients should download consensuses. Changing this - requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600, - 1800, 3600, 3600, 3600, 10800, 21600, 43200) +[[TestingClientConsensusDownloadInitialDelay]] **TestingClientConsensusDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download consensuses. Changing this + requires that **TestingTorNetwork** is set. (Default: 0) -[[TestingBridgeDownloadSchedule]] **TestingBridgeDownloadSchedule** __N__,__N__,__...__:: - Schedule for when clients should download each bridge descriptor when they +[[TestingBridgeDownloadInitialDelay]] **TestingBridgeDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download each bridge descriptor when they know that one or more of their configured bridges are running. Changing - this requires that **TestingTorNetwork** is set. (Default: 10800, 25200, - 54000, 111600, 262800) + this requires that **TestingTorNetwork** is set. (Default: 10800) -[[TestingBridgeBootstrapDownloadSchedule]] **TestingBridgeBootstrapDownloadSchedule** __N__,__N__,__...__:: - Schedule for when clients should download each bridge descriptor when they +[[TestingBridgeBootstrapDownloadInitialDelay]] **TestingBridgeBootstrapDownloadInitialDelay** __N__:: + Initial delay in seconds for when clients should download each bridge descriptor when they have just started, or when they can not contact any of their bridges. - Changing this requires that **TestingTorNetwork** is set. (Default: 0, 30, - 90, 600, 3600, 10800, 25200, 54000, 111600, 262800) + Changing this requires that **TestingTorNetwork** is set. (Default: 0) [[TestingClientMaxIntervalWithoutRequest]] **TestingClientMaxIntervalWithoutRequest** __N__ **seconds**|**minutes**:: When directory clients have only a few descriptors to request, they batch diff --git a/scripts/maint/updateFallbackDirs.py b/scripts/maint/updateFallbackDirs.py index c854830e18..b093463e08 100755 --- a/scripts/maint/updateFallbackDirs.py +++ b/scripts/maint/updateFallbackDirs.py @@ -173,12 +173,6 @@ MAX_LIST_FILE_SIZE = 1024 * 1024 # meant that we had to rebuild the list more often. We want fallbacks to be # stable for 2 years, so we set it to a few months. # -# There was a bug in Tor 0.2.8.1-alpha and earlier where a relay temporarily -# submits a 0 DirPort when restarted. -# This causes OnionOO to (correctly) reset its stability timer. -# Affected relays should upgrade to Tor 0.2.9 or later, which has a fix -# for this issue. -# # If a relay changes address or port, that's it, it's not useful any more, # because clients can't find it ADDRESS_AND_PORT_STABLE_DAYS = 90 diff --git a/src/common/aes.c b/src/common/aes.c index 5d0841dfa3..95737cffcc 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -116,7 +116,11 @@ 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) + EVP_CIPHER_CTX_reset(cipher); +#else EVP_CIPHER_CTX_cleanup(cipher); +#endif EVP_CIPHER_CTX_free(cipher); } void diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 9936c0aac4..707efe3892 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -494,51 +494,7 @@ tor_libevent_exit_loop_after_callback(struct event_base *base) event_base_loopbreak(base); } -#if defined(LIBEVENT_VERSION_NUMBER) && \ - LIBEVENT_VERSION_NUMBER >= V(2,1,1) && \ - !defined(TOR_UNIT_TESTS) -void -tor_gettimeofday_cached(struct timeval *tv) -{ - event_base_gettimeofday_cached(the_event_base, tv); -} -void -tor_gettimeofday_cache_clear(void) -{ - event_base_update_cache_time(the_event_base); -} -#else /* !(defined(LIBEVENT_VERSION_NUMBER) && ...) */ -/** Cache the current hi-res time; the cache gets reset when libevent - * calls us. */ -static struct timeval cached_time_hires = {0, 0}; - -/** Return a fairly recent view of the current time. */ -void -tor_gettimeofday_cached(struct timeval *tv) -{ - if (cached_time_hires.tv_sec == 0) { - tor_gettimeofday(&cached_time_hires); - } - *tv = cached_time_hires; -} - -/** Reset the cached view of the current time, so that the next time we try - * to learn it, we will get an up-to-date value. */ -void -tor_gettimeofday_cache_clear(void) -{ - cached_time_hires.tv_sec = 0; -} - -#ifdef TOR_UNIT_TESTS -/** For testing: force-update the cached time to a given value. */ -void -tor_gettimeofday_cache_set(const struct timeval *tv) -{ - tor_assert(tv); - memcpy(&cached_time_hires, tv, sizeof(*tv)); -} - +#if defined(TOR_UNIT_TESTS) /** For testing: called post-fork to make libevent reinitialize * kernel structures. */ void @@ -548,5 +504,4 @@ tor_libevent_postfork(void) tor_assert(r == 0); } #endif /* defined(TOR_UNIT_TESTS) */ -#endif /* defined(LIBEVENT_VERSION_NUMBER) && ... */ diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 29c6ad375a..e2747860a9 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -68,10 +68,7 @@ void tor_libevent_free_all(void); int tor_init_libevent_rng(void); -void tor_gettimeofday_cached(struct timeval *tv); -void tor_gettimeofday_cache_clear(void); #ifdef TOR_UNIT_TESTS -void tor_gettimeofday_cache_set(const struct timeval *tv); void tor_libevent_postfork(void); #endif diff --git a/src/common/compat_time.c b/src/common/compat_time.c index 966216768f..40847a8442 100644 --- a/src/common/compat_time.c +++ b/src/common/compat_time.c @@ -279,6 +279,7 @@ monotime_reset_ratchets_for_testing(void) * nanoseconds. */ static struct mach_timebase_info mach_time_info; +static struct mach_timebase_info mach_time_info_msec_cvt; static int monotime_shift = 0; static void @@ -296,6 +297,14 @@ monotime_init_internal(void) // requires that tor_log2(0) == 0. monotime_shift = tor_log2(ms_per_tick); } + { + // For converting ticks to milliseconds in a 32-bit-friendly way, we + // will first right-shift by 20, and then multiply by 20/19, since + // (1<<20) * 19/20 is about 1e6. We precompute a new numerate and + // denominator here to avoid multiple multiplies. + mach_time_info_msec_cvt.numer = mach_time_info.numer * 20; + mach_time_info_msec_cvt.denom = mach_time_info.denom * 19; + } } /** @@ -345,6 +354,22 @@ monotime_diff_nsec(const monotime_t *start, return diff_nsec; } +int32_t +monotime_coarse_diff_msec32_(const monotime_coarse_t *start, + const monotime_coarse_t *end) +{ + if (BUG(mach_time_info.denom == 0)) { + monotime_init(); + } + const int64_t diff_ticks = end->abstime_ - start->abstime_; + + /* We already require in di_ops.c that right-shift performs a sign-extend. */ + const int32_t diff_microticks = (int32_t)(diff_ticks >> 20); + + return (diff_microticks * mach_time_info_msec_cvt.numer) / + mach_time_info_msec_cvt.denom; +} + uint32_t monotime_coarse_to_stamp(const monotime_coarse_t *t) { @@ -443,6 +468,15 @@ monotime_diff_nsec(const monotime_t *start, return diff_nsec; } +int32_t +monotime_coarse_diff_msec32_(const monotime_coarse_t *start, + const monotime_coarse_t *end) +{ + const int32_t diff_sec = (int32_t)(end->ts_.tv_sec - start->ts_.tv_sec); + const int32_t diff_nsec = (int32_t)(end->ts_.tv_nsec - start->ts_.tv_nsec); + return diff_sec * 1000 + diff_nsec / ONE_MILLION; +} + /* This value is ONE_BILLION >> 20. */ static const uint32_t STAMP_TICKS_PER_SECOND = 953; @@ -592,6 +626,13 @@ monotime_coarse_diff_msec(const monotime_coarse_t *start, return diff_ticks; } +int32_t +monotime_coarse_diff_msec32_(const monotime_coarse_t *start, + const monotime_coarse_t *end) +{ + return (int32_t)monotime_coarse_diff_msec(start, end); +} + int64_t monotime_coarse_diff_usec(const monotime_coarse_t *start, const monotime_coarse_t *end) @@ -677,6 +718,15 @@ monotime_diff_nsec(const monotime_t *start, return (diff.tv_sec * ONE_BILLION + diff.tv_usec * 1000); } +int32_t +monotime_coarse_diff_msec32_(const monotime_coarse_t *start, + const monotime_coarse_t *end) +{ + struct timeval diff; + timersub(&end->tv_, &start->tv_, &diff); + return diff.tv_sec * 1000 + diff.tv_usec / 1000; +} + /* This value is ONE_MILLION >> 10. */ static const uint32_t STAMP_TICKS_PER_SECOND = 976; diff --git a/src/common/compat_time.h b/src/common/compat_time.h index 09dd6add3d..57ab20ab11 100644 --- a/src/common/compat_time.h +++ b/src/common/compat_time.h @@ -173,6 +173,33 @@ void monotime_coarse_add_msec(monotime_coarse_t *out, #define monotime_coarse_add_msec monotime_add_msec #endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */ +/** + * As monotime_coarse_diff_msec, but avoid 64-bit division. + * + * Requires that the difference fit into an int32_t; not for use with + * large time differences. + */ +int32_t monotime_coarse_diff_msec32_(const monotime_coarse_t *start, + const monotime_coarse_t *end); + +/** + * As monotime_coarse_diff_msec, but avoid 64-bit division if it is expensive. + * + * Requires that the difference fit into an int32_t; not for use with + * large time differences. + */ +static inline int32_t +monotime_coarse_diff_msec32(const monotime_coarse_t *start, + const monotime_coarse_t *end) +{ +#if SIZEOF_VOID_P == 8 + // on a 64-bit platform, let's assume 64/64 division is cheap. + return (int32_t) monotime_coarse_diff_msec(start, end); +#else + return monotime_coarse_diff_msec32_(start, end); +#endif +} + MOCK_DECL(void, tor_gettimeofday, (struct timeval *timeval)); #ifdef TOR_UNIT_TESTS diff --git a/src/common/crypto.c b/src/common/crypto.c index 9fcd17742c..c98a968757 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -43,6 +43,7 @@ DISABLE_GCC_WARNING(redundant-decls) #include <openssl/dh.h> #include <openssl/conf.h> #include <openssl/hmac.h> +#include <openssl/ssl.h> ENABLE_GCC_WARNING(redundant-decls) @@ -204,8 +205,15 @@ crypto_early_init(void) crypto_early_initialized_ = 1; +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | + OPENSSL_INIT_LOAD_CRYPTO_STRINGS | + OPENSSL_INIT_ADD_ALL_CIPHERS | + OPENSSL_INIT_ADD_ALL_DIGESTS, NULL); +#else ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); +#endif setup_openssl_threading(); @@ -1660,11 +1668,15 @@ memwipe(void *mem, uint8_t byte, size_t sz) int crypto_global_cleanup(void) { +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) EVP_cleanup(); +#endif #ifndef NEW_THREAD_API ERR_remove_thread_state(NULL); #endif +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) ERR_free_strings(); +#endif if (dh_param_p) BN_clear_free(dh_param_p); @@ -1676,11 +1688,15 @@ 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) ENGINE_cleanup(); #endif +#endif CONF_modules_unload(1); +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) CRYPTO_cleanup_all_ex_data(); +#endif crypto_openssl_free_all(); diff --git a/src/common/torint.h b/src/common/torint.h index 0b8061d24f..fc7818fe2c 100644 --- a/src/common/torint.h +++ b/src/common/torint.h @@ -40,6 +40,8 @@ #include <inttypes.h> #endif +#include <stdbool.h> + #if (SIZEOF_INT8_T != 0) #define HAVE_INT8_T #endif diff --git a/src/common/tortls.c b/src/common/tortls.c index 05e29e22ff..cd236363f8 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -56,10 +56,25 @@ ENABLE_GCC_WARNING(redundant-decls) #include "container.h" #include <string.h> +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) +#define X509_get_notBefore_const(cert) \ + X509_get0_notBefore(cert) +#define X509_get_notAfter_const(cert) \ + X509_get0_notAfter(cert) +#ifndef X509_get_notBefore +#define X509_get_notBefore(cert) \ + X509_getm_notBefore(cert) +#endif +#ifndef X509_get_notAfter +#define X509_get_notAfter(cert) \ + X509_getm_notAfter(cert) +#endif +#else /* ! OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */ #define X509_get_notBefore_const(cert) \ ((const ASN1_TIME*) X509_get_notBefore((X509 *)cert)) #define X509_get_notAfter_const(cert) \ ((const ASN1_TIME*) X509_get_notAfter((X509 *)cert)) +#endif /* Copied from or.h */ #define LEGAL_NICKNAME_CHARACTERS \ @@ -355,8 +370,12 @@ tor_tls_init(void) check_no_tls_errors(); if (!tls_library_is_initialized) { +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); +#else SSL_library_init(); SSL_load_error_strings(); +#endif #if (SIZEOF_VOID_P >= 8 && \ OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1)) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index c33dbbeb2d..24c32b710c 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -2802,16 +2802,13 @@ extend_info_from_node(const node_t *node, int for_direct_connect) return NULL; } - /* Choose a preferred address first, but fall back to an allowed address. - * choose_address returns 1 on success, but get_prim_orport returns 0. */ + /* Choose a preferred address first, but fall back to an allowed address. */ if (for_direct_connect) - valid_addr = fascist_firewall_choose_address_node(node, - FIREWALL_OR_CONNECTION, - 0, &ap); + fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0, &ap); else { node_get_prim_orport(node, &ap); - valid_addr = tor_addr_port_is_valid_ap(&ap, 0); } + valid_addr = tor_addr_port_is_valid_ap(&ap, 0); if (valid_addr) log_debug(LD_CIRC, "using %s for %s", diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c index 4b80124a77..e85301b26d 100644 --- a/src/or/circuitmux_ewma.c +++ b/src/or/circuitmux_ewma.c @@ -28,7 +28,7 @@ * **/ -#define TOR_CIRCUITMUX_EWMA_C_ +#define CIRCUITMUX_EWMA_PRIVATE #include "orconfig.h" @@ -169,8 +169,6 @@ TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol) static void add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma); static int compare_cell_ewma_counts(const void *p1, const void *p2); -static unsigned cell_ewma_tick_from_timeval(const struct timeval *now, - double *remainder_out); static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma); static inline double get_scale_factor(unsigned from_tick, unsigned to_tick); static cell_ewma_t * pop_first_cell_ewma(ewma_policy_data_t *pol); @@ -239,13 +237,24 @@ circuitmux_policy_t ewma_policy = { /*.cmp_cmux =*/ ewma_cmp_cmux }; +/** Have we initialized the ewma tick-counting logic? */ +static int ewma_ticks_initialized = 0; +/** At what monotime_coarse_t did the current tick begin? */ +static monotime_coarse_t start_of_current_tick; +/** What is the number of the current tick? */ +static unsigned current_tick_num; + /*** EWMA method implementations using the below EWMA helper functions ***/ /** Compute and return the current cell_ewma tick. */ static inline unsigned int cell_ewma_get_tick(void) { - return ((unsigned)approx_time() / EWMA_TICK_LEN); + monotime_coarse_t now; + monotime_coarse_get(&now); + int32_t msec_diff = monotime_coarse_diff_msec32(&start_of_current_tick, + &now); + return current_tick_num + msec_diff / (1000*EWMA_TICK_LEN); } /** @@ -421,8 +430,6 @@ ewma_notify_xmit_cells(circuitmux_t *cmux, ewma_policy_circ_data_t *cdata = NULL; unsigned int tick; double fractional_tick, ewma_increment; - /* The current (hi-res) time */ - struct timeval now_hires; cell_ewma_t *cell_ewma, *tmp; tor_assert(cmux); @@ -435,8 +442,7 @@ ewma_notify_xmit_cells(circuitmux_t *cmux, cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data); /* Rescale the EWMAs if needed */ - tor_gettimeofday_cached(&now_hires); - tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick); + tick = cell_ewma_get_current_tick_and_fraction(&fractional_tick); if (tick != pol->active_circuit_pqueue_last_recalibrated) { scale_active_circuits(pol, tick); @@ -597,24 +603,45 @@ cell_ewma_to_circuit(cell_ewma_t *ewma) rescale. */ -/** Given a timeval <b>now</b>, compute the cell_ewma tick in which it occurs - * and the fraction of the tick that has elapsed between the start of the tick - * and <b>now</b>. Return the former and store the latter in - * *<b>remainder_out</b>. +/** + * Initialize the system that tells which ewma tick we are in. + */ +STATIC void +cell_ewma_initialize_ticks(void) +{ + if (ewma_ticks_initialized) + return; + monotime_coarse_get(&start_of_current_tick); + crypto_rand((char*)¤t_tick_num, sizeof(current_tick_num)); + ewma_ticks_initialized = 1; +} + +/** Compute the current cell_ewma tick and the fraction of the tick that has + * elapsed between the start of the tick and the current time. Return the + * former and store the latter in *<b>remainder_out</b>. * * These tick values are not meant to be shared between Tor instances, or used * for other purposes. */ - -static unsigned -cell_ewma_tick_from_timeval(const struct timeval *now, - double *remainder_out) +STATIC unsigned +cell_ewma_get_current_tick_and_fraction(double *remainder_out) { - unsigned res = (unsigned) (now->tv_sec / EWMA_TICK_LEN); - /* rem */ - double rem = (now->tv_sec % EWMA_TICK_LEN) + - ((double)(now->tv_usec)) / 1.0e6; - *remainder_out = rem / EWMA_TICK_LEN; - return res; + if (BUG(!ewma_ticks_initialized)) { + cell_ewma_initialize_ticks(); // LCOV_EXCL_LINE + } + monotime_coarse_t now; + monotime_coarse_get(&now); + int32_t msec_diff = monotime_coarse_diff_msec32(&start_of_current_tick, + &now); + if (msec_diff > (1000*EWMA_TICK_LEN)) { + unsigned ticks_difference = msec_diff / (1000*EWMA_TICK_LEN); + monotime_coarse_add_msec(&start_of_current_tick, + &start_of_current_tick, + ticks_difference * 1000 * EWMA_TICK_LEN); + current_tick_num += ticks_difference; + msec_diff %= 1000*EWMA_TICK_LEN; + } + *remainder_out = ((double)msec_diff) / (1.0e3 * EWMA_TICK_LEN); + return current_tick_num; } /* Default value for the CircuitPriorityHalflifeMsec consensus parameter in @@ -678,6 +705,8 @@ cmux_ewma_set_options(const or_options_t *options, double halflife; const char *source; + cell_ewma_initialize_ticks(); + /* Both options and consensus can be NULL. This assures us to either get a * valid configured value or the default one. */ halflife = get_circuit_priority_halflife(options, consensus, &source); @@ -788,3 +817,12 @@ pop_first_cell_ewma(ewma_policy_data_t *pol) offsetof(cell_ewma_t, heap_index)); } +/** + * Drop all resources held by circuitmux_ewma.c, and deinitialize the + * module. */ +void +circuitmux_ewma_free_all(void) +{ + ewma_ticks_initialized = 0; +} + diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h index 2ef8c2586d..f0c4c36095 100644 --- a/src/or/circuitmux_ewma.h +++ b/src/or/circuitmux_ewma.h @@ -19,5 +19,12 @@ extern circuitmux_policy_t ewma_policy; void cmux_ewma_set_options(const or_options_t *options, const networkstatus_t *consensus); +void circuitmux_ewma_free_all(void); + +#ifdef CIRCUITMUX_EWMA_PRIVATE +STATIC unsigned cell_ewma_get_current_tick_and_fraction(double *remainder_out); +STATIC void cell_ewma_initialize_ticks(void); +#endif + #endif /* !defined(TOR_CIRCUITMUX_EWMA_H) */ diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index 6438319273..6015bcf851 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -35,6 +35,7 @@ #include "networkstatus.h" #include "rendclient.h" #include "rendservice.h" +#include "router.h" #include "statefile.h" #include "circuitlist.h" #include "circuituse.h" @@ -125,7 +126,7 @@ circuit_build_times_disabled_(const or_options_t *options, ignore_consensus ? 0 : networkstatus_get_param(NULL, "cbtdisabled", 0, 0, 1); int config_disabled = !options->LearnCircuitBuildTimeout; - int dirauth_disabled = options->AuthoritativeDir; + int dirauth_disabled = authdir_mode(options); int state_disabled = did_last_state_file_write_fail() ? 1 : 0; /* LearnCircuitBuildTimeout and Tor2web/Single Onion Services are * incompatible in two ways: diff --git a/src/or/config.c b/src/or/config.c index 87a3588db7..a2b84991a0 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1,3 +1,4 @@ + /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. @@ -79,7 +80,6 @@ #include "confparse.h" #include "cpuworker.h" #include "dirserv.h" -#include "dirvote.h" #include "dns.h" #include "dos.h" #include "entrynodes.h" @@ -104,12 +104,15 @@ #include "statefile.h" #include "transports.h" #include "ext_orport.h" +#include "voting_schedule.h" #ifdef _WIN32 #include <shlobj.h> #endif #include "procmon.h" +#include "dirauth/dirvote.h" + #ifdef HAVE_SYSTEMD # if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) /* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse @@ -126,6 +129,11 @@ static const char unix_socket_prefix[] = "unix:"; * configuration. */ static const char unix_q_socket_prefix[] = "unix:\""; +/** macro to help with the bulk rename of *DownloadSchedule to + * *DowloadInitialDelay . */ +#define DOWNLOAD_SCHEDULE(name) \ + { #name "DownloadSchedule", #name "DownloadInitialDelay", 0, 1 } + /** A list of abbreviations and aliases to map command-line options, obsolete * option names, or alternative option names, to their current values. */ static config_abbrev_t option_abbrevs_[] = { @@ -175,6 +183,16 @@ static config_abbrev_t option_abbrevs_[] = { { "_HSLayer2Nodes", "HSLayer2Nodes", 0, 1 }, { "_HSLayer3Nodes", "HSLayer3Nodes", 0, 1 }, + DOWNLOAD_SCHEDULE(ClientBootstrapConsensusAuthority), + DOWNLOAD_SCHEDULE(ClientBootstrapConsensusAuthorityOnly), + DOWNLOAD_SCHEDULE(ClientBootstrapConsensusFallback), + DOWNLOAD_SCHEDULE(TestingBridge), + DOWNLOAD_SCHEDULE(TestingBridgeBootstrap), + DOWNLOAD_SCHEDULE(TestingClient), + DOWNLOAD_SCHEDULE(TestingClientConsensus), + DOWNLOAD_SCHEDULE(TestingServer), + DOWNLOAD_SCHEDULE(TestingServerConsensus), + { NULL, NULL, 0, 0}, }; @@ -457,6 +475,7 @@ static config_var_t option_vars_[] = { V(NumCPUs, UINT, "0"), V(NumDirectoryGuards, UINT, "0"), V(NumEntryGuards, UINT, "0"), + V(NumPrimaryGuards, UINT, "0"), V(OfflineMasterKey, BOOL, "0"), OBSOLETE("ORListenAddress"), VPORT(ORPort), @@ -599,16 +618,10 @@ static config_var_t option_vars_[] = { VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), VAR("__OwningControllerFD",INT,OwningControllerFD, "-1"), V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"), - V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, " - "300, 900, 2147483647"), - V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 60, 300, 600, " - "2147483647"), - V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, " - "300, 600, 1800, 1800, 1800, 1800, " - "1800, 3600, 7200"), - V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, " - "300, 600, 1800, 3600, 3600, 3600, " - "10800, 21600, 43200"), + V(TestingServerDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingServerConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), /* With the ClientBootstrapConsensus*Download* below: * Clients with only authorities will try: * - at least 3 authorities over 10 seconds, then exponentially backoff, @@ -624,13 +637,11 @@ static config_var_t option_vars_[] = { * * When clients have authorities and fallbacks available, they use these * schedules: (we stagger the times to avoid thundering herds) */ - V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, - "6, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */), - V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, - "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"), + V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "6"), + V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"), /* When clients only have authorities available, they use this schedule: */ - V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, - "0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800"), + V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL, + "0"), /* We don't want to overwhelm slow networks (or mirrors whose replies are * blocked), but we also don't want to fail if only some mirrors are * blackholed. Clients will try 3 directories simultaneously. @@ -638,14 +649,12 @@ static config_var_t option_vars_[] = { V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"), /* When a client has any running bridges, check each bridge occasionally, * whether or not that bridge is actually up. */ - V(TestingBridgeDownloadSchedule, CSV_INTERVAL, - "10800, 25200, 54000, 111600, 262800"), + V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL,"10800"), /* When a client is just starting, or has no running bridges, check each * bridge a few times quickly, and then try again later. These schedules * are much longer than the other schedules, because we try each and every * configured bridge with this schedule. */ - V(TestingBridgeBootstrapDownloadSchedule, CSV_INTERVAL, - "0, 30, 90, 600, 3600, 10800, 25200, 54000, 111600, 262800"), + V(TestingBridgeBootstrapDownloadInitialDelay, CSV_INTERVAL, "0"), V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"), V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"), OBSOLETE("TestingConsensusMaxDownloadTries"), @@ -672,12 +681,10 @@ static const config_var_t testing_tor_network_defaults[] = { V(EnforceDistinctSubnets, BOOL, "0"), V(AssumeReachable, BOOL, "1"), V(AuthDirMaxServersPerAddr, UINT, "0"), - V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, - "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), - V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, - "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), - V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, - "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), + V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "0"), + V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"), + V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL, + "0"), V(ClientDNSRejectInternalAddresses, BOOL,"0"), V(ClientRejectInternalAddresses, BOOL, "0"), V(CountPrivateBandwidth, BOOL, "1"), @@ -692,17 +699,12 @@ static const config_var_t testing_tor_network_defaults[] = { V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"), V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"), V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), - V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 5, 10, 15, " - "20, 30, 60"), - V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, 15, 20, " - "30, 60"), - V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, " - "15, 20, 30, 60"), - V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, " - "15, 20, 30, 60"), - V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "10, 30, 60"), - V(TestingBridgeBootstrapDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, " - "15, 20, 30, 60"), + V(TestingServerDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingServerConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingClientConsensusDownloadInitialDelay, CSV_INTERVAL, "0"), + V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL, "10"), + V(TestingBridgeBootstrapDownloadInitialDelay, CSV_INTERVAL, "0"), V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"), V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"), V(TestingEnableConnBwEvent, BOOL, "1"), @@ -746,6 +748,8 @@ static int options_transition_affects_workers( const or_options_t *old_options, const or_options_t *new_options); static int options_transition_affects_descriptor( const or_options_t *old_options, const or_options_t *new_options); +static int options_transition_affects_dirauth_timing( + const or_options_t *old_options, const or_options_t *new_options); static int normalize_nickname_list(config_line_t **normalized_out, const config_line_t *lst, const char *name, char **msg); @@ -1745,6 +1749,32 @@ options_transition_affects_guards(const or_options_t *old_options, return 0; } +/** + * Return true if changing the configuration from <b>old</b> to <b>new</b> + * affects the timing of the voting subsystem + */ +static int +options_transition_affects_dirauth_timing(const or_options_t *old_options, + const or_options_t *new_options) +{ + tor_assert(old_options); + tor_assert(new_options); + + if (authdir_mode_v3(old_options) != authdir_mode_v3(new_options)) + return 1; + if (! authdir_mode_v3(new_options)) + return 0; + YES_IF_CHANGED_INT(V3AuthVotingInterval); + YES_IF_CHANGED_INT(V3AuthVoteDelay); + YES_IF_CHANGED_INT(V3AuthDistDelay); + YES_IF_CHANGED_INT(TestingV3AuthInitialVotingInterval); + YES_IF_CHANGED_INT(TestingV3AuthInitialVoteDelay); + YES_IF_CHANGED_INT(TestingV3AuthInitialDistDelay); + YES_IF_CHANGED_INT(TestingV3AuthVotingStartOffset); + + return 0; +} + /** Fetch the active option list, and take actions based on it. All of the * things we do should survive being done repeatedly. If present, * <b>old_options</b> contains the previous value of the options. @@ -2329,8 +2359,10 @@ options_act(const or_options_t *old_options) /* We may need to reschedule some directory stuff if our status changed. */ if (old_options) { - if (authdir_mode_v3(options) && !authdir_mode_v3(old_options)) - dirvote_recalculate_timing(options, time(NULL)); + if (options_transition_affects_dirauth_timing(old_options, options)) { + voting_schedule_recalculate_timing(options, time(NULL)); + reschedule_dirvote(options); + } if (!bool_eq(directory_fetches_dir_info_early(options), directory_fetches_dir_info_early(old_options)) || !bool_eq(directory_fetches_dir_info_later(options), @@ -3775,6 +3807,11 @@ options_validate(or_options_t *old_options, or_options_t *options, "http://freehaven.net/anonbib/#hs-attack06 for details."); } + if (options->NumPrimaryGuards && options->NumEntryGuards && + options->NumEntryGuards > options->NumPrimaryGuards) { + REJECT("NumEntryGuards must not be greater than NumPrimaryGuards."); + } + if (options->EntryNodes && routerset_is_list(options->EntryNodes) && (routerset_len(options->EntryNodes) == 1) && @@ -4373,12 +4410,12 @@ options_validate(or_options_t *old_options, or_options_t *options, CHECK_DEFAULT(TestingV3AuthVotingStartOffset); CHECK_DEFAULT(TestingAuthDirTimeToLearnReachability); CHECK_DEFAULT(TestingEstimatedDescriptorPropagationTime); - CHECK_DEFAULT(TestingServerDownloadSchedule); - CHECK_DEFAULT(TestingClientDownloadSchedule); - CHECK_DEFAULT(TestingServerConsensusDownloadSchedule); - CHECK_DEFAULT(TestingClientConsensusDownloadSchedule); - CHECK_DEFAULT(TestingBridgeDownloadSchedule); - CHECK_DEFAULT(TestingBridgeBootstrapDownloadSchedule); + CHECK_DEFAULT(TestingServerDownloadInitialDelay); + CHECK_DEFAULT(TestingClientDownloadInitialDelay); + CHECK_DEFAULT(TestingServerConsensusDownloadInitialDelay); + CHECK_DEFAULT(TestingClientConsensusDownloadInitialDelay); + CHECK_DEFAULT(TestingBridgeDownloadInitialDelay); + CHECK_DEFAULT(TestingBridgeBootstrapDownloadInitialDelay); CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest); CHECK_DEFAULT(TestingDirConnectionMaxStall); CHECK_DEFAULT(TestingAuthKeyLifetime); @@ -8101,7 +8138,10 @@ getinfo_helper_config(control_connection_t *conn, case CONFIG_TYPE_ISOTIME: type = "Time"; break; case CONFIG_TYPE_ROUTERSET: type = "RouterList"; break; case CONFIG_TYPE_CSV: type = "CommaList"; break; - case CONFIG_TYPE_CSV_INTERVAL: type = "TimeIntervalCommaList"; break; + /* This type accepts more inputs than TimeInterval, but it ignores + * everything after the first entry, so we may as well pretend + * it's a TimeInterval. */ + case CONFIG_TYPE_CSV_INTERVAL: type = "TimeInterval"; break; case CONFIG_TYPE_LINELIST: type = "LineList"; break; case CONFIG_TYPE_LINELIST_S: type = "Dependent"; break; case CONFIG_TYPE_LINELIST_V: type = "Virtual"; break; diff --git a/src/or/confparse.c b/src/or/confparse.c index 64ed9ee6bb..6bab790945 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -162,8 +162,6 @@ config_assign_value(const config_format_t *fmt, void *options, int i, ok; const config_var_t *var; void *lvalue; - int *csv_int; - smartlist_t *csv_str; CONFIG_CHECK(fmt, options); @@ -195,6 +193,30 @@ config_assign_value(const config_format_t *fmt, void *options, *(int *)lvalue = i; break; + case CONFIG_TYPE_CSV_INTERVAL: { + /* We used to have entire smartlists here. But now that all of our + * download schedules use exponential backoff, only the first part + * matters. */ + const char *comma = strchr(c->value, ','); + const char *val = c->value; + char *tmp = NULL; + if (comma) { + tmp = tor_strndup(c->value, comma - c->value); + val = tmp; + } + + i = config_parse_interval(val, &ok); + if (!ok) { + tor_asprintf(msg, + "Interval '%s %s' is malformed or out of bounds.", + c->key, c->value); + return -1; + } + *(int *)lvalue = i; + tor_free(tmp); + break; + } + case CONFIG_TYPE_INTERVAL: { i = config_parse_interval(c->value, &ok); if (!ok) { @@ -298,36 +320,6 @@ config_assign_value(const config_format_t *fmt, void *options, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); break; - case CONFIG_TYPE_CSV_INTERVAL: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t**)lvalue, int *, cp, tor_free(cp)); - smartlist_clear(*(smartlist_t**)lvalue); - } else { - *(smartlist_t**)lvalue = smartlist_new(); - } - csv_str = smartlist_new(); - smartlist_split_string(csv_str, c->value, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(csv_str, char *, str) - { - i = config_parse_interval(str, &ok); - if (!ok) { - tor_asprintf(msg, - "Interval in '%s %s' is malformed or out of bounds.", - c->key, c->value); - SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp)); - smartlist_free(csv_str); - return -1; - } - csv_int = tor_malloc_zero(sizeof(int)); - *csv_int = i; - smartlist_add(*(smartlist_t**)lvalue, csv_int); - } - SMARTLIST_FOREACH_END(str); - SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp)); - smartlist_free(csv_str); - break; - case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST_S: { @@ -528,7 +520,6 @@ config_get_assigned_option(const config_format_t *fmt, const void *options, const config_var_t *var; const void *value; config_line_t *result; - smartlist_t *csv_str; tor_assert(options && key); CONFIG_CHECK(fmt, options); @@ -571,6 +562,7 @@ config_get_assigned_option(const config_format_t *fmt, const void *options, break; } /* fall through */ + case CONFIG_TYPE_CSV_INTERVAL: case CONFIG_TYPE_INTERVAL: case CONFIG_TYPE_MSEC_INTERVAL: case CONFIG_TYPE_UINT: @@ -611,20 +603,6 @@ config_get_assigned_option(const config_format_t *fmt, const void *options, else result->value = tor_strdup(""); break; - case CONFIG_TYPE_CSV_INTERVAL: - if (*(smartlist_t**)value) { - csv_str = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(*(smartlist_t**)value, int *, i) - { - smartlist_add_asprintf(csv_str, "%d", *i); - } - SMARTLIST_FOREACH_END(i); - result->value = smartlist_join_strings(csv_str, ",", 0, NULL); - SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp)); - smartlist_free(csv_str); - } else - result->value = tor_strdup(""); - break; case CONFIG_TYPE_OBSOLETE: log_fn(LOG_INFO, LD_CONFIG, "You asked me for the value of an obsolete config option '%s'.", @@ -789,6 +767,7 @@ config_clear(const config_format_t *fmt, void *options, case CONFIG_TYPE_ISOTIME: *(time_t*)lvalue = 0; break; + case CONFIG_TYPE_CSV_INTERVAL: case CONFIG_TYPE_INTERVAL: case CONFIG_TYPE_MSEC_INTERVAL: case CONFIG_TYPE_UINT: @@ -816,13 +795,6 @@ config_clear(const config_format_t *fmt, void *options, *(smartlist_t **)lvalue = NULL; } break; - case CONFIG_TYPE_CSV_INTERVAL: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t **)lvalue, int *, cp, tor_free(cp)); - smartlist_free(*(smartlist_t **)lvalue); - *(smartlist_t **)lvalue = NULL; - } - break; case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST_S: config_free_lines(*(config_line_t **)lvalue); diff --git a/src/or/confparse.h b/src/or/confparse.h index f1f2030343..4b4bf0adb4 100644 --- a/src/or/confparse.h +++ b/src/or/confparse.h @@ -28,7 +28,9 @@ typedef enum config_type_t { * optional whitespace. */ CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and * optional whitespace, representing intervals in - * seconds, with optional units */ + * seconds, with optional units. We allow + * multiple values here for legacy reasons, but + * ignore every value after the first. */ CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */ CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines, * mixed with other keywords. */ @@ -62,7 +64,7 @@ typedef union { int *AUTOBOOL; time_t *ISOTIME; smartlist_t **CSV; - smartlist_t **CSV_INTERVAL; + int *CSV_INTERVAL; config_line_t **LINELIST; config_line_t **LINELIST_S; config_line_t **LINELIST_V; diff --git a/src/or/connection.c b/src/or/connection.c index de0f0485b0..c2673ade16 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -3440,7 +3440,6 @@ connection_handle_read(connection_t *conn) { int res; - tor_gettimeofday_cache_clear(); res = connection_handle_read_impl(conn); return res; } @@ -3983,7 +3982,6 @@ int connection_handle_write(connection_t *conn, int force) { int res; - tor_gettimeofday_cache_clear(); conn->in_connection_handle_write = 1; res = connection_handle_write_impl(conn, force); conn->in_connection_handle_write = 0; diff --git a/src/or/control.c b/src/or/control.c index dda8872182..bc7597707f 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -76,7 +76,7 @@ #include "router.h" #include "routerlist.h" #include "routerparse.h" -#include "shared_random.h" +#include "shared_random_client.h" #ifndef _WIN32 #include <pwd.h> diff --git a/src/or/dircollate.c b/src/or/dirauth/dircollate.c index dec6f75154..dec6f75154 100644 --- a/src/or/dircollate.c +++ b/src/or/dirauth/dircollate.c diff --git a/src/or/dircollate.h b/src/or/dirauth/dircollate.h index 0584b2fe06..0584b2fe06 100644 --- a/src/or/dircollate.h +++ b/src/or/dirauth/dircollate.h diff --git a/src/or/dirvote.c b/src/or/dirauth/dirvote.c index f3b8a19f00..66a530b6db 100644 --- a/src/or/dirvote.c +++ b/src/or/dirauth/dirvote.c @@ -12,6 +12,8 @@ #include "dirvote.h" #include "microdesc.h" #include "networkstatus.h" +#include "nodelist.h" +#include "parsecommon.h" #include "policies.h" #include "protover.h" #include "rephist.h" @@ -22,6 +24,7 @@ #include "entrynodes.h" /* needed for guardfraction methods */ #include "torcert.h" #include "shared_random_state.h" +#include "voting_schedule.h" /** * \file dirvote.c @@ -92,6 +95,30 @@ static int dirvote_compute_consensuses(void); static int dirvote_publish_consensus(void); /* ===== + * Certificate functions + * ===== */ + +/** Allocate and return a new authority_cert_t with the same contents as + * <b>cert</b>. */ +STATIC authority_cert_t * +authority_cert_dup(authority_cert_t *cert) +{ + authority_cert_t *out = tor_malloc(sizeof(authority_cert_t)); + tor_assert(cert); + + memcpy(out, cert, sizeof(authority_cert_t)); + /* Now copy pointed-to things. */ + out->cache_info.signed_descriptor_body = + tor_strndup(cert->cache_info.signed_descriptor_body, + cert->cache_info.signed_descriptor_len); + out->cache_info.saved_location = SAVED_NOWHERE; + out->identity_key = crypto_pk_dup_key(cert->identity_key); + out->signing_key = crypto_pk_dup_key(cert->signing_key); + + return out; +} + +/* ===== * Voting * =====*/ @@ -347,10 +374,73 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, return status; } +/** Set *<b>timing_out</b> to the intervals at which we would like to vote. + * Note that these aren't the intervals we'll use to vote; they're the ones + * that we'll vote to use. */ +static void +dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out) +{ + const or_options_t *options = get_options(); + + tor_assert(timing_out); + + timing_out->vote_interval = options->V3AuthVotingInterval; + timing_out->n_intervals_valid = options->V3AuthNIntervalsValid; + timing_out->vote_delay = options->V3AuthVoteDelay; + timing_out->dist_delay = options->V3AuthDistDelay; +} + /* ===== * Consensus generation * ===== */ +/** If <b>vrs</b> has a hash made for the consensus method <b>method</b> with + * the digest algorithm <b>alg</b>, decode it and copy it into + * <b>digest256_out</b> and return 0. Otherwise return -1. */ +static int +vote_routerstatus_find_microdesc_hash(char *digest256_out, + const vote_routerstatus_t *vrs, + int method, + digest_algorithm_t alg) +{ + /* XXXX only returns the sha256 method. */ + const vote_microdesc_hash_t *h; + char mstr[64]; + size_t mlen; + char dstr[64]; + + tor_snprintf(mstr, sizeof(mstr), "%d", method); + mlen = strlen(mstr); + tor_snprintf(dstr, sizeof(dstr), " %s=", + crypto_digest_algorithm_get_name(alg)); + + for (h = vrs->microdesc; h; h = h->next) { + const char *cp = h->microdesc_hash_line; + size_t num_len; + /* cp looks like \d+(,\d+)* (digesttype=val )+ . Let's hunt for mstr in + * the first part. */ + while (1) { + num_len = strspn(cp, "1234567890"); + if (num_len == mlen && fast_memeq(mstr, cp, mlen)) { + /* This is the line. */ + char buf[BASE64_DIGEST256_LEN+1]; + /* XXXX ignores extraneous stuff if the digest is too long. This + * seems harmless enough, right? */ + cp = strstr(cp, dstr); + if (!cp) + return -1; + cp += strlen(dstr); + strlcpy(buf, cp, sizeof(buf)); + return digest256_from_base64(digest256_out, buf); + } + if (num_len == 0 || cp[num_len] != ',') + break; + cp += num_len + 1; + } + } + return -1; +} + /** Given a vote <b>vote</b> (not a consensus!), return its associated * networkstatus_voter_info_t. */ static networkstatus_voter_info_t * @@ -363,20 +453,6 @@ get_voter(const networkstatus_t *vote) return smartlist_get(vote->voters, 0); } -/** Return the signature made by <b>voter</b> using the algorithm - * <b>alg</b>, or NULL if none is found. */ -document_signature_t * -voter_get_sig_by_algorithm(const networkstatus_voter_info_t *voter, - digest_algorithm_t alg) -{ - if (!voter->sigs) - return NULL; - SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig, - if (sig->alg == alg) - return sig); - return NULL; -} - /** Temporary structure used in constructing a list of dir-source entries * for a consensus. One of these is generated for every vote, and one more * for every legacy key in each vote. */ @@ -1328,7 +1404,7 @@ compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes) * behavior, and make the new behavior conditional on a new-enough * consensus_method. **/ -char * +STATIC char * networkstatus_compute_consensus(smartlist_t *votes, int total_authorities, crypto_pk_t *identity_key, @@ -2372,7 +2448,7 @@ compute_consensus_package_lines(smartlist_t *votes) * new signature is verifiable.) Return the number of signatures added or * changed, or -1 if the document signed by <b>sigs</b> isn't the same * document as <b>target</b>. */ -int +STATIC int networkstatus_add_detached_signatures(networkstatus_t *target, ns_detached_signatures_t *sigs, const char *source, @@ -2456,7 +2532,7 @@ networkstatus_add_detached_signatures(networkstatus_t *target, continue; } - old_sig = voter_get_sig_by_algorithm(target_voter, sig->alg); + old_sig = networkstatus_get_voter_sig_by_alg(target_voter, sig->alg); /* If the target already has a good signature from this voter, then skip * this one. */ @@ -2564,7 +2640,7 @@ networkstatus_format_signatures(networkstatus_t *consensus, * corresponding to the signatures on <b>consensuses</b>, which must contain * exactly one FLAV_NS consensus, and no more than one consensus for each * other flavor. */ -char * +STATIC char * networkstatus_get_detached_signatures(smartlist_t *consensuses) { smartlist_t *elements; @@ -2669,219 +2745,16 @@ get_detached_signatures_from_pending_consensuses(pending_consensus_t *pending, return signatures; } -/** Release all storage held in <b>s</b>. */ -void -ns_detached_signatures_free_(ns_detached_signatures_t *s) -{ - if (!s) - return; - if (s->signatures) { - STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) { - SMARTLIST_FOREACH(sigs, document_signature_t *, sig, - document_signature_free(sig)); - smartlist_free(sigs); - } STRMAP_FOREACH_END; - strmap_free(s->signatures, NULL); - strmap_free(s->digests, tor_free_); - } - - tor_free(s); -} - -/* ===== - * Certificate functions - * ===== */ - -/** Allocate and return a new authority_cert_t with the same contents as - * <b>cert</b>. */ -authority_cert_t * -authority_cert_dup(authority_cert_t *cert) -{ - authority_cert_t *out = tor_malloc(sizeof(authority_cert_t)); - tor_assert(cert); - - memcpy(out, cert, sizeof(authority_cert_t)); - /* Now copy pointed-to things. */ - out->cache_info.signed_descriptor_body = - tor_strndup(cert->cache_info.signed_descriptor_body, - cert->cache_info.signed_descriptor_len); - out->cache_info.saved_location = SAVED_NOWHERE; - out->identity_key = crypto_pk_dup_key(cert->identity_key); - out->signing_key = crypto_pk_dup_key(cert->signing_key); - - return out; -} - -/* ===== - * Vote scheduling - * ===== */ - -/** Set *<b>timing_out</b> to the intervals at which we would like to vote. - * Note that these aren't the intervals we'll use to vote; they're the ones - * that we'll vote to use. */ -void -dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out) -{ - const or_options_t *options = get_options(); - - tor_assert(timing_out); - - timing_out->vote_interval = options->V3AuthVotingInterval; - timing_out->n_intervals_valid = options->V3AuthNIntervalsValid; - timing_out->vote_delay = options->V3AuthVoteDelay; - timing_out->dist_delay = options->V3AuthDistDelay; -} - -/** Return the start of the next interval of size <b>interval</b> (in - * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always - * starts a fresh interval, and if the last interval of a day would be - * truncated to less than half its size, it is rolled into the - * previous interval. */ -time_t -dirvote_get_start_of_next_interval(time_t now, int interval, int offset) -{ - struct tm tm; - time_t midnight_today=0; - time_t midnight_tomorrow; - time_t next; - - tor_gmtime_r(&now, &tm); - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - - if (tor_timegm(&tm, &midnight_today) < 0) { - log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight."); - } - midnight_tomorrow = midnight_today + (24*60*60); - - next = midnight_today + ((now-midnight_today)/interval + 1)*interval; - - /* Intervals never cross midnight. */ - if (next > midnight_tomorrow) - next = midnight_tomorrow; - - /* If the interval would only last half as long as it's supposed to, then - * skip over to the next day. */ - if (next + interval/2 > midnight_tomorrow) - next = midnight_tomorrow; - - next += offset; - if (next - interval > now) - next -= interval; - - return next; -} - -/* Populate and return a new voting_schedule_t that can be used to schedule - * voting. The object is allocated on the heap and it's the responsibility of - * the caller to free it. Can't fail. */ -static voting_schedule_t * -get_voting_schedule(const or_options_t *options, time_t now, int severity) -{ - int interval, vote_delay, dist_delay; - time_t start; - time_t end; - networkstatus_t *consensus; - voting_schedule_t *new_voting_schedule; - - new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t)); - - consensus = networkstatus_get_live_consensus(now); - - if (consensus) { - interval = (int)( consensus->fresh_until - consensus->valid_after ); - vote_delay = consensus->vote_seconds; - dist_delay = consensus->dist_seconds; - } else { - interval = options->TestingV3AuthInitialVotingInterval; - vote_delay = options->TestingV3AuthInitialVoteDelay; - dist_delay = options->TestingV3AuthInitialDistDelay; - } - - tor_assert(interval > 0); - - if (vote_delay + dist_delay > interval/2) - vote_delay = dist_delay = interval / 4; - - start = new_voting_schedule->interval_starts = - dirvote_get_start_of_next_interval(now,interval, - options->TestingV3AuthVotingStartOffset); - end = dirvote_get_start_of_next_interval(start+1, interval, - options->TestingV3AuthVotingStartOffset); - - tor_assert(end > start); - - new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2); - new_voting_schedule->voting_ends = start - dist_delay; - new_voting_schedule->fetch_missing_votes = - start - dist_delay - (vote_delay/2); - new_voting_schedule->voting_starts = start - dist_delay - vote_delay; - - { - char tbuf[ISO_TIME_LEN+1]; - format_iso_time(tbuf, new_voting_schedule->interval_starts); - tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: " - "consensus_set=%d, interval=%d", - tbuf, consensus?1:0, interval); - } - - return new_voting_schedule; -} - -#define voting_schedule_free(s) \ - FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s)) - -/** Frees a voting_schedule_t. This should be used instead of the generic - * tor_free. */ -static void -voting_schedule_free_(voting_schedule_t *voting_schedule_to_free) -{ - if (!voting_schedule_to_free) - return; - tor_free(voting_schedule_to_free); -} - -static voting_schedule_t voting_schedule; - -/* Using the time <b>now</b>, return the next voting valid-after time. */ +/** + * Entry point: Take whatever voting actions are pending as of <b>now</b>. + * + * Return the time at which the next action should be taken. + */ time_t -dirvote_get_next_valid_after_time(void) -{ - /* This is a safe guard in order to make sure that the voting schedule - * static object is at least initialized. Using this function with a zeroed - * voting schedule can lead to bugs. */ - if (tor_mem_is_zero((const char *) &voting_schedule, - sizeof(voting_schedule))) { - dirvote_recalculate_timing(get_options(), time(NULL)); - voting_schedule.created_on_demand = 1; - } - return voting_schedule.interval_starts; -} - -/** Set voting_schedule to hold the timing for the next vote we should be - * doing. All type of tor do that because HS subsystem needs the timing as - * well to function properly. */ -void -dirvote_recalculate_timing(const or_options_t *options, time_t now) -{ - voting_schedule_t *new_voting_schedule; - - /* get the new voting schedule */ - new_voting_schedule = get_voting_schedule(options, now, LOG_INFO); - tor_assert(new_voting_schedule); - - /* Fill in the global static struct now */ - memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule)); - voting_schedule_free(new_voting_schedule); -} - -/** Entry point: Take whatever voting actions are pending as of <b>now</b>. */ -void dirvote_act(const or_options_t *options, time_t now) { if (!authdir_mode_v3(options)) - return; + return TIME_MAX; tor_assert_nonfatal(voting_schedule.voting_starts); /* If we haven't initialized this object through this codeflow, we need to * recalculate the timings to match our vote. The reason to do that is if we @@ -2895,35 +2768,43 @@ dirvote_act(const or_options_t *options, time_t now) "Mine is %s.", keys, hex_str(c->cache_info.identity_digest, DIGEST_LEN)); tor_free(keys); - dirvote_recalculate_timing(options, now); + voting_schedule_recalculate_timing(options, now); + } + +#define IF_TIME_FOR_NEXT_ACTION(when_field, done_field) \ + if (! voting_schedule.done_field) { \ + if (voting_schedule.when_field > now) { \ + return voting_schedule.when_field; \ + } else { +#define ENDIF \ + } \ } - if (voting_schedule.voting_starts < now && !voting_schedule.have_voted) { + + IF_TIME_FOR_NEXT_ACTION(voting_starts, have_voted) { log_notice(LD_DIR, "Time to vote."); dirvote_perform_vote(); voting_schedule.have_voted = 1; - } - if (voting_schedule.fetch_missing_votes < now && - !voting_schedule.have_fetched_missing_votes) { + } ENDIF + IF_TIME_FOR_NEXT_ACTION(fetch_missing_votes, have_fetched_missing_votes) { log_notice(LD_DIR, "Time to fetch any votes that we're missing."); dirvote_fetch_missing_votes(); voting_schedule.have_fetched_missing_votes = 1; - } - if (voting_schedule.voting_ends < now && - !voting_schedule.have_built_consensus) { + } ENDIF + IF_TIME_FOR_NEXT_ACTION(voting_ends, have_built_consensus) { log_notice(LD_DIR, "Time to compute a consensus."); dirvote_compute_consensuses(); /* XXXX We will want to try again later if we haven't got enough * votes yet. Implement this if it turns out to ever happen. */ voting_schedule.have_built_consensus = 1; - } - if (voting_schedule.fetch_missing_signatures < now && - !voting_schedule.have_fetched_missing_signatures) { + } ENDIF + IF_TIME_FOR_NEXT_ACTION(fetch_missing_signatures, + have_fetched_missing_signatures) { log_notice(LD_DIR, "Time to fetch any signatures that we're missing."); dirvote_fetch_missing_signatures(); voting_schedule.have_fetched_missing_signatures = 1; - } - if (voting_schedule.interval_starts < now && - !voting_schedule.have_published_consensus) { + } ENDIF + IF_TIME_FOR_NEXT_ACTION(interval_starts, + have_published_consensus) { log_notice(LD_DIR, "Time to publish the consensus and discard old votes"); dirvote_publish_consensus(); dirvote_clear_votes(0); @@ -2933,8 +2814,15 @@ dirvote_act(const or_options_t *options, time_t now) networkstatus_get_latest_consensus_by_flavor(FLAV_NS)); /* XXXX We will want to try again later if we haven't got enough * signatures yet. Implement this if it turns out to ever happen. */ - dirvote_recalculate_timing(options, now); - } + voting_schedule_recalculate_timing(options, now); + return voting_schedule.voting_starts; + } ENDIF + + tor_assert_nonfatal_unreached(); + return now + 1; + +#undef ENDIF +#undef IF_TIME_FOR_NEXT_ACTION } /** A vote networkstatus_t and its unparsed body: held around so we can @@ -3798,7 +3686,7 @@ dirvote_get_vote(const char *fp, int flags) /** Construct and return a new microdescriptor from a routerinfo <b>ri</b> * according to <b>consensus_method</b>. **/ -microdesc_t * +STATIC microdesc_t * dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) { microdesc_t *result = NULL; @@ -3893,7 +3781,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) * in a consensus vote document. Write it into the <b>out_len</b>-byte buffer * in <b>out</b>. Return -1 on failure and the number of characters written * on success. */ -ssize_t +static ssize_t dirvote_format_microdesc_vote_line(char *out_buf, size_t out_buf_len, const microdesc_t *md, int consensus_method_low, @@ -4001,50 +3889,654 @@ dirvote_format_all_microdesc_vote_lines(const routerinfo_t *ri, time_t now, return result; } -/** If <b>vrs</b> has a hash made for the consensus method <b>method</b> with - * the digest algorithm <b>alg</b>, decode it and copy it into - * <b>digest256_out</b> and return 0. Otherwise return -1. */ -int -vote_routerstatus_find_microdesc_hash(char *digest256_out, - const vote_routerstatus_t *vrs, - int method, - digest_algorithm_t alg) +/** Parse and extract all SR commits from <b>tokens</b> and place them in + * <b>ns</b>. */ +static void +extract_shared_random_commits(networkstatus_t *ns, const smartlist_t *tokens) { - /* XXXX only returns the sha256 method. */ - const vote_microdesc_hash_t *h; - char mstr[64]; - size_t mlen; - char dstr[64]; + smartlist_t *chunks = NULL; - tor_snprintf(mstr, sizeof(mstr), "%d", method); - mlen = strlen(mstr); - tor_snprintf(dstr, sizeof(dstr), " %s=", - crypto_digest_algorithm_get_name(alg)); + tor_assert(ns); + tor_assert(tokens); + /* Commits are only present in a vote. */ + tor_assert(ns->type == NS_TYPE_VOTE); - for (h = vrs->microdesc; h; h = h->next) { - const char *cp = h->microdesc_hash_line; - size_t num_len; - /* cp looks like \d+(,\d+)* (digesttype=val )+ . Let's hunt for mstr in - * the first part. */ - while (1) { - num_len = strspn(cp, "1234567890"); - if (num_len == mlen && fast_memeq(mstr, cp, mlen)) { - /* This is the line. */ - char buf[BASE64_DIGEST256_LEN+1]; - /* XXXX ignores extraneous stuff if the digest is too long. This - * seems harmless enough, right? */ - cp = strstr(cp, dstr); - if (!cp) - return -1; - cp += strlen(dstr); - strlcpy(buf, cp, sizeof(buf)); - return digest256_from_base64(digest256_out, buf); + ns->sr_info.commits = smartlist_new(); + + smartlist_t *commits = find_all_by_keyword(tokens, K_COMMIT); + /* It's normal that a vote might contain no commits even if it participates + * in the SR protocol. Don't treat it as an error. */ + if (commits == NULL) { + goto end; + } + + /* Parse the commit. We do NO validation of number of arguments or ordering + * for forward compatibility, it's the parse commit job to inform us if it's + * supported or not. */ + chunks = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(commits, directory_token_t *, tok) { + /* Extract all arguments and put them in the chunks list. */ + for (int i = 0; i < tok->n_args; i++) { + smartlist_add(chunks, tok->args[i]); + } + sr_commit_t *commit = sr_parse_commit(chunks); + smartlist_clear(chunks); + if (commit == NULL) { + /* Get voter identity so we can warn that this dirauth vote contains + * commit we can't parse. */ + networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0); + tor_assert(voter); + log_warn(LD_DIR, "SR: Unable to parse commit %s from vote of voter %s.", + escaped(tok->object_body), + hex_str(voter->identity_digest, + sizeof(voter->identity_digest))); + /* Commitment couldn't be parsed. Continue onto the next commit because + * this one could be unsupported for instance. */ + continue; + } + /* Add newly created commit object to the vote. */ + smartlist_add(ns->sr_info.commits, commit); + } SMARTLIST_FOREACH_END(tok); + + end: + smartlist_free(chunks); + smartlist_free(commits); +} + +/* Using the given directory tokens in tokens, parse the shared random commits + * and put them in the given vote document ns. + * + * This also sets the SR participation flag if present in the vote. */ +void +dirvote_parse_sr_commits(networkstatus_t *ns, const smartlist_t *tokens) +{ + /* Does this authority participates in the SR protocol? */ + directory_token_t *tok = find_opt_by_keyword(tokens, K_SR_FLAG); + if (tok) { + ns->sr_info.participate = 1; + /* Get the SR commitments and reveals from the vote. */ + extract_shared_random_commits(ns, tokens); + } +} + +/* For the given vote, free the shared random commits if any. */ +void +dirvote_clear_commits(networkstatus_t *ns) +{ + tor_assert(ns->type == NS_TYPE_VOTE); + + if (ns->sr_info.commits) { + SMARTLIST_FOREACH(ns->sr_info.commits, sr_commit_t *, c, + sr_commit_free(c)); + smartlist_free(ns->sr_info.commits); + } +} + +/* The given url is the /tor/status-vote GET directory request. Populates the + * items list with strings that we can compress on the fly and dir_items with + * cached_dir_t objects that have a precompressed deflated version. */ +void +dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, + smartlist_t *dir_items) +{ + int current; + + url += strlen("/tor/status-vote/"); + current = !strcmpstart(url, "current/"); + url = strchr(url, '/'); + tor_assert(url); + ++url; + if (!strcmp(url, "consensus")) { + const char *item; + tor_assert(!current); /* we handle current consensus specially above, + * since it wants to be spooled. */ + if ((item = dirvote_get_pending_consensus(FLAV_NS))) + smartlist_add(items, (char*)item); + } else if (!current && !strcmp(url, "consensus-signatures")) { + /* XXXX the spec says that we should implement + * current/consensus-signatures too. It doesn't seem to be needed, + * though. */ + const char *item; + if ((item=dirvote_get_pending_detached_signatures())) + smartlist_add(items, (char*)item); + } else if (!strcmp(url, "authority")) { + const cached_dir_t *d; + int flags = DGV_BY_ID | + (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); + if ((d=dirvote_get_vote(NULL, flags))) + smartlist_add(dir_items, (cached_dir_t*)d); + } else { + const cached_dir_t *d; + smartlist_t *fps = smartlist_new(); + int flags; + if (!strcmpstart(url, "d/")) { + url += 2; + flags = DGV_INCLUDE_PENDING | DGV_INCLUDE_PREVIOUS; + } else { + flags = DGV_BY_ID | + (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); + } + dir_split_resource_into_fingerprints(url, fps, NULL, + DSR_HEX|DSR_SORT_UNIQ); + SMARTLIST_FOREACH(fps, char *, fp, { + if ((d = dirvote_get_vote(fp, flags))) + smartlist_add(dir_items, (cached_dir_t*)d); + tor_free(fp); + }); + smartlist_free(fps); + } +} + +/** Get the best estimate of a router's bandwidth for dirauth purposes, + * preferring measured to advertised values if available. */ +static uint32_t +dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) +{ + uint32_t bw_kb = 0; + /* + * Yeah, measured bandwidths in measured_bw_line_t are (implicitly + * signed) longs and the ones router_get_advertised_bandwidth() returns + * are uint32_t. + */ + long mbw_kb = 0; + + if (ri) { + /* + * * First try to see if we have a measured bandwidth; don't bother with + * as_of_out here, on the theory that a stale measured bandwidth is still + * better to trust than an advertised one. + */ + if (dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest, + &mbw_kb, NULL)) { + /* Got one! */ + bw_kb = (uint32_t)mbw_kb; + } else { + /* If not, fall back to advertised */ + bw_kb = router_get_advertised_bandwidth(ri) / 1000; + } + } + + return bw_kb; +} + +/** Helper for sorting: compares two routerinfos first by address, and then by + * descending order of "usefulness". (An authority is more useful than a + * non-authority; a running router is more useful than a non-running router; + * and a router with more bandwidth is more useful than one with less.) + **/ +static int +compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) +{ + routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b; + int first_is_auth, second_is_auth; + uint32_t bw_kb_first, bw_kb_second; + const node_t *node_first, *node_second; + int first_is_running, second_is_running; + + /* we return -1 if first should appear before second... that is, + * if first is a better router. */ + if (first->addr < second->addr) + return -1; + else if (first->addr > second->addr) + return 1; + + /* Potentially, this next bit could cause k n lg n memeq calls. But in + * reality, we will almost never get here, since addresses will usually be + * different. */ + + first_is_auth = + router_digest_is_trusted_dir(first->cache_info.identity_digest); + second_is_auth = + router_digest_is_trusted_dir(second->cache_info.identity_digest); + + if (first_is_auth && !second_is_auth) + return -1; + else if (!first_is_auth && second_is_auth) + return 1; + + node_first = node_get_by_id(first->cache_info.identity_digest); + node_second = node_get_by_id(second->cache_info.identity_digest); + first_is_running = node_first && node_first->is_running; + second_is_running = node_second && node_second->is_running; + + if (first_is_running && !second_is_running) + return -1; + else if (!first_is_running && second_is_running) + return 1; + + bw_kb_first = dirserv_get_bandwidth_for_router_kb(first); + bw_kb_second = dirserv_get_bandwidth_for_router_kb(second); + + if (bw_kb_first > bw_kb_second) + return -1; + else if (bw_kb_first < bw_kb_second) + return 1; + + /* They're equal! Compare by identity digest, so there's a + * deterministic order and we avoid flapping. */ + return fast_memcmp(first->cache_info.identity_digest, + second->cache_info.identity_digest, + DIGEST_LEN); +} + +/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t + * whose keys are the identity digests of those routers that we're going to + * exclude for Sybil-like appearance. */ +static digestmap_t * +get_possible_sybil_list(const smartlist_t *routers) +{ + const or_options_t *options = get_options(); + digestmap_t *omit_as_sybil; + smartlist_t *routers_by_ip = smartlist_new(); + uint32_t last_addr; + int addr_count; + /* Allow at most this number of Tor servers on a single IP address, ... */ + int max_with_same_addr = options->AuthDirMaxServersPerAddr; + if (max_with_same_addr <= 0) + max_with_same_addr = INT_MAX; + + smartlist_add_all(routers_by_ip, routers); + smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_); + omit_as_sybil = digestmap_new(); + + last_addr = 0; + addr_count = 0; + SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) { + if (last_addr != ri->addr) { + last_addr = ri->addr; + addr_count = 1; + } else if (++addr_count > max_with_same_addr) { + digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); + } + } SMARTLIST_FOREACH_END(ri); + + smartlist_free(routers_by_ip); + return omit_as_sybil; +} + +/** Given a platform string as in a routerinfo_t (possibly null), return a + * newly allocated version string for a networkstatus document, or NULL if the + * platform doesn't give a Tor version. */ +static char * +version_from_platform(const char *platform) +{ + if (platform && !strcmpstart(platform, "Tor ")) { + const char *eos = find_whitespace(platform+4); + if (eos && !strcmpstart(eos, " (r")) { + /* XXXX Unify this logic with the other version extraction + * logic in routerparse.c. */ + eos = find_whitespace(eos+1); + } + if (eos) { + return tor_strndup(platform, eos-platform); + } + } + return NULL; +} + +/** Given a (possibly empty) list of config_line_t, each line of which contains + * a list of comma-separated version numbers surrounded by optional space, + * allocate and return a new string containing the version numbers, in order, + * separated by commas. Used to generate Recommended(Client|Server)?Versions + */ +static char * +format_versions_list(config_line_t *ln) +{ + smartlist_t *versions; + char *result; + versions = smartlist_new(); + for ( ; ln; ln = ln->next) { + smartlist_split_string(versions, ln->value, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + } + sort_version_list(versions, 1); + result = smartlist_join_strings(versions,",",0,NULL); + SMARTLIST_FOREACH(versions,char *,s,tor_free(s)); + smartlist_free(versions); + return result; +} + +/** If there are entries in <b>routers</b> with exactly the same ed25519 keys, + * remove the older one. If they are exactly the same age, remove the one + * with the greater descriptor digest. May alter the order of the list. */ +static void +routers_make_ed_keys_unique(smartlist_t *routers) +{ + routerinfo_t *ri2; + digest256map_t *by_ed_key = digest256map_new(); + + SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { + ri->omit_from_vote = 0; + if (ri->cache_info.signing_key_cert == NULL) + continue; /* No ed key */ + const uint8_t *pk = ri->cache_info.signing_key_cert->signing_key.pubkey; + if ((ri2 = digest256map_get(by_ed_key, pk))) { + /* Duplicate; must omit one. Set the omit_from_vote flag in whichever + * one has the earlier published_on. */ + const time_t ri_pub = ri->cache_info.published_on; + const time_t ri2_pub = ri2->cache_info.published_on; + if (ri2_pub < ri_pub || + (ri2_pub == ri_pub && + fast_memcmp(ri->cache_info.signed_descriptor_digest, + ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) { + digest256map_set(by_ed_key, pk, ri); + ri2->omit_from_vote = 1; + } else { + ri->omit_from_vote = 1; } - if (num_len == 0 || cp[num_len] != ',') - break; - cp += num_len + 1; + } else { + /* Add to map */ + digest256map_set(by_ed_key, pk, ri); } + } SMARTLIST_FOREACH_END(ri); + + digest256map_free(by_ed_key, NULL); + + /* Now remove every router where the omit_from_vote flag got set. */ + SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) { + if (ri->omit_from_vote) { + SMARTLIST_DEL_CURRENT(routers, ri); + } + } SMARTLIST_FOREACH_END(ri); +} + +/** Routerstatus <b>rs</b> is part of a group of routers that are on + * too narrow an IP-space. Clear out its flags since we don't want it be used + * because of its Sybil-like appearance. + * + * Leave its BadExit flag alone though, since if we think it's a bad exit, + * we want to vote that way in case all the other authorities are voting + * Running and Exit. + */ +static void +clear_status_flags_on_sybil(routerstatus_t *rs) +{ + rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast = + rs->is_flagged_running = rs->is_named = rs->is_valid = + rs->is_hs_dir = rs->is_v2_dir = rs->is_possible_guard = 0; + /* FFFF we might want some mechanism to check later on if we + * missed zeroing any flags: it's easy to add a new flag but + * forget to add it to this clause. */ +} + +/** Return a new networkstatus_t* containing our current opinion. (For v3 + * authorities) */ +networkstatus_t * +dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, + authority_cert_t *cert) +{ + const or_options_t *options = get_options(); + networkstatus_t *v3_out = NULL; + uint32_t addr; + char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; + const char *contact; + smartlist_t *routers, *routerstatuses; + char identity_digest[DIGEST_LEN]; + char signing_key_digest[DIGEST_LEN]; + int listbadexits = options->AuthDirListBadExits; + routerlist_t *rl = router_get_routerlist(); + time_t now = time(NULL); + time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; + networkstatus_voter_info_t *voter = NULL; + vote_timing_t timing; + digestmap_t *omit_as_sybil = NULL; + const int vote_on_reachability = running_long_enough_to_decide_unreachable(); + smartlist_t *microdescriptors = NULL; + + tor_assert(private_key); + tor_assert(cert); + + if (crypto_pk_get_digest(private_key, signing_key_digest)<0) { + log_err(LD_BUG, "Error computing signing key digest"); + return NULL; } - return -1; + if (crypto_pk_get_digest(cert->identity_key, identity_digest)<0) { + log_err(LD_BUG, "Error computing identity key digest"); + return NULL; + } + if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) { + log_warn(LD_NET, "Couldn't resolve my hostname"); + return NULL; + } + if (!hostname || !strchr(hostname, '.')) { + tor_free(hostname); + hostname = tor_dup_ip(addr); + } + + if (options->VersioningAuthoritativeDir) { + client_versions = format_versions_list(options->RecommendedClientVersions); + server_versions = format_versions_list(options->RecommendedServerVersions); + } + + contact = get_options()->ContactInfo; + if (!contact) + contact = "(none)"; + + /* + * Do this so dirserv_compute_performance_thresholds() and + * set_routerstatus_from_routerinfo() see up-to-date bandwidth info. + */ + if (options->V3BandwidthsFile) { + dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL); + } else { + /* + * No bandwidths file; clear the measured bandwidth cache in case we had + * one last time around. + */ + if (dirserv_get_measured_bw_cache_size() > 0) { + dirserv_clear_measured_bw_cache(); + } + } + + /* precompute this part, since we need it to decide what "stable" + * means. */ + SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, { + dirserv_set_router_is_running(ri, now); + }); + + routers = smartlist_new(); + smartlist_add_all(routers, rl->routers); + routers_make_ed_keys_unique(routers); + /* After this point, don't use rl->routers; use 'routers' instead. */ + routers_sort_by_identity(routers); + omit_as_sybil = get_possible_sybil_list(routers); + + DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) { + (void) ignore; + rep_hist_make_router_pessimal(sybil_id, now); + } DIGESTMAP_FOREACH_END; + + /* Count how many have measured bandwidths so we know how to assign flags; + * this must come before dirserv_compute_performance_thresholds() */ + dirserv_count_measured_bws(routers); + + dirserv_compute_performance_thresholds(omit_as_sybil); + + routerstatuses = smartlist_new(); + microdescriptors = smartlist_new(); + + SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { + if (ri->cache_info.published_on >= cutoff) { + routerstatus_t *rs; + vote_routerstatus_t *vrs; + node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest); + if (!node) + continue; + + vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); + rs = &vrs->status; + set_routerstatus_from_routerinfo(rs, node, ri, now, + listbadexits); + + if (ri->cache_info.signing_key_cert) { + memcpy(vrs->ed25519_id, + ri->cache_info.signing_key_cert->signing_key.pubkey, + ED25519_PUBKEY_LEN); + } + + if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) + clear_status_flags_on_sybil(rs); + + if (!vote_on_reachability) + rs->is_flagged_running = 0; + + vrs->version = version_from_platform(ri->platform); + if (ri->protocol_list) { + vrs->protocols = tor_strdup(ri->protocol_list); + } else { + vrs->protocols = tor_strdup( + protover_compute_for_old_tor(vrs->version)); + } + vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now, + microdescriptors); + + smartlist_add(routerstatuses, vrs); + } + } SMARTLIST_FOREACH_END(ri); + + { + smartlist_t *added = + microdescs_add_list_to_cache(get_microdesc_cache(), + microdescriptors, SAVED_NOWHERE, 0); + smartlist_free(added); + smartlist_free(microdescriptors); + } + + smartlist_free(routers); + digestmap_free(omit_as_sybil, NULL); + + /* Apply guardfraction information to routerstatuses. */ + if (options->GuardfractionFile) { + dirserv_read_guardfraction_file(options->GuardfractionFile, + routerstatuses); + } + + /* This pass through applies the measured bw lines to the routerstatuses */ + if (options->V3BandwidthsFile) { + dirserv_read_measured_bandwidths(options->V3BandwidthsFile, + routerstatuses); + } else { + /* + * No bandwidths file; clear the measured bandwidth cache in case we had + * one last time around. + */ + if (dirserv_get_measured_bw_cache_size() > 0) { + dirserv_clear_measured_bw_cache(); + } + } + + v3_out = tor_malloc_zero(sizeof(networkstatus_t)); + + v3_out->type = NS_TYPE_VOTE; + dirvote_get_preferred_voting_intervals(&timing); + v3_out->published = now; + { + char tbuf[ISO_TIME_LEN+1]; + networkstatus_t *current_consensus = + networkstatus_get_live_consensus(now); + long last_consensus_interval; /* only used to pick a valid_after */ + if (current_consensus) + last_consensus_interval = current_consensus->fresh_until - + current_consensus->valid_after; + else + last_consensus_interval = options->TestingV3AuthInitialVotingInterval; + v3_out->valid_after = + voting_schedule_get_start_of_next_interval(now, + (int)last_consensus_interval, + options->TestingV3AuthVotingStartOffset); + format_iso_time(tbuf, v3_out->valid_after); + log_notice(LD_DIR,"Choosing valid-after time in vote as %s: " + "consensus_set=%d, last_interval=%d", + tbuf, current_consensus?1:0, (int)last_consensus_interval); + } + v3_out->fresh_until = v3_out->valid_after + timing.vote_interval; + v3_out->valid_until = v3_out->valid_after + + (timing.vote_interval * timing.n_intervals_valid); + v3_out->vote_seconds = timing.vote_delay; + v3_out->dist_seconds = timing.dist_delay; + tor_assert(v3_out->vote_seconds > 0); + tor_assert(v3_out->dist_seconds > 0); + tor_assert(timing.n_intervals_valid > 0); + + v3_out->client_versions = client_versions; + v3_out->server_versions = server_versions; + + /* These are hardwired, to avoid disaster. */ + v3_out->recommended_relay_protocols = + tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); + v3_out->recommended_client_protocols = + tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); + v3_out->required_client_protocols = + tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); + v3_out->required_relay_protocols = + tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=3-4 LinkAuth=1 Microdesc=1 Relay=1-2"); + + /* We are not allowed to vote to require anything we don't have. */ + tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL)); + tor_assert(protover_all_supported(v3_out->required_client_protocols, NULL)); + + /* We should not recommend anything we don't have. */ + tor_assert_nonfatal(protover_all_supported( + v3_out->recommended_relay_protocols, NULL)); + tor_assert_nonfatal(protover_all_supported( + v3_out->recommended_client_protocols, NULL)); + + v3_out->package_lines = smartlist_new(); + { + config_line_t *cl; + for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) { + if (validate_recommended_package_line(cl->value)) + smartlist_add_strdup(v3_out->package_lines, cl->value); + } + } + + v3_out->known_flags = smartlist_new(); + smartlist_split_string(v3_out->known_flags, + "Authority Exit Fast Guard Stable V2Dir Valid HSDir", + 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + if (vote_on_reachability) + smartlist_add_strdup(v3_out->known_flags, "Running"); + if (listbadexits) + smartlist_add_strdup(v3_out->known_flags, "BadExit"); + smartlist_sort_strings(v3_out->known_flags); + + if (options->ConsensusParams) { + v3_out->net_params = smartlist_new(); + smartlist_split_string(v3_out->net_params, + options->ConsensusParams, NULL, 0, 0); + smartlist_sort_strings(v3_out->net_params); + } + + voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); + voter->nickname = tor_strdup(options->Nickname); + memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); + voter->sigs = smartlist_new(); + voter->address = hostname; + voter->addr = addr; + voter->dir_port = router_get_advertised_dir_port(options, 0); + voter->or_port = router_get_advertised_or_port(options); + voter->contact = tor_strdup(contact); + if (options->V3AuthUseLegacyKey) { + authority_cert_t *c = get_my_v3_legacy_cert(); + if (c) { + if (crypto_pk_get_digest(c->identity_key, voter->legacy_id_digest)) { + log_warn(LD_BUG, "Unable to compute digest of legacy v3 identity key"); + memset(voter->legacy_id_digest, 0, DIGEST_LEN); + } + } + } + + v3_out->voters = smartlist_new(); + smartlist_add(v3_out->voters, voter); + v3_out->cert = authority_cert_dup(cert); + v3_out->routerstatus_list = routerstatuses; + /* Note: networkstatus_digest is unset; it won't get set until we actually + * format the vote. */ + + return v3_out; } diff --git a/src/or/dirvote.h b/src/or/dirauth/dirvote.h index 8a317deb47..b69bbbf5d9 100644 --- a/src/or/dirvote.h +++ b/src/or/dirauth/dirvote.h @@ -12,8 +12,6 @@ #ifndef TOR_DIRVOTE_H #define TOR_DIRVOTE_H -#include "testsupport.h" - /* * Ideally, assuming synced clocks, we should only need 1 second for each of: * - Vote @@ -86,74 +84,27 @@ * get confused with the above macros.) */ #define DEFAULT_MAX_UNMEASURED_BW_KB 20 +/* Directory Get Vote (DGV) flags for dirvote_get_vote(). */ +#define DGV_BY_ID 1 +#define DGV_INCLUDE_PENDING 2 +#define DGV_INCLUDE_PREVIOUS 4 + +/* + * Public API. Used outside of the dirauth subsystem. + * + * We need to nullify them if the module is disabled. + */ +#ifdef HAVE_MODULE_DIRAUTH + +time_t dirvote_act(const or_options_t *options, time_t now); void dirvote_free_all(void); -/* vote manipulation */ -char *networkstatus_compute_consensus(smartlist_t *votes, - int total_authorities, - crypto_pk_t *identity_key, - crypto_pk_t *signing_key, - const char *legacy_identity_key_digest, - crypto_pk_t *legacy_signing_key, - consensus_flavor_t flavor); -int networkstatus_add_detached_signatures(networkstatus_t *target, - ns_detached_signatures_t *sigs, - const char *source, - int severity, - const char **msg_out); -char *networkstatus_get_detached_signatures(smartlist_t *consensuses); -void ns_detached_signatures_free_(ns_detached_signatures_t *s); -#define ns_detached_signatures_free(s) \ - FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s)) - -/* cert manipulation */ -authority_cert_t *authority_cert_dup(authority_cert_t *cert); - -/* vote scheduling */ - -/** Scheduling information for a voting interval. */ -typedef struct { - /** When do we generate and distribute our vote for this interval? */ - time_t voting_starts; - /** When do we send an HTTP request for any votes that we haven't - * been posted yet?*/ - time_t fetch_missing_votes; - /** When do we give up on getting more votes and generate a consensus? */ - time_t voting_ends; - /** When do we send an HTTP request for any signatures we're expecting to - * see on the consensus? */ - time_t fetch_missing_signatures; - /** When do we publish the consensus? */ - time_t interval_starts; - - /* True iff we have generated and distributed our vote. */ - int have_voted; - /* True iff we've requested missing votes. */ - int have_fetched_missing_votes; - /* True iff we have built a consensus and sent the signatures around. */ - int have_built_consensus; - /* True iff we've fetched missing signatures. */ - int have_fetched_missing_signatures; - /* True iff we have published our consensus. */ - int have_published_consensus; - - /* True iff this voting schedule was set on demand meaning not through the - * normal vote operation of a dirauth or when a consensus is set. This only - * applies to a directory authority that needs to recalculate the voting - * timings only for the first vote even though this object was initilized - * prior to voting. */ - int created_on_demand; -} voting_schedule_t; - -void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out); -time_t dirvote_get_start_of_next_interval(time_t now, - int interval, - int offset); -void dirvote_recalculate_timing(const or_options_t *options, time_t now); -void dirvote_act(const or_options_t *options, time_t now); -time_t dirvote_get_next_valid_after_time(void); - -/* invoked on timers and by outside triggers. */ +void dirvote_parse_sr_commits(networkstatus_t *ns, const smartlist_t *tokens); +void dirvote_clear_commits(networkstatus_t *ns); +void dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, + smartlist_t *dir_items); + +/* Storing signatures and votes functions */ struct pending_vote_t * dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out); @@ -161,15 +112,82 @@ int dirvote_add_signatures(const char *detached_signatures_body, const char *source, const char **msg_out); +#else /* HAVE_MODULE_DIRAUTH */ + +static inline time_t +dirvote_act(const or_options_t *options, time_t now) +{ + (void) options; + (void) now; + return TIME_MAX; +} + +static inline void +dirvote_free_all(void) +{ +} + +static inline void +dirvote_parse_sr_commits(networkstatus_t *ns, const smartlist_t *tokens) +{ + (void) ns; + (void) tokens; +} + +static inline void +dirvote_clear_commits(networkstatus_t *ns) +{ + (void) ns; +} + +static inline void +dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, + smartlist_t *dir_items) +{ + (void) url; + (void) items; + (void) dir_items; +} + +static inline struct pending_vote_t * +dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) +{ + (void) vote_body; + /* If the dirauth module is disabled, this should NEVER be called else we + * failed to safeguard the dirauth module. */ + tor_assert_nonfatal_unreached(); + + /* We need to send out an error code. */ + *status_out = 400; + *msg_out = "No directory authority support"; + return NULL; +} + +static inline int +dirvote_add_signatures(const char *detached_signatures_body, const char *source, + const char **msg_out) +{ + (void) detached_signatures_body; + (void) source; + (void) msg_out; + /* If the dirauth module is disabled, this should NEVER be called else we + * failed to safeguard the dirauth module. */ + tor_assert_nonfatal_unreached(); + return 0; +} + +#endif /* HAVE_MODULE_DIRAUTH */ + /* Item access */ MOCK_DECL(const char*, dirvote_get_pending_consensus, (consensus_flavor_t flav)); MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void)); - -#define DGV_BY_ID 1 -#define DGV_INCLUDE_PENDING 2 -#define DGV_INCLUDE_PREVIOUS 4 const cached_dir_t *dirvote_get_vote(const char *fp, int flags); + +/* + * API used _only_ by the dirauth subsystem. + */ + void set_routerstatus_from_routerinfo(routerstatus_t *rs, node_t *node, routerinfo_t *ri, time_t now, @@ -178,26 +196,18 @@ networkstatus_t * dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, authority_cert_t *cert); -microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, - int consensus_method); -ssize_t dirvote_format_microdesc_vote_line(char *out, size_t out_len, - const microdesc_t *md, - int consensus_method_low, - int consensus_method_high); vote_microdesc_hash_t *dirvote_format_all_microdesc_vote_lines( const routerinfo_t *ri, time_t now, smartlist_t *microdescriptors_out); -int vote_routerstatus_find_microdesc_hash(char *digest256_out, - const vote_routerstatus_t *vrs, - int method, - digest_algorithm_t alg); -document_signature_t *voter_get_sig_by_algorithm( - const networkstatus_voter_info_t *voter, - digest_algorithm_t alg); - +/* + * Exposed functions for unit tests. + */ #ifdef DIRVOTE_PRIVATE + +/* Cert manipulation */ +STATIC authority_cert_t *authority_cert_dup(authority_cert_t *cert); STATIC int32_t dirvote_get_intermediate_param_value( const smartlist_t *param_list, const char *keyword, @@ -212,6 +222,25 @@ STATIC int networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, int64_t M, int64_t E, int64_t D, int64_t T, int64_t weight_scale); +STATIC +char *networkstatus_compute_consensus(smartlist_t *votes, + int total_authorities, + crypto_pk_t *identity_key, + crypto_pk_t *signing_key, + const char *legacy_identity_key_digest, + crypto_pk_t *legacy_signing_key, + consensus_flavor_t flavor); +STATIC +int networkstatus_add_detached_signatures(networkstatus_t *target, + ns_detached_signatures_t *sigs, + const char *source, + int severity, + const char **msg_out); +STATIC +char *networkstatus_get_detached_signatures(smartlist_t *consensuses); +STATIC microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, + int consensus_method); + #endif /* defined(DIRVOTE_PRIVATE) */ #endif /* !defined(TOR_DIRVOTE_H) */ diff --git a/src/or/shared_random.c b/src/or/dirauth/shared_random.c index 13416d6bc7..f7ff5c58bb 100644 --- a/src/or/shared_random.c +++ b/src/or/dirauth/shared_random.c @@ -91,13 +91,16 @@ #include "shared_random.h" #include "config.h" #include "confparse.h" -#include "dirvote.h" #include "networkstatus.h" #include "routerkeys.h" #include "router.h" #include "routerlist.h" #include "shared_random_state.h" +#include "shared_random_client.h" #include "util.h" +#include "voting_schedule.h" + +#include "dirauth/dirvote.h" /* String prefix of shared random values in votes/consensuses. */ static const char previous_srv_str[] = "shared-rand-previous-value"; @@ -498,20 +501,6 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase) return vote_line; } -/* Convert a given srv object to a string for the control port. This doesn't - * fail and the srv object MUST be valid. */ -static char * -srv_to_control_string(const sr_srv_t *srv) -{ - char *srv_str; - char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1]; - tor_assert(srv); - - sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv); - tor_asprintf(&srv_str, "%s", srv_hash_encoded); - return srv_str; -} - /* Return a heap allocated string that contains the given <b>srv</b> string * representation formatted for a networkstatus document using the * <b>key</b> as the start of the line. This doesn't return NULL. */ @@ -874,27 +863,6 @@ get_majority_srv_from_votes(const smartlist_t *votes, int current) return the_srv; } -/* Encode the given shared random value and put it in dst. Destination - * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */ -void -sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv) -{ - int ret; - /* Extra byte for the NULL terminated char. */ - char buf[SR_SRV_VALUE_BASE64_LEN + 1]; - - tor_assert(dst); - tor_assert(srv); - tor_assert(dst_len >= sizeof(buf)); - - ret = base64_encode(buf, sizeof(buf), (const char *) srv->value, - sizeof(srv->value), 0); - /* Always expect the full length without the NULL byte. */ - tor_assert(ret == (sizeof(buf) - 1)); - tor_assert(ret <= (int) dst_len); - strlcpy(dst, buf, dst_len); -} - /* Free a commit object. */ void sr_commit_free_(sr_commit_t *commit) @@ -1036,55 +1004,6 @@ sr_compute_srv(void) tor_free(reveals); } -/* Parse a list of arguments from a SRV value either from a vote, consensus - * or from our disk state and return a newly allocated srv object. NULL is - * returned on error. - * - * The arguments' order: - * num_reveals, value - */ -sr_srv_t * -sr_parse_srv(const smartlist_t *args) -{ - char *value; - int ok, ret; - uint64_t num_reveals; - sr_srv_t *srv = NULL; - - tor_assert(args); - - if (smartlist_len(args) < 2) { - goto end; - } - - /* First argument is the number of reveal values */ - num_reveals = tor_parse_uint64(smartlist_get(args, 0), - 10, 0, UINT64_MAX, &ok, NULL); - if (!ok) { - goto end; - } - /* Second and last argument is the shared random value it self. */ - value = smartlist_get(args, 1); - if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) { - goto end; - } - - srv = tor_malloc_zero(sizeof(*srv)); - srv->num_reveals = num_reveals; - /* We subtract one byte from the srclen because the function ignores the - * '=' character in the given buffer. This is broken but it's a documented - * behavior of the implementation. */ - ret = base64_decode((char *) srv->value, sizeof(srv->value), value, - SR_SRV_VALUE_BASE64_LEN - 1); - if (ret != sizeof(srv->value)) { - tor_free(srv); - srv = NULL; - goto end; - } - end: - return srv; -} - /* Parse a commit from a vote or from our disk state and return a newly * allocated commit object. NULL is returned on error. * @@ -1333,7 +1252,7 @@ sr_act_post_consensus(const networkstatus_t *consensus) } /* Prepare our state so that it's ready for the next voting period. */ - sr_state_update(dirvote_get_next_valid_after_time()); + sr_state_update(voting_schedule_get_next_valid_after_time()); } /* Initialize shared random subsystem. This MUST be called early in the boot @@ -1352,84 +1271,6 @@ sr_save_and_cleanup(void) sr_cleanup(); } -/* Return the current SRV string representation for the control port. Return a - * newly allocated string on success containing the value else "" if not found - * or if we don't have a valid consensus yet. */ -char * -sr_get_current_for_control(void) -{ - char *srv_str; - const networkstatus_t *c = networkstatus_get_latest_consensus(); - if (c && c->sr_info.current_srv) { - srv_str = srv_to_control_string(c->sr_info.current_srv); - } else { - srv_str = tor_strdup(""); - } - return srv_str; -} - -/* Return the previous SRV string representation for the control port. Return - * a newly allocated string on success containing the value else "" if not - * found or if we don't have a valid consensus yet. */ -char * -sr_get_previous_for_control(void) -{ - char *srv_str; - const networkstatus_t *c = networkstatus_get_latest_consensus(); - if (c && c->sr_info.previous_srv) { - srv_str = srv_to_control_string(c->sr_info.previous_srv); - } else { - srv_str = tor_strdup(""); - } - return srv_str; -} - -/* Return current shared random value from the latest consensus. Caller can - * NOT keep a reference to the returned pointer. Return NULL if none. */ -const sr_srv_t * -sr_get_current(const networkstatus_t *ns) -{ - const networkstatus_t *consensus; - - /* Use provided ns else get a live one */ - if (ns) { - consensus = ns; - } else { - consensus = networkstatus_get_live_consensus(approx_time()); - } - /* Ideally we would never be asked for an SRV without a live consensus. Make - * sure this assumption is correct. */ - tor_assert_nonfatal(consensus); - - if (consensus) { - return consensus->sr_info.current_srv; - } - return NULL; -} - -/* Return previous shared random value from the latest consensus. Caller can - * NOT keep a reference to the returned pointer. Return NULL if none. */ -const sr_srv_t * -sr_get_previous(const networkstatus_t *ns) -{ - const networkstatus_t *consensus; - - /* Use provided ns else get a live one */ - if (ns) { - consensus = ns; - } else { - consensus = networkstatus_get_live_consensus(approx_time()); - } - /* Ideally we would never be asked for an SRV without a live consensus. Make - * sure this assumption is correct. */ - tor_assert_nonfatal(consensus); - - if (consensus) { - return consensus->sr_info.previous_srv; - } - return NULL; -} - #ifdef TOR_UNIT_TESTS /* Set the global value of number of SRV agreements so the test can play diff --git a/src/or/shared_random.h b/src/or/dirauth/shared_random.h index 675a8d8b06..1778ce8f09 100644 --- a/src/or/shared_random.h +++ b/src/or/dirauth/shared_random.h @@ -101,21 +101,48 @@ typedef struct sr_commit_t { /* API */ -/* Public methods: */ +/* Public methods used _outside_ of the module. + * + * We need to nullify them if the module is disabled. */ +#ifdef HAVE_MODULE_DIRAUTH int sr_init(int save_to_disk); void sr_save_and_cleanup(void); void sr_act_post_consensus(const networkstatus_t *consensus); + +#else /* HAVE_MODULE_DIRAUTH */ + +static inline int +sr_init(int save_to_disk) +{ + (void) save_to_disk; + /* Always return success. */ + return 0; +} + +static inline void +sr_save_and_cleanup(void) +{ +} + +static inline void +sr_act_post_consensus(const networkstatus_t *consensus) +{ + (void) consensus; +} + +#endif /* HAVE_MODULE_DIRAUTH */ + +/* Public methods used only by dirauth code. */ + void sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key); sr_commit_t *sr_parse_commit(const smartlist_t *args); -sr_srv_t *sr_parse_srv(const smartlist_t *args); char *sr_get_string_for_vote(void); char *sr_get_string_for_consensus(const smartlist_t *votes, int32_t num_srv_agreements); void sr_commit_free_(sr_commit_t *commit); #define sr_commit_free(sr) FREE_AND_NULL(sr_commit_t, sr_commit_free_, (sr)) -void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv); /* Private methods (only used by shared_random_state.c): */ static inline @@ -128,12 +155,6 @@ void sr_compute_srv(void); sr_commit_t *sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert); -char *sr_get_current_for_control(void); -char *sr_get_previous_for_control(void); - -const sr_srv_t *sr_get_current(const networkstatus_t *ns); -const sr_srv_t *sr_get_previous(const networkstatus_t *ns); - #ifdef SHARED_RANDOM_PRIVATE /* Encode */ diff --git a/src/or/shared_random_state.c b/src/or/dirauth/shared_random_state.c index 53782af59a..56c12c8c72 100644 --- a/src/or/shared_random_state.c +++ b/src/or/dirauth/shared_random_state.c @@ -14,10 +14,13 @@ #include "shared_random.h" #include "config.h" #include "confparse.h" -#include "dirvote.h" +#include "voting_schedule.h" #include "networkstatus.h" #include "router.h" #include "shared_random_state.h" +#include "shared_random_client.h" + +#include "dirauth/dirvote.h" /* Default filename of the shared random state on disk. */ static const char default_fname[] = "sr-state"; @@ -53,10 +56,6 @@ DUMMY_TYPECHECK_INSTANCE(sr_disk_state_t); VAR(#member, conftype, member, initvalue) /* Our persistent state magic number. */ #define SR_DISK_STATE_MAGIC 0x98AB1254 -/* Each protocol phase has 12 rounds */ -#define SHARED_RANDOM_N_ROUNDS 12 -/* Number of phase we have in a protocol. */ -#define SHARED_RANDOM_N_PHASES 2 static int disk_state_validate_cb(void *old_state, void *state, void *default_state, @@ -115,81 +114,6 @@ get_phase_str(sr_phase_t phase) return the_string; } - -/* Return the voting interval of the tor vote subsystem. */ -static int -get_voting_interval(void) -{ - int interval; - networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL)); - - if (consensus) { - interval = (int)(consensus->fresh_until - consensus->valid_after); - } else { - /* Same for both a testing and real network. We voluntarily ignore the - * InitialVotingInterval since it complexifies things and it doesn't - * affect the SR protocol. */ - interval = get_options()->V3AuthVotingInterval; - } - tor_assert(interval > 0); - return interval; -} - -/* Given the time <b>now</b>, return the start time of the current round of - * the SR protocol. For example, if it's 23:47:08, the current round thus - * started at 23:47:00 for a voting interval of 10 seconds. */ -STATIC time_t -get_start_time_of_current_round(void) -{ - const or_options_t *options = get_options(); - int voting_interval = get_voting_interval(); - /* First, get the start time of the next round */ - time_t next_start = dirvote_get_next_valid_after_time(); - /* Now roll back next_start by a voting interval to find the start time of - the current round. */ - time_t curr_start = dirvote_get_start_of_next_interval( - next_start - voting_interval - 1, - voting_interval, - options->TestingV3AuthVotingStartOffset); - return curr_start; -} - -/** Return the start time of the current SR protocol run. For example, if the - * time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this - * function should return 23/06/2017 00:00:00. */ -time_t -sr_state_get_start_time_of_current_protocol_run(time_t now) -{ - int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; - int voting_interval = get_voting_interval(); - /* Find the time the current round started. */ - time_t beginning_of_current_round = get_start_time_of_current_round(); - - /* Get current SR protocol round */ - int current_round = (now / voting_interval) % total_rounds; - - /* Get start time by subtracting the time elapsed from the beginning of the - protocol run */ - time_t time_elapsed_since_start_of_run = current_round * voting_interval; - return beginning_of_current_round - time_elapsed_since_start_of_run; -} - -/** Return the time (in seconds) it takes to complete a full SR protocol phase - * (e.g. the commit phase). */ -unsigned int -sr_state_get_phase_duration(void) -{ - return SHARED_RANDOM_N_ROUNDS * get_voting_interval(); -} - -/** Return the time (in seconds) it takes to complete a full SR protocol run */ -unsigned int -sr_state_get_protocol_run_duration(void) -{ - int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; - return total_protocol_rounds * get_voting_interval(); -} - /* Return the time we should expire the state file created at <b>now</b>. * We expire the state file in the beginning of the next protocol run. */ STATIC time_t @@ -1369,7 +1293,7 @@ sr_state_init(int save_to_disk, int read_from_disk) /* We have a state in memory, let's make sure it's updated for the current * and next voting round. */ { - time_t valid_after = dirvote_get_next_valid_after_time(); + time_t valid_after = voting_schedule_get_next_valid_after_time(); sr_state_update(valid_after); } return 0; diff --git a/src/or/shared_random_state.h b/src/or/dirauth/shared_random_state.h index fdbbf4919a..60a326f86c 100644 --- a/src/or/shared_random_state.h +++ b/src/or/dirauth/shared_random_state.h @@ -121,16 +121,11 @@ int sr_state_is_initialized(void); void sr_state_save(void); void sr_state_free_all(void); -time_t sr_state_get_start_time_of_current_protocol_run(time_t now); -unsigned int sr_state_get_phase_duration(void); -unsigned int sr_state_get_protocol_run_duration(void); - #ifdef SHARED_RANDOM_STATE_PRIVATE STATIC int disk_state_load_from_disk_impl(const char *fname); STATIC sr_phase_t get_sr_protocol_phase(time_t valid_after); -STATIC time_t get_start_time_of_current_round(void); STATIC time_t get_state_valid_until_time(time_t now); STATIC const char *get_phase_str(sr_phase_t phase); diff --git a/src/or/directory.c b/src/or/directory.c index c419b61d02..2c5ee23f3a 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -20,7 +20,6 @@ #include "compat.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" #include "entrynodes.h" #include "geoip.h" #include "hs_cache.h" @@ -41,7 +40,7 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO) #if !defined(OpenBSD) @@ -49,6 +48,8 @@ #endif #endif +#include "dirauth/dirvote.h" + /** * \file directory.c * \brief Code to send and fetch information from directory authorities and @@ -794,9 +795,9 @@ directory_choose_address_routerstatus(const routerstatus_t *status, * Use the preferred address and port if they are reachable, otherwise, * use the alternate address and port (if any). */ - have_or = fascist_firewall_choose_address_rs(status, - FIREWALL_OR_CONNECTION, 0, - use_or_ap); + fascist_firewall_choose_address_rs(status, FIREWALL_OR_CONNECTION, 0, + use_or_ap); + have_or = tor_addr_port_is_valid_ap(use_or_ap, 0); } /* DirPort connections @@ -805,9 +806,9 @@ directory_choose_address_routerstatus(const routerstatus_t *status, indirection == DIRIND_ANON_DIRPORT || (indirection == DIRIND_ONEHOP && !directory_must_use_begindir(options))) { - have_dir = fascist_firewall_choose_address_rs(status, - FIREWALL_DIR_CONNECTION, 0, - use_dir_ap); + fascist_firewall_choose_address_rs(status, FIREWALL_DIR_CONNECTION, 0, + use_dir_ap); + have_dir = tor_addr_port_is_valid_ap(use_dir_ap, 0); } /* We rejected all addresses in the relay's status. This means we can't @@ -4437,59 +4438,15 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args) { const char *url = args->url; { - int current; ssize_t body_len = 0; ssize_t estimated_len = 0; + int lifetime = 60; /* XXXX?? should actually use vote intervals. */ /* This smartlist holds strings that we can compress on the fly. */ smartlist_t *items = smartlist_new(); /* This smartlist holds cached_dir_t objects that have a precompressed * deflated version. */ smartlist_t *dir_items = smartlist_new(); - int lifetime = 60; /* XXXX?? should actually use vote intervals. */ - url += strlen("/tor/status-vote/"); - current = !strcmpstart(url, "current/"); - url = strchr(url, '/'); - tor_assert(url); - ++url; - if (!strcmp(url, "consensus")) { - const char *item; - tor_assert(!current); /* we handle current consensus specially above, - * since it wants to be spooled. */ - if ((item = dirvote_get_pending_consensus(FLAV_NS))) - smartlist_add(items, (char*)item); - } else if (!current && !strcmp(url, "consensus-signatures")) { - /* XXXX the spec says that we should implement - * current/consensus-signatures too. It doesn't seem to be needed, - * though. */ - const char *item; - if ((item=dirvote_get_pending_detached_signatures())) - smartlist_add(items, (char*)item); - } else if (!strcmp(url, "authority")) { - const cached_dir_t *d; - int flags = DGV_BY_ID | - (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); - if ((d=dirvote_get_vote(NULL, flags))) - smartlist_add(dir_items, (cached_dir_t*)d); - } else { - const cached_dir_t *d; - smartlist_t *fps = smartlist_new(); - int flags; - if (!strcmpstart(url, "d/")) { - url += 2; - flags = DGV_INCLUDE_PENDING | DGV_INCLUDE_PREVIOUS; - } else { - flags = DGV_BY_ID | - (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); - } - dir_split_resource_into_fingerprints(url, fps, NULL, - DSR_HEX|DSR_SORT_UNIQ); - SMARTLIST_FOREACH(fps, char *, fp, { - if ((d = dirvote_get_vote(fp, flags))) - smartlist_add(dir_items, (cached_dir_t*)d); - tor_free(fp); - }); - smartlist_free(fps); - } + dirvote_dirreq_get_status_vote(url, items, dir_items); if (!smartlist_len(dir_items) && !smartlist_len(items)) { write_short_http_response(conn, 404, "Not found"); goto vote_done; @@ -5300,84 +5257,71 @@ connection_dir_finished_connecting(dir_connection_t *conn) /** Decide which download schedule we want to use based on descriptor type * in <b>dls</b> and <b>options</b>. - * Then return a list of int pointers defining download delays in seconds. + * + * Then, return the initial delay for that download schedule, in seconds. + * * Helper function for download_status_increment_failure(), * download_status_reset(), and download_status_increment_attempt(). */ -STATIC const smartlist_t * -find_dl_schedule(const download_status_t *dls, const or_options_t *options) +STATIC int +find_dl_min_delay(const download_status_t *dls, const or_options_t *options) { + tor_assert(dls); + tor_assert(options); + switch (dls->schedule) { case DL_SCHED_GENERIC: /* Any other directory document */ if (dir_server_mode(options)) { /* A directory authority or directory mirror */ - return options->TestingServerDownloadSchedule; + return options->TestingServerDownloadInitialDelay; } else { - return options->TestingClientDownloadSchedule; + return options->TestingClientDownloadInitialDelay; } case DL_SCHED_CONSENSUS: if (!networkstatus_consensus_can_use_multiple_directories(options)) { /* A public relay */ - return options->TestingServerConsensusDownloadSchedule; + return options->TestingServerConsensusDownloadInitialDelay; } else { /* A client or bridge */ if (networkstatus_consensus_is_bootstrapping(time(NULL))) { /* During bootstrapping */ if (!networkstatus_consensus_can_use_extra_fallbacks(options)) { /* A bootstrapping client without extra fallback directories */ - return - options->ClientBootstrapConsensusAuthorityOnlyDownloadSchedule; + return options-> + ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay; } else if (dls->want_authority) { /* A bootstrapping client with extra fallback directories, but * connecting to an authority */ return - options->ClientBootstrapConsensusAuthorityDownloadSchedule; + options->ClientBootstrapConsensusAuthorityDownloadInitialDelay; } else { /* A bootstrapping client connecting to extra fallback directories */ return - options->ClientBootstrapConsensusFallbackDownloadSchedule; + options->ClientBootstrapConsensusFallbackDownloadInitialDelay; } } else { /* A client with a reasonably live consensus, with or without * certificates */ - return options->TestingClientConsensusDownloadSchedule; + return options->TestingClientConsensusDownloadInitialDelay; } } case DL_SCHED_BRIDGE: if (options->UseBridges && num_bridges_usable(0) > 0) { /* A bridge client that is sure that one or more of its bridges are * running can afford to wait longer to update bridge descriptors. */ - return options->TestingBridgeDownloadSchedule; + return options->TestingBridgeDownloadInitialDelay; } else { /* A bridge client which might have no running bridges, must try to * get bridge descriptors straight away. */ - return options->TestingBridgeBootstrapDownloadSchedule; + return options->TestingBridgeBootstrapDownloadInitialDelay; } default: tor_assert(0); } /* Impossible, but gcc will fail with -Werror without a `return`. */ - return NULL; -} - -/** Decide which minimum delay step we want to use based on - * descriptor type in <b>dls</b> and <b>options</b>. - * Helper function for download_status_schedule_get_delay(). */ -STATIC int -find_dl_min_delay(download_status_t *dls, const or_options_t *options) -{ - tor_assert(dls); - tor_assert(options); - - /* - * For now, just use the existing schedule config stuff and pick the - * first/last entries off to get min/max delay for backoff purposes - */ - const smartlist_t *schedule = find_dl_schedule(dls, options); - tor_assert(schedule != NULL && smartlist_len(schedule) >= 2); - return *(int *)(smartlist_get(schedule, 0)); + return 0; } /** As next_random_exponential_delay() below, but does not compute a random @@ -5634,10 +5578,9 @@ download_status_increment_attempt(download_status_t *dls, const char *item, static time_t download_status_get_initial_delay_from_now(const download_status_t *dls) { - const smartlist_t *schedule = find_dl_schedule(dls, get_options()); /* We use constant initial delays, even in exponential backoff * schedules. */ - return time(NULL) + *(int *)smartlist_get(schedule, 0); + return time(NULL) + find_dl_min_delay(dls, get_options()); } /** Reset <b>dls</b> so that it will be considered downloadable diff --git a/src/or/directory.h b/src/or/directory.h index aa4d29a5bb..5f5ff7eca6 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -259,9 +259,7 @@ STATIC char* authdir_type_to_string(dirinfo_type_t auth); STATIC const char * dir_conn_purpose_to_string(int purpose); STATIC int should_use_directory_guards(const or_options_t *options); STATIC compression_level_t choose_compression_level(ssize_t n_bytes); -STATIC const smartlist_t *find_dl_schedule(const download_status_t *dls, - const or_options_t *options); -STATIC int find_dl_min_delay(download_status_t *dls, +STATIC int find_dl_min_delay(const download_status_t *dls, const or_options_t *options); STATIC int next_random_exponential_delay(int delay, diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 68727f0718..e058e04f8b 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -18,7 +18,6 @@ #include "control.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" #include "hibernate.h" #include "keypin.h" #include "main.h" @@ -33,6 +32,9 @@ #include "routerparse.h" #include "routerset.h" #include "torcert.h" +#include "voting_schedule.h" + +#include "dirauth/dirvote.h" /** * \file dirserv.c @@ -74,7 +76,6 @@ static int routers_with_measured_bw = 0; static void directory_remove_invalid(void); -static char *format_versions_list(config_line_t *ln); struct authdir_config_t; static uint32_t dirserv_get_status_impl(const char *fp, const char *nickname, @@ -87,7 +88,6 @@ static const signed_descriptor_t *get_signed_descriptor_by_fp( int extrainfo); static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei, const char **msg); -static uint32_t dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri); static uint32_t dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri); static int spooled_resource_lookup_body(const spooled_resource_t *spooled, @@ -259,11 +259,12 @@ dirserv_load_fingerprint_file(void) * identity to stop doing so. This is going to be essential for good identity * security: otherwise anybody who can attack RSA-1024 but not Ed25519 could * just sign fake descriptors missing the Ed25519 key. But we won't actually - * be able to prevent that kind of thing until we're confident that there - * isn't actually a legit reason to downgrade to 0.2.5. So for now, we have - * to leave this #undef. + * be able to prevent that kind of thing until we're confident that there isn't + * actually a legit reason to downgrade to 0.2.5. Now we are not recommending + * 0.2.5 anymore so there is no reason to keep the #undef. */ -#undef DISABLE_DISABLING_ED25519 + +#define DISABLE_DISABLING_ED25519 /** Check whether <b>router</b> has a nickname/identity key combination that * we recognize from the fingerprint list, or an IP we automatically act on @@ -921,7 +922,7 @@ list_single_server_status(const routerinfo_t *desc, int is_live) } /* DOCDOC running_long_enough_to_decide_unreachable */ -static inline int +int running_long_enough_to_decide_unreachable(void) { return time_of_process_start @@ -1056,28 +1057,6 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out, return 0; } -/** Given a (possibly empty) list of config_line_t, each line of which contains - * a list of comma-separated version numbers surrounded by optional space, - * allocate and return a new string containing the version numbers, in order, - * separated by commas. Used to generate Recommended(Client|Server)?Versions - */ -static char * -format_versions_list(config_line_t *ln) -{ - smartlist_t *versions; - char *result; - versions = smartlist_new(); - for ( ; ln; ln = ln->next) { - smartlist_split_string(versions, ln->value, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - } - sort_version_list(versions, 1); - result = smartlist_join_strings(versions,",",0,NULL); - SMARTLIST_FOREACH(versions,char *,s,tor_free(s)); - smartlist_free(versions); - return result; -} - /** Return 1 if <b>ri</b>'s descriptor is "active" -- running, valid, * not hibernating, having observed bw greater 0, and not too old. Else * return 0. @@ -1467,6 +1446,24 @@ router_counts_toward_thresholds(const node_t *node, time_t now, (have_mbw || !require_mbw); } +/** Look through the routerlist, and using the measured bandwidth cache count + * how many measured bandwidths we know. This is used to decide whether we + * ever trust advertised bandwidths for purposes of assigning flags. */ +void +dirserv_count_measured_bws(const smartlist_t *routers) +{ + /* Initialize this first */ + routers_with_measured_bw = 0; + + /* Iterate over the routerlist and count measured bandwidths */ + SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) { + /* Check if we know a measured bandwidth for this one */ + if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) { + ++routers_with_measured_bw; + } + } SMARTLIST_FOREACH_END(ri); +} + /** Look through the routerlist, the Mean Time Between Failure history, and * the Weighted Fractional Uptime history, and use them to set thresholds for * the Stable, Fast, and Guard flags. Update the fields stable_uptime, @@ -1474,7 +1471,7 @@ router_counts_toward_thresholds(const node_t *node, time_t now, * guard_bandwidth_including_exits, and guard_bandwidth_excluding_exits. * * Also, set the is_exit flag of each router appropriately. */ -static void +void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil) { int n_active, n_active_nonexit, n_familiar; @@ -1705,7 +1702,7 @@ dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, } /** Clear and free the measured bandwidth cache */ -STATIC void +void dirserv_clear_measured_bw_cache(void) { if (mbw_cache) { @@ -1737,18 +1734,10 @@ dirserv_expire_measured_bw_cache(time_t now) } } -/** Get the current size of the measured bandwidth cache */ -STATIC int -dirserv_get_measured_bw_cache_size(void) -{ - if (mbw_cache) return digestmap_size(mbw_cache); - else return 0; -} - /** Query the cache by identity digest, return value indicates whether * we found it. The bw_out and as_of_out pointers receive the cached * bandwidth value and the time it was cached if not NULL. */ -STATIC int +int dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out, time_t *as_of_out) { @@ -1769,61 +1758,18 @@ dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out, } /** Predicate wrapper for dirserv_query_measured_bw_cache() */ -STATIC int +int dirserv_has_measured_bw(const char *node_id) { return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL); } -/** Get the best estimate of a router's bandwidth for dirauth purposes, - * preferring measured to advertised values if available. */ - -static uint32_t -dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) -{ - uint32_t bw_kb = 0; - /* - * Yeah, measured bandwidths in measured_bw_line_t are (implicitly - * signed) longs and the ones router_get_advertised_bandwidth() returns - * are uint32_t. - */ - long mbw_kb = 0; - - if (ri) { - /* - * * First try to see if we have a measured bandwidth; don't bother with - * as_of_out here, on the theory that a stale measured bandwidth is still - * better to trust than an advertised one. - */ - if (dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest, - &mbw_kb, NULL)) { - /* Got one! */ - bw_kb = (uint32_t)mbw_kb; - } else { - /* If not, fall back to advertised */ - bw_kb = router_get_advertised_bandwidth(ri) / 1000; - } - } - - return bw_kb; -} - -/** Look through the routerlist, and using the measured bandwidth cache count - * how many measured bandwidths we know. This is used to decide whether we - * ever trust advertised bandwidths for purposes of assigning flags. */ -static void -dirserv_count_measured_bws(const smartlist_t *routers) +/** Get the current size of the measured bandwidth cache */ +int +dirserv_get_measured_bw_cache_size(void) { - /* Initialize this first */ - routers_with_measured_bw = 0; - - /* Iterate over the routerlist and count measured bandwidths */ - SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) { - /* Check if we know a measured bandwidth for this one */ - if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) { - ++routers_with_measured_bw; - } - } SMARTLIST_FOREACH_END(ri); + if (mbw_cache) return digestmap_size(mbw_cache); + else return 0; } /** Return the bandwidth we believe for assigning flags; prefer measured @@ -1886,26 +1832,6 @@ dirserv_get_flag_thresholds_line(void) return result; } -/** Given a platform string as in a routerinfo_t (possibly null), return a - * newly allocated version string for a networkstatus document, or NULL if the - * platform doesn't give a Tor version. */ -static char * -version_from_platform(const char *platform) -{ - if (platform && !strcmpstart(platform, "Tor ")) { - const char *eos = find_whitespace(platform+4); - if (eos && !strcmpstart(eos, " (r")) { - /* XXXX Unify this logic with the other version extraction - * logic in routerparse.c. */ - eos = find_whitespace(eos+1); - } - if (eos) { - return tor_strndup(platform, eos-platform); - } - } - return NULL; -} - /** Helper: write the router-status information in <b>rs</b> into a newly * allocated character buffer. Use the same format as in network-status * documents. If <b>version</b> is non-NULL, add a "v" line for the platform. @@ -2094,145 +2020,6 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, return result; } -/** Helper for sorting: compares two routerinfos first by address, and then by - * descending order of "usefulness". (An authority is more useful than a - * non-authority; a running router is more useful than a non-running router; - * and a router with more bandwidth is more useful than one with less.) - **/ -static int -compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) -{ - routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b; - int first_is_auth, second_is_auth; - uint32_t bw_kb_first, bw_kb_second; - const node_t *node_first, *node_second; - int first_is_running, second_is_running; - - /* we return -1 if first should appear before second... that is, - * if first is a better router. */ - if (first->addr < second->addr) - return -1; - else if (first->addr > second->addr) - return 1; - - /* Potentially, this next bit could cause k n lg n memeq calls. But in - * reality, we will almost never get here, since addresses will usually be - * different. */ - - first_is_auth = - router_digest_is_trusted_dir(first->cache_info.identity_digest); - second_is_auth = - router_digest_is_trusted_dir(second->cache_info.identity_digest); - - if (first_is_auth && !second_is_auth) - return -1; - else if (!first_is_auth && second_is_auth) - return 1; - - node_first = node_get_by_id(first->cache_info.identity_digest); - node_second = node_get_by_id(second->cache_info.identity_digest); - first_is_running = node_first && node_first->is_running; - second_is_running = node_second && node_second->is_running; - - if (first_is_running && !second_is_running) - return -1; - else if (!first_is_running && second_is_running) - return 1; - - bw_kb_first = dirserv_get_bandwidth_for_router_kb(first); - bw_kb_second = dirserv_get_bandwidth_for_router_kb(second); - - if (bw_kb_first > bw_kb_second) - return -1; - else if (bw_kb_first < bw_kb_second) - return 1; - - /* They're equal! Compare by identity digest, so there's a - * deterministic order and we avoid flapping. */ - return fast_memcmp(first->cache_info.identity_digest, - second->cache_info.identity_digest, - DIGEST_LEN); -} - -/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t - * whose keys are the identity digests of those routers that we're going to - * exclude for Sybil-like appearance. */ -static digestmap_t * -get_possible_sybil_list(const smartlist_t *routers) -{ - const or_options_t *options = get_options(); - digestmap_t *omit_as_sybil; - smartlist_t *routers_by_ip = smartlist_new(); - uint32_t last_addr; - int addr_count; - /* Allow at most this number of Tor servers on a single IP address, ... */ - int max_with_same_addr = options->AuthDirMaxServersPerAddr; - if (max_with_same_addr <= 0) - max_with_same_addr = INT_MAX; - - smartlist_add_all(routers_by_ip, routers); - smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_); - omit_as_sybil = digestmap_new(); - - last_addr = 0; - addr_count = 0; - SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) { - if (last_addr != ri->addr) { - last_addr = ri->addr; - addr_count = 1; - } else if (++addr_count > max_with_same_addr) { - digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); - } - } SMARTLIST_FOREACH_END(ri); - - smartlist_free(routers_by_ip); - return omit_as_sybil; -} - -/** If there are entries in <b>routers</b> with exactly the same ed25519 keys, - * remove the older one. If they are exactly the same age, remove the one - * with the greater descriptor digest. May alter the order of the list. */ -static void -routers_make_ed_keys_unique(smartlist_t *routers) -{ - routerinfo_t *ri2; - digest256map_t *by_ed_key = digest256map_new(); - - SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { - ri->omit_from_vote = 0; - if (ri->cache_info.signing_key_cert == NULL) - continue; /* No ed key */ - const uint8_t *pk = ri->cache_info.signing_key_cert->signing_key.pubkey; - if ((ri2 = digest256map_get(by_ed_key, pk))) { - /* Duplicate; must omit one. Set the omit_from_vote flag in whichever - * one has the earlier published_on. */ - const time_t ri_pub = ri->cache_info.published_on; - const time_t ri2_pub = ri2->cache_info.published_on; - if (ri2_pub < ri_pub || - (ri2_pub == ri_pub && - fast_memcmp(ri->cache_info.signed_descriptor_digest, - ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) { - digest256map_set(by_ed_key, pk, ri); - ri2->omit_from_vote = 1; - } else { - ri->omit_from_vote = 1; - } - } else { - /* Add to map */ - digest256map_set(by_ed_key, pk, ri); - } - } SMARTLIST_FOREACH_END(ri); - - digest256map_free(by_ed_key, NULL); - - /* Now remove every router where the omit_from_vote flag got set. */ - SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) { - if (ri->omit_from_vote) { - SMARTLIST_DEL_CURRENT(routers, ri); - } - } SMARTLIST_FOREACH_END(ri); -} - /** Extract status information from <b>ri</b> and from other authority * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is * set. @@ -2345,25 +2132,6 @@ dirserv_set_routerstatus_testing(routerstatus_t *rs) } } -/** Routerstatus <b>rs</b> is part of a group of routers that are on - * too narrow an IP-space. Clear out its flags since we don't want it be used - * because of its Sybil-like appearance. - * - * Leave its BadExit flag alone though, since if we think it's a bad exit, - * we want to vote that way in case all the other authorities are voting - * Running and Exit. - */ -static void -clear_status_flags_on_sybil(routerstatus_t *rs) -{ - rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast = - rs->is_flagged_running = rs->is_named = rs->is_valid = - rs->is_hs_dir = rs->is_v2_dir = rs->is_possible_guard = 0; - /* FFFF we might want some mechanism to check later on if we - * missed zeroing any flags: it's easy to add a new flag but - * forget to add it to this clause. */ -} - /** The guardfraction of the guard with identity fingerprint <b>guard_id</b> * is <b>guardfraction_percentage</b>. See if we have a vote routerstatus for * this guard in <b>vote_routerstatuses</b>, and if we do, register the @@ -2801,14 +2569,23 @@ dirserv_read_measured_bandwidths(const char *from_file, time_t file_time, now; int ok; + /* Initialise line, so that we can't possibly run off the end. */ + memset(line, 0, sizeof(line)); + if (fp == NULL) { log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s", from_file); return -1; } - if (!fgets(line, sizeof(line), fp) - || !strlen(line) || line[strlen(line)-1] != '\n') { + /* If fgets fails, line is either unmodified, or indeterminate. */ + if (!fgets(line, sizeof(line), fp)) { + log_warn(LD_DIRSERV, "Empty bandwidth file"); + fclose(fp); + return -1; + } + + if (!strlen(line) || line[strlen(line)-1] != '\n') { log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s", escaped(line)); fclose(fp); @@ -2857,286 +2634,6 @@ dirserv_read_measured_bandwidths(const char *from_file, return 0; } -/** Return a new networkstatus_t* containing our current opinion. (For v3 - * authorities) */ -networkstatus_t * -dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, - authority_cert_t *cert) -{ - const or_options_t *options = get_options(); - networkstatus_t *v3_out = NULL; - uint32_t addr; - char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; - const char *contact; - smartlist_t *routers, *routerstatuses; - char identity_digest[DIGEST_LEN]; - char signing_key_digest[DIGEST_LEN]; - int listbadexits = options->AuthDirListBadExits; - routerlist_t *rl = router_get_routerlist(); - time_t now = time(NULL); - time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; - networkstatus_voter_info_t *voter = NULL; - vote_timing_t timing; - digestmap_t *omit_as_sybil = NULL; - const int vote_on_reachability = running_long_enough_to_decide_unreachable(); - smartlist_t *microdescriptors = NULL; - - tor_assert(private_key); - tor_assert(cert); - - if (crypto_pk_get_digest(private_key, signing_key_digest)<0) { - log_err(LD_BUG, "Error computing signing key digest"); - return NULL; - } - if (crypto_pk_get_digest(cert->identity_key, identity_digest)<0) { - log_err(LD_BUG, "Error computing identity key digest"); - return NULL; - } - if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) { - log_warn(LD_NET, "Couldn't resolve my hostname"); - return NULL; - } - if (!hostname || !strchr(hostname, '.')) { - tor_free(hostname); - hostname = tor_dup_ip(addr); - } - - if (options->VersioningAuthoritativeDir) { - client_versions = format_versions_list(options->RecommendedClientVersions); - server_versions = format_versions_list(options->RecommendedServerVersions); - } - - contact = get_options()->ContactInfo; - if (!contact) - contact = "(none)"; - - /* - * Do this so dirserv_compute_performance_thresholds() and - * set_routerstatus_from_routerinfo() see up-to-date bandwidth info. - */ - if (options->V3BandwidthsFile) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL); - } else { - /* - * No bandwidths file; clear the measured bandwidth cache in case we had - * one last time around. - */ - if (dirserv_get_measured_bw_cache_size() > 0) { - dirserv_clear_measured_bw_cache(); - } - } - - /* precompute this part, since we need it to decide what "stable" - * means. */ - SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, { - dirserv_set_router_is_running(ri, now); - }); - - routers = smartlist_new(); - smartlist_add_all(routers, rl->routers); - routers_make_ed_keys_unique(routers); - /* After this point, don't use rl->routers; use 'routers' instead. */ - routers_sort_by_identity(routers); - omit_as_sybil = get_possible_sybil_list(routers); - - DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) { - (void) ignore; - rep_hist_make_router_pessimal(sybil_id, now); - } DIGESTMAP_FOREACH_END; - - /* Count how many have measured bandwidths so we know how to assign flags; - * this must come before dirserv_compute_performance_thresholds() */ - dirserv_count_measured_bws(routers); - - dirserv_compute_performance_thresholds(omit_as_sybil); - - routerstatuses = smartlist_new(); - microdescriptors = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { - if (ri->cache_info.published_on >= cutoff) { - routerstatus_t *rs; - vote_routerstatus_t *vrs; - node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest); - if (!node) - continue; - - vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); - rs = &vrs->status; - set_routerstatus_from_routerinfo(rs, node, ri, now, - listbadexits); - - if (ri->cache_info.signing_key_cert) { - memcpy(vrs->ed25519_id, - ri->cache_info.signing_key_cert->signing_key.pubkey, - ED25519_PUBKEY_LEN); - } - - if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) - clear_status_flags_on_sybil(rs); - - if (!vote_on_reachability) - rs->is_flagged_running = 0; - - vrs->version = version_from_platform(ri->platform); - if (ri->protocol_list) { - vrs->protocols = tor_strdup(ri->protocol_list); - } else { - vrs->protocols = tor_strdup( - protover_compute_for_old_tor(vrs->version)); - } - vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now, - microdescriptors); - - smartlist_add(routerstatuses, vrs); - } - } SMARTLIST_FOREACH_END(ri); - - { - smartlist_t *added = - microdescs_add_list_to_cache(get_microdesc_cache(), - microdescriptors, SAVED_NOWHERE, 0); - smartlist_free(added); - smartlist_free(microdescriptors); - } - - smartlist_free(routers); - digestmap_free(omit_as_sybil, NULL); - - /* Apply guardfraction information to routerstatuses. */ - if (options->GuardfractionFile) { - dirserv_read_guardfraction_file(options->GuardfractionFile, - routerstatuses); - } - - /* This pass through applies the measured bw lines to the routerstatuses */ - if (options->V3BandwidthsFile) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, - routerstatuses); - } else { - /* - * No bandwidths file; clear the measured bandwidth cache in case we had - * one last time around. - */ - if (dirserv_get_measured_bw_cache_size() > 0) { - dirserv_clear_measured_bw_cache(); - } - } - - v3_out = tor_malloc_zero(sizeof(networkstatus_t)); - - v3_out->type = NS_TYPE_VOTE; - dirvote_get_preferred_voting_intervals(&timing); - v3_out->published = now; - { - char tbuf[ISO_TIME_LEN+1]; - networkstatus_t *current_consensus = - networkstatus_get_live_consensus(now); - long last_consensus_interval; /* only used to pick a valid_after */ - if (current_consensus) - last_consensus_interval = current_consensus->fresh_until - - current_consensus->valid_after; - else - last_consensus_interval = options->TestingV3AuthInitialVotingInterval; - v3_out->valid_after = - dirvote_get_start_of_next_interval(now, (int)last_consensus_interval, - options->TestingV3AuthVotingStartOffset); - format_iso_time(tbuf, v3_out->valid_after); - log_notice(LD_DIR,"Choosing valid-after time in vote as %s: " - "consensus_set=%d, last_interval=%d", - tbuf, current_consensus?1:0, (int)last_consensus_interval); - } - v3_out->fresh_until = v3_out->valid_after + timing.vote_interval; - v3_out->valid_until = v3_out->valid_after + - (timing.vote_interval * timing.n_intervals_valid); - v3_out->vote_seconds = timing.vote_delay; - v3_out->dist_seconds = timing.dist_delay; - tor_assert(v3_out->vote_seconds > 0); - tor_assert(v3_out->dist_seconds > 0); - tor_assert(timing.n_intervals_valid > 0); - - v3_out->client_versions = client_versions; - v3_out->server_versions = server_versions; - - /* These are hardwired, to avoid disaster. */ - v3_out->recommended_relay_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); - v3_out->recommended_client_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); - v3_out->required_client_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); - v3_out->required_relay_protocols = - tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=3-4 LinkAuth=1 Microdesc=1 Relay=1-2"); - - /* We are not allowed to vote to require anything we don't have. */ - tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL)); - tor_assert(protover_all_supported(v3_out->required_client_protocols, NULL)); - - /* We should not recommend anything we don't have. */ - tor_assert_nonfatal(protover_all_supported( - v3_out->recommended_relay_protocols, NULL)); - tor_assert_nonfatal(protover_all_supported( - v3_out->recommended_client_protocols, NULL)); - - v3_out->package_lines = smartlist_new(); - { - config_line_t *cl; - for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) { - if (validate_recommended_package_line(cl->value)) - smartlist_add_strdup(v3_out->package_lines, cl->value); - } - } - - v3_out->known_flags = smartlist_new(); - smartlist_split_string(v3_out->known_flags, - "Authority Exit Fast Guard Stable V2Dir Valid HSDir", - 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - if (vote_on_reachability) - smartlist_add_strdup(v3_out->known_flags, "Running"); - if (listbadexits) - smartlist_add_strdup(v3_out->known_flags, "BadExit"); - smartlist_sort_strings(v3_out->known_flags); - - if (options->ConsensusParams) { - v3_out->net_params = smartlist_new(); - smartlist_split_string(v3_out->net_params, - options->ConsensusParams, NULL, 0, 0); - smartlist_sort_strings(v3_out->net_params); - } - - voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); - voter->nickname = tor_strdup(options->Nickname); - memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); - voter->sigs = smartlist_new(); - voter->address = hostname; - voter->addr = addr; - voter->dir_port = router_get_advertised_dir_port(options, 0); - voter->or_port = router_get_advertised_or_port(options); - voter->contact = tor_strdup(contact); - if (options->V3AuthUseLegacyKey) { - authority_cert_t *c = get_my_v3_legacy_cert(); - if (c) { - if (crypto_pk_get_digest(c->identity_key, voter->legacy_id_digest)) { - log_warn(LD_BUG, "Unable to compute digest of legacy v3 identity key"); - memset(voter->legacy_id_digest, 0, DIGEST_LEN); - } - } - } - - v3_out->voters = smartlist_new(); - smartlist_add(v3_out->voters, voter); - v3_out->cert = authority_cert_dup(cert); - v3_out->routerstatus_list = routerstatuses; - /* Note: networkstatus_digest is unset; it won't get set until we actually - * format the vote. */ - - return v3_out; -} - /** As dirserv_get_routerdescs(), but instead of getting signed_descriptor_t * pointers, adds copies of digests to fps_out, and doesn't use the * /tor/server/ prefix. For a /d/ request, adds descriptor digests; for other diff --git a/src/or/dirserv.h b/src/or/dirserv.h index b9af68ff6e..f0b8913c5c 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -157,6 +157,15 @@ void cached_dir_decref(cached_dir_t *d); cached_dir_t *new_cached_dir(char *s, time_t published); int validate_recommended_package_line(const char *line); +int dirserv_query_measured_bw_cache_kb(const char *node_id, + long *bw_out, + time_t *as_of_out); +void dirserv_clear_measured_bw_cache(void); +int dirserv_has_measured_bw(const char *node_id); +int dirserv_get_measured_bw_cache_size(void); +void dirserv_count_measured_bws(const smartlist_t *routers); +int running_long_enough_to_decide_unreachable(void); +void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil); #ifdef DIRSERV_PRIVATE @@ -172,13 +181,7 @@ STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line, STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, time_t as_of); -STATIC void dirserv_clear_measured_bw_cache(void); STATIC void dirserv_expire_measured_bw_cache(time_t now); -STATIC int dirserv_get_measured_bw_cache_size(void); -STATIC int dirserv_query_measured_bw_cache_kb(const char *node_id, - long *bw_out, - time_t *as_of_out); -STATIC int dirserv_has_measured_bw(const char *node_id); STATIC int dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str, diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 96e6ccaace..2c2bf99925 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -432,14 +432,15 @@ get_guard_confirmed_min_lifetime(void) STATIC int get_n_primary_guards(void) { - const int n = get_options()->NumEntryGuards; - const int n_dir = get_options()->NumDirectoryGuards; - if (n > 5) { - return MAX(n_dir, n + n / 2); - } else if (n >= 1) { - return MAX(n_dir, n * 2); + /* If the user has explicitly configured the number of primary guards, do + * what the user wishes to do */ + const int configured_primaries = get_options()->NumPrimaryGuards; + if (configured_primaries) { + return configured_primaries; } + /* otherwise check for consensus parameter and if that's not set either, just + * use the default value. */ return networkstatus_get_param(NULL, "guard-n-primary-guards", DFLT_N_PRIMARY_GUARDS, 1, INT32_MAX); @@ -454,6 +455,9 @@ get_n_primary_guards_to_use(guard_usage_t usage) int configured; const char *param_name; int param_default; + + /* If the user has explicitly configured the amount of guards, use + that. Otherwise, fall back to the default value. */ if (usage == GUARD_USAGE_DIRGUARD) { configured = get_options()->NumDirectoryGuards; param_name = "guard-n-primary-dir-guards-to-use"; diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index 3a674f6223..4174470636 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -193,11 +193,8 @@ register_intro_circ(const hs_service_intro_point_t *ip, tor_assert(circ); if (ip->base.is_only_legacy) { - uint8_t digest[DIGEST_LEN]; - if (BUG(crypto_pk_get_digest(ip->legacy_key, (char *) digest) < 0)) { - return; - } - hs_circuitmap_register_intro_circ_v2_service_side(circ, digest); + hs_circuitmap_register_intro_circ_v2_service_side(circ, + ip->legacy_key_digest); } else { hs_circuitmap_register_intro_circ_v3_service_side(circ, &ip->auth_key_kp.pubkey); @@ -675,22 +672,14 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip, origin_circuit_t * hs_circ_service_get_intro_circ(const hs_service_intro_point_t *ip) { - origin_circuit_t *circ = NULL; - tor_assert(ip); if (ip->base.is_only_legacy) { - uint8_t digest[DIGEST_LEN]; - if (BUG(crypto_pk_get_digest(ip->legacy_key, (char *) digest) < 0)) { - goto end; - } - circ = hs_circuitmap_get_intro_circ_v2_service_side(digest); + return hs_circuitmap_get_intro_circ_v2_service_side(ip->legacy_key_digest); } else { - circ = hs_circuitmap_get_intro_circ_v3_service_side( + return hs_circuitmap_get_intro_circ_v3_service_side( &ip->auth_key_kp.pubkey); } - end: - return circ; } /* Called when we fail building a rendezvous circuit at some point other than diff --git a/src/or/hs_common.c b/src/or/hs_common.c index 24eb7a104a..5edddd8940 100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@ -28,8 +28,8 @@ #include "rendservice.h" #include "routerset.h" #include "router.h" -#include "shared_random.h" -#include "shared_random_state.h" +#include "shared_random_client.h" +#include "dirauth/shared_random_state.h" /* Trunnel */ #include "ed25519_cert.h" @@ -103,7 +103,7 @@ compare_digest_to_fetch_hsdir_index(const void *_key, const void **_member) { const char *key = _key; const node_t *node = *_member; - return tor_memcmp(key, node->hsdir_index->fetch, DIGEST256_LEN); + return tor_memcmp(key, node->hsdir_index.fetch, DIGEST256_LEN); } /* Helper function: The key is a digest that we compare to a node_t object @@ -114,7 +114,7 @@ compare_digest_to_store_first_hsdir_index(const void *_key, { const char *key = _key; const node_t *node = *_member; - return tor_memcmp(key, node->hsdir_index->store_first, DIGEST256_LEN); + return tor_memcmp(key, node->hsdir_index.store_first, DIGEST256_LEN); } /* Helper function: The key is a digest that we compare to a node_t object @@ -125,7 +125,7 @@ compare_digest_to_store_second_hsdir_index(const void *_key, { const char *key = _key; const node_t *node = *_member; - return tor_memcmp(key, node->hsdir_index->store_second, DIGEST256_LEN); + return tor_memcmp(key, node->hsdir_index.store_second, DIGEST256_LEN); } /* Helper function: Compare two node_t objects current hsdir_index. */ @@ -134,8 +134,8 @@ compare_node_fetch_hsdir_index(const void **a, const void **b) { const node_t *node1= *a; const node_t *node2 = *b; - return tor_memcmp(node1->hsdir_index->fetch, - node2->hsdir_index->fetch, + return tor_memcmp(node1->hsdir_index.fetch, + node2->hsdir_index.fetch, DIGEST256_LEN); } @@ -145,8 +145,8 @@ compare_node_store_first_hsdir_index(const void **a, const void **b) { const node_t *node1= *a; const node_t *node2 = *b; - return tor_memcmp(node1->hsdir_index->store_first, - node2->hsdir_index->store_first, + return tor_memcmp(node1->hsdir_index.store_first, + node2->hsdir_index.store_first, DIGEST256_LEN); } @@ -156,8 +156,8 @@ compare_node_store_second_hsdir_index(const void **a, const void **b) { const node_t *node1= *a; const node_t *node2 = *b; - return tor_memcmp(node1->hsdir_index->store_second, - node2->hsdir_index->store_second, + return tor_memcmp(node1->hsdir_index.store_second, + node2->hsdir_index.store_second, DIGEST256_LEN); } @@ -1288,18 +1288,15 @@ node_has_hsdir_index(const node_t *node) /* At this point, since the node has a desc, this node must also have an * hsdir index. If not, something went wrong, so BUG out. */ - if (BUG(node->hsdir_index == NULL)) { - return 0; - } - if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->fetch, + if (BUG(tor_mem_is_zero((const char*)node->hsdir_index.fetch, DIGEST256_LEN))) { return 0; } - if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->store_first, + if (BUG(tor_mem_is_zero((const char*)node->hsdir_index.store_first, DIGEST256_LEN))) { return 0; } - if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->store_second, + if (BUG(tor_mem_is_zero((const char*)node->hsdir_index.store_second, DIGEST256_LEN))) { return 0; } diff --git a/src/or/hs_common.h b/src/or/hs_common.h index 83ba1b8599..ef7d5dca2b 100644 --- a/src/or/hs_common.h +++ b/src/or/hs_common.h @@ -156,19 +156,6 @@ typedef struct rend_service_port_config_t { char unix_addr[FLEXIBLE_ARRAY_MEMBER]; } rend_service_port_config_t; -/* Hidden service directory index used in a node_t which is set once we set - * the consensus. */ -typedef struct hsdir_index_t { - /* HSDir index to use when fetching a descriptor. */ - uint8_t fetch[DIGEST256_LEN]; - - /* HSDir index used by services to store their first and second - * descriptor. The first descriptor is chronologically older than the second - * one and uses older TP and SRV values. */ - uint8_t store_first[DIGEST256_LEN]; - uint8_t store_second[DIGEST256_LEN]; -} hsdir_index_t; - void hs_init(void); void hs_free_all(void); diff --git a/src/or/hs_control.c b/src/or/hs_control.c index 87b4e3fca8..eca9ed1dd5 100644 --- a/src/or/hs_control.c +++ b/src/or/hs_control.c @@ -39,9 +39,8 @@ hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk, * can't pick a node without an hsdir_index. */ hsdir_node = node_get_by_id(hsdir_rs->identity_digest); tor_assert(hsdir_node); - tor_assert(hsdir_node->hsdir_index); /* This is a fetch event. */ - hsdir_index = hsdir_node->hsdir_index->fetch; + hsdir_index = hsdir_node->hsdir_index.fetch; /* Trigger the event. */ control_event_hs_descriptor_requested(onion_address, REND_NO_AUTH, diff --git a/src/or/hs_service.c b/src/or/hs_service.c index f6c7e3cd81..cf2760760a 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -24,7 +24,7 @@ #include "router.h" #include "routerkeys.h" #include "routerlist.h" -#include "shared_random_state.h" +#include "shared_random_client.h" #include "statefile.h" #include "hs_circuit.h" @@ -80,6 +80,7 @@ static smartlist_t *hs_service_staging_list; * reupload if needed */ static int consider_republishing_hs_descriptors = 0; +/* Static declaration. */ static void set_descriptor_revision_counter(hs_descriptor_t *hs_desc); static void move_descriptors(hs_service_t *src, hs_service_t *dst); @@ -152,6 +153,12 @@ register_service(hs_service_ht *map, hs_service_t *service) } /* Taking ownership of the object at this point. */ HT_INSERT(hs_service_ht, map, service); + + /* If we just modified the global map, we notify. */ + if (map == hs_service_map) { + hs_service_map_has_changed(); + } + return 0; } @@ -178,6 +185,11 @@ remove_service(hs_service_ht *map, hs_service_t *service) "while removing service %s", escaped(service->config.directory_path)); } + + /* If we just modified the global map, we notify. */ + if (map == hs_service_map) { + hs_service_map_has_changed(); + } } /* Set the default values for a service configuration object <b>c</b>. */ @@ -429,6 +441,10 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy) if (crypto_pk_generate_key(ip->legacy_key) < 0) { goto err; } + if (crypto_pk_get_digest(ip->legacy_key, + (char *) ip->legacy_key_digest) < 0) { + goto err; + } } if (ei == NULL) { @@ -916,6 +932,11 @@ register_all_services(void) smartlist_clear(hs_service_staging_list); service_free_all(); hs_service_map = new_service_map; + /* We've just register services into the new map and now we've replaced the + * global map with it so we have to notify that the change happened. When + * registering a service, the notify is only triggered if the destination + * map is the global map for which in here it was not. */ + hs_service_map_has_changed(); } /* Write the onion address of a given service to the given filename fname_ in @@ -2287,8 +2308,8 @@ upload_descriptor_to_hsdir(const hs_service_t *service, /* Logging so we know where it was sent. */ { int is_next_desc = (service->desc_next == desc); - const uint8_t *idx = (is_next_desc) ? hsdir->hsdir_index->store_second: - hsdir->hsdir_index->store_first; + const uint8_t *idx = (is_next_desc) ? hsdir->hsdir_index.store_second: + hsdir->hsdir_index.store_first; log_info(LD_REND, "Service %s %s descriptor of revision %" PRIu64 " initiated upload request to %s with index %s", safe_str_client(service->onion_address), @@ -2936,6 +2957,17 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list) /* Public API */ /* ========== */ +/* This is called everytime the service map (v2 or v3) changes that is if an + * element is added or removed. */ +void +hs_service_map_has_changed(void) +{ + /* If we now have services where previously we had not, we need to enable + * the HS service main loop event. If we changed to having no services, we + * need to disable the event. */ + rescan_periodic_events(get_options()); +} + /* Upload an encoded descriptor in encoded_desc of the given version. This * descriptor is for the service identity_pk and blinded_pk used to setup the * directory connection identifier. It is uploaded to the directory hsdir_rs diff --git a/src/or/hs_service.h b/src/or/hs_service.h index d163eeef28..5494b6f5fa 100644 --- a/src/or/hs_service.h +++ b/src/or/hs_service.h @@ -50,6 +50,9 @@ typedef struct hs_service_intro_point_t { /* Legacy key if that intro point doesn't support v3. This should be used if * the base object legacy flag is set. */ crypto_pk_t *legacy_key; + /* Legacy key SHA1 public key digest. This should be used only if the base + * object legacy flag is set. */ + uint8_t legacy_key_digest[DIGEST_LEN]; /* Amount of INTRODUCE2 cell accepted from this intro point. */ uint64_t introduce2_count; @@ -260,6 +263,7 @@ void hs_service_lists_fnames_for_sandbox(smartlist_t *file_list, int hs_service_set_conn_addr_port(const origin_circuit_t *circ, edge_connection_t *conn); +void hs_service_map_has_changed(void); void hs_service_dir_info_changed(void); void hs_service_run_scheduled_events(time_t now); void hs_service_circuit_has_opened(origin_circuit_t *circ); diff --git a/src/or/include.am b/src/or/include.am index 9a68df5c3e..9cae7d0039 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -41,10 +41,8 @@ LIBTOR_A_SOURCES = \ src/or/consdiffmgr.c \ src/or/control.c \ src/or/cpuworker.c \ - src/or/dircollate.c \ src/or/directory.c \ src/or/dirserv.c \ - src/or/dirvote.c \ src/or/dns.c \ src/or/dnsserv.c \ src/or/dos.c \ @@ -76,8 +74,6 @@ LIBTOR_A_SOURCES = \ src/or/onion.c \ src/or/onion_fast.c \ src/or/onion_tap.c \ - src/or/shared_random.c \ - src/or/shared_random_state.c \ src/or/transports.c \ src/or/parsecommon.c \ src/or/periodic.c \ @@ -107,15 +103,34 @@ LIBTOR_A_SOURCES = \ src/or/scheduler.c \ src/or/scheduler_kist.c \ src/or/scheduler_vanilla.c \ + src/or/shared_random_client.c \ src/or/statefile.c \ src/or/status.c \ src/or/torcert.c \ src/or/tor_api.c \ + src/or/voting_schedule.c \ src/or/onion_ntor.c \ $(tor_platform_source) +# +# Modules are conditionnally compiled in tor starting here. We add the C files +# only if the modules has been enabled at configure time. We always add the +# source files of every module to libtor-testing.a so we can build the unit +# tests for everything. +# + +# The Directory Authority module. +MODULE_DIRAUTH_SOURCES = \ + src/or/dirauth/dircollate.c \ + src/or/dirauth/dirvote.c \ + src/or/dirauth/shared_random.c \ + src/or/dirauth/shared_random_state.c +if BUILD_MODULE_DIRAUTH +LIBTOR_A_SOURCES += $(MODULE_DIRAUTH_SOURCES) +endif + src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES) -src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES) +src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES) $(MODULE_DIRAUTH_SOURCES) src_or_tor_SOURCES = src/or/tor_main.c AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or @@ -185,10 +200,8 @@ ORHEADERS = \ src/or/consdiffmgr.h \ src/or/control.h \ src/or/cpuworker.h \ - src/or/dircollate.h \ src/or/directory.h \ src/or/dirserv.h \ - src/or/dirvote.h \ src/or/dns.h \ src/or/dns_structs.h \ src/or/dnsserv.h \ @@ -225,8 +238,6 @@ ORHEADERS = \ src/or/onion_ntor.h \ src/or/onion_tap.h \ src/or/or.h \ - src/or/shared_random.h \ - src/or/shared_random_state.h \ src/or/transports.h \ src/or/parsecommon.h \ src/or/periodic.h \ @@ -254,10 +265,22 @@ ORHEADERS = \ src/or/routerset.h \ src/or/routerparse.h \ src/or/scheduler.h \ + src/or/shared_random_client.h \ src/or/statefile.h \ src/or/status.h \ src/or/torcert.h \ - src/or/tor_api_internal.h + src/or/tor_api_internal.h \ + src/or/voting_schedule.h + +# We add the headers of the modules even though they are disabled so we can +# properly compiled the entry points stub. + +# The Directory Authority module headers. +ORHEADERS += \ + src/or/dirauth/dircollate.h \ + src/or/dirauth/dirvote.h \ + src/or/dirauth/shared_random.h \ + src/or/dirauth/shared_random_state.h # This may someday want to be an installed file? noinst_HEADERS += src/or/tor_api.h diff --git a/src/or/main.c b/src/or/main.c index 47275c84f5..b492166f87 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -59,6 +59,7 @@ #include "circuitbuild.h" #include "circuitlist.h" #include "circuituse.h" +#include "circuitmux_ewma.h" #include "command.h" #include "compress.h" #include "config.h" @@ -72,7 +73,6 @@ #include "crypto_s2k.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" #include "dns.h" #include "dnsserv.h" #include "dos.h" @@ -103,7 +103,7 @@ #include "routerlist.h" #include "routerparse.h" #include "scheduler.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #include "statefile.h" #include "status.h" #include "tor_api.h" @@ -118,6 +118,8 @@ #include <event2/event.h> +#include "dirauth/dirvote.h" + #ifdef HAVE_SYSTEMD # if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) /* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse @@ -1352,6 +1354,7 @@ CALLBACK(check_for_reachability_bw); CALLBACK(check_onion_keys_expiry_time); CALLBACK(clean_caches); CALLBACK(clean_consdiffmgr); +CALLBACK(dirvote); CALLBACK(downrate_stability); CALLBACK(expire_old_ciruits_serverside); CALLBACK(fetch_networkstatus); @@ -1367,6 +1370,7 @@ CALLBACK(retry_listeners); CALLBACK(rotate_onion_key); CALLBACK(rotate_x509_certificate); CALLBACK(save_stability); +CALLBACK(save_state); CALLBACK(write_bridge_ns); CALLBACK(write_stats_file); @@ -1388,6 +1392,7 @@ STATIC periodic_event_item_t periodic_events[] = { CALLBACK(reset_padding_counts, PERIODIC_EVENT_ROLE_ALL, 0), CALLBACK(retry_listeners, PERIODIC_EVENT_ROLE_ALL, PERIODIC_EVENT_FLAG_NEED_NET), + CALLBACK(save_state, PERIODIC_EVENT_ROLE_ALL, 0), CALLBACK(rotate_x509_certificate, PERIODIC_EVENT_ROLE_ALL, 0), CALLBACK(write_stats_file, PERIODIC_EVENT_ROLE_ALL, 0), @@ -1398,7 +1403,6 @@ STATIC periodic_event_item_t periodic_events[] = { CALLBACK(check_for_reachability_bw, PERIODIC_EVENT_ROLE_ROUTER, PERIODIC_EVENT_FLAG_NEED_NET), CALLBACK(check_onion_keys_expiry_time, PERIODIC_EVENT_ROLE_ROUTER, 0), - CALLBACK(clean_consdiffmgr, PERIODIC_EVENT_ROLE_ROUTER, 0), CALLBACK(expire_old_ciruits_serverside, PERIODIC_EVENT_ROLE_ROUTER, PERIODIC_EVENT_FLAG_NEED_NET), CALLBACK(retry_dns, PERIODIC_EVENT_ROLE_ROUTER, 0), @@ -1412,6 +1416,7 @@ STATIC periodic_event_item_t periodic_events[] = { /* Directory authority only. */ CALLBACK(check_authority_cert, PERIODIC_EVENT_ROLE_DIRAUTH, 0), + CALLBACK(dirvote, PERIODIC_EVENT_ROLE_DIRAUTH, PERIODIC_EVENT_FLAG_NEED_NET), /* Relay only. */ CALLBACK(check_canonical_channels, PERIODIC_EVENT_ROLE_RELAY, @@ -1432,6 +1437,9 @@ STATIC periodic_event_item_t periodic_events[] = { /* Bridge Authority only. */ CALLBACK(write_bridge_ns, PERIODIC_EVENT_ROLE_BRIDGEAUTH, 0), + /* Directory server only. */ + CALLBACK(clean_consdiffmgr, PERIODIC_EVENT_ROLE_DIRSERVER, 0), + END_OF_PERIODIC_EVENTS }; #undef CALLBACK @@ -1441,9 +1449,11 @@ STATIC periodic_event_item_t periodic_events[] = { * can access them by name. We also keep them inside periodic_events[] * so that we can implement "reset all timers" in a reasonable way. */ static periodic_event_item_t *check_descriptor_event=NULL; +static periodic_event_item_t *dirvote_event=NULL; static periodic_event_item_t *fetch_networkstatus_event=NULL; static periodic_event_item_t *launch_descriptor_fetches_event=NULL; static periodic_event_item_t *check_dns_honesty_event=NULL; +static periodic_event_item_t *save_state_event=NULL; /** Reset all the periodic events so we'll do all our actions again as if we * just started up. @@ -1488,6 +1498,7 @@ get_my_roles(const or_options_t *options) int is_bridgeauth = authdir_mode_bridge(options); int is_hidden_service = !!hs_service_get_num_services() || !!rend_num_services(); + int is_dirserver = dir_server_mode(options); if (is_bridge) roles |= PERIODIC_EVENT_ROLE_BRIDGE; if (is_client) roles |= PERIODIC_EVENT_ROLE_CLIENT; @@ -1495,6 +1506,7 @@ get_my_roles(const or_options_t *options) if (is_dirauth) roles |= PERIODIC_EVENT_ROLE_DIRAUTH; if (is_bridgeauth) roles |= PERIODIC_EVENT_ROLE_BRIDGEAUTH; if (is_hidden_service) roles |= PERIODIC_EVENT_ROLE_HS_SERVICE; + if (is_dirserver) roles |= PERIODIC_EVENT_ROLE_DIRSERVER; return roles; } @@ -1537,9 +1549,11 @@ initialize_periodic_events(void) STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END NAMED_CALLBACK(check_descriptor); + NAMED_CALLBACK(dirvote); NAMED_CALLBACK(fetch_networkstatus); NAMED_CALLBACK(launch_descriptor_fetches); NAMED_CALLBACK(check_dns_honesty); + NAMED_CALLBACK(save_state); struct timeval one_second = { 1, 0 }; initialize_periodic_events_event = tor_evtimer_new( @@ -1565,6 +1579,13 @@ rescan_periodic_events(const or_options_t *options) { tor_assert(options); + /* Avoid scanning the event list if we haven't initialized it yet. This is + * particularly useful for unit tests in order to avoid initializing main + * loop events everytime. */ + if (!periodic_events_initialized) { + return; + } + int roles = get_my_roles(options); for (int i = 0; periodic_events[i].name; ++i) { @@ -1610,8 +1631,9 @@ periodic_events_on_new_options(const or_options_t *options) void reschedule_descriptor_update_check(void) { - tor_assert(check_descriptor_event); - periodic_event_reschedule(check_descriptor_event); + if (check_descriptor_event) { + periodic_event_reschedule(check_descriptor_event); + } } /** @@ -1705,10 +1727,6 @@ run_scheduled_events(time_t now) accounting_run_housekeeping(now); } - if (authdir_mode_v3(options)) { - dirvote_act(options, now); - } - /* 3a. Every second, we examine pending circuits and prune the * ones which have been pending for more than a few seconds. * We do this before step 4, so it can try building more if @@ -1749,10 +1767,6 @@ run_scheduled_events(time_t now) run_connection_housekeeping(i, now); } - /* 8b. And if anything in our state is ready to get flushed to disk, we - * flush it. */ - or_state_save(now); - /* 11b. check pending unconfigured managed proxies */ if (!net_is_disabled() && pt_proxies_configuration_pending()) pt_configure_remaining_proxies(); @@ -1965,6 +1979,40 @@ check_authority_cert_callback(time_t now, const or_options_t *options) } /** + * Scheduled callback: Run directory-authority voting functionality. + * + * The schedule is a bit complicated here, so dirvote_act() manages the + * schedule itself. + **/ +static int +dirvote_callback(time_t now, const or_options_t *options) +{ + if (!authdir_mode_v3(options)) { + tor_assert_nonfatal_unreached(); + return 3600; + } + + time_t next = dirvote_act(options, now); + if (BUG(next == TIME_MAX)) { + /* This shouldn't be returned unless we called dirvote_act() without + * being an authority. If it happens, maybe our configuration will + * fix itself in an hour or so? */ + return 3600; + } + return safe_timer_diff(now, next); +} + +/** Reschedule the directory-authority voting event. Run this whenever the + * schedule has changed. */ +void +reschedule_dirvote(const or_options_t *options) +{ + if (periodic_events_initialized && authdir_mode_v3(options)) { + periodic_event_reschedule(dirvote_event); + } +} + +/** * Periodic callback: If our consensus is too old, recalculate whether * we can actually use it. */ @@ -1987,6 +2035,34 @@ check_expired_networkstatus_callback(time_t now, const or_options_t *options) } /** + * Scheduled callback: Save the state file to disk if appropriate. + */ +static int +save_state_callback(time_t now, const or_options_t *options) +{ + (void) options; + (void) or_state_save(now); // only saves if appropriate + const time_t next_write = get_or_state()->next_write; + if (next_write == TIME_MAX) { + return 86400; + } + return safe_timer_diff(now, next_write); +} + +/** Reschedule the event for saving the state file. + * + * Run this when the state becomes dirty. */ +void +reschedule_or_state_save(void) +{ + if (save_state_event == NULL) { + /* This can happen early on during startup. */ + return; + } + periodic_event_reschedule(save_state_event); +} + +/** * Periodic callback: Write statistics to disk if appropriate. */ static int @@ -2338,7 +2414,7 @@ static int clean_consdiffmgr_callback(time_t now, const or_options_t *options) { (void)now; - if (server_mode(options)) { + if (dir_server_mode(options)) { consdiffmgr_cleanup(); } return CDM_CLEAN_CALLBACK_INTERVAL; @@ -3529,6 +3605,7 @@ tor_free_all(int postfork) consdiffmgr_free_all(); hs_free_all(); dos_free_all(); + circuitmux_ewma_free_all(); if (!postfork) { config_free_all(); or_state_free_all(); diff --git a/src/or/main.h b/src/or/main.h index 2447339fb5..a312b51e05 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -61,6 +61,8 @@ void dns_servers_relaunch_checks(void); void reset_all_main_loop_timers(void); void reschedule_descriptor_update_check(void); void reschedule_directory_downloads(void); +void reschedule_or_state_save(void); +void reschedule_dirvote(const or_options_t *options); void mainloop_schedule_postloop_cleanup(void); void rescan_periodic_events(const or_options_t *options); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index b0db0cecbc..ac3e94e884 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -50,7 +50,6 @@ #include "control.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" #include "dos.h" #include "entrynodes.h" #include "hibernate.h" @@ -64,10 +63,13 @@ #include "routerlist.h" #include "routerparse.h" #include "scheduler.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #include "transports.h" #include "torcert.h" #include "channelpadding.h" +#include "voting_schedule.h" + +#include "dirauth/dirvote.h" /** Most recently received and validated v3 "ns"-flavored consensus network * status. */ @@ -365,9 +367,7 @@ networkstatus_vote_free_(networkstatus_t *ns) digestmap_free(ns->desc_digest_map, NULL); if (ns->sr_info.commits) { - SMARTLIST_FOREACH(ns->sr_info.commits, sr_commit_t *, c, - sr_commit_free(c)); - smartlist_free(ns->sr_info.commits); + dirvote_clear_commits(ns); } tor_free(ns->sr_info.previous_srv); tor_free(ns->sr_info.current_srv); @@ -391,6 +391,20 @@ networkstatus_get_voter_by_id(networkstatus_t *vote, return NULL; } +/** Return the signature made by <b>voter</b> using the algorithm + * <b>alg</b>, or NULL if none is found. */ +document_signature_t * +networkstatus_get_voter_sig_by_alg(const networkstatus_voter_info_t *voter, + digest_algorithm_t alg) +{ + if (!voter->sigs) + return NULL; + SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig, + if (sig->alg == alg) + return sig); + return NULL; +} + /** Check whether the signature <b>sig</b> is correctly signed with the * signing key in <b>cert</b>. Return -1 if <b>cert</b> doesn't match the * signing key; otherwise set the good_signature or bad_signature flag on @@ -2001,7 +2015,8 @@ networkstatus_set_current_consensus(const char *consensus, * the first thing we need to do is recalculate the voting schedule static * object so we can use the timings in there needed by some subsystems * such as hidden service and shared random. */ - dirvote_recalculate_timing(options, now); + voting_schedule_recalculate_timing(options, now); + reschedule_dirvote(options); nodelist_set_consensus(c); @@ -2641,6 +2656,25 @@ networkstatus_check_required_protocols(const networkstatus_t *ns, return 0; } +/** Release all storage held in <b>s</b>. */ +void +ns_detached_signatures_free_(ns_detached_signatures_t *s) +{ + if (!s) + return; + if (s->signatures) { + STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) { + SMARTLIST_FOREACH(sigs, document_signature_t *, sig, + document_signature_free(sig)); + smartlist_free(sigs); + } STRMAP_FOREACH_END; + strmap_free(s->signatures, NULL); + strmap_free(s->digests, tor_free_); + } + + tor_free(s); +} + /** Free all storage held locally in this module. */ void networkstatus_free_all(void) diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 04cf277d2f..0c325959d7 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -24,9 +24,16 @@ void routerstatus_free_(routerstatus_t *rs); void networkstatus_vote_free_(networkstatus_t *ns); #define networkstatus_vote_free(ns) \ FREE_AND_NULL(networkstatus_t, networkstatus_vote_free_, (ns)) +void ns_detached_signatures_free_(ns_detached_signatures_t *s); +#define ns_detached_signatures_free(s) \ + FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s)) networkstatus_voter_info_t *networkstatus_get_voter_by_id( networkstatus_t *vote, const char *identity); +document_signature_t *networkstatus_get_voter_sig_by_alg( + const networkstatus_voter_info_t *voter, + digest_algorithm_t alg); + int networkstatus_check_consensus_signature(networkstatus_t *consensus, int warn); int networkstatus_check_document_signature(const networkstatus_t *consensus, diff --git a/src/or/nodelist.c b/src/or/nodelist.c index e7342f9799..675cbb0056 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -225,7 +225,6 @@ node_get_or_create(const char *identity_digest) smartlist_add(the_nodelist->nodes, node); node->nodelist_idx = smartlist_len(the_nodelist->nodes) - 1; - node->hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t)); node->country = -1; @@ -350,26 +349,26 @@ node_set_hsdir_index(node_t *node, const networkstatus_t *ns) /* Build the fetch index. */ hs_build_hsdir_index(node_identity_pk, fetch_srv, fetch_tp, - node->hsdir_index->fetch); + node->hsdir_index.fetch); /* If we are in the time segment between SRV#N and TP#N, the fetch index is the same as the first store index */ if (!hs_in_period_between_tp_and_srv(ns, now)) { - memcpy(node->hsdir_index->store_first, node->hsdir_index->fetch, - sizeof(node->hsdir_index->store_first)); + memcpy(node->hsdir_index.store_first, node->hsdir_index.fetch, + sizeof(node->hsdir_index.store_first)); } else { hs_build_hsdir_index(node_identity_pk, store_first_srv, store_first_tp, - node->hsdir_index->store_first); + node->hsdir_index.store_first); } /* If we are in the time segment between TP#N and SRV#N+1, the fetch index is the same as the second store index */ if (hs_in_period_between_tp_and_srv(ns, now)) { - memcpy(node->hsdir_index->store_second, node->hsdir_index->fetch, - sizeof(node->hsdir_index->store_second)); + memcpy(node->hsdir_index.store_second, node->hsdir_index.fetch, + sizeof(node->hsdir_index.store_second)); } else { hs_build_hsdir_index(node_identity_pk, store_second_srv, store_second_tp, - node->hsdir_index->store_second); + node->hsdir_index.store_second); } done: @@ -720,7 +719,6 @@ node_free_(node_t *node) if (node->md) node->md->held_by_nodes--; tor_assert(node->nodelist_idx == -1); - tor_free(node->hsdir_index); tor_free(node); } diff --git a/src/or/or.h b/src/or/or.h index e27f25197b..cd77b21056 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -57,7 +57,6 @@ #ifdef HAVE_TIME_H #include <time.h> #endif -#include <stdbool.h> #ifdef _WIN32 #include <winsock2.h> @@ -895,8 +894,19 @@ rend_data_v2_t *TO_REND_DATA_V2(const rend_data_t *d) struct hs_ident_edge_conn_t; struct hs_ident_dir_conn_t; struct hs_ident_circuit_t; -/* Stub because we can't include hs_common.h. */ -struct hsdir_index_t; + +/* Hidden service directory index used in a node_t which is set once we set + * the consensus. */ +typedef struct hsdir_index_t { + /* HSDir index to use when fetching a descriptor. */ + uint8_t fetch[DIGEST256_LEN]; + + /* HSDir index used by services to store their first and second + * descriptor. The first descriptor is chronologically older than the second + * one and uses older TP and SRV values. */ + uint8_t store_first[DIGEST256_LEN]; + uint8_t store_second[DIGEST256_LEN]; +} hsdir_index_t; /** Time interval for tracking replays of DH public keys received in * INTRODUCE2 cells. Used only to avoid launching multiple @@ -2562,7 +2572,7 @@ typedef struct node_t { /* Hidden service directory index data. This is used by a service or client * in order to know what's the hs directory index for this node at the time * the consensus is set. */ - struct hsdir_index_t *hsdir_index; + struct hsdir_index_t hsdir_index; } node_t; /** Linked list of microdesc hash lines for a single router in a directory @@ -4149,6 +4159,8 @@ typedef struct { int NumDirectoryGuards; /**< How many dir guards do we try to establish? * If 0, use value from NumEntryGuards. */ + int NumPrimaryGuards; /**< How many primary guards do we want? */ + int RephistTrackTime; /**< How many seconds do we keep rephist info? */ /** Should we always fetch our dir info on the mirror schedule (which * means directly from the authorities) no matter our other config? */ @@ -4317,19 +4329,19 @@ typedef struct { /** Schedule for when servers should download things in general. Only * altered on testing networks. */ - smartlist_t *TestingServerDownloadSchedule; + int TestingServerDownloadInitialDelay; /** Schedule for when clients should download things in general. Only * altered on testing networks. */ - smartlist_t *TestingClientDownloadSchedule; + int TestingClientDownloadInitialDelay; /** Schedule for when servers should download consensuses. Only altered * on testing networks. */ - smartlist_t *TestingServerConsensusDownloadSchedule; + int TestingServerConsensusDownloadInitialDelay; /** Schedule for when clients should download consensuses. Only altered * on testing networks. */ - smartlist_t *TestingClientConsensusDownloadSchedule; + int TestingClientConsensusDownloadInitialDelay; /** Schedule for when clients should download consensuses from authorities * if they are bootstrapping (that is, they don't have a usable, reasonably @@ -4339,7 +4351,7 @@ typedef struct { * This schedule is incremented by (potentially concurrent) connection * attempts, unlike other schedules, which are incremented by connection * failures. Only altered on testing networks. */ - smartlist_t *ClientBootstrapConsensusAuthorityDownloadSchedule; + int ClientBootstrapConsensusAuthorityDownloadInitialDelay; /** Schedule for when clients should download consensuses from fallback * directory mirrors if they are bootstrapping (that is, they don't have a @@ -4349,7 +4361,7 @@ typedef struct { * This schedule is incremented by (potentially concurrent) connection * attempts, unlike other schedules, which are incremented by connection * failures. Only altered on testing networks. */ - smartlist_t *ClientBootstrapConsensusFallbackDownloadSchedule; + int ClientBootstrapConsensusFallbackDownloadInitialDelay; /** Schedule for when clients should download consensuses from authorities * if they are bootstrapping (that is, they don't have a usable, reasonably @@ -4359,15 +4371,15 @@ typedef struct { * This schedule is incremented by (potentially concurrent) connection * attempts, unlike other schedules, which are incremented by connection * failures. Only altered on testing networks. */ - smartlist_t *ClientBootstrapConsensusAuthorityOnlyDownloadSchedule; + int ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay; /** Schedule for when clients should download bridge descriptors. Only * altered on testing networks. */ - smartlist_t *TestingBridgeDownloadSchedule; + int TestingBridgeDownloadInitialDelay; /** Schedule for when clients should download bridge descriptors when they * have no running bridges. Only altered on testing networks. */ - smartlist_t *TestingBridgeBootstrapDownloadSchedule; + int TestingBridgeBootstrapDownloadInitialDelay; /** When directory clients have only a few descriptors to request, they * batch them until they have more, or until this amount of time has @@ -4745,15 +4757,6 @@ typedef struct { time_t LastRotatedOnionKey; } or_state_t; -/** Change the next_write time of <b>state</b> to <b>when</b>, unless the - * state is already scheduled to be written to disk earlier than <b>when</b>. - */ -static inline void or_state_mark_dirty(or_state_t *state, time_t when) -{ - if (state->next_write > when) - state->next_write = when; -} - #define MAX_SOCKS_REPLY_LEN 1024 #define MAX_SOCKS_ADDR_LEN 256 #define SOCKS_NO_AUTH 0x00 diff --git a/src/or/parsecommon.c b/src/or/parsecommon.c index 6c3dd3100e..9bd00e17ce 100644 --- a/src/or/parsecommon.c +++ b/src/or/parsecommon.c @@ -426,7 +426,7 @@ find_by_keyword_(smartlist_t *s, directory_keyword keyword, * NULL if no such keyword is found. */ directory_token_t * -find_opt_by_keyword(smartlist_t *s, directory_keyword keyword) +find_opt_by_keyword(const smartlist_t *s, directory_keyword keyword) { SMARTLIST_FOREACH(s, directory_token_t *, t, if (t->tp == keyword) return t); return NULL; diff --git a/src/or/parsecommon.h b/src/or/parsecommon.h index 903d94478b..d33faf8ec7 100644 --- a/src/or/parsecommon.h +++ b/src/or/parsecommon.h @@ -314,7 +314,7 @@ directory_token_t *find_by_keyword_(smartlist_t *s, #define find_by_keyword(s, keyword) \ find_by_keyword_((s), (keyword), #keyword) -directory_token_t *find_opt_by_keyword(smartlist_t *s, +directory_token_t *find_opt_by_keyword(const smartlist_t *s, directory_keyword keyword); smartlist_t * find_all_by_keyword(const smartlist_t *s, directory_keyword k); diff --git a/src/or/periodic.h b/src/or/periodic.h index a044b34fdd..e8208b2475 100644 --- a/src/or/periodic.h +++ b/src/or/periodic.h @@ -14,6 +14,7 @@ #define PERIODIC_EVENT_ROLE_DIRAUTH (1U << 3) #define PERIODIC_EVENT_ROLE_BRIDGEAUTH (1U << 4) #define PERIODIC_EVENT_ROLE_HS_SERVICE (1U << 5) +#define PERIODIC_EVENT_ROLE_DIRSERVER (1U << 6) /* Helper macro to make it a bit less annoying to defined groups of roles that * are often used. */ diff --git a/src/or/policies.c b/src/or/policies.c index f718ded326..e0dbb021c6 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -825,9 +825,8 @@ fascist_firewall_choose_address(const tor_addr_port_t *a, * If pref_only, only choose preferred addresses. In either case, choose * a preferred address before an address that's not preferred. * If both addresses could be chosen (they are both preferred or both allowed) - * choose IPv6 if pref_ipv6 is true, otherwise choose IPv4. - * If neither address is chosen, return 0, else return 1. */ -static int + * choose IPv6 if pref_ipv6 is true, otherwise choose IPv4. */ +static void fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr, uint16_t ipv4_orport, uint16_t ipv4_dirport, @@ -845,6 +844,9 @@ fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr, tor_assert(ipv6_addr); tor_assert(ap); + tor_addr_make_null(&ap->addr, AF_UNSPEC); + ap->port = 0; + tor_addr_port_t ipv4_ap; tor_addr_copy(&ipv4_ap.addr, ipv4_addr); ipv4_ap.port = (fw_connection == FIREWALL_OR_CONNECTION @@ -865,17 +867,12 @@ fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr, if (result) { tor_addr_copy(&ap->addr, &result->addr); ap->port = result->port; - return 1; - } else { - tor_addr_make_null(&ap->addr, AF_UNSPEC); - ap->port = 0; - return 0; } } /** Like fascist_firewall_choose_address_base(), but takes a host-order IPv4 * address as the first parameter. */ -static int +static void fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, uint16_t ipv4_orport, uint16_t ipv4_dirport, @@ -889,11 +886,16 @@ fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, { tor_addr_t ipv4_addr; tor_addr_from_ipv4h(&ipv4_addr, ipv4h_addr); - return fascist_firewall_choose_address_base(&ipv4_addr, ipv4_orport, - ipv4_dirport, ipv6_addr, - ipv6_orport, ipv6_dirport, - fw_connection, pref_only, - pref_ipv6, ap); + tor_assert(ap); + + tor_addr_make_null(&ap->addr, AF_UNSPEC); + ap->port = 0; + + fascist_firewall_choose_address_base(&ipv4_addr, ipv4_orport, + ipv4_dirport, ipv6_addr, + ipv6_orport, ipv6_dirport, + fw_connection, pref_only, + pref_ipv6, ap); } /* Some microdescriptor consensus methods have no IPv6 addresses in rs: they @@ -944,23 +946,25 @@ node_awaiting_ipv6(const or_options_t* options, const node_t *node) * This should only happen when there's no valid consensus, and rs doesn't * correspond to a bridge client's bridge. */ -int +void fascist_firewall_choose_address_rs(const routerstatus_t *rs, firewall_connection_t fw_connection, int pref_only, tor_addr_port_t* ap) { + tor_assert(ap); + + tor_addr_make_null(&ap->addr, AF_UNSPEC); + ap->port = 0; + if (!rs) { - return 0; + return; } - tor_assert(ap); - const or_options_t *options = get_options(); const node_t *node = node_get_by_id(rs->identity_digest); if (node && !node_awaiting_ipv6(options, node)) { - return fascist_firewall_choose_address_node(node, fw_connection, pref_only, - ap); + fascist_firewall_choose_address_node(node, fw_connection, pref_only, ap); } else { /* There's no node-specific IPv6 preference, so use the generic IPv6 * preference instead. */ @@ -970,33 +974,31 @@ fascist_firewall_choose_address_rs(const routerstatus_t *rs, /* Assume IPv4 and IPv6 DirPorts are the same. * Assume the IPv6 OR and Dir addresses are the same. */ - return fascist_firewall_choose_address_ipv4h(rs->addr, - rs->or_port, - rs->dir_port, - &rs->ipv6_addr, - rs->ipv6_orport, - rs->dir_port, - fw_connection, - pref_only, - pref_ipv6, - ap); + fascist_firewall_choose_address_ipv4h(rs->addr, rs->or_port, rs->dir_port, + &rs->ipv6_addr, rs->ipv6_orport, + rs->dir_port, fw_connection, + pref_only, pref_ipv6, ap); } } /** Like fascist_firewall_choose_address_base(), but takes <b>node</b>, and * looks up the node's IPv6 preference rather than taking an argument * for pref_ipv6. */ -int +void fascist_firewall_choose_address_node(const node_t *node, firewall_connection_t fw_connection, int pref_only, tor_addr_port_t *ap) { + tor_assert(ap); + + tor_addr_make_null(&ap->addr, AF_UNSPEC); + ap->port = 0; + if (!node) { - return 0; + return; } node_assert_ok(node); - /* Calling fascist_firewall_choose_address_node() when the node is missing * IPv6 information breaks IPv6-only clients. * If the node is a hard-coded fallback directory or authority, call @@ -1006,7 +1008,7 @@ fascist_firewall_choose_address_node(const node_t *node, * descriptor (routerinfo), or is one of our configured bridges before * calling this function. */ if (BUG(node_awaiting_ipv6(get_options(), node))) { - return 0; + return; } const int pref_ipv6_node = (fw_connection == FIREWALL_OR_CONNECTION @@ -1024,27 +1026,27 @@ fascist_firewall_choose_address_node(const node_t *node, node_get_pref_ipv6_dirport(node, &ipv6_dir_ap); /* Assume the IPv6 OR and Dir addresses are the same. */ - return fascist_firewall_choose_address_base(&ipv4_or_ap.addr, - ipv4_or_ap.port, - ipv4_dir_ap.port, - &ipv6_or_ap.addr, - ipv6_or_ap.port, - ipv6_dir_ap.port, - fw_connection, - pref_only, - pref_ipv6_node, - ap); + fascist_firewall_choose_address_base(&ipv4_or_ap.addr, ipv4_or_ap.port, + ipv4_dir_ap.port, &ipv6_or_ap.addr, + ipv6_or_ap.port, ipv6_dir_ap.port, + fw_connection, pref_only, + pref_ipv6_node, ap); } /** Like fascist_firewall_choose_address_rs(), but takes <b>ds</b>. */ -int +void fascist_firewall_choose_address_dir_server(const dir_server_t *ds, firewall_connection_t fw_connection, int pref_only, tor_addr_port_t *ap) { + tor_assert(ap); + + tor_addr_make_null(&ap->addr, AF_UNSPEC); + ap->port = 0; + if (!ds) { - return 0; + return; } /* A dir_server_t always has a fake_status. As long as it has the same @@ -1052,8 +1054,8 @@ fascist_firewall_choose_address_dir_server(const dir_server_t *ds, * (See #17867.) * This function relies on fascist_firewall_choose_address_rs looking up the * node if it can, because that will get the latest info for the relay. */ - return fascist_firewall_choose_address_rs(&ds->fake_status, fw_connection, - pref_only, ap); + fascist_firewall_choose_address_rs(&ds->fake_status, fw_connection, + pref_only, ap); } /** Return 1 if <b>addr</b> is permitted to connect to our dir port, diff --git a/src/or/policies.h b/src/or/policies.h index 35220a812f..4879acdd8d 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -55,13 +55,13 @@ int fascist_firewall_allows_dir_server(const dir_server_t *ds, firewall_connection_t fw_connection, int pref_only); -int fascist_firewall_choose_address_rs(const routerstatus_t *rs, - firewall_connection_t fw_connection, - int pref_only, tor_addr_port_t* ap); -int fascist_firewall_choose_address_node(const node_t *node, - firewall_connection_t fw_connection, - int pref_only, tor_addr_port_t* ap); -int fascist_firewall_choose_address_dir_server(const dir_server_t *ds, +void fascist_firewall_choose_address_rs(const routerstatus_t *rs, + firewall_connection_t fw_connection, + int pref_only, tor_addr_port_t* ap); +void fascist_firewall_choose_address_node(const node_t *node, + firewall_connection_t fw_connection, + int pref_only, tor_addr_port_t* ap); +void fascist_firewall_choose_address_dir_server(const dir_server_t *ds, firewall_connection_t fw_connection, int pref_only, tor_addr_port_t* ap); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 1a93c36433..afaeabe5dc 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -348,6 +348,13 @@ rend_add_service(smartlist_t *service_list, rend_service_t *service) /* The service passed all the checks */ tor_assert(s_list); smartlist_add(s_list, service); + + /* Notify that our global service list has changed only if this new service + * went into our global list. If not, when we move service from the staging + * list to the new list, a notify is triggered. */ + if (s_list == rend_service_list) { + hs_service_map_has_changed(); + } return 0; } @@ -609,6 +616,8 @@ rend_service_prune_list_impl_(void) circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED); } smartlist_free(surviving_services); + /* Notify that our global service list has changed. */ + hs_service_map_has_changed(); } /* Try to prune our main service list using the temporary one that we just @@ -958,6 +967,8 @@ rend_service_del_ephemeral(const char *service_id) } } SMARTLIST_FOREACH_END(circ); smartlist_remove(rend_service_list, s); + /* Notify that we just removed a service from our global list. */ + hs_service_map_has_changed(); rend_service_free(s); log_debug(LD_CONFIG, "Removed ephemeral Onion Service: %s", service_id); diff --git a/src/or/rephist.c b/src/or/rephist.c index ac3e9f502e..bac2efb1f4 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -85,8 +85,8 @@ #include "routerlist.h" #include "ht.h" #include "channelpadding.h" - #include "connection_or.h" +#include "statefile.h" static void bw_arrays_init(void); static void predicted_ports_alloc(void); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 1bfbd9f670..7eb9ec7990 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -101,7 +101,6 @@ #include "control.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" #include "entrynodes.h" #include "fp_pair.h" #include "geoip.h" @@ -122,6 +121,8 @@ #include "sandbox.h" #include "torcert.h" +#include "dirauth/dirvote.h" + // #define DEBUG_ROUTERLIST /****************************************************************************/ diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 1834cfad24..a729aa4b11 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -59,7 +59,6 @@ #include "config.h" #include "circuitstats.h" #include "dirserv.h" -#include "dirvote.h" #include "parsecommon.h" #include "policies.h" #include "protover.h" @@ -75,11 +74,15 @@ #include "entrynodes.h" #include "torcert.h" #include "sandbox.h" -#include "shared_random.h" +#include "shared_random_client.h" +#include "voting_schedule.h" +#include "dirauth/shared_random.h" #undef log #include <math.h> +#include "dirauth/dirvote.h" + /****************************************************************************/ /** List of tokens recognized in router descriptors */ @@ -3282,60 +3285,6 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method) return valid; } -/** Parse and extract all SR commits from <b>tokens</b> and place them in - * <b>ns</b>. */ -static void -extract_shared_random_commits(networkstatus_t *ns, smartlist_t *tokens) -{ - smartlist_t *chunks = NULL; - - tor_assert(ns); - tor_assert(tokens); - /* Commits are only present in a vote. */ - tor_assert(ns->type == NS_TYPE_VOTE); - - ns->sr_info.commits = smartlist_new(); - - smartlist_t *commits = find_all_by_keyword(tokens, K_COMMIT); - /* It's normal that a vote might contain no commits even if it participates - * in the SR protocol. Don't treat it as an error. */ - if (commits == NULL) { - goto end; - } - - /* Parse the commit. We do NO validation of number of arguments or ordering - * for forward compatibility, it's the parse commit job to inform us if it's - * supported or not. */ - chunks = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(commits, directory_token_t *, tok) { - /* Extract all arguments and put them in the chunks list. */ - for (int i = 0; i < tok->n_args; i++) { - smartlist_add(chunks, tok->args[i]); - } - sr_commit_t *commit = sr_parse_commit(chunks); - smartlist_clear(chunks); - if (commit == NULL) { - /* Get voter identity so we can warn that this dirauth vote contains - * commit we can't parse. */ - networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0); - tor_assert(voter); - log_warn(LD_DIR, "SR: Unable to parse commit %s from vote of voter %s.", - escaped(tok->object_body), - hex_str(voter->identity_digest, - sizeof(voter->identity_digest))); - /* Commitment couldn't be parsed. Continue onto the next commit because - * this one could be unsupported for instance. */ - continue; - } - /* Add newly created commit object to the vote. */ - smartlist_add(ns->sr_info.commits, commit); - } SMARTLIST_FOREACH_END(tok); - - end: - smartlist_free(chunks); - smartlist_free(commits); -} - /** Check if a shared random value of type <b>srv_type</b> is in * <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return * -1 on failure, 0 on success. The resulting srv is allocated on the heap and @@ -3773,13 +3722,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, /* If this is a vote document, check if information about the shared randomness protocol is included, and extract it. */ if (ns->type == NS_TYPE_VOTE) { - /* Does this authority participates in the SR protocol? */ - tok = find_opt_by_keyword(tokens, K_SR_FLAG); - if (tok) { - ns->sr_info.participate = 1; - /* Get the SR commitments and reveals from the vote. */ - extract_shared_random_commits(ns, tokens); - } + dirvote_parse_sr_commits(ns, tokens); } /* For both a vote and consensus, extract the shared random values. */ if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) { @@ -3969,7 +3912,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } } - if (voter_get_sig_by_algorithm(v, sig->alg)) { + if (networkstatus_get_voter_sig_by_alg(v, sig->alg)) { /* We already parsed a vote with this algorithm from this voter. Use the first one. */ log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus " diff --git a/src/or/shared_random_client.c b/src/or/shared_random_client.c new file mode 100644 index 0000000000..3aef83cef4 --- /dev/null +++ b/src/or/shared_random_client.c @@ -0,0 +1,259 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file shared_random_client.c + * \brief This file contains functions that are from the shared random + * subsystem but used by many part of tor. The full feature is built + * as part of the dirauth module. + **/ + +#define SHARED_RANDOM_CLIENT_PRIVATE +#include "shared_random_client.h" + +#include "config.h" +#include "voting_schedule.h" +#include "networkstatus.h" +#include "util.h" +#include "util_format.h" + +/* Convert a given srv object to a string for the control port. This doesn't + * fail and the srv object MUST be valid. */ +static char * +srv_to_control_string(const sr_srv_t *srv) +{ + char *srv_str; + char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1]; + tor_assert(srv); + + sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv); + tor_asprintf(&srv_str, "%s", srv_hash_encoded); + return srv_str; +} + +/* Return the voting interval of the tor vote subsystem. */ +int +get_voting_interval(void) +{ + int interval; + networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL)); + + if (consensus) { + interval = (int)(consensus->fresh_until - consensus->valid_after); + } else { + /* Same for both a testing and real network. We voluntarily ignore the + * InitialVotingInterval since it complexifies things and it doesn't + * affect the SR protocol. */ + interval = get_options()->V3AuthVotingInterval; + } + tor_assert(interval > 0); + return interval; +} + +/* Given the time <b>now</b>, return the start time of the current round of + * the SR protocol. For example, if it's 23:47:08, the current round thus + * started at 23:47:00 for a voting interval of 10 seconds. */ +time_t +get_start_time_of_current_round(void) +{ + const or_options_t *options = get_options(); + int voting_interval = get_voting_interval(); + /* First, get the start time of the next round */ + time_t next_start = voting_schedule_get_next_valid_after_time(); + /* Now roll back next_start by a voting interval to find the start time of + the current round. */ + time_t curr_start = voting_schedule_get_start_of_next_interval( + next_start - voting_interval - 1, + voting_interval, + options->TestingV3AuthVotingStartOffset); + return curr_start; +} + +/* + * Public API + */ + +/* Encode the given shared random value and put it in dst. Destination + * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */ +void +sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv) +{ + int ret; + /* Extra byte for the NULL terminated char. */ + char buf[SR_SRV_VALUE_BASE64_LEN + 1]; + + tor_assert(dst); + tor_assert(srv); + tor_assert(dst_len >= sizeof(buf)); + + ret = base64_encode(buf, sizeof(buf), (const char *) srv->value, + sizeof(srv->value), 0); + /* Always expect the full length without the NULL byte. */ + tor_assert(ret == (sizeof(buf) - 1)); + tor_assert(ret <= (int) dst_len); + strlcpy(dst, buf, dst_len); +} + +/* Return the current SRV string representation for the control port. Return a + * newly allocated string on success containing the value else "" if not found + * or if we don't have a valid consensus yet. */ +char * +sr_get_current_for_control(void) +{ + char *srv_str; + const networkstatus_t *c = networkstatus_get_latest_consensus(); + if (c && c->sr_info.current_srv) { + srv_str = srv_to_control_string(c->sr_info.current_srv); + } else { + srv_str = tor_strdup(""); + } + return srv_str; +} + +/* Return the previous SRV string representation for the control port. Return + * a newly allocated string on success containing the value else "" if not + * found or if we don't have a valid consensus yet. */ +char * +sr_get_previous_for_control(void) +{ + char *srv_str; + const networkstatus_t *c = networkstatus_get_latest_consensus(); + if (c && c->sr_info.previous_srv) { + srv_str = srv_to_control_string(c->sr_info.previous_srv); + } else { + srv_str = tor_strdup(""); + } + return srv_str; +} + +/* Return current shared random value from the latest consensus. Caller can + * NOT keep a reference to the returned pointer. Return NULL if none. */ +const sr_srv_t * +sr_get_current(const networkstatus_t *ns) +{ + const networkstatus_t *consensus; + + /* Use provided ns else get a live one */ + if (ns) { + consensus = ns; + } else { + consensus = networkstatus_get_live_consensus(approx_time()); + } + /* Ideally we would never be asked for an SRV without a live consensus. Make + * sure this assumption is correct. */ + tor_assert_nonfatal(consensus); + + if (consensus) { + return consensus->sr_info.current_srv; + } + return NULL; +} + +/* Return previous shared random value from the latest consensus. Caller can + * NOT keep a reference to the returned pointer. Return NULL if none. */ +const sr_srv_t * +sr_get_previous(const networkstatus_t *ns) +{ + const networkstatus_t *consensus; + + /* Use provided ns else get a live one */ + if (ns) { + consensus = ns; + } else { + consensus = networkstatus_get_live_consensus(approx_time()); + } + /* Ideally we would never be asked for an SRV without a live consensus. Make + * sure this assumption is correct. */ + tor_assert_nonfatal(consensus); + + if (consensus) { + return consensus->sr_info.previous_srv; + } + return NULL; +} + +/* Parse a list of arguments from a SRV value either from a vote, consensus + * or from our disk state and return a newly allocated srv object. NULL is + * returned on error. + * + * The arguments' order: + * num_reveals, value + */ +sr_srv_t * +sr_parse_srv(const smartlist_t *args) +{ + char *value; + int ok, ret; + uint64_t num_reveals; + sr_srv_t *srv = NULL; + + tor_assert(args); + + if (smartlist_len(args) < 2) { + goto end; + } + + /* First argument is the number of reveal values */ + num_reveals = tor_parse_uint64(smartlist_get(args, 0), + 10, 0, UINT64_MAX, &ok, NULL); + if (!ok) { + goto end; + } + /* Second and last argument is the shared random value it self. */ + value = smartlist_get(args, 1); + if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) { + goto end; + } + + srv = tor_malloc_zero(sizeof(*srv)); + srv->num_reveals = num_reveals; + /* We subtract one byte from the srclen because the function ignores the + * '=' character in the given buffer. This is broken but it's a documented + * behavior of the implementation. */ + ret = base64_decode((char *) srv->value, sizeof(srv->value), value, + SR_SRV_VALUE_BASE64_LEN - 1); + if (ret != sizeof(srv->value)) { + tor_free(srv); + srv = NULL; + goto end; + } + end: + return srv; +} + +/** Return the start time of the current SR protocol run. For example, if the + * time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this + * function should return 23/06/2017 00:00:00. */ +time_t +sr_state_get_start_time_of_current_protocol_run(time_t now) +{ + int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; + int voting_interval = get_voting_interval(); + /* Find the time the current round started. */ + time_t beginning_of_current_round = get_start_time_of_current_round(); + + /* Get current SR protocol round */ + int current_round = (now / voting_interval) % total_rounds; + + /* Get start time by subtracting the time elapsed from the beginning of the + protocol run */ + time_t time_elapsed_since_start_of_run = current_round * voting_interval; + return beginning_of_current_round - time_elapsed_since_start_of_run; +} + +/** Return the time (in seconds) it takes to complete a full SR protocol phase + * (e.g. the commit phase). */ +unsigned int +sr_state_get_phase_duration(void) +{ + return SHARED_RANDOM_N_ROUNDS * get_voting_interval(); +} + +/** Return the time (in seconds) it takes to complete a full SR protocol run */ +unsigned int +sr_state_get_protocol_run_duration(void) +{ + int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; + return total_protocol_rounds * get_voting_interval(); +} + diff --git a/src/or/shared_random_client.h b/src/or/shared_random_client.h new file mode 100644 index 0000000000..89c608d45f --- /dev/null +++ b/src/or/shared_random_client.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file shared_random_client.h + * \brief Header file for shared_random_client.c. + **/ + +#ifndef TOR_SHARED_RANDOM_CLIENT_H +#define TOR_SHARED_RANDOM_CLIENT_H + +/* Dirauth module. */ +#include "dirauth/shared_random.h" + +/* Helper functions. */ +void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv); +int get_voting_interval(void); + +/* Control port functions. */ +char *sr_get_current_for_control(void); +char *sr_get_previous_for_control(void); + +/* SRV functions. */ +const sr_srv_t *sr_get_current(const networkstatus_t *ns); +const sr_srv_t *sr_get_previous(const networkstatus_t *ns); +sr_srv_t *sr_parse_srv(const smartlist_t *args); + +/* + * Shared Random State API + */ + +/* Each protocol phase has 12 rounds */ +#define SHARED_RANDOM_N_ROUNDS 12 +/* Number of phase we have in a protocol. */ +#define SHARED_RANDOM_N_PHASES 2 + +time_t sr_state_get_start_time_of_current_protocol_run(time_t now); +unsigned int sr_state_get_phase_duration(void); +unsigned int sr_state_get_protocol_run_duration(void); +time_t get_start_time_of_current_round(void); + +#ifdef TOR_UNIT_TESTS + +#endif /* TOR_UNIT_TESTS */ + +#endif /* TOR_SHARED_RANDOM_CLIENT_H */ + diff --git a/src/or/statefile.c b/src/or/statefile.c index cc114f0a2b..c81ea44e06 100644 --- a/src/or/statefile.c +++ b/src/or/statefile.c @@ -37,6 +37,7 @@ #include "control.h" #include "entrynodes.h" #include "hibernate.h" +#include "main.h" #include "rephist.h" #include "router.h" #include "sandbox.h" @@ -680,6 +681,18 @@ save_transport_to_state(const char *transport, tor_free(transport_addrport); } +/** Change the next_write time of <b>state</b> to <b>when</b>, unless the + * state is already scheduled to be written to disk earlier than <b>when</b>. + */ +void +or_state_mark_dirty(or_state_t *state, time_t when) +{ + if (state->next_write > when) { + state->next_write = when; + reschedule_or_state_save(); + } +} + STATIC void or_state_free_(or_state_t *state) { diff --git a/src/or/statefile.h b/src/or/statefile.h index b4cc4d1dc6..5aa2ca9320 100644 --- a/src/or/statefile.h +++ b/src/or/statefile.h @@ -17,6 +17,7 @@ char *get_stored_bindaddr_for_server_transport(const char *transport); int or_state_load(void); int or_state_loaded(void); void or_state_free_all(void); +void or_state_mark_dirty(or_state_t *state, time_t when); #ifdef STATEFILE_PRIVATE STATIC config_line_t *get_transport_in_state_by_name(const char *transport); diff --git a/src/or/voting_schedule.c b/src/or/voting_schedule.c new file mode 100644 index 0000000000..b7676d5e79 --- /dev/null +++ b/src/or/voting_schedule.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file voting_schedule.c + * \brief This file contains functions that are from the directory authority + * subsystem related to voting specifically but used by many part of + * tor. The full feature is built as part of the dirauth module. + **/ + +#define VOTING_SCHEDULE_PRIVATE +#include "voting_schedule.h" + +#include "or.h" +#include "config.h" +#include "networkstatus.h" + +/* ===== + * Vote scheduling + * ===== */ + +/** Return the start of the next interval of size <b>interval</b> (in + * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always + * starts a fresh interval, and if the last interval of a day would be + * truncated to less than half its size, it is rolled into the + * previous interval. */ +time_t +voting_schedule_get_start_of_next_interval(time_t now, int interval, + int offset) +{ + struct tm tm; + time_t midnight_today=0; + time_t midnight_tomorrow; + time_t next; + + tor_gmtime_r(&now, &tm); + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + + if (tor_timegm(&tm, &midnight_today) < 0) { + log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight."); + } + midnight_tomorrow = midnight_today + (24*60*60); + + next = midnight_today + ((now-midnight_today)/interval + 1)*interval; + + /* Intervals never cross midnight. */ + if (next > midnight_tomorrow) + next = midnight_tomorrow; + + /* If the interval would only last half as long as it's supposed to, then + * skip over to the next day. */ + if (next + interval/2 > midnight_tomorrow) + next = midnight_tomorrow; + + next += offset; + if (next - interval > now) + next -= interval; + + return next; +} + +/* Populate and return a new voting_schedule_t that can be used to schedule + * voting. The object is allocated on the heap and it's the responsibility of + * the caller to free it. Can't fail. */ +static voting_schedule_t * +get_voting_schedule(const or_options_t *options, time_t now, int severity) +{ + int interval, vote_delay, dist_delay; + time_t start; + time_t end; + networkstatus_t *consensus; + voting_schedule_t *new_voting_schedule; + + new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t)); + + consensus = networkstatus_get_live_consensus(now); + + if (consensus) { + interval = (int)( consensus->fresh_until - consensus->valid_after ); + vote_delay = consensus->vote_seconds; + dist_delay = consensus->dist_seconds; + } else { + interval = options->TestingV3AuthInitialVotingInterval; + vote_delay = options->TestingV3AuthInitialVoteDelay; + dist_delay = options->TestingV3AuthInitialDistDelay; + } + + tor_assert(interval > 0); + + if (vote_delay + dist_delay > interval/2) + vote_delay = dist_delay = interval / 4; + + start = new_voting_schedule->interval_starts = + voting_schedule_get_start_of_next_interval(now,interval, + options->TestingV3AuthVotingStartOffset); + end = voting_schedule_get_start_of_next_interval(start+1, interval, + options->TestingV3AuthVotingStartOffset); + + tor_assert(end > start); + + new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2); + new_voting_schedule->voting_ends = start - dist_delay; + new_voting_schedule->fetch_missing_votes = + start - dist_delay - (vote_delay/2); + new_voting_schedule->voting_starts = start - dist_delay - vote_delay; + + { + char tbuf[ISO_TIME_LEN+1]; + format_iso_time(tbuf, new_voting_schedule->interval_starts); + tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: " + "consensus_set=%d, interval=%d", + tbuf, consensus?1:0, interval); + } + + return new_voting_schedule; +} + +#define voting_schedule_free(s) \ + FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s)) + +/** Frees a voting_schedule_t. This should be used instead of the generic + * tor_free. */ +static void +voting_schedule_free_(voting_schedule_t *voting_schedule_to_free) +{ + if (!voting_schedule_to_free) + return; + tor_free(voting_schedule_to_free); +} + +voting_schedule_t voting_schedule; + +/* Using the time <b>now</b>, return the next voting valid-after time. */ +time_t +voting_schedule_get_next_valid_after_time(void) +{ + /* This is a safe guard in order to make sure that the voting schedule + * static object is at least initialized. Using this function with a zeroed + * voting schedule can lead to bugs. */ + if (tor_mem_is_zero((const char *) &voting_schedule, + sizeof(voting_schedule))) { + voting_schedule_recalculate_timing(get_options(), time(NULL)); + voting_schedule.created_on_demand = 1; + } + return voting_schedule.interval_starts; +} + +/** Set voting_schedule to hold the timing for the next vote we should be + * doing. All type of tor do that because HS subsystem needs the timing as + * well to function properly. */ +void +voting_schedule_recalculate_timing(const or_options_t *options, time_t now) +{ + voting_schedule_t *new_voting_schedule; + + /* get the new voting schedule */ + new_voting_schedule = get_voting_schedule(options, now, LOG_INFO); + tor_assert(new_voting_schedule); + + /* Fill in the global static struct now */ + memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule)); + voting_schedule_free(new_voting_schedule); +} + diff --git a/src/or/voting_schedule.h b/src/or/voting_schedule.h new file mode 100644 index 0000000000..4f9d584031 --- /dev/null +++ b/src/or/voting_schedule.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file voting_schedule.h + * \brief Header file for voting_schedule.c. + **/ + +#ifndef TOR_VOTING_SCHEDULE_H +#define TOR_VOTING_SCHEDULE_H + +#include "or.h" + +/** Scheduling information for a voting interval. */ +typedef struct { + /** When do we generate and distribute our vote for this interval? */ + time_t voting_starts; + /** When do we send an HTTP request for any votes that we haven't + * been posted yet?*/ + time_t fetch_missing_votes; + /** When do we give up on getting more votes and generate a consensus? */ + time_t voting_ends; + /** When do we send an HTTP request for any signatures we're expecting to + * see on the consensus? */ + time_t fetch_missing_signatures; + /** When do we publish the consensus? */ + time_t interval_starts; + + /* True iff we have generated and distributed our vote. */ + int have_voted; + /* True iff we've requested missing votes. */ + int have_fetched_missing_votes; + /* True iff we have built a consensus and sent the signatures around. */ + int have_built_consensus; + /* True iff we've fetched missing signatures. */ + int have_fetched_missing_signatures; + /* True iff we have published our consensus. */ + int have_published_consensus; + + /* True iff this voting schedule was set on demand meaning not through the + * normal vote operation of a dirauth or when a consensus is set. This only + * applies to a directory authority that needs to recalculate the voting + * timings only for the first vote even though this object was initilized + * prior to voting. */ + int created_on_demand; +} voting_schedule_t; + +/* Public API. */ + +extern voting_schedule_t voting_schedule; + +void voting_schedule_recalculate_timing(const or_options_t *options, + time_t now); + +time_t voting_schedule_get_start_of_next_interval(time_t now, + int interval, + int offset); +time_t voting_schedule_get_next_valid_after_time(void); + +#endif /* TOR_VOTING_SCHEDULE_H */ + diff --git a/src/test/test_channel.c b/src/test/test_channel.c index 812ec6c1ac..7d5018ef5b 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -544,6 +544,8 @@ test_channel_outbound_cell(void *arg) (void) arg; + cmux_ewma_set_options(NULL,NULL); + /* The channel will be freed so we need to hijack this so the scheduler * doesn't get confused. */ MOCK(scheduler_release_channel, scheduler_release_channel_mock); diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c index 75b7a0ea47..14c7598703 100644 --- a/src/test/test_circuitmux.c +++ b/src/test/test_circuitmux.c @@ -3,6 +3,7 @@ #define TOR_CHANNEL_INTERNAL_ #define CIRCUITMUX_PRIVATE +#define CIRCUITMUX_EWMA_PRIVATE #define RELAY_PRIVATE #include "or.h" #include "channel.h" @@ -79,8 +80,47 @@ test_cmux_destroy_cell_queue(void *arg) tor_free(dc); } +static void +test_cmux_compute_ticks(void *arg) +{ + const int64_t NS_PER_S = 1000 * 1000 * 1000; + const int64_t START_NS = U64_LITERAL(1217709000)*NS_PER_S; + int64_t now; + double rem; + unsigned tick; + (void)arg; + circuitmux_ewma_free_all(); + monotime_enable_test_mocking(); + + monotime_coarse_set_mock_time_nsec(START_NS); + cell_ewma_initialize_ticks(); + const unsigned tick_zero = cell_ewma_get_current_tick_and_fraction(&rem); + tt_double_op(rem, OP_GT, -1e-9); + tt_double_op(rem, OP_LT, 1e-9); + + /* 1.5 second later and we should still be in the same tick. */ + now = START_NS + NS_PER_S + NS_PER_S/2; + monotime_coarse_set_mock_time_nsec(now); + tick = cell_ewma_get_current_tick_and_fraction(&rem); + tt_uint_op(tick, OP_EQ, tick_zero); + tt_double_op(rem, OP_GT, .149999999); + tt_double_op(rem, OP_LT, .150000001); + + /* 25 second later and we should be in another tick. */ + now = START_NS + NS_PER_S * 25; + monotime_coarse_set_mock_time_nsec(now); + tick = cell_ewma_get_current_tick_and_fraction(&rem); + tt_uint_op(tick, OP_EQ, tick_zero + 2); + tt_double_op(rem, OP_GT, .499999999); + tt_double_op(rem, OP_LT, .500000001); + + done: + ; +} + struct testcase_t circuitmux_tests[] = { { "destroy_cell_queue", test_cmux_destroy_cell_queue, TT_FORK, NULL, NULL }, + { "compute_ticks", test_cmux_compute_ticks, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_config.c b/src/test/test_config.c index 7983106a2f..461aa646d6 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -24,7 +24,7 @@ #include "control.h" #include "cpuworker.h" #include "dirserv.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "dns.h" #include "entrynodes.h" #include "transports.h" diff --git a/src/test/test_dir.c b/src/test/test_dir.c index df5ae2d594..4927530296 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -25,7 +25,7 @@ #include "crypto_ed25519.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "entrynodes.h" #include "hibernate.h" #include "memarea.h" @@ -35,12 +35,13 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" -#include "shared_random_state.h" +#include "dirauth/shared_random_state.h" #include "test.h" #include "test_dir_common.h" #include "torcert.h" #include "relay.h" #include "log_test_helpers.h" +#include "voting_schedule.h" #define NS_MODULE dir @@ -2379,7 +2380,7 @@ test_a_networkstatus( sign_skey_2 = crypto_pk_new(); sign_skey_3 = crypto_pk_new(); sign_skey_leg1 = pk_generate(4); - dirvote_recalculate_timing(get_options(), now); + voting_schedule_recalculate_timing(get_options(), now); sr_state_init(0, 0); tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_1, @@ -4065,34 +4066,19 @@ test_dir_download_status_increment(void *arg) DL_WANT_ANY_DIRSERVER, DL_SCHED_INCREMENT_ATTEMPT, 0, 0 }; - int no_delay = 0; - int delay0 = -1; - int delay1 = -1; - int delay2 = -1; - smartlist_t *schedule = smartlist_new(); - smartlist_t *schedule_no_initial_delay = smartlist_new(); or_options_t test_options; time_t current_time = time(NULL); - /* Provide some values for the schedules */ - delay0 = 10; - delay1 = 99; - delay2 = 20; - - /* Make the schedules */ - smartlist_add(schedule, (void *)&delay0); - smartlist_add(schedule, (void *)&delay1); - smartlist_add(schedule, (void *)&delay2); - - smartlist_add(schedule_no_initial_delay, (void *)&no_delay); - smartlist_add(schedule_no_initial_delay, (void *)&delay1); - smartlist_add(schedule_no_initial_delay, (void *)&delay2); + const int delay0 = 10; + const int no_delay = 0; + const int schedule = 10; + const int schedule_no_initial_delay = 0; /* Put it in the options */ mock_options = &test_options; reset_options(mock_options, &mock_get_options_calls); - mock_options->TestingBridgeBootstrapDownloadSchedule = schedule; - mock_options->TestingClientDownloadSchedule = schedule; + mock_options->TestingBridgeBootstrapDownloadInitialDelay = schedule; + mock_options->TestingClientDownloadInitialDelay = schedule; MOCK(get_options, mock_get_options); @@ -4100,13 +4086,13 @@ test_dir_download_status_increment(void *arg) * whether or not it was reset before being used */ /* regression test for 17750: no initial delay */ - mock_options->TestingClientDownloadSchedule = schedule_no_initial_delay; + mock_options->TestingClientDownloadInitialDelay = schedule_no_initial_delay; mock_get_options_calls = 0; /* we really want to test that it's equal to time(NULL) + delay0, but that's * an unrealiable test, because time(NULL) might change. */ /* regression test for 17750: exponential, no initial delay */ - mock_options->TestingClientDownloadSchedule = schedule_no_initial_delay; + mock_options->TestingClientDownloadInitialDelay = schedule_no_initial_delay; mock_get_options_calls = 0; /* we really want to test that it's equal to time(NULL) + delay0, but that's * an unrealiable test, because time(NULL) might change. */ @@ -4119,7 +4105,7 @@ test_dir_download_status_increment(void *arg) tt_int_op(mock_get_options_calls, OP_GE, 1); /* regression test for 17750: exponential, initial delay */ - mock_options->TestingClientDownloadSchedule = schedule; + mock_options->TestingClientDownloadInitialDelay = schedule; mock_get_options_calls = 0; /* we really want to test that it's equal to time(NULL) + delay0, but that's * an unrealiable test, because time(NULL) might change. */ @@ -4132,9 +4118,6 @@ test_dir_download_status_increment(void *arg) tt_int_op(mock_get_options_calls, OP_GE, 1); done: - /* the pointers in schedule are allocated on the stack */ - smartlist_free(schedule); - smartlist_free(schedule_no_initial_delay); UNMOCK(get_options); mock_options = NULL; mock_get_options_calls = 0; @@ -5452,7 +5435,7 @@ mock_num_bridges_usable(int use_maybe_reachable) * fallbacks. */ static void -test_dir_find_dl_schedule(void* data) +test_dir_find_dl_min_delay(void* data) { const char *str = (const char *)data; @@ -5485,44 +5468,45 @@ test_dir_find_dl_schedule(void* data) mock_num_bridges_usable); download_status_t dls; - smartlist_t server, client, server_cons, client_cons; - smartlist_t client_boot_auth_only_cons, client_boot_auth_cons; - smartlist_t client_boot_fallback_cons, bridge, bridge_bootstrap; + + const int server=10, client=20, server_cons=30, client_cons=40; + const int client_boot_auth_only_cons=50, client_boot_auth_cons=60; + const int client_boot_fallback_cons=70, bridge=80, bridge_bootstrap=90; mock_options = tor_malloc(sizeof(or_options_t)); reset_options(mock_options, &mock_get_options_calls); MOCK(get_options, mock_get_options); - mock_options->TestingServerDownloadSchedule = &server; - mock_options->TestingClientDownloadSchedule = &client; - mock_options->TestingServerConsensusDownloadSchedule = &server_cons; - mock_options->TestingClientConsensusDownloadSchedule = &client_cons; - mock_options->ClientBootstrapConsensusAuthorityOnlyDownloadSchedule = - &client_boot_auth_only_cons; - mock_options->ClientBootstrapConsensusAuthorityDownloadSchedule = - &client_boot_auth_cons; - mock_options->ClientBootstrapConsensusFallbackDownloadSchedule = - &client_boot_fallback_cons; - mock_options->TestingBridgeDownloadSchedule = &bridge; - mock_options->TestingBridgeBootstrapDownloadSchedule = &bridge_bootstrap; + mock_options->TestingServerDownloadInitialDelay = server; + mock_options->TestingClientDownloadInitialDelay = client; + mock_options->TestingServerConsensusDownloadInitialDelay = server_cons; + mock_options->TestingClientConsensusDownloadInitialDelay = client_cons; + mock_options->ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay = + client_boot_auth_only_cons; + mock_options->ClientBootstrapConsensusAuthorityDownloadInitialDelay = + client_boot_auth_cons; + mock_options->ClientBootstrapConsensusFallbackDownloadInitialDelay = + client_boot_fallback_cons; + mock_options->TestingBridgeDownloadInitialDelay = bridge; + mock_options->TestingBridgeBootstrapDownloadInitialDelay = bridge_bootstrap; dls.schedule = DL_SCHED_GENERIC; /* client */ mock_options->ClientOnly = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, client); mock_options->ClientOnly = 0; /* dir mode */ mock_options->DirPort_set = 1; mock_options->DirCache = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, server); mock_options->DirPort_set = 0; mock_options->DirCache = 0; dls.schedule = DL_SCHED_CONSENSUS; /* public server mode */ mock_options->ORPort_set = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server_cons); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, server_cons); mock_options->ORPort_set = 0; /* client and bridge modes */ @@ -5531,30 +5515,30 @@ test_dir_find_dl_schedule(void* data) dls.want_authority = 1; /* client */ mock_options->ClientOnly = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, - &client_boot_auth_cons); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, + client_boot_auth_cons); mock_options->ClientOnly = 0; /* bridge relay */ mock_options->ORPort_set = 1; mock_options->BridgeRelay = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, - &client_boot_auth_cons); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, + client_boot_auth_cons); mock_options->ORPort_set = 0; mock_options->BridgeRelay = 0; dls.want_authority = 0; /* client */ mock_options->ClientOnly = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, - &client_boot_fallback_cons); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, + client_boot_fallback_cons); mock_options->ClientOnly = 0; /* bridge relay */ mock_options->ORPort_set = 1; mock_options->BridgeRelay = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, - &client_boot_fallback_cons); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, + client_boot_fallback_cons); mock_options->ORPort_set = 0; mock_options->BridgeRelay = 0; @@ -5562,30 +5546,30 @@ test_dir_find_dl_schedule(void* data) /* dls.want_authority is ignored */ /* client */ mock_options->ClientOnly = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, - &client_boot_auth_only_cons); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, + client_boot_auth_only_cons); mock_options->ClientOnly = 0; /* bridge relay */ mock_options->ORPort_set = 1; mock_options->BridgeRelay = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, - &client_boot_auth_only_cons); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, + client_boot_auth_only_cons); mock_options->ORPort_set = 0; mock_options->BridgeRelay = 0; } } else { /* client */ mock_options->ClientOnly = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, - &client_cons); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, + client_cons); mock_options->ClientOnly = 0; /* bridge relay */ mock_options->ORPort_set = 1; mock_options->BridgeRelay = 1; - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, - &client_cons); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, + client_cons); mock_options->ORPort_set = 0; mock_options->BridgeRelay = 0; } @@ -5595,9 +5579,9 @@ test_dir_find_dl_schedule(void* data) mock_options->ClientOnly = 1; mock_options->UseBridges = 1; if (num_bridges_usable(0) > 0) { - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, bridge); } else { - tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge_bootstrap); + tt_int_op(find_dl_min_delay(&dls, mock_options), OP_EQ, bridge_bootstrap); } done: @@ -5871,14 +5855,14 @@ struct testcase_t dir_tests[] = { DIR(dump_unparseable_descriptors, 0), DIR(populate_dump_desc_fifo, 0), DIR(populate_dump_desc_fifo_2, 0), - DIR_ARG(find_dl_schedule, TT_FORK, "bfd"), - DIR_ARG(find_dl_schedule, TT_FORK, "bad"), - DIR_ARG(find_dl_schedule, TT_FORK, "cfd"), - DIR_ARG(find_dl_schedule, TT_FORK, "cad"), - DIR_ARG(find_dl_schedule, TT_FORK, "bfr"), - DIR_ARG(find_dl_schedule, TT_FORK, "bar"), - DIR_ARG(find_dl_schedule, TT_FORK, "cfr"), - DIR_ARG(find_dl_schedule, TT_FORK, "car"), + DIR_ARG(find_dl_min_delay, TT_FORK, "bfd"), + DIR_ARG(find_dl_min_delay, TT_FORK, "bad"), + DIR_ARG(find_dl_min_delay, TT_FORK, "cfd"), + DIR_ARG(find_dl_min_delay, TT_FORK, "cad"), + DIR_ARG(find_dl_min_delay, TT_FORK, "bfr"), + DIR_ARG(find_dl_min_delay, TT_FORK, "bar"), + DIR_ARG(find_dl_min_delay, TT_FORK, "cfr"), + DIR_ARG(find_dl_min_delay, TT_FORK, "car"), DIR(assumed_flags, 0), DIR(networkstatus_compute_bw_weights_v10, 0), DIR(platform_str, 0), diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c index fdf43533a8..5f92154ea9 100644 --- a/src/test/test_dir_common.c +++ b/src/test/test_dir_common.c @@ -9,10 +9,11 @@ #include "test.h" #include "container.h" #include "or.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "nodelist.h" #include "routerlist.h" #include "test_dir_common.h" +#include "voting_schedule.h" void dir_common_setup_vote(networkstatus_t **vote, time_t now); networkstatus_t * dir_common_add_rs_and_parse(networkstatus_t *vote, diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 71faf70af2..688d26bdc1 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -30,8 +30,9 @@ #include "proto_http.h" #include "geoip.h" #include "dirserv.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "log_test_helpers.h" +#include "voting_schedule.h" #ifdef _WIN32 /* For mkdir() */ @@ -2056,7 +2057,7 @@ test_dir_handle_get_status_vote_d(void* data) mock_options->TestingV3AuthInitialDistDelay = 1; time_t now = 1441223455 -1; - dirvote_recalculate_timing(mock_options, now); + voting_schedule_recalculate_timing(mock_options, now); const char *msg_out = NULL; int status_out = 0; @@ -2402,7 +2403,7 @@ test_dir_handle_get_status_vote_next_authority(void* data) mock_options->TestingV3AuthInitialDistDelay = 1; time_t now = 1441223455 -1; - dirvote_recalculate_timing(mock_options, now); + voting_schedule_recalculate_timing(mock_options, now); struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out, &status_out); @@ -2481,7 +2482,7 @@ test_dir_handle_get_status_vote_current_authority(void* data) mock_options->TestingV3AuthInitialDistDelay = 1; time_t now = 1441223455; - dirvote_recalculate_timing(mock_options, now-1); + voting_schedule_recalculate_timing(mock_options, now-1); struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out, &status_out); diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index 92a860360d..f55e9f0173 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -2679,6 +2679,23 @@ test_enty_guard_should_expire_waiting(void *arg) tor_free(fake_state); } +/** Test that the number of primary guards can be controlled using torrc */ +static void +test_entry_guard_number_of_primaries(void *arg) +{ + (void) arg; + + /* Get default value */ + tt_int_op(get_n_primary_guards(), OP_EQ, DFLT_N_PRIMARY_GUARDS); + + /* Set number of primaries using torrc */ + get_options_mutable()->NumPrimaryGuards = 42; + tt_int_op(get_n_primary_guards(), OP_EQ, 42); + + done: + ; +} + static void mock_directory_initiate_request(directory_request_t *req) { @@ -2826,6 +2843,8 @@ struct testcase_t entrynodes_tests[] = { test_entry_guard_parse_from_state_broken, TT_FORK, NULL, NULL }, { "get_guard_selection_by_name", test_entry_guard_get_guard_selection_by_name, TT_FORK, NULL, NULL }, + { "number_of_primaries", + test_entry_guard_number_of_primaries, TT_FORK, NULL, NULL }, BFN_TEST(choose_selection_initial), BFN_TEST(add_single_guard), BFN_TEST(node_filter), diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 58e12abca0..50dca588ed 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -397,21 +397,25 @@ test_client_pick_intro(void *arg) } SMARTLIST_FOREACH_END(ip); /* Try to get a random intro: Should return the chosen one! */ - extend_info_t *ip = client_get_random_intro(&service_kp.pubkey); - tor_assert(ip); - tt_assert(!tor_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN)); - tt_mem_op(ip->identity_digest, OP_EQ, chosen_intro_ei->identity_digest, - DIGEST_LEN); + /* (We try several times, to make sure this behavior is consistent, and to + * cover the different cases of client_get_random_intro().) */ + for (int i = 0; i < 64; ++i) { + extend_info_t *ip = client_get_random_intro(&service_kp.pubkey); + tor_assert(ip); + tt_assert(!tor_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN)); + tt_mem_op(ip->identity_digest, OP_EQ, chosen_intro_ei->identity_digest, + DIGEST_LEN); + extend_info_free(ip); + } extend_info_free(chosen_intro_ei); - extend_info_free(ip); /* Now also mark the chosen one as failed: See that we can't get any intro points anymore. */ hs_cache_client_intro_state_note(&service_kp.pubkey, &chosen_intro_point->auth_key_cert->signed_key, INTRO_POINT_FAILURE_TIMEOUT); - ip = client_get_random_intro(&service_kp.pubkey); + extend_info_t *ip = client_get_random_intro(&service_kp.pubkey); tor_assert(!ip); } diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index 17ba11ca7d..94d3d52e98 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -23,13 +23,14 @@ #include "config.h" #include "networkstatus.h" #include "directory.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "nodelist.h" #include "routerlist.h" #include "statefile.h" #include "circuitlist.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #include "util.h" +#include "voting_schedule.h" /** Test the validation of HS v3 addresses */ static void @@ -359,11 +360,8 @@ mock_networkstatus_get_live_consensus(time_t now) static void test_responsible_hsdirs(void *arg) { - time_t now = approx_time(); smartlist_t *responsible_dirs = smartlist_new(); networkstatus_t *ns = NULL; - int retval; - (void) arg; hs_init(); @@ -385,12 +383,12 @@ test_responsible_hsdirs(void *arg) helper_add_hsdir_to_networkstatus(ns, 3, "spyro", 0); } - ed25519_keypair_t kp; - retval = ed25519_keypair_generate(&kp, 0); - tt_int_op(retval, OP_EQ , 0); + /* Use a fixed time period and pub key so we always take the same path */ + ed25519_public_key_t pubkey; + uint64_t time_period_num = 17653; // 2 May, 2018, 14:00. + memset(&pubkey, 42, sizeof(pubkey)); - uint64_t time_period_num = hs_get_time_period_num(now); - hs_get_responsible_hsdirs(&kp.pubkey, time_period_num, + hs_get_responsible_hsdirs(&pubkey, time_period_num, 0, 0, responsible_dirs); /* Make sure that we only found 2 responsible HSDirs. @@ -812,7 +810,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 26 Oct 1985 01:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), ns.valid_after); + voting_schedule_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 0); @@ -820,7 +818,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 26 Oct 1985 12:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), ns.valid_after); + voting_schedule_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 0); @@ -828,7 +826,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), ns.valid_after); + voting_schedule_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 1); @@ -836,7 +834,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 27 Oct 1985 00:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), ns.valid_after); + voting_schedule_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 1); @@ -844,7 +842,7 @@ test_time_between_tp_and_srv(void *arg) tt_int_op(ret, OP_EQ, 0); ret = parse_rfc1123_time("Sat, 27 Oct 1985 01:00:00 UTC", &ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), ns.valid_after); + voting_schedule_recalculate_timing(get_options(), ns.valid_after); ret = hs_in_period_between_tp_and_srv(&ns, 0); tt_int_op(ret, OP_EQ, 0); @@ -1331,7 +1329,8 @@ run_reachability_scenario(const reachability_cfg_t *cfg, int num_scenario) &mock_service_ns->valid_until); set_consensus_times(cfg->service_valid_until, &mock_service_ns->fresh_until); - dirvote_recalculate_timing(get_options(), mock_service_ns->valid_after); + voting_schedule_recalculate_timing(get_options(), + mock_service_ns->valid_after); /* Set client consensus time. */ set_consensus_times(cfg->client_valid_after, &mock_client_ns->valid_after); @@ -1339,7 +1338,8 @@ run_reachability_scenario(const reachability_cfg_t *cfg, int num_scenario) &mock_client_ns->valid_until); set_consensus_times(cfg->client_valid_until, &mock_client_ns->fresh_until); - dirvote_recalculate_timing(get_options(), mock_client_ns->valid_after); + voting_schedule_recalculate_timing(get_options(), + mock_client_ns->valid_after); /* New time period checks for this scenario. */ tt_int_op(hs_in_period_between_tp_and_srv(mock_service_ns, 0), OP_EQ, @@ -1563,7 +1563,7 @@ helper_set_consensus_and_system_time(networkstatus_t *ns, int position) } else { tt_assert(0); } - dirvote_recalculate_timing(get_options(), ns->valid_after); + voting_schedule_recalculate_timing(get_options(), ns->valid_after); /* Set system time: pretend to be just 2 minutes before consensus expiry */ real_time = ns->valid_until - 120; diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c index 207a55de6d..308843e9b8 100644 --- a/src/test/test_hs_control.c +++ b/src/test/test_hs_control.c @@ -76,9 +76,8 @@ mock_node_get_by_id(const char *digest) { static node_t node; memcpy(node.identity, digest, DIGEST_LEN); - node.hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t)); - memset(node.hsdir_index->fetch, 'C', DIGEST256_LEN); - memset(node.hsdir_index->store_first, 'D', DIGEST256_LEN); + memset(node.hsdir_index.fetch, 'C', DIGEST256_LEN); + memset(node.hsdir_index.store_first, 'D', DIGEST256_LEN); return &node; } diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 481521520c..d2198c621b 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -34,7 +34,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "crypto.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "networkstatus.h" #include "nodelist.h" #include "relay.h" @@ -51,7 +51,8 @@ #include "main.h" #include "rendservice.h" #include "statefile.h" -#include "shared_random_state.h" +#include "dirauth/shared_random_state.h" +#include "voting_schedule.h" /* Trunnel */ #include "hs/cell_establish_intro.h" @@ -1057,7 +1058,7 @@ test_rotate_descriptors(void *arg) ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), mock_ns.valid_after); + voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); /* Create a service with a default descriptor and state. It's added to the * global map. */ @@ -1095,7 +1096,7 @@ test_rotate_descriptors(void *arg) ret = parse_rfc1123_time("Sat, 27 Oct 1985 02:00:00 UTC", &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), mock_ns.valid_after); + voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); /* Note down what to expect for the next rotation time which is 01:00 + 23h * meaning 00:00:00. */ @@ -1157,7 +1158,7 @@ test_build_update_descriptors(void *arg) ret = parse_rfc1123_time("Sat, 26 Oct 1985 04:00:00 UTC", &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), mock_ns.valid_after); + voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); /* Create a service without a current descriptor to trigger a build. */ service = helper_create_service(); diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index 3d02fc1a59..4b168f49ed 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -5,7 +5,8 @@ #include "or.h" #include "config.h" -#include "dirvote.h" +#define DIRVOTE_PRIVATE +#include "dirauth/dirvote.h" #include "microdesc.h" #include "networkstatus.h" #include "routerlist.h" diff --git a/src/test/test_options.c b/src/test/test_options.c index 9974ed2575..65564f324c 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -2067,12 +2067,12 @@ test_options_validate__testing(void *ignored) ENSURE_DEFAULT(TestingV3AuthVotingStartOffset, 3000); ENSURE_DEFAULT(TestingAuthDirTimeToLearnReachability, 3000); ENSURE_DEFAULT(TestingEstimatedDescriptorPropagationTime, 3000); - ENSURE_DEFAULT(TestingServerDownloadSchedule, 3000); - ENSURE_DEFAULT(TestingClientDownloadSchedule, 3000); - ENSURE_DEFAULT(TestingServerConsensusDownloadSchedule, 3000); - ENSURE_DEFAULT(TestingClientConsensusDownloadSchedule, 3000); - ENSURE_DEFAULT(TestingBridgeDownloadSchedule, 3000); - ENSURE_DEFAULT(TestingBridgeBootstrapDownloadSchedule, 3000); + ENSURE_DEFAULT(TestingServerDownloadInitialDelay, 3000); + ENSURE_DEFAULT(TestingClientDownloadInitialDelay, 3000); + ENSURE_DEFAULT(TestingServerConsensusDownloadInitialDelay, 3000); + ENSURE_DEFAULT(TestingClientConsensusDownloadInitialDelay, 3000); + ENSURE_DEFAULT(TestingBridgeDownloadInitialDelay, 3000); + ENSURE_DEFAULT(TestingBridgeBootstrapDownloadInitialDelay, 3000); ENSURE_DEFAULT(TestingClientMaxIntervalWithoutRequest, 3000); ENSURE_DEFAULT(TestingDirConnectionMaxStall, 3000); ENSURE_DEFAULT(TestingAuthKeyLifetime, 3000); diff --git a/src/test/test_periodic_event.c b/src/test/test_periodic_event.c index bebbb5e584..34689b64f4 100644 --- a/src/test/test_periodic_event.c +++ b/src/test/test_periodic_event.c @@ -69,7 +69,7 @@ test_pe_initialize(void *arg) static void test_pe_launch(void *arg) { - hs_service_t service; + hs_service_t service, *to_remove = NULL; or_options_t *options; (void) arg; @@ -118,6 +118,10 @@ test_pe_launch(void *arg) options->ORPort_set = 1; periodic_events_on_new_options(options); + unsigned roles = get_my_roles(options); + tt_uint_op(roles, OP_EQ, + PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER); + for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; /* Only Client role should be disabled. */ @@ -130,8 +134,8 @@ test_pe_launch(void *arg) tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1); tt_u64_op(item->last_action_time, OP_NE, 0); } - /* Non Relay role should be disabled! */ - if (!(item->roles & PERIODIC_EVENT_ROLE_RELAY)) { + /* Non Relay role should be disabled, except for Dirserver. */ + if (!(item->roles & roles)) { tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); } } @@ -152,8 +156,11 @@ test_pe_launch(void *arg) options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1; register_dummy_hidden_service(&service); periodic_events_on_new_options(options); - /* Remove it now so the hs_free_all() doesn't try to free stack memory. */ - remove_service(get_hs_service_map(), &service); + /* Note down the reference because we need to remove this service from the + * global list before the hs_free_all() call so it doesn't try to free + * memory on the stack. Furthermore, we can't remove it now else it will + * trigger a rescan of the event disabling the HS service event. */ + to_remove = &service; for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; @@ -161,6 +168,9 @@ test_pe_launch(void *arg) } done: + if (to_remove) { + remove_service(get_hs_service_map(), to_remove); + } hs_free_all(); } @@ -190,19 +200,21 @@ test_pe_get_roles(void *arg) options->ORPort_set = 1; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, - (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY)); + (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY | + PERIODIC_EVENT_ROLE_DIRSERVER)); /* Now add a Bridge. */ options->BridgeRelay = 1; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY | - PERIODIC_EVENT_ROLE_BRIDGE)); + PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER)); tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER); /* Unset client so we can solely test Router role. */ options->SocksPort_set = 0; roles = get_my_roles(options); - tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_ROUTER); + tt_int_op(roles, OP_EQ, + PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER); /* Reset options so we can test authorities. */ options->SocksPort_set = 0; @@ -212,24 +224,28 @@ test_pe_get_roles(void *arg) tt_int_op(roles, OP_EQ, 0); /* Now upgrade to Dirauth. */ + options->DirPort_set = 1; options->AuthoritativeDir = 1; options->V3AuthoritativeDir = 1; roles = get_my_roles(options); - tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_DIRAUTH); + tt_int_op(roles, OP_EQ, + PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER); tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); /* Now Bridge Authority. */ options->V3AuthoritativeDir = 0; options->BridgeAuthoritativeDir = 1; roles = get_my_roles(options); - tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_BRIDGEAUTH); + tt_int_op(roles, OP_EQ, + PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER); tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); /* Move that bridge auth to become a relay. */ options->ORPort_set = 1; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, - (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY)); + (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY + | PERIODIC_EVENT_ROLE_DIRSERVER)); tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); /* And now an Hidden service. */ @@ -240,13 +256,69 @@ test_pe_get_roles(void *arg) remove_service(get_hs_service_map(), &service); tt_int_op(roles, OP_EQ, (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY | - PERIODIC_EVENT_ROLE_HS_SERVICE)); + PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER)); tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); done: hs_free_all(); } +static void +test_pe_hs_service(void *arg) +{ + hs_service_t service, *to_remove = NULL; + + (void) arg; + + hs_init(); + /* We need to put tor in hibernation live state so the events requiring + * network gets enabled. */ + consider_hibernation(time(NULL)); + /* Initialize the events so we can enable them */ + initialize_periodic_events(); + + /* Hack: We'll set a dumb fn() of each events so they don't get called when + * dispatching them. We just want to test the state of the callbacks, not + * the whole code path. */ + for (int i = 0; periodic_events[i].name; ++i) { + periodic_event_item_t *item = &periodic_events[i]; + item->fn = dumb_event_fn; + } + + /* This should trigger a rescan of the list and enable the HS service + * events. */ + register_dummy_hidden_service(&service); + /* Note down the reference because we need to remove this service from the + * global list before the hs_free_all() call so it doesn't try to free + * memory on the stack. Furthermore, we can't remove it now else it will + * trigger a rescan of the event disabling the HS service event. */ + to_remove = &service; + + for (int i = 0; periodic_events[i].name; ++i) { + periodic_event_item_t *item = &periodic_events[i]; + if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) { + tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1); + } + } + to_remove = NULL; + + /* Remove the service from the global map, it should trigger a rescan and + * disable the HS service events. */ + remove_service(get_hs_service_map(), &service); + for (int i = 0; periodic_events[i].name; ++i) { + periodic_event_item_t *item = &periodic_events[i]; + if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) { + tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); + } + } + + done: + if (to_remove) { + remove_service(get_hs_service_map(), to_remove); + } + hs_free_all(); +} + #define PE_TEST(name) \ { #name, test_pe_## name , TT_FORK, NULL, NULL } @@ -254,6 +326,7 @@ struct testcase_t periodic_event_tests[] = { PE_TEST(initialize), PE_TEST(launch), PE_TEST(get_roles), + PE_TEST(hs_service), END_OF_TESTCASES }; diff --git a/src/test/test_policy.c b/src/test/test_policy.c index f8aa8ac40b..f180585861 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -1923,11 +1923,8 @@ test_policies_fascist_firewall_allows_address(void *arg) tor_addr_port_t chosen_rs_ap; \ tor_addr_make_null(&chosen_rs_ap.addr, AF_INET); \ chosen_rs_ap.port = 0; \ - tt_int_op(fascist_firewall_choose_address_rs(&(fake_rs), \ - (fw_connection), \ - (pref_only), \ - &chosen_rs_ap), \ - OP_EQ, (expect_rv)); \ + fascist_firewall_choose_address_rs(&(fake_rs), (fw_connection), \ + (pref_only), &chosen_rs_ap); \ tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_rs_ap.addr)); \ tt_int_op((expect_ap).port, OP_EQ, chosen_rs_ap.port); \ STMT_END @@ -1940,11 +1937,8 @@ test_policies_fascist_firewall_allows_address(void *arg) tor_addr_port_t chosen_node_ap; \ tor_addr_make_null(&chosen_node_ap.addr, AF_INET); \ chosen_node_ap.port = 0; \ - tt_int_op(fascist_firewall_choose_address_node(&(fake_node), \ - (fw_connection), \ - (pref_only), \ - &chosen_node_ap), \ - OP_EQ, (expect_rv)); \ + fascist_firewall_choose_address_node(&(fake_node),(fw_connection), \ + (pref_only), &chosen_node_ap); \ tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_node_ap.addr)); \ tt_int_op((expect_ap).port, OP_EQ, chosen_node_ap.port); \ STMT_END diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index c19d66ef9d..71b487f35b 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -19,7 +19,7 @@ #include "container.h" #include "control.h" #include "directory.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "entrynodes.h" #include "hibernate.h" #include "microdesc.h" @@ -30,7 +30,7 @@ #include "routerlist.h" #include "routerset.h" #include "routerparse.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #include "statefile.h" #include "test.h" #include "test_dir_common.h" diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index 4fe9ee45f0..775a1c1d23 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -9,15 +9,17 @@ #include "or.h" #include "test.h" #include "config.h" -#include "dirvote.h" -#include "shared_random.h" -#include "shared_random_state.h" +#include "dirauth/dirvote.h" +#include "dirauth/shared_random.h" +#include "dirauth/shared_random_state.h" #include "routerkeys.h" #include "routerlist.h" #include "router.h" #include "routerparse.h" +#include "shared_random_client.h" #include "networkstatus.h" #include "log_test_helpers.h" +#include "voting_schedule.h" static authority_cert_t *mock_cert; @@ -170,7 +172,7 @@ test_get_state_valid_until_time(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), current_time); + voting_schedule_recalculate_timing(get_options(), current_time); valid_until_time = get_state_valid_until_time(current_time); /* Compare it with the correct result */ @@ -182,7 +184,7 @@ test_get_state_valid_until_time(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 19:22:00 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), current_time); + voting_schedule_recalculate_timing(get_options(), current_time); valid_until_time = get_state_valid_until_time(current_time); format_iso_time(tbuf, valid_until_time); @@ -193,7 +195,7 @@ test_get_state_valid_until_time(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:00 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), current_time); + voting_schedule_recalculate_timing(get_options(), current_time); valid_until_time = get_state_valid_until_time(current_time); format_iso_time(tbuf, valid_until_time); @@ -204,7 +206,7 @@ test_get_state_valid_until_time(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), current_time); + voting_schedule_recalculate_timing(get_options(), current_time); valid_until_time = get_state_valid_until_time(current_time); format_iso_time(tbuf, valid_until_time); @@ -242,7 +244,7 @@ test_get_start_time_of_current_run(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), current_time); + voting_schedule_recalculate_timing(get_options(), current_time); run_start_time = sr_state_get_start_time_of_current_protocol_run(current_time); @@ -255,7 +257,7 @@ test_get_start_time_of_current_run(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:59 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), current_time); + voting_schedule_recalculate_timing(get_options(), current_time); run_start_time = sr_state_get_start_time_of_current_protocol_run(current_time); @@ -268,7 +270,7 @@ test_get_start_time_of_current_run(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), current_time); + voting_schedule_recalculate_timing(get_options(), current_time); run_start_time = sr_state_get_start_time_of_current_protocol_run(current_time); @@ -291,7 +293,7 @@ test_get_start_time_of_current_run(void *arg) retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:15:32 UTC", ¤t_time); tt_int_op(retval, OP_EQ, 0); - dirvote_recalculate_timing(get_options(), current_time); + voting_schedule_recalculate_timing(get_options(), current_time); run_start_time = sr_state_get_start_time_of_current_protocol_run(current_time); @@ -324,7 +326,7 @@ test_get_start_time_functions(void *arg) tt_int_op(retval, OP_EQ, 0); time_t now = mock_consensus.valid_after; - dirvote_recalculate_timing(get_options(), now); + voting_schedule_recalculate_timing(get_options(), now); time_t start_time_of_protocol_run = sr_state_get_start_time_of_current_protocol_run(now); tt_assert(start_time_of_protocol_run); diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 29f7cc9c37..ef1be139a6 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -203,6 +203,17 @@ test_tortls_tor_tls_get_error(void *data) } static void +library_init(void) +{ +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); +#else + SSL_library_init(); + SSL_load_error_strings(); +#endif +} + +static void test_tortls_get_state_description(void *ignored) { (void)ignored; @@ -210,9 +221,7 @@ test_tortls_get_state_description(void *ignored) char *buf; SSL_CTX *ctx; - SSL_library_init(); - SSL_load_error_strings(); - + library_init(); ctx = SSL_CTX_new(SSLv23_method()); buf = tor_malloc_zero(1000); @@ -274,8 +283,7 @@ test_tortls_get_by_ssl(void *ignored) SSL_CTX *ctx; SSL *ssl; - SSL_library_init(); - SSL_load_error_strings(); + library_init(); tor_tls_allocate_tor_tls_object_ex_data_index(); ctx = SSL_CTX_new(SSLv23_method()); @@ -322,8 +330,7 @@ test_tortls_log_one_error(void *ignored) SSL_CTX *ctx; SSL *ssl = NULL; - SSL_library_init(); - SSL_load_error_strings(); + library_init(); ctx = SSL_CTX_new(SSLv23_method()); tls = tor_malloc_zero(sizeof(tor_tls_t)); @@ -415,8 +422,7 @@ test_tortls_get_error(void *ignored) int ret; SSL_CTX *ctx; - SSL_library_init(); - SSL_load_error_strings(); + library_init(); ctx = SSL_CTX_new(SSLv23_method()); setup_capture_of_logs(LOG_INFO); @@ -792,8 +798,8 @@ test_tortls_classify_client_ciphers(void *ignored) STACK_OF(SSL_CIPHER) *ciphers; SSL_CIPHER *tmp_cipher; - SSL_library_init(); - SSL_load_error_strings(); + library_init(); + tor_tls_allocate_tor_tls_object_ex_data_index(); tls = tor_malloc_zero(sizeof(tor_tls_t)); @@ -897,8 +903,7 @@ test_tortls_client_is_using_v2_ciphers(void *ignored) SSL_SESSION *sess; STACK_OF(SSL_CIPHER) *ciphers; - SSL_library_init(); - SSL_load_error_strings(); + library_init(); ctx = SSL_CTX_new(TLSv1_method()); ssl = SSL_new(ctx); @@ -1541,8 +1546,8 @@ test_tortls_session_secret_cb(void *ignored) STACK_OF(SSL_CIPHER) *ciphers = NULL; SSL_CIPHER *one; - SSL_library_init(); - SSL_load_error_strings(); + library_init(); + tor_tls_allocate_tor_tls_object_ex_data_index(); tls = tor_malloc_zero(sizeof(tor_tls_t)); @@ -1733,8 +1738,7 @@ test_tortls_find_cipher_by_id(void *ignored) fixed_cipher2 = tor_malloc_zero(sizeof(SSL_CIPHER)); fixed_cipher2->id = 0xC00A; - SSL_library_init(); - SSL_load_error_strings(); + library_init(); ctx = SSL_CTX_new(m); ssl = SSL_new(ctx); @@ -1825,8 +1829,7 @@ test_tortls_server_info_callback(void *ignored) SSL_CTX *ctx; SSL *ssl; - SSL_library_init(); - SSL_load_error_strings(); + library_init(); ctx = SSL_CTX_new(TLSv1_method()); ssl = SSL_new(ctx); diff --git a/src/test/test_util.c b/src/test/test_util.c index 350273bf4c..9c028fd47d 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -6035,6 +6035,9 @@ test_util_monotonic_time_add_msec(void *arg) monotime_coarse_add_msec(&ct2, &ct1, 1337); tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337); tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337); + // The 32-bit variant must be within 1% of the regular one. + tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_GT, 1323); + tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_LT, 1350); /* Add 1337 msec twice more; make sure that any second rollover issues * worked. */ @@ -6044,6 +6047,8 @@ test_util_monotonic_time_add_msec(void *arg) monotime_coarse_add_msec(&ct2, &ct2, 1337); tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337*3); tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337*3); + tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_GT, 3970); + tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_LT, 4051); done: ; |