Browse Source

Merge remote-tracking branch 'dgoulet/ticket25610_034_01-squashed'

Nick Mathewson 6 years ago
parent
commit
d018bf199c

+ 3 - 3
Makefile.am

@@ -51,14 +51,14 @@ AM_ETAGSFLAGS=--regex='{c}/MOCK_IMPL([^,]+,\W*\([a-zA-Z0-9_]+\)\W*,/\1/s'
 if COVERAGE_ENABLED
 TEST_CFLAGS=-fno-inline -fprofile-arcs -ftest-coverage
 if DISABLE_ASSERTS_IN_UNIT_TESTS
-TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE -DDISABLE_ASSERTS_IN_UNIT_TESTS
+TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE -DDISABLE_ASSERTS_IN_UNIT_TESTS @TOR_MODULES_ALL_ENABLED@
 else
-TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE
+TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE @TOR_MODULES_ALL_ENABLED@
 endif
 TEST_NETWORK_FLAGS=--coverage --hs-multi-client 1
 else
 TEST_CFLAGS=
-TEST_CPPFLAGS=-DTOR_UNIT_TESTS
+TEST_CPPFLAGS=-DTOR_UNIT_TESTS @TOR_MODULES_ALL_ENABLED@
 TEST_NETWORK_FLAGS=--hs-multi-client 1
 endif
 TEST_NETWORK_WARNING_FLAGS=--quiet --only-warnings

+ 25 - 0
configure.ac

@@ -230,6 +230,31 @@ if test "x$enable_android" = "xyes"; then
 
 fi
 
+dnl ---
+dnl Tor modules options. These options are namespaced with --disable-module-XXX
+dnl ---
+
+dnl All our modules.
+m4_define(MODULES, dirauth)
+
+dnl Directory Authority module.
+AC_ARG_ENABLE([module-dirauth],
+              AS_HELP_STRING([--disable-module-dirauth],
+                             [Do not build tor with the dirauth module]),
+              [], dnl Action if-given
+              AC_DEFINE([HAVE_MODULE_DIRAUTH], [1],
+                        [Compile with Directory Authority feature support]))
+AM_CONDITIONAL(BUILD_MODULE_DIRAUTH, [test "x$enable_module_dirauth" != "xno"])
+
+dnl Helper variables.
+TOR_MODULES_ALL_ENABLED=
+AC_DEFUN([ADD_MODULE], [
+    MODULE=m4_toupper($1)
+    TOR_MODULES_ALL_ENABLED="${TOR_MODULES_ALL_ENABLED} -DHAVE_MODULE_${MODULE}=1"
+])
+m4_foreach_w([module], MODULES, [ADD_MODULE([module])])
+AC_SUBST(TOR_MODULES_ALL_ENABLED)
+
 dnl check for the correct "ar" when cross-compiling.
 dnl   (AM_PROG_AR was new in automake 1.11.2, which we do not yet require,
 dnl    so kludge up a replacement for the case where it isn't there yet.)

+ 2 - 1
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:

+ 2 - 1
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

+ 1 - 1
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>

+ 0 - 0
src/or/dircollate.c → src/or/dirauth/dircollate.c


+ 0 - 0
src/or/dircollate.h → src/or/dirauth/dircollate.h


+ 722 - 266
src/or/dirvote.c → 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"
@@ -91,6 +94,30 @@ static void dirvote_clear_votes(int all_votes);
 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;
 }
 

+ 114 - 85
src/or/dirvote.h → 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) */

+ 4 - 163
src/or/shared_random.c → 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

+ 30 - 9
src/or/shared_random.h → 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 */

+ 4 - 80
src/or/shared_random_state.c → 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

+ 0 - 5
src/or/shared_random_state.h → 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);

+ 5 - 48
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;

+ 31 - 544
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

+ 9 - 6
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,

+ 195 - 0
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;
+}
+

+ 66 - 0
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 */
+

+ 2 - 2
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"

+ 1 - 1
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"

+ 32 - 9
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
 

+ 3 - 2
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

+ 23 - 5
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)

+ 3 - 0
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);

+ 2 - 1
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
 
 /****************************************************************************/

+ 7 - 64
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 "

+ 259 - 0
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();
+}
+

+ 47 - 0
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 */
+

+ 1 - 1
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"

+ 2 - 2
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"

+ 2 - 1
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"

+ 1 - 1
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

+ 2 - 2
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 */

+ 2 - 2
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"

+ 2 - 1
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"

+ 2 - 2
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"

+ 4 - 3
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"