aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/channel.c4
-rw-r--r--src/or/circuitbuild.c9
-rw-r--r--src/or/circuitlist.c1
-rw-r--r--src/or/circuitstats.c3
-rw-r--r--src/or/config.c134
-rw-r--r--src/or/confparse.c80
-rw-r--r--src/or/confparse.h6
-rw-r--r--src/or/consdiffmgr.c33
-rw-r--r--src/or/control.c2
-rw-r--r--src/or/dirauth/dircollate.c (renamed from src/or/dircollate.c)0
-rw-r--r--src/or/dirauth/dircollate.h (renamed from src/or/dircollate.h)0
-rw-r--r--src/or/dirauth/dirvote.c (renamed from src/or/dirvote.c)1060
-rw-r--r--src/or/dirauth/dirvote.h (renamed from src/or/dirvote.h)201
-rw-r--r--src/or/dirauth/shared_random.c (renamed from src/or/shared_random.c)169
-rw-r--r--src/or/dirauth/shared_random.h (renamed from src/or/shared_random.h)39
-rw-r--r--src/or/dirauth/shared_random_state.c (renamed from src/or/shared_random_state.c)86
-rw-r--r--src/or/dirauth/shared_random_state.h (renamed from src/or/shared_random_state.h)5
-rw-r--r--src/or/directory.c119
-rw-r--r--src/or/directory.h4
-rw-r--r--src/or/dirserv.c584
-rw-r--r--src/or/dirserv.h15
-rw-r--r--src/or/entrynodes.c16
-rw-r--r--src/or/hibernate.c10
-rw-r--r--src/or/hs_common.c31
-rw-r--r--src/or/hs_common.h13
-rw-r--r--src/or/hs_control.c3
-rw-r--r--src/or/hs_service.c34
-rw-r--r--src/or/hs_service.h1
-rw-r--r--src/or/include.am43
-rw-r--r--src/or/main.c249
-rw-r--r--src/or/main.h5
-rw-r--r--src/or/networkstatus.c46
-rw-r--r--src/or/networkstatus.h7
-rw-r--r--src/or/nodelist.c16
-rw-r--r--src/or/or.h47
-rw-r--r--src/or/parsecommon.c2
-rw-r--r--src/or/parsecommon.h2
-rw-r--r--src/or/periodic.h16
-rw-r--r--src/or/policies.c98
-rw-r--r--src/or/policies.h14
-rw-r--r--src/or/rendservice.c11
-rw-r--r--src/or/rephist.c2
-rw-r--r--src/or/routerlist.c3
-rw-r--r--src/or/routerparse.c71
-rw-r--r--src/or/shared_random_client.c259
-rw-r--r--src/or/shared_random_client.h47
-rw-r--r--src/or/statefile.c13
-rw-r--r--src/or/statefile.h1
-rw-r--r--src/or/voting_schedule.c166
-rw-r--r--src/or/voting_schedule.h61
50 files changed, 2167 insertions, 1674 deletions
diff --git a/src/or/channel.c b/src/or/channel.c
index 68245db497..c30e508018 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -69,6 +69,7 @@
#include "circuitmux.h"
#include "entrynodes.h"
#include "geoip.h"
+#include "main.h"
#include "nodelist.h"
#include "relay.h"
#include "rephist.h"
@@ -404,6 +405,7 @@ channel_register(channel_t *chan)
/* Put it in the finished list, creating it if necessary */
if (!finished_channels) finished_channels = smartlist_new();
smartlist_add(finished_channels, chan);
+ mainloop_schedule_postloop_cleanup();
} else {
/* Put it in the active list, creating it if necessary */
if (!active_channels) active_channels = smartlist_new();
@@ -1548,6 +1550,7 @@ channel_change_state_(channel_t *chan, channel_state_t to_state)
if (active_channels) smartlist_remove(active_channels, chan);
if (!finished_channels) finished_channels = smartlist_new();
smartlist_add(finished_channels, chan);
+ mainloop_schedule_postloop_cleanup();
}
/* Need to put on active list? */
else if (!was_active && is_active) {
@@ -1666,6 +1669,7 @@ channel_listener_change_state(channel_listener_t *chan_l,
if (active_listeners) smartlist_remove(active_listeners, chan_l);
if (!finished_listeners) finished_listeners = smartlist_new();
smartlist_add(finished_listeners, chan_l);
+ mainloop_schedule_postloop_cleanup();
}
/* Need to put on active list? */
else if (!was_active && is_active) {
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 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/circuitlist.c b/src/or/circuitlist.c
index 9a82713cbe..ea2f2c15c5 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -2067,6 +2067,7 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
circuits_pending_close = smartlist_new();
smartlist_add(circuits_pending_close, circ);
+ mainloop_schedule_postloop_cleanup();
log_info(LD_GENERAL, "Circuit %u (id: %" PRIu32 ") marked for close at "
"%s:%d (orig reason: %d, new reason: %d)",
diff --git a/src/or/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/consdiffmgr.c b/src/or/consdiffmgr.c
index 02b905a520..323f4f9ca0 100644
--- a/src/or/consdiffmgr.c
+++ b/src/or/consdiffmgr.c
@@ -99,6 +99,14 @@ static const compress_method_t compress_diffs_with[] = {
#endif
};
+/**
+ * Event for rescanning the cache.
+ */
+static mainloop_event_t *consdiffmgr_rescan_ev = NULL;
+
+static void consdiffmgr_rescan_cb(mainloop_event_t *ev, void *arg);
+static void mark_cdm_cache_dirty(void);
+
/** How many different methods will we try to use for diff compression? */
STATIC unsigned
n_diff_compression_methods(void)
@@ -372,7 +380,9 @@ cdm_cache_init(void)
} else {
consdiffmgr_set_cache_flags();
}
- cdm_cache_dirty = 1;
+ consdiffmgr_rescan_ev =
+ mainloop_event_postloop_new(consdiffmgr_rescan_cb, NULL);
+ mark_cdm_cache_dirty();
cdm_cache_loaded = 0;
}
@@ -1095,6 +1105,24 @@ consdiffmgr_rescan(void)
cdm_cache_dirty = 0;
}
+/** Callback wrapper for consdiffmgr_rescan */
+static void
+consdiffmgr_rescan_cb(mainloop_event_t *ev, void *arg)
+{
+ (void)ev;
+ (void)arg;
+ consdiffmgr_rescan();
+}
+
+/** Mark the cache as dirty, and schedule a rescan event. */
+static void
+mark_cdm_cache_dirty(void)
+{
+ cdm_cache_dirty = 1;
+ tor_assert(consdiffmgr_rescan_ev);
+ mainloop_event_activate(consdiffmgr_rescan_ev);
+}
+
/**
* Helper: compare two files by their from-valid-after and valid-after labels,
* trying to sort in ascending order by from-valid-after (when present) and
@@ -1219,6 +1247,7 @@ consdiffmgr_free_all(void)
memset(latest_consensus, 0, sizeof(latest_consensus));
consensus_cache_free(cons_diff_cache);
cons_diff_cache = NULL;
+ mainloop_event_free(consdiffmgr_rescan_ev);
}
/* =====
@@ -1750,7 +1779,7 @@ consensus_compress_worker_replyfn(void *work_)
compress_consensus_with,
job->out,
"consensus");
- cdm_cache_dirty = 1;
+ mark_cdm_cache_dirty();
unsigned u;
consensus_flavor_t f = job->flavor;
diff --git a/src/or/control.c b/src/or/control.c
index 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..fd629ca6ff 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-gove 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..61383aa861 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
@@ -2857,286 +2625,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/hibernate.c b/src/or/hibernate.c
index 9fed338555..c4ae63acc4 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -1111,10 +1111,18 @@ getinfo_helper_accounting(control_connection_t *conn,
static void
on_hibernate_state_change(hibernate_state_t prev_state)
{
- (void)prev_state; /* Should we do something with this? */
control_event_server_status(LOG_NOTICE,
"HIBERNATION_STATUS STATUS=%s",
hibernate_state_to_string(hibernate_state));
+
+ /* We are changing hibernation state, this can affect the main loop event
+ * list. Rescan it to update the events state. We do this whatever the new
+ * hibernation state because they can each possibly affect an event. The
+ * initial state means we are booting up so we shouldn't scan here because
+ * at this point the events in the list haven't been initialized. */
+ if (prev_state != HIBERNATE_STATE_INITIAL) {
+ rescan_periodic_events(get_options());
+ }
}
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index 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..e40e9203e7 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>. */
@@ -916,6 +928,11 @@ register_all_services(void)
smartlist_clear(hs_service_staging_list);
service_free_all();
hs_service_map = new_service_map;
+ /* We've just register services into the new map and now we've replaced the
+ * global map with it so we have to notify that the change happened. When
+ * registering a service, the notify is only triggered if the destination
+ * map is the global map for which in here it was not. */
+ hs_service_map_has_changed();
}
/* Write the onion address of a given service to the given filename fname_ in
@@ -2287,8 +2304,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 +2953,17 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list)
/* Public API */
/* ========== */
+/* This is called everytime the service map (v2 or v3) changes that is if an
+ * element is added or removed. */
+void
+hs_service_map_has_changed(void)
+{
+ /* If we now have services where previously we had not, we need to enable
+ * the HS service main loop event. If we changed to having no services, we
+ * need to disable the event. */
+ rescan_periodic_events(get_options());
+}
+
/* Upload an encoded descriptor in encoded_desc of the given version. This
* descriptor is for the service identity_pk and blinded_pk used to setup the
* directory connection identifier. It is uploaded to the directory hsdir_rs
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index d163eeef28..2e27d8a899 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -260,6 +260,7 @@ void hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
int hs_service_set_conn_addr_port(const origin_circuit_t *circ,
edge_connection_t *conn);
+void hs_service_map_has_changed(void);
void hs_service_dir_info_changed(void);
void hs_service_run_scheduled_events(time_t now);
void hs_service_circuit_has_opened(origin_circuit_t *circ);
diff --git a/src/or/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 ee6a2459a9..cf0df9ba79 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -73,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"
@@ -104,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"
@@ -119,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
@@ -151,7 +152,6 @@ static int run_main_loop_until_done(void);
static void process_signal(int sig);
static void shutdown_did_not_work_callback(evutil_socket_t fd, short event,
void *arg) ATTR_NORETURN;
-static void rescan_periodic_events(const or_options_t *options);
/********* START VARIABLES **********/
@@ -447,6 +447,7 @@ add_connection_to_closeable_list(connection_t *conn)
tor_assert(conn->marked_for_close);
assert_connection_ok(conn, time(NULL));
smartlist_add(closeable_connection_lst, conn);
+ mainloop_schedule_postloop_cleanup();
}
/** Return 1 if conn is on the closeable list, else return 0. */
@@ -1341,6 +1342,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);
@@ -1356,60 +1358,76 @@ 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);
#undef CALLBACK
/* Now we declare an array of periodic_event_item_t for each periodic event */
-#define CALLBACK(name, r) PERIODIC_EVENT(name, r)
+#define CALLBACK(name, r, f) PERIODIC_EVENT(name, r, f)
STATIC periodic_event_item_t periodic_events[] = {
/* Everyone needs to run those. */
- CALLBACK(add_entropy, PERIODIC_EVENT_ROLE_ALL),
- CALLBACK(check_expired_networkstatus, PERIODIC_EVENT_ROLE_ALL),
- CALLBACK(clean_caches, PERIODIC_EVENT_ROLE_ALL),
- CALLBACK(fetch_networkstatus, PERIODIC_EVENT_ROLE_ALL),
- CALLBACK(heartbeat, PERIODIC_EVENT_ROLE_ALL),
- CALLBACK(launch_descriptor_fetches, PERIODIC_EVENT_ROLE_ALL),
- CALLBACK(reset_padding_counts, PERIODIC_EVENT_ROLE_ALL),
- CALLBACK(retry_listeners, PERIODIC_EVENT_ROLE_ALL),
- CALLBACK(rotate_x509_certificate, PERIODIC_EVENT_ROLE_ALL),
- CALLBACK(write_stats_file, PERIODIC_EVENT_ROLE_ALL),
+ CALLBACK(add_entropy, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(check_expired_networkstatus, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(clean_caches, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(fetch_networkstatus, PERIODIC_EVENT_ROLE_ALL,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(heartbeat, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(launch_descriptor_fetches, PERIODIC_EVENT_ROLE_ALL,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(reset_padding_counts, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(retry_listeners, PERIODIC_EVENT_ROLE_ALL,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(save_state, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(rotate_x509_certificate, PERIODIC_EVENT_ROLE_ALL, 0),
+ CALLBACK(write_stats_file, PERIODIC_EVENT_ROLE_ALL, 0),
/* Routers (bridge and relay) only. */
- CALLBACK(check_descriptor, PERIODIC_EVENT_ROLE_ROUTER),
- CALLBACK(check_ed_keys, PERIODIC_EVENT_ROLE_ROUTER),
- CALLBACK(check_for_reachability_bw, PERIODIC_EVENT_ROLE_ROUTER),
- CALLBACK(check_onion_keys_expiry_time, PERIODIC_EVENT_ROLE_ROUTER),
- CALLBACK(clean_consdiffmgr, PERIODIC_EVENT_ROLE_ROUTER),
- CALLBACK(expire_old_ciruits_serverside, PERIODIC_EVENT_ROLE_ROUTER),
- CALLBACK(retry_dns, PERIODIC_EVENT_ROLE_ROUTER),
- CALLBACK(rotate_onion_key, PERIODIC_EVENT_ROLE_ROUTER),
+ CALLBACK(check_descriptor, PERIODIC_EVENT_ROLE_ROUTER,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(check_ed_keys, PERIODIC_EVENT_ROLE_ROUTER, 0),
+ CALLBACK(check_for_reachability_bw, PERIODIC_EVENT_ROLE_ROUTER,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(check_onion_keys_expiry_time, PERIODIC_EVENT_ROLE_ROUTER, 0),
+ CALLBACK(expire_old_ciruits_serverside, PERIODIC_EVENT_ROLE_ROUTER,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(retry_dns, PERIODIC_EVENT_ROLE_ROUTER, 0),
+ CALLBACK(rotate_onion_key, PERIODIC_EVENT_ROLE_ROUTER, 0),
/* Authorities (bridge and directory) only. */
- CALLBACK(downrate_stability, PERIODIC_EVENT_ROLE_AUTHORITIES),
- CALLBACK(launch_reachability_tests, PERIODIC_EVENT_ROLE_AUTHORITIES),
- CALLBACK(save_stability, PERIODIC_EVENT_ROLE_AUTHORITIES),
+ CALLBACK(downrate_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0),
+ CALLBACK(launch_reachability_tests, PERIODIC_EVENT_ROLE_AUTHORITIES,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(save_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0),
/* Directory authority only. */
- CALLBACK(check_authority_cert, PERIODIC_EVENT_ROLE_DIRAUTH),
+ 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),
- CALLBACK(check_dns_honesty, PERIODIC_EVENT_ROLE_RELAY),
+ CALLBACK(check_canonical_channels, PERIODIC_EVENT_ROLE_RELAY,
+ PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(check_dns_honesty, PERIODIC_EVENT_ROLE_RELAY,
+ PERIODIC_EVENT_FLAG_NEED_NET),
/* Hidden Service service only. */
- CALLBACK(hs_service, PERIODIC_EVENT_ROLE_HS_SERVICE),
+ CALLBACK(hs_service, PERIODIC_EVENT_ROLE_HS_SERVICE,
+ PERIODIC_EVENT_FLAG_NEED_NET),
/* Bridge only. */
- CALLBACK(record_bridge_stats, PERIODIC_EVENT_ROLE_BRIDGE),
+ CALLBACK(record_bridge_stats, PERIODIC_EVENT_ROLE_BRIDGE, 0),
/* Client only. */
- CALLBACK(rend_cache_failure_clean, PERIODIC_EVENT_ROLE_CLIENT),
+ CALLBACK(rend_cache_failure_clean, PERIODIC_EVENT_ROLE_CLIENT, 0),
/* Bridge Authority only. */
- CALLBACK(write_bridge_ns, PERIODIC_EVENT_ROLE_BRIDGEAUTH),
+ 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
@@ -1419,9 +1437,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.
@@ -1466,6 +1486,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;
@@ -1473,6 +1494,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;
}
@@ -1515,9 +1537,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(
@@ -1538,16 +1562,29 @@ teardown_periodic_events(void)
/** Do a pass at all our periodic events, disable those we don't need anymore
* and enable those we need now using the given options. */
-static void
+void
rescan_periodic_events(const or_options_t *options)
{
tor_assert(options);
+ /* Avoid scanning the event list if we haven't initialized it yet. This is
+ * particularly useful for unit tests in order to avoid initializing main
+ * loop events everytime. */
+ if (!periodic_events_initialized) {
+ return;
+ }
+
int roles = get_my_roles(options);
for (int i = 0; periodic_events[i].name; ++i) {
periodic_event_item_t *item = &periodic_events[i];
+ /* Handle the event flags. */
+ if (net_is_disabled() &&
+ (item->flags & PERIODIC_EVENT_FLAG_NEED_NET)) {
+ continue;
+ }
+
/* Enable the event if needed. It is safe to enable an event that was
* already enabled. Same goes for disabling it. */
if (item->roles & roles) {
@@ -1582,8 +1619,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);
+ }
}
/**
@@ -1600,6 +1638,30 @@ reschedule_directory_downloads(void)
periodic_event_reschedule(launch_descriptor_fetches_event);
}
+/** Mainloop callback: clean up circuits, channels, and connections
+ * that are pending close. */
+static void
+postloop_cleanup_cb(mainloop_event_t *ev, void *arg)
+{
+ (void)ev;
+ (void)arg;
+ circuit_close_all_marked();
+ close_closeable_connections();
+ channel_run_cleanup();
+ channel_listener_run_cleanup();
+}
+
+/** Event to run postloop_cleanup_cb */
+static mainloop_event_t *postloop_cleanup_ev=NULL;
+
+/** Schedule a post-loop event to clean up marked channels, connections, and
+ * circuits. */
+void
+mainloop_schedule_postloop_cleanup(void)
+{
+ mainloop_event_activate(postloop_cleanup_ev);
+}
+
#define LONGEST_TIMER_PERIOD (30 * 86400)
/** Helper: Return the number of seconds between <b>now</b> and <b>next</b>,
* clipped to the range [1 second, LONGEST_TIMER_PERIOD]. */
@@ -1661,10 +1723,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
@@ -1698,12 +1756,6 @@ run_scheduled_events(time_t now)
circuit_expire_old_circs_as_needed(now);
}
- if (!net_is_disabled()) {
- /* This is usually redundant with circuit_build_needed_circs() above,
- * but it is very fast when there is no work to do. */
- connection_ap_attach_pending(0);
- }
-
/* 5. We do housekeeping for each connection... */
channel_update_bad_for_new_circs(NULL, 0);
int i;
@@ -1711,32 +1763,9 @@ run_scheduled_events(time_t now)
run_connection_housekeeping(i, now);
}
- /* 6. And remove any marked circuits... */
- circuit_close_all_marked();
-
- /* 8. and blow away any connections that need to die. have to do this now,
- * because if we marked a conn for close and left its socket -1, then
- * we'll pass it to poll/select and bad things will happen.
- */
- close_closeable_connections();
-
- /* 8b. And if anything in our state is ready to get flushed to disk, we
- * flush it. */
- or_state_save(now);
-
- /* 8c. Do channel cleanup just like for connections */
- channel_run_cleanup();
- channel_listener_run_cleanup();
-
/* 11b. check pending unconfigured managed proxies */
if (!net_is_disabled() && pt_proxies_configuration_pending())
pt_configure_remaining_proxies();
-
- /* 12. launch diff computations. (This is free if there are none to
- * launch.) */
- if (dir_server_mode(options)) {
- consdiffmgr_rescan();
- }
}
/* Periodic callback: rotate the onion keys after the period defined by the
@@ -1946,6 +1975,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.
*/
@@ -1968,6 +2031,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
@@ -2319,7 +2410,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;
@@ -2612,6 +2703,20 @@ do_hup(void)
return 0;
}
+/** Initialize some mainloop_event_t objects that we require. */
+STATIC void
+initialize_mainloop_events(void)
+{
+ if (!schedule_active_linked_connections_event) {
+ schedule_active_linked_connections_event =
+ mainloop_event_postloop_new(schedule_active_linked_connections_cb, NULL);
+ }
+ if (!postloop_cleanup_ev) {
+ postloop_cleanup_ev =
+ mainloop_event_postloop_new(postloop_cleanup_cb, NULL);
+ }
+}
+
/** Tor main loop. */
int
do_main_loop(void)
@@ -2625,10 +2730,7 @@ do_main_loop(void)
initialize_periodic_events();
}
- if (!schedule_active_linked_connections_event) {
- schedule_active_linked_connections_event =
- mainloop_event_postloop_new(schedule_active_linked_connections_cb, NULL);
- }
+ initialize_mainloop_events();
/* initialize dns resolve map, spawn workers if needed */
if (dns_init() < 0) {
@@ -3514,6 +3616,7 @@ tor_free_all(int postfork)
tor_event_free(initialize_periodic_events_event);
mainloop_event_free(directory_all_unreachable_cb_event);
mainloop_event_free(schedule_active_linked_connections_event);
+ mainloop_event_free(postloop_cleanup_ev);
#ifdef HAVE_SYSTEMD_209
periodic_timer_free(systemd_watchdog_timer);
diff --git a/src/or/main.h b/src/or/main.h
index 0e3de7d4be..a312b51e05 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -61,6 +61,10 @@ 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);
MOCK_DECL(long,get_uptime,(void));
MOCK_DECL(void,reset_uptime,(void));
@@ -96,6 +100,7 @@ extern token_bucket_rw_t global_relayed_bucket;
#ifdef MAIN_PRIVATE
STATIC void init_connection_lists(void);
+STATIC void initialize_mainloop_events(void);
STATIC void close_closeable_connections(void);
STATIC void initialize_periodic_events(void);
STATIC void teardown_periodic_events(void);
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 dde27db5af..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. */
@@ -29,6 +30,15 @@
(PERIODIC_EVENT_ROLE_AUTHORITIES | PERIODIC_EVENT_ROLE_CLIENT | \
PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_ROUTER)
+/*
+ * Event flags which can change the behavior of an event.
+ */
+
+/* Indicate that the event needs the network meaning that if we are in
+ * DisableNetwork or hibernation mode, the event won't be enabled. This obey
+ * the net_is_disabled() check. */
+#define PERIODIC_EVENT_FLAG_NEED_NET (1U << 0)
+
/** Callback function for a periodic event to take action. The return value
* influences the next time the function will get called. Return
* PERIODIC_EVENT_NO_UPDATE to not update <b>last_action_time</b> and be polled
@@ -49,13 +59,15 @@ typedef struct periodic_event_item_t {
/* Bitmask of roles define above for which this event applies. */
uint32_t roles;
+ /* Bitmask of flags which can change the behavior of the event. */
+ uint32_t flags;
/* Indicate that this event has been enabled that is scheduled. */
unsigned int enabled : 1;
} periodic_event_item_t;
/** events will get their interval from first execution */
-#define PERIODIC_EVENT(fn, r) { fn##_callback, 0, NULL, #fn, r, 0 }
-#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL, 0, 0 }
+#define PERIODIC_EVENT(fn, r, f) { fn##_callback, 0, NULL, #fn, r, f, 0 }
+#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL, 0, 0, 0 }
/* Return true iff the given event was setup before thus is enabled to be
* scheduled. */
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 */
+