summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2018-05-01 10:29:05 -0400
committerNick Mathewson <nickm@torproject.org>2018-05-01 10:29:05 -0400
commitd018bf199c9e566202eef0bb7fb68c2567c4a25e (patch)
tree83b81729b2c25a21bd2ef61e9350667d2fa56d7e /src
parent4cf6b67f5e83e1a26b4b2fbdb3b169c137a30687 (diff)
parentd8509b450a1de815399a42b5df8c6b25789429c7 (diff)
downloadtor-d018bf199c9e566202eef0bb7fb68c2567c4a25e.tar.gz
tor-d018bf199c9e566202eef0bb7fb68c2567c4a25e.zip
Merge remote-tracking branch 'dgoulet/ticket25610_034_01-squashed'
Diffstat (limited to 'src')
-rw-r--r--src/or/circuitstats.c3
-rw-r--r--src/or/config.c3
-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)988
-rw-r--r--src/or/dirauth/dirvote.h (renamed from src/or/dirvote.h)199
-rw-r--r--src/or/dirauth/shared_random.c (renamed from src/or/shared_random.c)167
-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)84
-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.c53
-rw-r--r--src/or/dirserv.c575
-rw-r--r--src/or/dirserv.h15
-rw-r--r--src/or/dirvote_common.c195
-rw-r--r--src/or/dirvote_common.h66
-rw-r--r--src/or/hs_common.c4
-rw-r--r--src/or/hs_service.c2
-rw-r--r--src/or/include.am41
-rw-r--r--src/or/main.c5
-rw-r--r--src/or/networkstatus.c28
-rw-r--r--src/or/networkstatus.h3
-rw-r--r--src/or/routerlist.c3
-rw-r--r--src/or/routerparse.c71
-rw-r--r--src/or/shared_random_common.c259
-rw-r--r--src/or/shared_random_common.h47
-rw-r--r--src/test/test_config.c2
-rw-r--r--src/test/test_dir.c4
-rw-r--r--src/test/test_dir_common.c3
-rw-r--r--src/test/test_dir_handle_get.c2
-rw-r--r--src/test/test_hs_common.c4
-rw-r--r--src/test/test_hs_service.c4
-rw-r--r--src/test/test_microdesc.c3
-rw-r--r--src/test/test_routerlist.c4
-rw-r--r--src/test/test_shared_random.c7
35 files changed, 1582 insertions, 1308 deletions
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 54f3930fc5..3719ac8847 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -79,7 +79,6 @@
#include "confparse.h"
#include "cpuworker.h"
#include "dirserv.h"
-#include "dirvote.h"
#include "dns.h"
#include "dos.h"
#include "entrynodes.h"
@@ -110,6 +109,8 @@
#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
diff --git a/src/or/control.c b/src/or/control.c
index dda8872182..aab854ecdd 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_common.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..dc978f26e9 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirauth/dirvote.c
@@ -10,8 +10,11 @@
#include "directory.h"
#include "dirserv.h"
#include "dirvote.h"
+#include "dirvote_common.h"
#include "microdesc.h"
#include "networkstatus.h"
+#include "nodelist.h"
+#include "parsecommon.h"
#include "policies.h"
#include "protover.h"
#include "rephist.h"
@@ -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
* =====*/
@@ -351,6 +378,53 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
* 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 +437,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 +1388,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 +2432,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 +2516,7 @@ networkstatus_add_detached_signatures(networkstatus_t *target,
continue;
}
- old_sig = voter_get_sig_by_algorithm(target_voter, sig->alg);
+ old_sig = dirvote_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 +2624,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,213 +2729,6 @@ 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. */
-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)
@@ -3798,7 +3651,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 +3746,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 +3854,653 @@ 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, 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, 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 =
+ 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;
}
diff --git a/src/or/dirvote.h b/src/or/dirauth/dirvote.h
index 8a317deb47..f69e872c8e 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
-void dirvote_free_all(void);
+/* 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
-/* 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);
+void dirvote_free_all(void);
-/* invoked on timers and by outside triggers. */
+void dirvote_parse_sr_commits(networkstatus_t *ns, 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 void
+dirvote_act(const or_options_t *options, time_t now)
+{
+ (void) options;
+ (void) now;
+}
+
+static inline void
+dirvote_free_all(void)
+{
+}
+
+static inline void
+dirvote_parse_sr_commits(networkstatus_t *ns, 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 */
+
+void dirvote_recalculate_timing(const or_options_t *options, time_t now);
/* 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..7e49204159 100644
--- a/src/or/shared_random.c
+++ b/src/or/dirauth/shared_random.c
@@ -91,14 +91,17 @@
#include "shared_random.h"
#include "config.h"
#include "confparse.h"
-#include "dirvote.h"
+#include "dirvote_common.h"
#include "networkstatus.h"
#include "routerkeys.h"
#include "router.h"
#include "routerlist.h"
#include "shared_random_state.h"
+#include "shared_random_common.h"
#include "util.h"
+#include "dirauth/dirvote.h"
+
/* String prefix of shared random values in votes/consensuses. */
static const char previous_srv_str[] = "shared-rand-previous-value";
static const char current_srv_str[] = "shared-rand-current-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.
*
@@ -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..ac8fb5c151 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 "dirvote_common.h"
#include "networkstatus.h"
#include "router.h"
#include "shared_random_state.h"
+#include "shared_random_common.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
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 6d38f752a2..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
@@ -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;
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 68727f0718..e7b55b152b 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -18,7 +18,7 @@
#include "control.h"
#include "directory.h"
#include "dirserv.h"
-#include "dirvote.h"
+#include "dirvote_common.h"
#include "hibernate.h"
#include "keypin.h"
#include "main.h"
@@ -34,6 +34,8 @@
#include "routerset.h"
#include "torcert.h"
+#include "dirauth/dirvote.h"
+
/**
* \file dirserv.c
* \brief Directory server core implementation. Manages directory
@@ -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,
@@ -921,7 +921,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 +1056,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 +1445,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 +1470,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 +1701,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 +1733,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 +1757,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 +1831,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 +2019,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 +2131,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 +2624,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/dirvote_common.c b/src/or/dirvote_common.c
new file mode 100644
index 0000000000..a28a441d64
--- /dev/null
+++ b/src/or/dirvote_common.c
@@ -0,0 +1,195 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dirvote_common.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 DIRVOTE_COMMON_PRIVATE
+#include "dirvote_common.h"
+
+#include "or.h"
+#include "config.h"
+#include "networkstatus.h"
+
+/* =====
+ * 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);
+}
+
+voting_schedule_t voting_schedule;
+
+/* Using the time <b>now</b>, return the next voting valid-after time. */
+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);
+}
+
+/** Return the signature made by <b>voter</b> using the algorithm
+ * <b>alg</b>, or NULL if none is found. */
+document_signature_t *
+dirvote_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;
+}
+
diff --git a/src/or/dirvote_common.h b/src/or/dirvote_common.h
new file mode 100644
index 0000000000..857b8497ee
--- /dev/null
+++ b/src/or/dirvote_common.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dirvote_common.h
+ * \brief Header file for dirvote_common.c.
+ **/
+
+#ifndef TOR_DIRVOTE_COMMON_H
+#define TOR_DIRVOTE_COMMON_H
+
+#include "or.h"
+
+/* Dirauth module. */
+#include "dirauth/dirvote.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 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);
+time_t dirvote_get_next_valid_after_time(void);
+
+document_signature_t *dirvote_get_voter_sig_by_alg(
+ const networkstatus_voter_info_t *voter,
+ digest_algorithm_t alg);
+
+#endif /* TOR_DIRVOTE_COMMON_H */
+
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index c01428099e..46903a2960 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_common.h"
+#include "dirauth/shared_random_state.h"
/* Trunnel */
#include "ed25519_cert.h"
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 4686eda0b1..c4a473a5f7 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_common.h"
#include "statefile.h"
#include "hs_circuit.h"
diff --git a/src/or/include.am b/src/or/include.am
index 9a68df5c3e..bb505937ac 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -41,10 +41,9 @@ 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/dirvote_common.c \
src/or/dns.c \
src/or/dnsserv.c \
src/or/dos.c \
@@ -76,8 +75,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,6 +104,7 @@ LIBTOR_A_SOURCES = \
src/or/scheduler.c \
src/or/scheduler_kist.c \
src/or/scheduler_vanilla.c \
+ src/or/shared_random_common.c \
src/or/statefile.c \
src/or/status.c \
src/or/torcert.c \
@@ -114,8 +112,25 @@ LIBTOR_A_SOURCES = \
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,9 @@ 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/dirvote_common.h \
src/or/dns.h \
src/or/dns_structs.h \
src/or/dnsserv.h \
@@ -225,8 +239,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,11 +266,22 @@ ORHEADERS = \
src/or/routerset.h \
src/or/routerparse.h \
src/or/scheduler.h \
+ src/or/shared_random_common.h \
src/or/statefile.h \
src/or/status.h \
src/or/torcert.h \
src/or/tor_api_internal.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 f1b8308304..1160f377e4 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -72,7 +72,6 @@
#include "crypto_s2k.h"
#include "directory.h"
#include "dirserv.h"
-#include "dirvote.h"
#include "dns.h"
#include "dnsserv.h"
#include "dos.h"
@@ -103,7 +102,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "scheduler.h"
-#include "shared_random.h"
+#include "dirauth/shared_random.h"
#include "statefile.h"
#include "status.h"
#include "tor_api.h"
@@ -118,6 +117,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
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index b0db0cecbc..5ca320d284 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,11 +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 "dirauth/dirvote.h"
+
/** Most recently received and validated v3 "ns"-flavored consensus network
* status. */
STATIC networkstatus_t *current_ns_consensus = NULL;
@@ -365,9 +366,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);
@@ -2641,6 +2640,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..bd78292050 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -24,6 +24,9 @@ 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);
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..3a55c00cc1 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_common.h"
+#include "dirvote_common.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 (dirvote_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_common.c b/src/or/shared_random_common.c
new file mode 100644
index 0000000000..7d897d9531
--- /dev/null
+++ b/src/or/shared_random_common.c
@@ -0,0 +1,259 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random_common.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_COMMON_PRIVATE
+#include "shared_random_common.h"
+
+#include "config.h"
+#include "dirvote_common.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 = 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;
+}
+
+/*
+ * 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_common.h b/src/or/shared_random_common.h
new file mode 100644
index 0000000000..35e08ddf32
--- /dev/null
+++ b/src/or/shared_random_common.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random_common.h
+ * \brief Header file for shared_random_common.c.
+ **/
+
+#ifndef TOR_SHARED_RANDOM_COMMON_H
+#define TOR_SHARED_RANDOM_COMMON_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_COMMON_H */
+
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 7983106a2f..461aa646d6 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -24,7 +24,7 @@
#include "control.h"
#include "cpuworker.h"
#include "dirserv.h"
-#include "dirvote.h"
+#include "dirauth/dirvote.h"
#include "dns.h"
#include "entrynodes.h"
#include "transports.h"
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 957f646de9..84fdb96e45 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -25,7 +25,7 @@
#include "crypto_ed25519.h"
#include "directory.h"
#include "dirserv.h"
-#include "dirvote.h"
+#include "dirauth/dirvote.h"
#include "entrynodes.h"
#include "hibernate.h"
#include "memarea.h"
@@ -35,7 +35,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
-#include "shared_random_state.h"
+#include "dirauth/shared_random_state.h"
#include "test.h"
#include "test_dir_common.h"
#include "torcert.h"
diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c
index fdf43533a8..02d3295ca6 100644
--- a/src/test/test_dir_common.c
+++ b/src/test/test_dir_common.c
@@ -9,7 +9,8 @@
#include "test.h"
#include "container.h"
#include "or.h"
-#include "dirvote.h"
+#include "dirauth/dirvote.h"
+#include "dirvote_common.h"
#include "nodelist.h"
#include "routerlist.h"
#include "test_dir_common.h"
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index 71faf70af2..1bff249df5 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -30,7 +30,7 @@
#include "proto_http.h"
#include "geoip.h"
#include "dirserv.h"
-#include "dirvote.h"
+#include "dirauth/dirvote.h"
#include "log_test_helpers.h"
#ifdef _WIN32
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index 17ba11ca7d..43a2c5e80c 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -23,12 +23,12 @@
#include "config.h"
#include "networkstatus.h"
#include "directory.h"
-#include "dirvote.h"
+#include "dirauth/dirvote.h"
#include "nodelist.h"
#include "routerlist.h"
#include "statefile.h"
#include "circuitlist.h"
-#include "shared_random.h"
+#include "dirauth/shared_random.h"
#include "util.h"
/** Test the validation of HS v3 addresses */
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index 481521520c..fc8e016b52 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -34,7 +34,7 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "crypto.h"
-#include "dirvote.h"
+#include "dirauth/dirvote.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "relay.h"
@@ -51,7 +51,7 @@
#include "main.h"
#include "rendservice.h"
#include "statefile.h"
-#include "shared_random_state.h"
+#include "dirauth/shared_random_state.h"
/* Trunnel */
#include "hs/cell_establish_intro.h"
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
index 3d02fc1a59..4b168f49ed 100644
--- a/src/test/test_microdesc.c
+++ b/src/test/test_microdesc.c
@@ -5,7 +5,8 @@
#include "or.h"
#include "config.h"
-#include "dirvote.h"
+#define DIRVOTE_PRIVATE
+#include "dirauth/dirvote.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "routerlist.h"
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index c19d66ef9d..71b487f35b 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -19,7 +19,7 @@
#include "container.h"
#include "control.h"
#include "directory.h"
-#include "dirvote.h"
+#include "dirauth/dirvote.h"
#include "entrynodes.h"
#include "hibernate.h"
#include "microdesc.h"
@@ -30,7 +30,7 @@
#include "routerlist.h"
#include "routerset.h"
#include "routerparse.h"
-#include "shared_random.h"
+#include "dirauth/shared_random.h"
#include "statefile.h"
#include "test.h"
#include "test_dir_common.h"
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index 4fe9ee45f0..95a7ff28f4 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -9,13 +9,14 @@
#include "or.h"
#include "test.h"
#include "config.h"
-#include "dirvote.h"
-#include "shared_random.h"
-#include "shared_random_state.h"
+#include "dirauth/dirvote.h"
+#include "dirauth/shared_random.h"
+#include "dirauth/shared_random_state.h"
#include "routerkeys.h"
#include "routerlist.h"
#include "router.h"
#include "routerparse.h"
+#include "shared_random_common.h"
#include "networkstatus.h"
#include "log_test_helpers.h"