[or-cvs] [tor/master] Merge branch 'maint-0.2.2'

arma at torproject.org arma at torproject.org
Sun Jan 16 03:40:09 UTC 2011


commit 4ff97e3775316ceb500cb5d2a562ec5dfd8dfb23
Merge: 8e9b25e 7699014
Author: Roger Dingledine <arma at torproject.org>
Date:   Sat Jan 15 22:39:15 2011 -0500

    Merge branch 'maint-0.2.2'

 changes/bug2317        |    9 +++++
 doc/spec/dir-spec.txt  |    8 +++-
 doc/spec/path-spec.txt |   28 ++++++++++++++--
 src/or/circuitbuild.c  |   84 ++++++++++++++++++++++++++++++++++-------------
 src/or/circuitbuild.h  |    2 +
 src/or/circuitlist.c   |    4 ++-
 src/or/connection_or.c |    8 ++--
 src/or/dirvote.h       |    3 --
 src/or/networkstatus.c |   54 ++++++++++++++++++++++++------
 src/or/networkstatus.h |    5 +--
 src/or/or.h            |   27 +++++++++++++++-
 src/or/relay.c         |    6 ++--
 src/or/router.c        |    2 +-
 src/or/routerlist.c    |    3 +-
 src/or/routerparse.c   |    3 +-
 src/test/test_dir.c    |    7 +++-
 16 files changed, 193 insertions(+), 60 deletions(-)

diff --combined doc/spec/dir-spec.txt
index 1498437,eebceea..e937025
--- a/doc/spec/dir-spec.txt
+++ b/doc/spec/dir-spec.txt
@@@ -530,8 -530,16 +530,8 @@@
          dns logic.  Versions of Tor with this field set to false SHOULD NOT
          be used for reverse hostname lookups.
  
 -        [All versions of Tor before 0.1.2.2-alpha should be assumed to have
 -         this option set to 0 if it is not present.  All Tor versions at
 -         0.1.2.2-alpha or later should be assumed to have this option set to
 -         1 if it is not present.  Until 0.1.2.1-alpha-dev, this option was
 -         not generated, even when the new DNS code was in use.  Versions of Tor
 -         before 0.1.2.1-alpha-dev did not parse this option, so it should be
 -         marked "opt".  The dnsworker logic has been removed, so this option
 -         should not be used by new server code.  However, it can still be
 -         used, and should still be recognized by new code until Tor 0.1.2.x
 -         is obsolete.]
 +        [This option is obsolete.  All Tor current servers should be presumed
 +         to have the evdns backend.]
  
     "caches-extra-info" NL
  
@@@ -622,12 -630,6 +622,12 @@@
  
          As documented in 2.1 above.  See migration notes in section 2.2.1.
  
 +    "geoip-db-digest" Digest NL
 +        [At most once.]
 +
 +        SHA1 digest of the GeoIP database file that is used to resolve IP
 +        addresses to country codes.
 +
      ("geoip-start" YYYY-MM-DD HH:MM:SS NL)
      ("geoip-client-origins" CC=N,CC=N,... NL)
  
@@@ -848,20 -850,6 +848,20 @@@
          Mean number of circuits that are included in any of the deciles,
          rounded up to the next integer.
  
 +    "conn-bi-direct" YYYY-MM-DD HH:MM:SS (NSEC s) BELOW,READ,WRITE,BOTH NL
 +        [At most once]
 +
 +        Number of connections, split into 10-second intervals, that are
 +        used uni-directionally or bi-directionally as observed in the NSEC
 +        seconds (usually 86400 seconds) before YYYY-MM-DD HH:MM:SS.  Every
 +        10 seconds, we determine for every connection whether we read and
 +        wrote less than a threshold of 20 KiB (BELOW), read at least 10
 +        times more than we wrote (READ), wrote at least 10 times more than
 +        we read (WRITE), or read and wrote more than the threshold, but
 +        not 10 times more in either direction (BOTH).  After classifying a
 +        connection, read and write counters are reset for the next
 +        10-second interval.
 +
      "exit-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
          [At most once.]
  
@@@ -1173,12 -1161,14 +1173,14 @@@
          research indicates that a lower value would mean fewer cells in
          transit in the network at any given time. Obeyed by Tor 0.2.1.20
          and later.
+         Min: 100, Max: 1000
  
          "CircuitPriorityHalflifeMsec" -- the halflife parameter used when
          weighting which circuit will send the next cell. Obeyed by Tor
          0.2.2.10-alpha and later.  (Versions of Tor between 0.2.2.7-alpha
          and 0.2.2.10-alpha recognized a "CircPriorityHalflifeMsec" parameter,
          but mishandled it badly.)
+         Min: -1, Max: 2147483647 (INT32_MAX)
  
          "perconnbwrate" and "perconnbwburst" -- if set, each relay sets
          up a separate token bucket for every client OR connection,
@@@ -1188,12 -1178,14 +1190,14 @@@
          and later. (Note that relays running 0.2.2.7-alpha through
          0.2.2.14-alpha looked for bwconnrate and bwconnburst, but then
          did the wrong thing with them; see bug 1830 for details.)
+         Min: 1, Max: 2147483647 (INT32_MAX)
  
-         "refuseunknownexits" -- if set and non-zero, exit relays look at
+         "refuseunknownexits" -- if set to one, exit relays look at
          the previous hop of circuits that ask to open an exit stream,
          and refuse to exit if they don't recognize it as a relay. The
          goal is to make it harder for people to use them as one-hop
          proxies. See trac entry 1751 for details.
+         Min: 0, Max: 1
  
          See also "2.4.5. Consensus parameters governing behavior"
          in path-spec.txt for a series of circuit build time related
@@@ -1832,7 -1824,7 +1836,7 @@@
  
    To ensure consensus, all calculations are performed using integer math
    with a fixed precision determined by the bwweightscale consensus
-   parameter (defaults at 10000).
+   parameter (defaults at 10000, Min: 1, Max: INT32_MAX).
  
    For future balancing improvements, Tor clients support 11 additional weights
    for directory requests and middle weighting. These weights are currently
diff --combined src/or/circuitbuild.c
index d4505c2,3788959..fd02bae
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@@ -23,7 -23,6 +23,7 @@@
  #include "directory.h"
  #include "main.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "onion.h"
  #include "policies.h"
  #include "relay.h"
@@@ -55,8 -54,8 +55,8 @@@ extern circuit_t *global_circuitlist
  
  /** An entry_guard_t represents our information about a chosen long-term
   * first hop, known as a "helper" node in the literature. We can't just
 - * use a routerinfo_t, since we want to remember these even when we
 - * don't have a directory. */
 + * use a node_t, since we want to remember these even when we
 + * don't have any directory info. */
  typedef struct {
    char nickname[MAX_NICKNAME_LEN+1];
    char identity[DIGEST_LEN];
@@@ -95,7 -94,7 +95,7 @@@ static int circuit_deliver_create_cell(
  static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit);
  static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
  static int onion_extend_cpath(origin_circuit_t *circ);
 -static int count_acceptable_routers(smartlist_t *routers);
 +static int count_acceptable_nodes(smartlist_t *routers);
  static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
  
  static void entry_guards_changed(void);
@@@ -107,7 -106,7 +107,7 @@@ circuit_build_times_disabled(void
      return 0;
    } else {
      int consensus_disabled = networkstatus_get_param(NULL, "cbtdisabled",
-                                                      0);
+                                                      0, 0, 1);
      int config_disabled = !get_options()->LearnCircuitBuildTimeout;
      int dirauth_disabled = get_options()->AuthoritativeDir;
      int state_disabled = (get_or_state()->LastWritten == -1);
@@@ -129,16 -128,19 +129,19 @@@
  static int32_t
  circuit_build_times_max_timeouts(void)
  {
-   int32_t num = networkstatus_get_param(NULL, "cbtmaxtimeouts",
-           CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT);
-   return num;
+   return networkstatus_get_param(NULL, "cbtmaxtimeouts",
+                                  CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT,
+                                  CBT_MIN_MAX_RECENT_TIMEOUT_COUNT,
+                                  CBT_MAX_MAX_RECENT_TIMEOUT_COUNT);
  }
  
  static int32_t
  circuit_build_times_default_num_xm_modes(void)
  {
    int32_t num = networkstatus_get_param(NULL, "cbtnummodes",
-           CBT_DEFAULT_NUM_XM_MODES);
+                                         CBT_DEFAULT_NUM_XM_MODES,
+                                         CBT_MIN_NUM_XM_MODES,
+                                         CBT_MAX_NUM_XM_MODES);
    return num;
  }
  
@@@ -146,7 -148,9 +149,9 @@@ static int32_
  circuit_build_times_min_circs_to_observe(void)
  {
    int32_t num = networkstatus_get_param(NULL, "cbtmincircs",
-                 CBT_DEFAULT_MIN_CIRCUITS_TO_OBSERVE);
+                                         CBT_DEFAULT_MIN_CIRCUITS_TO_OBSERVE,
+                                         CBT_MIN_MIN_CIRCUITS_TO_OBSERVE,
+                                         CBT_MAX_MIN_CIRCUITS_TO_OBSERVE);
    return num;
  }
  
@@@ -162,24 -166,46 +167,46 @@@ doubl
  circuit_build_times_quantile_cutoff(void)
  {
    int32_t num = networkstatus_get_param(NULL, "cbtquantile",
-                 CBT_DEFAULT_QUANTILE_CUTOFF);
+                                         CBT_DEFAULT_QUANTILE_CUTOFF,
+                                         CBT_MIN_QUANTILE_CUTOFF,
+                                         CBT_MAX_QUANTILE_CUTOFF);
    return num/100.0;
  }
  
+ int
+ circuit_build_times_get_bw_scale(networkstatus_t *ns)
+ {
+   return networkstatus_get_param(ns, "bwweightscale",
+                                  BW_WEIGHT_SCALE,
+                                  BW_MIN_WEIGHT_SCALE,
+                                  BW_MAX_WEIGHT_SCALE);
+ }
+ 
  static double
  circuit_build_times_close_quantile(void)
  {
-   int32_t num = networkstatus_get_param(NULL, "cbtclosequantile",
-           CBT_DEFAULT_CLOSE_QUANTILE);
- 
-   return num/100.0;
+   int32_t param;
+   /* Cast is safe - circuit_build_times_quantile_cutoff() is capped */
+   int32_t min = (int)tor_lround(100*circuit_build_times_quantile_cutoff());
+   param = networkstatus_get_param(NULL, "cbtclosequantile",
+              CBT_DEFAULT_CLOSE_QUANTILE,
+              CBT_MIN_CLOSE_QUANTILE,
+              CBT_MAX_CLOSE_QUANTILE);
+   if (param < min) {
+     log_warn(LD_DIR, "Consensus parameter cbtclosequantile is "
+              "too small, raising to %d", min);
+     param = min;
+   }
+   return param / 100.0;
  }
  
  static int32_t
  circuit_build_times_test_frequency(void)
  {
    int32_t num = networkstatus_get_param(NULL, "cbttestfreq",
-                 CBT_DEFAULT_TEST_FREQUENCY);
+                                         CBT_DEFAULT_TEST_FREQUENCY,
+                                         CBT_MIN_TEST_FREQUENCY,
+                                         CBT_MAX_TEST_FREQUENCY);
    return num;
  }
  
@@@ -187,24 -213,35 +214,35 @@@ static int32_
  circuit_build_times_min_timeout(void)
  {
    int32_t num = networkstatus_get_param(NULL, "cbtmintimeout",
-                 CBT_DEFAULT_TIMEOUT_MIN_VALUE);
+                                         CBT_DEFAULT_TIMEOUT_MIN_VALUE,
+                                         CBT_MIN_TIMEOUT_MIN_VALUE,
+                                         CBT_MAX_TIMEOUT_MIN_VALUE);
    return num;
  }
  
  int32_t
  circuit_build_times_initial_timeout(void)
  {
-   int32_t num = networkstatus_get_param(NULL, "cbtinitialtimeout",
-                 CBT_DEFAULT_TIMEOUT_INITIAL_VALUE);
-   return num;
+   int32_t min = circuit_build_times_min_timeout();
+   int32_t param = networkstatus_get_param(NULL, "cbtinitialtimeout",
+                                           CBT_DEFAULT_TIMEOUT_INITIAL_VALUE,
+                                           CBT_MIN_TIMEOUT_INITIAL_VALUE,
+                                           CBT_MAX_TIMEOUT_INITIAL_VALUE);
+   if (param < min) {
+     log_warn(LD_DIR, "Consensus parameter cbtinitialtimeout is too small, "
+              "raising to %d", min);
+     param = min;
+   }
+   return param;
  }
  
  static int32_t
- circuit_build_times_recent_circuit_count(void)
+ circuit_build_times_recent_circuit_count(networkstatus_t *ns)
  {
-   int32_t num = networkstatus_get_param(NULL, "cbtrecentcount",
-                 CBT_DEFAULT_RECENT_CIRCUITS);
-   return num;
+   return networkstatus_get_param(ns, "cbtrecentcount",
+                                  CBT_DEFAULT_RECENT_CIRCUITS,
+                                  CBT_MIN_RECENT_CIRCUITS,
+                                  CBT_MAX_RECENT_CIRCUITS);
  }
  
  /**
@@@ -217,8 -254,7 +255,7 @@@ voi
  circuit_build_times_new_consensus_params(circuit_build_times_t *cbt,
                                           networkstatus_t *ns)
  {
-   int32_t num = networkstatus_get_param(ns, "cbtrecentcount",
-                    CBT_DEFAULT_RECENT_CIRCUITS);
+   int32_t num = circuit_build_times_recent_circuit_count(ns);
  
    if (num > 0 && num != cbt->liveness.num_recent_circs) {
      int8_t *recent_circs;
@@@ -308,7 -344,8 +345,8 @@@ voi
  circuit_build_times_init(circuit_build_times_t *cbt)
  {
    memset(cbt, 0, sizeof(*cbt));
-   cbt->liveness.num_recent_circs = circuit_build_times_recent_circuit_count();
+   cbt->liveness.num_recent_circs =
+       circuit_build_times_recent_circuit_count(NULL);
    cbt->liveness.timeouts_after_firsthop = tor_malloc_zero(sizeof(int8_t)*
                                        cbt->liveness.num_recent_circs);
    cbt->close_ms = cbt->timeout_ms = circuit_build_times_get_initial_timeout();
@@@ -1402,9 -1439,10 +1440,9 @@@ circuit_list_path_impl(origin_circuit_
  
    hop = circ->cpath;
    do {
 -    routerinfo_t *ri;
 -    routerstatus_t *rs;
      char *elt;
      const char *id;
 +    const node_t *node;
      if (!hop)
        break;
      if (!verbose && hop->state != CPATH_STATE_OPEN)
@@@ -1414,8 -1452,10 +1452,8 @@@
      id = hop->extend_info->identity_digest;
      if (verbose_names) {
        elt = tor_malloc(MAX_VERBOSE_NICKNAME_LEN+1);
 -      if ((ri = router_get_by_digest(id))) {
 -        router_get_verbose_nickname(elt, ri);
 -      } else if ((rs = router_get_consensus_status_by_id(id))) {
 -        routerstatus_get_verbose_nickname(elt, rs);
 +      if ((node = node_get_by_id(id))) {
 +        node_get_verbose_nickname(node, elt);
        } else if (is_legal_nickname(hop->extend_info->nickname)) {
          elt[0] = '$';
          base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
@@@ -1427,9 -1467,9 +1465,9 @@@
          base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
        }
      } else { /* ! verbose_names */
 -      if ((ri = router_get_by_digest(id)) &&
 -          ri->is_named) {
 -        elt = tor_strdup(hop->extend_info->nickname);
 +      node = node_get_by_id(id);
 +      if (node && node_is_named(node)) {
 +        elt = tor_strdup(node_get_nickname(node));
        } else {
          elt = tor_malloc(HEX_DIGEST_LEN+2);
          elt[0] = '$';
@@@ -1498,28 -1538,31 +1536,28 @@@ voi
  circuit_rep_hist_note_result(origin_circuit_t *circ)
  {
    crypt_path_t *hop;
 -  char *prev_digest = NULL;
 -  routerinfo_t *router;
 +  const char *prev_digest = NULL;
    hop = circ->cpath;
    if (!hop) /* circuit hasn't started building yet. */
      return;
    if (server_mode(get_options())) {
 -    routerinfo_t *me = router_get_my_routerinfo();
 +    const routerinfo_t *me = router_get_my_routerinfo();
      if (!me)
        return;
      prev_digest = me->cache_info.identity_digest;
    }
    do {
 -    router = router_get_by_digest(hop->extend_info->identity_digest);
 -    if (router) {
 +    const node_t *node = node_get_by_id(hop->extend_info->identity_digest);
 +    if (node) { /* Why do we check this?  We know the identity. -NM XXXX */
        if (prev_digest) {
          if (hop->state == CPATH_STATE_OPEN)
 -          rep_hist_note_extend_succeeded(prev_digest,
 -                                         router->cache_info.identity_digest);
 +          rep_hist_note_extend_succeeded(prev_digest, node->identity);
          else {
 -          rep_hist_note_extend_failed(prev_digest,
 -                                      router->cache_info.identity_digest);
 +          rep_hist_note_extend_failed(prev_digest, node->identity);
            break;
          }
        }
 -      prev_digest = router->cache_info.identity_digest;
 +      prev_digest = node->identity;
      } else {
        prev_digest = NULL;
      }
@@@ -1789,7 -1832,7 +1827,7 @@@ in
  inform_testing_reachability(void)
  {
    char dirbuf[128];
 -  routerinfo_t *me = router_get_my_routerinfo();
 +  const routerinfo_t *me = router_get_my_routerinfo();
    if (!me)
      return 0;
    control_event_server_status(LOG_NOTICE,
@@@ -1861,7 -1904,7 +1899,7 @@@ in
  circuit_send_next_onion_skin(origin_circuit_t *circ)
  {
    crypt_path_t *hop;
 -  routerinfo_t *router;
 +  const node_t *node;
    char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN];
    char *onionskin;
    size_t payload_len;
@@@ -1877,7 -1920,7 +1915,7 @@@
      else
        control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0);
  
 -    router = router_get_by_digest(circ->_base.n_conn->identity_digest);
 +    node = node_get_by_id(circ->_base.n_conn->identity_digest);
      fast = should_use_create_fast_for_circuit(circ);
      if (!fast) {
        /* We are an OR and we know the right onion key: we should
@@@ -1911,7 -1954,7 +1949,7 @@@
      circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
      log_info(LD_CIRC,"First hop: finished sending %s cell to '%s'",
               fast ? "CREATE_FAST" : "CREATE",
 -             router ? router->nickname : "<unnamed>");
 +             node ? node_get_nickname(node) : "<unnamed>");
    } else {
      tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
      tor_assert(circ->_base.state == CIRCUIT_STATE_BUILDING);
@@@ -1924,7 -1967,7 +1962,7 @@@
          struct timeval end;
          long timediff;
          tor_gettimeofday(&end);
 -        timediff = tv_mdiff(&circ->_base.highres_created, &end);
 +        timediff = tv_mdiff(&circ->_base.timestamp_created, &end);
  
          /*
           * If the circuit build time is much greater than we would have cut
@@@ -2387,12 -2430,12 +2425,12 @@@ onionskin_answer(or_circuit_t *circ, ui
   */
  static int
  new_route_len(uint8_t purpose, extend_info_t *exit,
 -              smartlist_t *routers)
 +              smartlist_t *nodes)
  {
    int num_acceptable_routers;
    int routelen;
  
 -  tor_assert(routers);
 +  tor_assert(nodes);
  
    routelen = DEFAULT_ROUTE_LEN;
    if (exit &&
@@@ -2400,10 -2443,10 +2438,10 @@@
        purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)
      routelen++;
  
 -  num_acceptable_routers = count_acceptable_routers(routers);
 +  num_acceptable_routers = count_acceptable_nodes(nodes);
  
    log_debug(LD_CIRC,"Chosen route length %d (%d/%d routers suitable).",
 -            routelen, num_acceptable_routers, smartlist_len(routers));
 +            routelen, num_acceptable_routers, smartlist_len(nodes));
  
    if (num_acceptable_routers < 2) {
      log_info(LD_CIRC,
@@@ -2421,12 -2464,24 +2459,12 @@@
    return routelen;
  }
  
 -/** Fetch the list of predicted ports, dup it into a smartlist of
 - * uint16_t's, remove the ones that are already handled by an
 - * existing circuit, and return it.
 - */
 +/** Return a newly allocated list of uint16_t * for each predicted port not
 + * handled by a current circuit. */
  static smartlist_t *
  circuit_get_unhandled_ports(time_t now)
  {
 -  smartlist_t *source = rep_hist_get_predicted_ports(now);
 -  smartlist_t *dest = smartlist_create();
 -  uint16_t *tmp;
 -  int i;
 -
 -  for (i = 0; i < smartlist_len(source); ++i) {
 -    tmp = tor_malloc(sizeof(uint16_t));
 -    memcpy(tmp, smartlist_get(source, i), sizeof(uint16_t));
 -    smartlist_add(dest, tmp);
 -  }
 -
 +  smartlist_t *dest = rep_hist_get_predicted_ports(now);
    circuit_remove_handled_ports(dest);
    return dest;
  }
@@@ -2460,12 -2515,12 +2498,12 @@@ circuit_all_predicted_ports_handled(tim
    return enough;
  }
  
 -/** Return 1 if <b>router</b> can handle one or more of the ports in
 +/** Return 1 if <b>node</b> can handle one or more of the ports in
   * <b>needed_ports</b>, else return 0.
   */
  static int
 -router_handles_some_port(routerinfo_t *router, smartlist_t *needed_ports)
 -{
 +node_handles_some_port(const node_t *node, smartlist_t *needed_ports)
 +{ /* XXXX MOVE */
    int i;
    uint16_t port;
  
@@@ -2475,10 -2530,7 +2513,10 @@@
         needed_ports is explicitly a smartlist of uint16_t's */
      port = *(uint16_t *)smartlist_get(needed_ports, i);
      tor_assert(port);
 -    r = compare_addr_to_addr_policy(0, port, router->exit_policy);
 +    if (node)
 +      r = compare_addr_to_node_policy(0, port, node);
 +    else
 +      continue;
      if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED)
        return 1;
    }
@@@ -2511,17 -2563,18 +2549,17 @@@ ap_stream_wants_exit_attention(connecti
   *
   * Return NULL if we can't find any suitable routers.
   */
 -static routerinfo_t *
 -choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
 -                                int need_capacity)
 +static const node_t *
 +choose_good_exit_server_general(int need_uptime, int need_capacity)
  {
    int *n_supported;
 -  int i;
    int n_pending_connections = 0;
    smartlist_t *connections;
    int best_support = -1;
    int n_best_support=0;
 -  routerinfo_t *router;
    or_options_t *options = get_options();
 +  const smartlist_t *the_nodes;
 +  const node_t *node=NULL;
  
    connections = get_connection_array();
  
@@@ -2542,11 -2595,10 +2580,11 @@@
     *
     * -1 means "Don't use this router at all."
     */
 -  n_supported = tor_malloc(sizeof(int)*smartlist_len(dir->routers));
 -  for (i = 0; i < smartlist_len(dir->routers); ++i) {/* iterate over routers */
 -    router = smartlist_get(dir->routers, i);
 -    if (router_is_me(router)) {
 +  the_nodes = nodelist_get_list();
 +  n_supported = tor_malloc(sizeof(int)*smartlist_len(the_nodes));
 +  SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) {
 +    const int i = node_sl_idx;
 +    if (router_digest_is_me(node->identity)) {
        n_supported[i] = -1;
  //      log_fn(LOG_DEBUG,"Skipping node %s -- it's me.", router->nickname);
        /* XXX there's probably a reverse predecessor attack here, but
@@@ -2554,15 -2606,13 +2592,15 @@@
         */
        continue;
      }
 -    if (!router->is_running || router->is_bad_exit) {
 +    if (!node_has_descriptor(node))
 +      continue;
 +    if (!node->is_running || node->is_bad_exit) {
        n_supported[i] = -1;
        continue; /* skip routers that are known to be down or bad exits */
      }
 -    if (router_is_unreliable(router, need_uptime, need_capacity, 0) &&
 +    if (node_is_unreliable(node, need_uptime, need_capacity, 0) &&
          (!options->ExitNodes ||
 -         !routerset_contains_router(options->ExitNodes, router))) {
 +         !routerset_contains_node(options->ExitNodes, node))) {
        /* FFFF Someday, differentiate between a routerset that names
         * routers, and a routerset that names countries, and only do this
         * check if they've asked for specific exit relays. Or if the country
@@@ -2571,19 -2621,18 +2609,19 @@@
        continue; /* skip routers that are not suitable, unless we have
                   * ExitNodes set, in which case we asked for it */
      }
 -    if (!(router->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) {
 +    if (!(node->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) {
        /* if it's invalid and we don't want it */
        n_supported[i] = -1;
  //      log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- invalid router.",
  //             router->nickname, i);
        continue; /* skip invalid routers */
      }
 -    if (options->ExcludeSingleHopRelays && router->allow_single_hop_exits) {
 +    if (options->ExcludeSingleHopRelays &&
 +        node_allows_single_hop_exits(node)) {
        n_supported[i] = -1;
        continue;
      }
 -    if (router_exit_policy_rejects_all(router)) {
 +    if (node_exit_policy_rejects_all(node)) {
        n_supported[i] = -1;
  //      log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.",
  //             router->nickname, i);
@@@ -2591,10 -2640,11 +2629,10 @@@
      }
      n_supported[i] = 0;
      /* iterate over connections */
 -    SMARTLIST_FOREACH(connections, connection_t *, conn,
 -    {
 +    SMARTLIST_FOREACH_BEGIN(connections, connection_t *, conn) {
        if (!ap_stream_wants_exit_attention(conn))
          continue; /* Skip everything but APs in CIRCUIT_WAIT */
 -      if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), router, 1)) {
 +      if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), node, 1)) {
          ++n_supported[i];
  //        log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.",
  //               router->nickname, i, n_supported[i]);
@@@ -2602,7 -2652,7 +2640,7 @@@
  //        log_fn(LOG_DEBUG,"%s (index %d) would reject this stream.",
  //               router->nickname, i);
        }
 -    }); /* End looping over connections. */
 +    } SMARTLIST_FOREACH_END(conn);
      if (n_pending_connections > 0 && n_supported[i] == 0) {
        /* Leave best_support at -1 if that's where it is, so we can
         * distinguish it later. */
@@@ -2619,7 -2669,7 +2657,7 @@@
         * count of equally good routers.*/
        ++n_best_support;
      }
 -  }
 +  } SMARTLIST_FOREACH_END(node);
    log_info(LD_CIRC,
             "Found %d servers that might support %d/%d pending connections.",
             n_best_support, best_support >= 0 ? best_support : 0,
@@@ -2630,19 -2680,18 +2668,19 @@@
    if (best_support > 0) {
      smartlist_t *supporting = smartlist_create(), *use = smartlist_create();
  
 -    for (i = 0; i < smartlist_len(dir->routers); i++)
 -      if (n_supported[i] == best_support)
 -        smartlist_add(supporting, smartlist_get(dir->routers, i));
 +    SMARTLIST_FOREACH(the_nodes, const node_t *, node, {
 +      if (n_supported[node_sl_idx] == best_support)
 +        smartlist_add(supporting, (void*)node);
 +    });
  
 -    routersets_get_disjunction(use, supporting, options->ExitNodes,
 +    routersets_get_node_disjunction(use, supporting, options->ExitNodes,
                                 options->_ExcludeExitNodesUnion, 1);
      if (smartlist_len(use) == 0 && options->ExitNodes &&
          !options->StrictNodes) { /* give up on exitnodes and try again */
 -      routersets_get_disjunction(use, supporting, NULL,
 +      routersets_get_node_disjunction(use, supporting, NULL,
                                   options->_ExcludeExitNodesUnion, 1);
      }
 -    router = routerlist_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
 +    node = node_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
      smartlist_free(use);
      smartlist_free(supporting);
    } else {
@@@ -2661,7 -2710,7 +2699,7 @@@
                   need_capacity?", fast":"",
                   need_uptime?", stable":"");
          tor_free(n_supported);
 -        return choose_good_exit_server_general(dir, 0, 0);
 +        return choose_good_exit_server_general(0, 0);
        }
        log_notice(LD_CIRC, "All routers are down or won't exit%s -- "
                   "choosing a doomed exit at random.",
@@@ -2673,29 -2722,28 +2711,29 @@@
      for (attempt = 0; attempt < 2; attempt++) {
        /* try once to pick only from routers that satisfy a needed port,
         * then if there are none, pick from any that support exiting. */
 -      for (i = 0; i < smartlist_len(dir->routers); i++) {
 -        router = smartlist_get(dir->routers, i);
 -        if (n_supported[i] != -1 &&
 -            (attempt || router_handles_some_port(router, needed_ports))) {
 +      SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) {
 +        if (!node_has_descriptor(node))
 +          continue;
 +        if (n_supported[node_sl_idx] != -1 &&
 +            (attempt || node_handles_some_port(node, needed_ports))) {
  //          log_fn(LOG_DEBUG,"Try %d: '%s' is a possibility.",
  //                 try, router->nickname);
 -          smartlist_add(supporting, router);
 +          smartlist_add(supporting, (void*)node);
          }
 -      }
 +      } SMARTLIST_FOREACH_END(node);
  
 -      routersets_get_disjunction(use, supporting, options->ExitNodes,
 +      routersets_get_node_disjunction(use, supporting, options->ExitNodes,
                                   options->_ExcludeExitNodesUnion, 1);
        if (smartlist_len(use) == 0 && options->ExitNodes &&
            !options->StrictNodes) { /* give up on exitnodes and try again */
 -        routersets_get_disjunction(use, supporting, NULL,
 +        routersets_get_node_disjunction(use, supporting, NULL,
                                     options->_ExcludeExitNodesUnion, 1);
        }
        /* FFF sometimes the above results in null, when the requested
         * exit node is considered down by the consensus. we should pick
         * it anyway, since the user asked for it. */
 -      router = routerlist_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
 -      if (router)
 +      node = node_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
 +      if (node)
          break;
        smartlist_clear(supporting);
        smartlist_clear(use);
@@@ -2707,9 -2755,9 +2745,9 @@@
    }
  
    tor_free(n_supported);
 -  if (router) {
 -    log_info(LD_CIRC, "Chose exit server '%s'", router->nickname);
 -    return router;
 +  if (node) {
 +    log_info(LD_CIRC, "Chose exit server '%s'", node_get_nickname(node));
 +    return node;
    }
    if (options->ExitNodes && options->StrictNodes) {
      log_warn(LD_CIRC,
@@@ -2729,12 -2777,12 +2767,12 @@@
   * For client-side rendezvous circuits, choose a random node, weighted
   * toward the preferences in 'options'.
   */
 -static routerinfo_t *
 -choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
 +static const node_t *
 +choose_good_exit_server(uint8_t purpose,
                          int need_uptime, int need_capacity, int is_internal)
  {
    or_options_t *options = get_options();
 -  router_crn_flags_t flags = 0;
 +  router_crn_flags_t flags = CRN_NEED_DESC;
    if (need_uptime)
      flags |= CRN_NEED_UPTIME;
    if (need_capacity)
@@@ -2747,7 -2795,7 +2785,7 @@@
        if (is_internal) /* pick it like a middle hop */
          return router_choose_random_node(NULL, options->ExcludeNodes, flags);
        else
 -        return choose_good_exit_server_general(dir,need_uptime,need_capacity);
 +        return choose_good_exit_server_general(need_uptime,need_capacity);
      case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
        if (options->_AllowInvalid & ALLOW_INVALID_RENDEZVOUS)
          flags |= CRN_ALLOW_INVALID;
@@@ -2828,12 -2876,13 +2866,12 @@@ static in
  onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
  {
    cpath_build_state_t *state = circ->build_state;
 -  routerlist_t *rl = router_get_routerlist();
  
    if (state->onehop_tunnel) {
      log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel.");
      state->desired_path_len = 1;
    } else {
 -    int r = new_route_len(circ->_base.purpose, exit, rl->routers);
 +    int r = new_route_len(circ->_base.purpose, exit, nodelist_get_list());
      if (r < 1) /* must be at least 1 */
        return -1;
      state->desired_path_len = r;
@@@ -2844,15 -2893,14 +2882,15 @@@
      log_info(LD_CIRC,"Using requested exit node '%s'", exit->nickname);
      exit = extend_info_dup(exit);
    } else { /* we have to decide one */
 -    routerinfo_t *router =
 -      choose_good_exit_server(circ->_base.purpose, rl, state->need_uptime,
 +    const node_t *node =
 +      choose_good_exit_server(circ->_base.purpose, state->need_uptime,
                                state->need_capacity, state->is_internal);
 -    if (!router) {
 +    if (!node) {
        log_warn(LD_CIRC,"failed to choose an exit server");
        return -1;
      }
 -    exit = extend_info_from_router(router);
 +    exit = extend_info_from_node(node);
 +    tor_assert(exit);
    }
    state->chosen_exit = exit;
    return 0;
@@@ -2903,30 -2951,35 +2941,30 @@@ circuit_extend_to_new_exit(origin_circu
   * and available for building circuits through.
   */
  static int
 -count_acceptable_routers(smartlist_t *routers)
 +count_acceptable_nodes(smartlist_t *nodes)
  {
 -  int i, n;
    int num=0;
 -  routerinfo_t *r;
  
 -  n = smartlist_len(routers);
 -  for (i=0;i<n;i++) {
 -    r = smartlist_get(routers, i);
 -//    log_debug(LD_CIRC,
 +  SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) {
 +    //    log_debug(LD_CIRC,
  //              "Contemplating whether router %d (%s) is a new option.",
  //              i, r->nickname);
 -    if (r->is_running == 0) {
 +    if (! node->is_running)
  //      log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i);
 -      goto next_i_loop;
 -    }
 -    if (r->is_valid == 0) {
 +      continue;
 +    if (! node->is_valid)
  //      log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
 -      goto next_i_loop;
 +      continue;
 +    if (! node_has_descriptor(node))
 +      continue;
        /* XXX This clause makes us count incorrectly: if AllowInvalidRouters
         * allows this node in some places, then we're getting an inaccurate
         * count. For now, be conservative and don't count it. But later we
         * should try to be smarter. */
 -    }
 -    num++;
 +    ++num;
 +  } SMARTLIST_FOREACH_END(node);
 +
  //    log_debug(LD_CIRC,"I like %d. num_acceptable_routers now %d.",i, num);
 -    next_i_loop:
 -      ; /* C requires an explicit statement after the label */
 -  }
  
    return num;
  }
@@@ -2954,31 -3007,31 +2992,31 @@@ onion_append_to_cpath(crypt_path_t **he
   * circuit. In particular, make sure we don't pick the exit node or its
   * family, and make sure we don't duplicate any previous nodes or their
   * families. */
 -static routerinfo_t *
 +static const node_t *
  choose_good_middle_server(uint8_t purpose,
                            cpath_build_state_t *state,
                            crypt_path_t *head,
                            int cur_len)
  {
    int i;
 -  routerinfo_t *r, *choice;
 +  const node_t *r, *choice;
    crypt_path_t *cpath;
    smartlist_t *excluded;
    or_options_t *options = get_options();
 -  router_crn_flags_t flags = 0;
 +  router_crn_flags_t flags = CRN_NEED_DESC;
    tor_assert(_CIRCUIT_PURPOSE_MIN <= purpose &&
               purpose <= _CIRCUIT_PURPOSE_MAX);
  
    log_debug(LD_CIRC, "Contemplating intermediate hop: random choice.");
    excluded = smartlist_create();
 -  if ((r = build_state_get_exit_router(state))) {
 -    smartlist_add(excluded, r);
 -    routerlist_add_family(excluded, r);
 +  if ((r = build_state_get_exit_node(state))) {
 +    smartlist_add(excluded, (void*) r);
 +    nodelist_add_node_family(excluded, r);
    }
    for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) {
 -    if ((r = router_get_by_digest(cpath->extend_info->identity_digest))) {
 -      smartlist_add(excluded, r);
 -      routerlist_add_family(excluded, r);
 +    if ((r = node_get_by_id(cpath->extend_info->identity_digest))) {
 +      smartlist_add(excluded, (void*)r);
 +      nodelist_add_node_family(excluded, r);
      }
    }
  
@@@ -3001,45 -3054,44 +3039,45 @@@
   * If <b>state</b> is NULL, we're choosing a router to serve as an entry
   * guard, not for any particular circuit.
   */
 -static routerinfo_t *
 +static const node_t *
  choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
  {
 -  routerinfo_t *r, *choice;
 +  const node_t *choice;
    smartlist_t *excluded;
    or_options_t *options = get_options();
 -  router_crn_flags_t flags = CRN_NEED_GUARD;
 +  router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC;
 +  const node_t *node;
  
    if (state && options->UseEntryGuards &&
        (purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) {
 +    /* This is request for an entry server to use for a regular circuit,
 +     * and we use entry guard nodes.  Just return one of the guard nodes.  */
      return choose_random_entry(state);
    }
  
    excluded = smartlist_create();
  
 -  if (state && (r = build_state_get_exit_router(state))) {
 -    smartlist_add(excluded, r);
 -    routerlist_add_family(excluded, r);
 +  if (state && (node = build_state_get_exit_node(state))) {
 +    /* Exclude the exit node from the state, if we have one.  Also exclude its
 +     * family. */
 +    smartlist_add(excluded, (void*)node);
 +    nodelist_add_node_family(excluded, node);
    }
    if (firewall_is_fascist_or()) {
 -    /*XXXX This could slow things down a lot; use a smarter implementation */
 -    /* exclude all ORs that listen on the wrong port, if anybody notices. */
 -    routerlist_t *rl = router_get_routerlist();
 -    int i;
 -
 -    for (i=0; i < smartlist_len(rl->routers); i++) {
 -      r = smartlist_get(rl->routers, i);
 -      if (!fascist_firewall_allows_or(r))
 -        smartlist_add(excluded, r);
 -    }
 +    /* Exclude all ORs that we can't reach through our firewall */
 +    smartlist_t *nodes = nodelist_get_list();
 +    SMARTLIST_FOREACH(nodes, const node_t *, node, {
 +      if (!fascist_firewall_allows_node(node))
 +        smartlist_add(excluded, (void*)node);
 +    });
    }
 -  /* and exclude current entry guards, if applicable */
 +  /* and exclude current entry guards and their families, if applicable */
    if (options->UseEntryGuards && entry_guards) {
      SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
        {
 -        if ((r = router_get_by_digest(entry->identity))) {
 -          smartlist_add(excluded, r);
 -          routerlist_add_family(excluded, r);
 +        if ((node = node_get_by_id(entry->identity))) {
 +          smartlist_add(excluded, (void*)node);
 +          nodelist_add_node_family(excluded, node);
          }
        });
    }
@@@ -3095,18 -3147,14 +3133,18 @@@ onion_extend_cpath(origin_circuit_t *ci
    if (cur_len == state->desired_path_len - 1) { /* Picking last node */
      info = extend_info_dup(state->chosen_exit);
    } else if (cur_len == 0) { /* picking first node */
 -    routerinfo_t *r = choose_good_entry_server(purpose, state);
 -    if (r)
 -      info = extend_info_from_router(r);
 +    const node_t *r = choose_good_entry_server(purpose, state);
 +    if (r) {
 +      info = extend_info_from_node(r);
 +      tor_assert(info);
 +    }
    } else {
 -    routerinfo_t *r =
 +    const node_t *r =
        choose_good_middle_server(purpose, state, circ->cpath, cur_len);
 -    if (r)
 -      info = extend_info_from_router(r);
 +    if (r) {
 +      info = extend_info_from_node(r);
 +      tor_assert(info);
 +    }
    }
  
    if (!info) {
@@@ -3165,7 -3213,7 +3203,7 @@@ extend_info_alloc(const char *nickname
  /** Allocate and return a new extend_info_t that can be used to build a
   * circuit to or through the router <b>r</b>. */
  extend_info_t *
 -extend_info_from_router(routerinfo_t *r)
 +extend_info_from_router(const routerinfo_t *r)
  {
    tor_addr_t addr;
    tor_assert(r);
@@@ -3174,29 -3222,6 +3212,29 @@@
                             r->onion_pkey, &addr, r->or_port);
  }
  
 +/** Allocate and return a new extend_info that can be used to build a ircuit
 + * to or through the node <b>node</b>.  May return NULL if there is not
 + * enough info about <b>node</b> to extend to it--for example, if there
 + * is no routerinfo_t or microdesc_t.
 + **/
 +extend_info_t *
 +extend_info_from_node(const node_t *node)
 +{
 +  if (node->ri) {
 +    return extend_info_from_router(node->ri);
 +  } else if (node->rs && node->md) {
 +    tor_addr_t addr;
 +    tor_addr_from_ipv4h(&addr, node->rs->addr);
 +    return extend_info_alloc(node->rs->nickname,
 +                             node->identity,
 +                             node->md->onion_pkey,
 +                             &addr,
 +                             node->rs->or_port);
 +  } else {
 +    return NULL;
 +  }
 +}
 +
  /** Release storage held by an extend_info_t struct. */
  void
  extend_info_free(extend_info_t *info)
@@@ -3227,12 -3252,12 +3265,12 @@@ extend_info_dup(extend_info_t *info
   * If there is no chosen exit, or if we don't know the routerinfo_t for
   * the chosen exit, return NULL.
   */
 -routerinfo_t *
 -build_state_get_exit_router(cpath_build_state_t *state)
 +const node_t *
 +build_state_get_exit_node(cpath_build_state_t *state)
  {
    if (!state || !state->chosen_exit)
      return NULL;
 -  return router_get_by_digest(state->chosen_exit->identity_digest);
 +  return node_get_by_id(state->chosen_exit->identity_digest);
  }
  
  /** Return the nickname for the chosen exit router in <b>state</b>. If
@@@ -3254,8 -3279,9 +3292,8 @@@ build_state_get_exit_nickname(cpath_bui
   *
   * If it's not usable, set *<b>reason</b> to a static string explaining why.
   */
 -/*XXXX take a routerstatus, not a routerinfo. */
  static int
 -entry_guard_set_status(entry_guard_t *e, routerinfo_t *ri,
 +entry_guard_set_status(entry_guard_t *e, const node_t *node,
                         time_t now, or_options_t *options, const char **reason)
  {
    char buf[HEX_DIGEST_LEN+1];
@@@ -3264,17 -3290,16 +3302,17 @@@
    *reason = NULL;
  
    /* Do we want to mark this guard as bad? */
 -  if (!ri)
 +  if (!node)
      *reason = "unlisted";
 -  else if (!ri->is_running)
 +  else if (!node->is_running)
      *reason = "down";
 -  else if (options->UseBridges && ri->purpose != ROUTER_PURPOSE_BRIDGE)
 +  else if (options->UseBridges && (!node->ri ||
 +                                   node->ri->purpose != ROUTER_PURPOSE_BRIDGE))
      *reason = "not a bridge";
 -  else if (!options->UseBridges && !ri->is_possible_guard &&
 -           !routerset_contains_router(options->EntryNodes,ri))
 +  else if (!options->UseBridges && !node->is_possible_guard &&
 +           !routerset_contains_node(options->EntryNodes,node))
      *reason = "not recommended as a guard";
 -  else if (routerset_contains_router(options->ExcludeNodes, ri))
 +  else if (routerset_contains_node(options->ExcludeNodes, node))
      *reason = "excluded";
  
    if (*reason && ! e->bad_since) {
@@@ -3318,7 -3343,7 +3356,7 @@@ entry_is_time_to_retry(entry_guard_t *e
      return now > (e->last_attempted + 36*60*60);
  }
  
 -/** Return the router corresponding to <b>e</b>, if <b>e</b> is
 +/** Return the node corresponding to <b>e</b>, if <b>e</b> is
   * working well enough that we are willing to use it as an entry
   * right now. (Else return NULL.) In particular, it must be
   * - Listed as either up or never yet contacted;
@@@ -3332,11 -3357,11 +3370,11 @@@
   *
   * If the answer is no, set *<b>msg</b> to an explanation of why.
   */
 -static INLINE routerinfo_t *
 +static INLINE const node_t *
  entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
                int assume_reachable, const char **msg)
  {
 -  routerinfo_t *r;
 +  const node_t *node;
    or_options_t *options = get_options();
    tor_assert(msg);
  
@@@ -3350,36 -3375,33 +3388,36 @@@
      *msg = "unreachable";
      return NULL;
    }
 -  r = router_get_by_digest(e->identity);
 -  if (!r) {
 +  node = node_get_by_id(e->identity);
 +  if (!node || !node_has_descriptor(node)) {
      *msg = "no descriptor";
      return NULL;
    }
 -  if (get_options()->UseBridges && r->purpose != ROUTER_PURPOSE_BRIDGE) {
 -    *msg = "not a bridge";
 -    return NULL;
 -  }
 -  if (!get_options()->UseBridges && r->purpose != ROUTER_PURPOSE_GENERAL) {
 -    *msg = "not general-purpose";
 -    return NULL;
 +  if (get_options()->UseBridges) {
 +    if (node_get_purpose(node) != ROUTER_PURPOSE_BRIDGE) {
 +      *msg = "not a bridge";
 +      return NULL;
 +    }
 +  } else { /* !get_options()->UseBridges */
 +    if (node_get_purpose(node) != ROUTER_PURPOSE_GENERAL) {
 +      *msg = "not general-purpose";
 +      return NULL;
 +    }
    }
    if (options->EntryNodes &&
 -      routerset_contains_router(options->EntryNodes, r)) {
 +      routerset_contains_node(options->EntryNodes, node)) {
      /* they asked for it, they get it */
      need_uptime = need_capacity = 0;
    }
 -  if (router_is_unreliable(r, need_uptime, need_capacity, 0)) {
 +  if (node_is_unreliable(node, need_uptime, need_capacity, 0)) {
      *msg = "not fast/stable";
      return NULL;
    }
 -  if (!fascist_firewall_allows_or(r)) {
 +  if (!fascist_firewall_allows_node(node)) {
      *msg = "unreachable by config";
      return NULL;
    }
 -  return r;
 +  return node;
  }
  
  /** Return the number of entry guards that we think are usable. */
@@@ -3477,15 -3499,15 +3515,15 @@@ control_event_guard_deferred(void
   * If <b>chosen</b> is defined, use that one, and if it's not
   * already in our entry_guards list, put it at the *beginning*.
   * Else, put the one we pick at the end of the list. */
 -static routerinfo_t *
 -add_an_entry_guard(routerinfo_t *chosen, int reset_status)
 +static const node_t *
 +add_an_entry_guard(const node_t *chosen, int reset_status)
  {
 -  routerinfo_t *router;
 +  const node_t *node;
    entry_guard_t *entry;
  
    if (chosen) {
 -    router = chosen;
 -    entry = is_an_entry_guard(router->cache_info.identity_digest);
 +    node = chosen;
 +    entry = is_an_entry_guard(node->identity);
      if (entry) {
        if (reset_status) {
          entry->bad_since = 0;
@@@ -3494,15 -3516,14 +3532,15 @@@
        return NULL;
      }
    } else {
 -    router = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
 -    if (!router)
 +    node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
 +    if (!node)
        return NULL;
    }
    entry = tor_malloc_zero(sizeof(entry_guard_t));
 -  log_info(LD_CIRC, "Chose '%s' as new entry guard.", router->nickname);
 -  strlcpy(entry->nickname, router->nickname, sizeof(entry->nickname));
 -  memcpy(entry->identity, router->cache_info.identity_digest, DIGEST_LEN);
 +  log_info(LD_CIRC, "Chose '%s' as new entry guard.",
 +           node_get_nickname(node));
 +  strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname));
 +  memcpy(entry->identity, node->identity, DIGEST_LEN);
    /* Choose expiry time smudged over the past month. The goal here
     * is to a) spread out when Tor clients rotate their guards, so they
     * don't all select them on the same day, and b) avoid leaving a
@@@ -3517,7 -3538,7 +3555,7 @@@
    control_event_guard(entry->nickname, entry->identity, "NEW");
    control_event_guard_deferred();
    log_entry_guards(LOG_INFO);
 -  return router;
 +  return node;
  }
  
  /** If the use of entry guards is configured, choose more entry guards
@@@ -3671,7 -3692,7 +3709,7 @@@ entry_guards_compute_status(or_options_
    reasons = digestmap_new();
    SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry)
      {
 -      routerinfo_t *r = router_get_by_digest(entry->identity);
 +      const node_t *r = node_get_by_id(entry->identity);
        const char *reason = NULL;
        if (entry_guard_set_status(entry, r, now, options, &reason))
          changed = 1;
@@@ -3692,7 -3713,7 +3730,7 @@@
      SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
        const char *reason = digestmap_get(reasons, entry->identity);
        const char *live_msg = "";
 -      routerinfo_t *r = entry_is_live(entry, 0, 1, 0, &live_msg);
 +      const node_t *r = entry_is_live(entry, 0, 1, 0, &live_msg);
        log_info(LD_CIRC, "Summary: Entry '%s' is %s, %s%s%s, and %s%s.",
                 entry->nickname,
                 entry->unreachable_since ? "unreachable" : "reachable",
@@@ -3809,7 -3830,7 +3847,7 @@@ entry_guard_register_connect_status(con
            break;
          if (e->made_contact) {
            const char *msg;
 -          routerinfo_t *r = entry_is_live(e, 0, 1, 1, &msg);
 +          const node_t *r = entry_is_live(e, 0, 1, 1, &msg);
            if (r && e->unreachable_since) {
              refuse_conn = 1;
              e->can_retry = 1;
@@@ -3850,7 -3871,7 +3888,7 @@@ entry_nodes_should_be_added(void
  static void
  entry_guards_prepend_from_config(or_options_t *options)
  {
 -  smartlist_t *entry_routers, *entry_fps;
 +  smartlist_t *entry_nodes, *entry_fps;
    smartlist_t *old_entry_guards_on_list, *old_entry_guards_not_on_list;
    tor_assert(entry_guards);
  
@@@ -3870,22 -3891,22 +3908,22 @@@
      tor_free(string);
    }
  
 -  entry_routers = smartlist_create();
 +  entry_nodes = smartlist_create();
    entry_fps = smartlist_create();
    old_entry_guards_on_list = smartlist_create();
    old_entry_guards_not_on_list = smartlist_create();
  
    /* Split entry guards into those on the list and those not. */
  
 -  /* XXXX022 Now that we allow countries and IP ranges in EntryNodes, this is
 -   *  potentially an enormous list. For now, we disable such values for
 -   *  EntryNodes in options_validate(); really, this wants a better solution.
 -   *  Perhaps we should do this calculation once whenever the list of routers
 -   *  changes or the entrynodes setting changes.
 -   */
 -  routerset_get_all_routers(entry_routers, options->EntryNodes, 0);
 -  SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri,
 -                    smartlist_add(entry_fps,ri->cache_info.identity_digest));
 +  /* Now that we allow countries and IP ranges in EntryNodes, this is
 +   * potentially an enormous list. It's not so bad though because we
 +   * only call this function when a) we're making a new circuit, and b)
 +   * we've called directory_info_has_arrived() or changed our EntryNodes
 +   * since the last time we made a circuit. */
 +  routerset_get_all_nodes(entry_nodes, options->EntryNodes, 0);
 +  SMARTLIST_FOREACH(entry_nodes, const node_t *,node,
 +                    smartlist_add(entry_fps, (void*)node->identity));
 +
    SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, {
      if (smartlist_digest_isin(entry_fps, e->identity))
        smartlist_add(old_entry_guards_on_list, e);
@@@ -3894,9 -3915,9 +3932,9 @@@
    });
  
    /* Remove all currently configured entry guards from entry_routers. */
 -  SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, {
 -    if (is_an_entry_guard(ri->cache_info.identity_digest)) {
 -      SMARTLIST_DEL_CURRENT(entry_routers, ri);
 +  SMARTLIST_FOREACH(entry_nodes, const node_t *, node, {
 +    if (is_an_entry_guard(node->identity)) {
 +      SMARTLIST_DEL_CURRENT(entry_nodes, node);
      }
    });
  
@@@ -3905,8 -3926,8 +3943,8 @@@
    /* First, the previously configured guards that are in EntryNodes. */
    smartlist_add_all(entry_guards, old_entry_guards_on_list);
    /* Next, the rest of EntryNodes */
 -  SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, {
 -    add_an_entry_guard(ri, 0);
 +  SMARTLIST_FOREACH(entry_nodes, const node_t *, node, {
 +    add_an_entry_guard(node, 0);
    });
    /* Finally, the remaining previously configured guards that are not in
     * EntryNodes, unless we're strict in which case we drop them */
@@@ -3917,7 -3938,7 +3955,7 @@@
      smartlist_add_all(entry_guards, old_entry_guards_not_on_list);
    }
  
 -  smartlist_free(entry_routers);
 +  smartlist_free(entry_nodes);
    smartlist_free(entry_fps);
    smartlist_free(old_entry_guards_on_list);
    smartlist_free(old_entry_guards_not_on_list);
@@@ -3955,22 -3976,21 +3993,22 @@@ entry_list_is_totally_static(or_options
   * make sure not to pick this circuit's exit or any node in the
   * exit's family. If <b>state</b> is NULL, we're looking for a random
   * guard (likely a bridge). */
 -routerinfo_t *
 +const node_t *
  choose_random_entry(cpath_build_state_t *state)
  {
    or_options_t *options = get_options();
    smartlist_t *live_entry_guards = smartlist_create();
    smartlist_t *exit_family = smartlist_create();
 -  routerinfo_t *chosen_exit = state?build_state_get_exit_router(state) : NULL;
 -  routerinfo_t *r = NULL;
 +  const node_t *chosen_exit =
 +    state?build_state_get_exit_node(state) : NULL;
 +  const node_t *node = NULL;
    int need_uptime = state ? state->need_uptime : 0;
    int need_capacity = state ? state->need_capacity : 0;
    int preferred_min, consider_exit_family = 0;
  
    if (chosen_exit) {
 -    smartlist_add(exit_family, chosen_exit);
 -    routerlist_add_family(exit_family, chosen_exit);
 +    smartlist_add(exit_family, (void*) chosen_exit);
 +    nodelist_add_node_family(exit_family, chosen_exit);
      consider_exit_family = 1;
    }
  
@@@ -3986,15 -4006,16 +4024,15 @@@
  
   retry:
    smartlist_clear(live_entry_guards);
 -  SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
 -    {
 +  SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
        const char *msg;
 -      r = entry_is_live(entry, need_uptime, need_capacity, 0, &msg);
 -      if (!r)
 +      node = entry_is_live(entry, need_uptime, need_capacity, 0, &msg);
 +      if (!node)
          continue; /* down, no point */
 -      if (consider_exit_family && smartlist_isin(exit_family, r))
 +      if (consider_exit_family && smartlist_isin(exit_family, node))
          continue; /* avoid relays that are family members of our exit */
        if (options->EntryNodes &&
 -          !routerset_contains_router(options->EntryNodes, r)) {
 +          !routerset_contains_node(options->EntryNodes, node)) {
          /* We've come to the end of our preferred entry nodes. */
          if (smartlist_len(live_entry_guards))
            goto choose_and_finish; /* only choose from the ones we like */
@@@ -4007,7 -4028,7 +4045,7 @@@
                     "No relays from EntryNodes available. Using others.");
          }
        }
 -      smartlist_add(live_entry_guards, r);
 +      smartlist_add(live_entry_guards, (void*)node);
        if (!entry->made_contact) {
          /* Always start with the first not-yet-contacted entry
           * guard. Otherwise we might add several new ones, pick
@@@ -4017,7 -4038,7 +4055,7 @@@
        }
        if (smartlist_len(live_entry_guards) >= options->NumEntryGuards)
          break; /* we have enough */
 -    });
 +  } SMARTLIST_FOREACH_END(entry);
  
    if (entry_list_is_constrained(options)) {
      /* If we prefer the entry nodes we've got, and we have at least
@@@ -4037,8 -4058,8 +4075,8 @@@
        /* XXX if guard doesn't imply fast and stable, then we need
         * to tell add_an_entry_guard below what we want, or it might
         * be a long time til we get it. -RD */
 -      r = add_an_entry_guard(NULL, 0);
 -      if (r) {
 +      node = add_an_entry_guard(NULL, 0);
 +      if (node) {
          entry_guards_changed();
          /* XXX we start over here in case the new node we added shares
           * a family with our exit node. There's a chance that we'll just
@@@ -4048,16 -4069,16 +4086,16 @@@
          goto retry;
        }
      }
 -    if (!r && need_uptime) {
 +    if (!node && need_uptime) {
        need_uptime = 0; /* try without that requirement */
        goto retry;
      }
 -    if (!r && need_capacity) {
 +    if (!node && need_capacity) {
        /* still no? last attempt, try without requiring capacity */
        need_capacity = 0;
        goto retry;
      }
 -    if (!r && entry_list_is_constrained(options) && consider_exit_family) {
 +    if (!node && entry_list_is_constrained(options) && consider_exit_family) {
        /* still no? if we're using bridges or have strictentrynodes
         * set, and our chosen exit is in the same family as all our
         * bridges/entry guards, then be flexible about families. */
@@@ -4071,16 -4092,16 +4109,16 @@@
    if (entry_list_is_constrained(options)) {
      /* We need to weight by bandwidth, because our bridges or entryguards
       * were not already selected proportional to their bandwidth. */
 -    r = routerlist_sl_choose_by_bandwidth(live_entry_guards, WEIGHT_FOR_GUARD);
 +    node = node_sl_choose_by_bandwidth(live_entry_guards, WEIGHT_FOR_GUARD);
    } else {
      /* We choose uniformly at random here, because choose_good_entry_server()
       * already weights its choices by bandwidth, so we don't want to
       * *double*-weight our guard selection. */
 -    r = smartlist_choose(live_entry_guards);
 +    node = smartlist_choose(live_entry_guards);
    }
    smartlist_free(live_entry_guards);
    smartlist_free(exit_family);
 -  return r;
 +  return node;
  }
  
  /** Parse <b>state</b> and learn about the entry guards it describes.
@@@ -4327,7 -4348,7 +4365,7 @@@ getinfo_helper_entry_guards(control_con
          char *c = tor_malloc(len);
          const char *status = NULL;
          time_t when = 0;
 -        routerinfo_t *ri;
 +        const node_t *node;
  
          if (!e->made_contact) {
            status = "never-connected";
@@@ -4338,9 -4359,9 +4376,9 @@@
            status = "up";
          }
  
 -        ri = router_get_by_digest(e->identity);
 -        if (ri) {
 -          router_get_verbose_nickname(nbuf, ri);
 +        node = node_get_by_id(e->identity);
 +        if (node) {
 +          node_get_verbose_nickname(node, nbuf);
          } else {
            nbuf[0] = '$';
            base16_encode(nbuf+1, sizeof(nbuf)-1, e->identity, DIGEST_LEN);
@@@ -4417,7 -4438,7 +4455,7 @@@ get_configured_bridge_by_addr_port_dige
  /** Wrapper around get_configured_bridge_by_addr_port_digest() to look
   * it up via router descriptor <b>ri</b>. */
  static bridge_info_t *
 -get_configured_bridge_by_routerinfo(routerinfo_t *ri)
 +get_configured_bridge_by_routerinfo(const routerinfo_t *ri)
  {
    tor_addr_t addr;
    tor_addr_from_ipv4h(&addr, ri->addr);
@@@ -4427,7 -4448,7 +4465,7 @@@
  
  /** Return 1 if <b>ri</b> is one of our known bridges, else 0. */
  int
 -routerinfo_is_a_configured_bridge(routerinfo_t *ri)
 +routerinfo_is_a_configured_bridge(const routerinfo_t *ri)
  {
    return get_configured_bridge_by_routerinfo(ri) ? 1 : 0;
  }
@@@ -4586,17 -4607,14 +4624,17 @@@ learned_bridge_descriptor(routerinfo_t 
      int first = !any_bridge_descriptors_known();
      bridge_info_t *bridge = get_configured_bridge_by_routerinfo(ri);
      time_t now = time(NULL);
 -    ri->is_running = 1;
 +    router_set_status(ri->cache_info.identity_digest, 1);
  
      if (bridge) { /* if we actually want to use this one */
 +      const node_t *node;
        /* it's here; schedule its re-fetch for a long time from now. */
        if (!from_cache)
          download_status_reset(&bridge->fetch_status);
  
 -      add_an_entry_guard(ri, 1);
 +      node = node_get_by_id(ri->cache_info.identity_digest);
 +      tor_assert(node);
 +      add_an_entry_guard(node, 1);
        log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname,
                   from_cache ? "cached" : "fresh");
        /* set entry->made_contact so if it goes down we don't drop it from
@@@ -4650,18 -4668,19 +4688,18 @@@ any_pending_bridge_descriptor_fetches(v
  static int
  entries_retry_helper(or_options_t *options, int act)
  {
 -  routerinfo_t *ri;
 +  const node_t *node;
    int any_known = 0;
    int any_running = 0;
 -  int purpose = options->UseBridges ?
 -                  ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL;
 +  int need_bridges = options->UseBridges != 0;
    if (!entry_guards)
      entry_guards = smartlist_create();
 -  SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e,
 -    {
 -      ri = router_get_by_digest(e->identity);
 -      if (ri && ri->purpose == purpose) {
 +  SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) {
 +      node = node_get_by_id(e->identity);
 +      if (node && node_has_descriptor(node) &&
 +          node_is_bridge(node) == need_bridges) {
          any_known = 1;
 -        if (ri->is_running)
 +        if (node->is_running)
            any_running = 1; /* some entry is both known and running */
          else if (act) {
            /* Mark all current connections to this OR as unhealthy, since
@@@ -4670,15 -4689,15 +4708,15 @@@
             * the node down and undermine the retry attempt. We mark even
             * the established conns, since if the network just came back
             * we'll want to attach circuits to fresh conns. */
 -          connection_or_set_bad_connections(ri->cache_info.identity_digest, 1);
 +          connection_or_set_bad_connections(node->identity, 1);
  
            /* mark this entry node for retry */
 -          router_set_status(ri->cache_info.identity_digest, 1);
 +          router_set_status(node->identity, 1);
            e->can_retry = 1;
            e->bad_since = 0;
          }
        }
 -    });
 +  } SMARTLIST_FOREACH_END(e);
    log_debug(LD_DIR, "%d: any_known %d, any_running %d",
              act, any_known, any_running);
    return any_known && !any_running;
diff --combined src/or/circuitbuild.h
index c664440,af24931..33d483d
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@@ -44,11 -44,10 +44,11 @@@ void onion_append_to_cpath(crypt_path_
  extend_info_t *extend_info_alloc(const char *nickname, const char *digest,
                                   crypto_pk_env_t *onion_key,
                                   const tor_addr_t *addr, uint16_t port);
 -extend_info_t *extend_info_from_router(routerinfo_t *r);
 +extend_info_t *extend_info_from_router(const routerinfo_t *r);
 +extend_info_t *extend_info_from_node(const node_t *node);
  extend_info_t *extend_info_dup(extend_info_t *info);
  void extend_info_free(extend_info_t *info);
 -routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
 +const node_t *build_state_get_exit_node(cpath_build_state_t *state);
  const char *build_state_get_exit_nickname(cpath_build_state_t *state);
  
  void entry_guards_compute_status(or_options_t *options, time_t now);
@@@ -56,7 -55,7 +56,7 @@@ int entry_guard_register_connect_status
                                          int mark_relay_status, time_t now);
  void entry_nodes_should_be_added(void);
  int entry_list_is_constrained(or_options_t *options);
 -routerinfo_t *choose_random_entry(cpath_build_state_t *state);
 +const node_t *choose_random_entry(cpath_build_state_t *state);
  int entry_guards_parse_state(or_state_t *state, int set, char **msg);
  void entry_guards_update_state(or_state_t *state);
  int getinfo_helper_entry_guards(control_connection_t *conn,
@@@ -64,9 -63,9 +64,9 @@@
                                  const char **errmsg);
  
  void clear_bridge_list(void);
 -int routerinfo_is_a_configured_bridge(routerinfo_t *ri);
 -void
 -learned_router_identity(tor_addr_t *addr, uint16_t port, const char *digest);
 +int routerinfo_is_a_configured_bridge(const routerinfo_t *ri);
 +void learned_router_identity(tor_addr_t *addr, uint16_t port,
 +                             const char *digest);
  void bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
                              char *digest);
  void retry_bridge_descriptor_fetch_directly(const char *digest);
@@@ -122,5 -121,7 +122,7 @@@ void circuit_build_times_network_is_liv
  int circuit_build_times_network_check_live(circuit_build_times_t *cbt);
  void circuit_build_times_network_circ_success(circuit_build_times_t *cbt);
  
+ int circuit_build_times_get_bw_scale(networkstatus_t *ns);
+ 
  #endif
  
diff --combined src/or/circuitlist.c
index d033cc6,b4f5f45..d5c74ee
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@@ -19,7 -19,6 +19,7 @@@
  #include "connection_or.h"
  #include "control.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "onion.h"
  #include "relay.h"
  #include "rendclient.h"
@@@ -87,7 -86,10 +87,7 @@@ orconn_circid_circuit_map_t *_last_circ
  /** Implementation helper for circuit_set_{p,n}_circid_orconn: A circuit ID
   * and/or or_connection for circ has just changed from <b>old_conn, old_id</b>
   * to <b>conn, id</b>.  Adjust the conn,circid map as appropriate, removing
 - * the old entry (if any) and adding a new one.  If <b>active</b> is true,
 - * remove the circuit from the list of active circuits on old_conn and add it
 - * to the list of active circuits on conn.
 - * XXX "active" isn't an arg anymore */
 + * the old entry (if any) and adding a new one. */
  static void
  circuit_set_circid_orconn_helper(circuit_t *circ, int direction,
                                   circid_t id,
@@@ -382,7 -384,9 +382,9 @@@ circuit_purpose_to_controller_string(ui
  int32_t
  circuit_initial_package_window(void)
  {
-   int32_t num = networkstatus_get_param(NULL, "circwindow", CIRCWINDOW_START);
+   int32_t num = networkstatus_get_param(NULL, "circwindow", CIRCWINDOW_START,
+                                         CIRCWINDOW_START_MIN,
+                                         CIRCWINDOW_START_MAX);
    /* If the consensus tells us a negative number, we'd assert. */
    if (num < 0)
      num = CIRCWINDOW_START;
@@@ -394,7 -398,8 +396,7 @@@
  static void
  init_circuit_base(circuit_t *circ)
  {
 -  circ->timestamp_created = time(NULL);
 -  tor_gettimeofday(&circ->highres_created);
 +  tor_gettimeofday(&circ->timestamp_created);
  
    circ->package_window = circuit_initial_package_window();
    circ->deliver_window = CIRCWINDOW_START;
@@@ -606,8 -611,7 +608,8 @@@ circuit_dump_details(int severity, circ
    log(severity, LD_CIRC, "Conn %d has %s circuit: circID %d (other side %d), "
        "state %d (%s), born %d:",
        conn_array_index, type, this_circid, other_circid, circ->state,
 -      circuit_state_to_string(circ->state), (int)circ->timestamp_created);
 +      circuit_state_to_string(circ->state),
 +      (int)circ->timestamp_created.tv_sec);
    if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */
      circuit_log_path(severity, LD_CIRC, TO_ORIGIN_CIRCUIT(circ));
    }
@@@ -944,15 -948,15 +946,15 @@@ circuit_find_to_cannibalize(uint8_t pur
          if (info) {
            /* need to make sure we don't duplicate hops */
            crypt_path_t *hop = circ->cpath;
 -          routerinfo_t *ri1 = router_get_by_digest(info->identity_digest);
 +          const node_t *ri1 = node_get_by_id(info->identity_digest);
            do {
 -            routerinfo_t *ri2;
 +            const node_t *ri2;
              if (!memcmp(hop->extend_info->identity_digest,
                          info->identity_digest, DIGEST_LEN))
                goto next;
              if (ri1 &&
 -                (ri2 = router_get_by_digest(hop->extend_info->identity_digest))
 -                && routers_in_same_family(ri1, ri2))
 +                (ri2 = node_get_by_id(hop->extend_info->identity_digest))
 +                && nodes_in_same_family(ri1, ri2))
                goto next;
              hop=hop->next;
            } while (hop!=circ->cpath);
diff --combined src/or/connection_or.c
index 5b440bc,b93699c..5e28f94
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@@ -22,17 -22,12 +22,17 @@@
  #include "geoip.h"
  #include "main.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "reasons.h"
  #include "relay.h"
  #include "rephist.h"
  #include "router.h"
  #include "routerlist.h"
  
 +#ifdef USE_BUFFEREVENTS
 +#include <event2/bufferevent_ssl.h>
 +#endif
 +
  static int connection_tls_finish_handshake(or_connection_t *conn);
  static int connection_or_process_cells_from_inbuf(or_connection_t *conn);
  static int connection_or_send_versions(or_connection_t *conn);
@@@ -42,14 -37,6 +42,14 @@@ static int connection_or_check_valid_tl
                                                     int started_here,
                                                     char *digest_rcvd_out);
  
 +static void connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn);
 +
 +#ifdef USE_BUFFEREVENTS
 +static void connection_or_handle_event_cb(struct bufferevent *bufev,
 +                                          short event, void *arg);
 +#include <event2/buffer.h>/*XXXX REMOVE */
 +#endif
 +
  /**************************************************************/
  
  /** Map from identity digest of connected OR or desired OR to a connection_t
@@@ -191,8 -178,7 +191,8 @@@ var_cell_pack_header(const var_cell_t *
  var_cell_t *
  var_cell_new(uint16_t payload_len)
  {
 -  var_cell_t *cell = tor_malloc(sizeof(var_cell_t)+payload_len-1);
 +  size_t size = STRUCT_OFFSET(var_cell_t, payload) + payload_len;
 +  var_cell_t *cell = tor_malloc(size);
    cell->payload_len = payload_len;
    cell->command = 0;
    cell->circ_id = 0;
@@@ -241,14 -227,6 +241,14 @@@ connection_or_process_inbuf(or_connecti
        }
  
        return ret;
 +#ifdef USE_BUFFEREVENTS
 +    case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
 +      if (tor_tls_server_got_renegotiate(conn->tls))
 +        connection_or_tls_renegotiated_cb(conn->tls, conn);
 +      if (conn->_base.marked_for_close)
 +        return 0;
 +      /* fall through. */
 +#endif
      case OR_CONN_STATE_OPEN:
      case OR_CONN_STATE_OR_HANDSHAKING:
        return connection_or_process_cells_from_inbuf(conn);
@@@ -270,7 -248,7 +270,7 @@@
  int
  connection_or_flushed_some(or_connection_t *conn)
  {
 -  size_t datalen = buf_datalen(conn->_base.outbuf);
 +  size_t datalen = connection_get_outbuf_len(TO_CONN(conn));
    /* If we're under the low water mark, add cells until we're just over the
     * high water mark. */
    if (datalen < OR_CONN_LOWWATER) {
@@@ -303,6 -281,7 +303,6 @@@ connection_or_finished_flushing(or_conn
      case OR_CONN_STATE_PROXY_HANDSHAKING:
      case OR_CONN_STATE_OPEN:
      case OR_CONN_STATE_OR_HANDSHAKING:
 -      connection_stop_writing(TO_CONN(conn));
        break;
      default:
        log_err(LD_BUG,"Called in unexpected state %d.", conn->_base.state);
@@@ -363,7 -342,7 +363,7 @@@ connection_or_digest_is_known_relay(con
  {
    if (router_get_consensus_status_by_id(id_digest))
      return 1; /* It's in the consensus: "yes" */
 -  if (router_get_by_digest(id_digest))
 +  if (router_get_by_id_digest(id_digest))
      return 1; /* Not in the consensus, but we have a descriptor for
                 * it. Probably it was in a recent consensus. "Yes". */
    return 0;
@@@ -391,31 -370,15 +391,31 @@@ connection_or_update_token_buckets_help
       * bandwidth parameters in the consensus, but allow local config
       * options to override. */
      rate = options->PerConnBWRate ? (int)options->PerConnBWRate :
-         (int)networkstatus_get_param(NULL, "perconnbwrate",
-                                      (int)options->BandwidthRate);
+         networkstatus_get_param(NULL, "perconnbwrate",
+                                 (int)options->BandwidthRate, 1, INT32_MAX);
      burst = options->PerConnBWBurst ? (int)options->PerConnBWBurst :
-         (int)networkstatus_get_param(NULL, "perconnbwburst",
-                                      (int)options->BandwidthBurst);
+         networkstatus_get_param(NULL, "perconnbwburst",
+                                 (int)options->BandwidthBurst, 1, INT32_MAX);
    }
  
    conn->bandwidthrate = rate;
    conn->bandwidthburst = burst;
 +#ifdef USE_BUFFEREVENTS
 +  {
 +    const struct timeval *tick = tor_libevent_get_one_tick_timeout();
 +    struct ev_token_bucket_cfg *cfg, *old_cfg;
 +    int rate_per_tick = rate / TOR_LIBEVENT_TICKS_PER_SECOND;
 +    cfg = ev_token_bucket_cfg_new(rate_per_tick, burst, rate_per_tick,
 +                                  burst, tick);
 +    old_cfg = conn->bucket_cfg;
 +    if (conn->_base.bufev)
 +      bufferevent_set_rate_limit(conn->_base.bufev, cfg);
 +    if (old_cfg)
 +      ev_token_bucket_cfg_free(old_cfg);
 +    conn->bucket_cfg = cfg;
 +    (void) reset; /* No way to do this with libevent yet. */
 +  }
 +#else
    if (reset) { /* set up the token buckets to be full */
      conn->read_bucket = conn->write_bucket = burst;
      return;
@@@ -426,7 -389,6 +426,7 @@@
      conn->read_bucket = burst;
    if (conn->write_bucket > burst)
      conn->write_bucket = burst;
 +#endif
  }
  
  /** Either our set of relays or our per-conn rate limits have changed.
@@@ -450,7 -412,7 +450,7 @@@ connection_or_init_conn_from_address(or
                                       const char *id_digest,
                                       int started_here)
  {
 -  routerinfo_t *r = router_get_by_digest(id_digest);
 +  const node_t *r = node_get_by_id(id_digest);
    connection_or_set_identity_digest(conn, id_digest);
    connection_or_update_token_buckets_helper(conn, 1, get_options());
  
@@@ -458,10 -420,8 +458,10 @@@
    tor_addr_copy(&conn->_base.addr, addr);
    tor_addr_copy(&conn->real_addr, addr);
    if (r) {
 +    tor_addr_t node_addr;
 +    node_get_addr(r, &node_addr);
      /* XXXX proposal 118 will make this more complex. */
 -    if (tor_addr_eq_ipv4h(&conn->_base.addr, r->addr))
 +    if (tor_addr_eq(&conn->_base.addr, &node_addr))
        conn->is_canonical = 1;
      if (!started_here) {
        /* Override the addr/port, so our log messages will make sense.
@@@ -474,12 -434,12 +474,12 @@@
         * right IP address and port 56244, that wouldn't be as helpful. now we
         * log the "right" port too, so we know if it's moria1 or moria2.
         */
 -      tor_addr_from_ipv4h(&conn->_base.addr, r->addr);
 -      conn->_base.port = r->or_port;
 +      tor_addr_copy(&conn->_base.addr, &node_addr);
 +      conn->_base.port = node_get_orport(r);
      }
 -    conn->nickname = tor_strdup(r->nickname);
 +    conn->nickname = tor_strdup(node_get_nickname(r));
      tor_free(conn->_base.address);
 -    conn->_base.address = tor_strdup(r->address);
 +    conn->_base.address = tor_dup_addr(&node_addr);
    } else {
      const char *n;
      /* If we're an authoritative directory server, we may know a
@@@ -899,7 -859,6 +899,7 @@@ in
  connection_tls_start_handshake(or_connection_t *conn, int receiving)
  {
    conn->_base.state = OR_CONN_STATE_TLS_HANDSHAKING;
 +  tor_assert(!conn->tls);
    conn->tls = tor_tls_new(conn->_base.s, receiving);
    tor_tls_set_logged_address(conn->tls, // XXX client and relay?
        escaped_safe_str(conn->_base.address));
@@@ -907,38 -866,12 +907,38 @@@
      log_warn(LD_BUG,"tor_tls_new failed. Closing.");
      return -1;
    }
 +#ifdef USE_BUFFEREVENTS
 +  if (connection_type_uses_bufferevent(TO_CONN(conn))) {
 +    const int filtering = get_options()->_UseFilteringSSLBufferevents;
 +    struct bufferevent *b =
 +      tor_tls_init_bufferevent(conn->tls, conn->_base.bufev, conn->_base.s,
 +                               receiving, filtering);
 +    if (!b) {
 +      log_warn(LD_BUG,"tor_tls_init_bufferevent failed. Closing.");
 +      return -1;
 +    }
 +    conn->_base.bufev = b;
 +    if (conn->bucket_cfg)
 +      bufferevent_set_rate_limit(conn->_base.bufev, conn->bucket_cfg);
 +    connection_enable_rate_limiting(TO_CONN(conn));
 +
 +    connection_configure_bufferevent_callbacks(TO_CONN(conn));
 +    bufferevent_setcb(b,
 +                      connection_handle_read_cb,
 +                      connection_handle_write_cb,
 +                      connection_or_handle_event_cb,/* overriding this one*/
 +                      TO_CONN(conn));
 +  }
 +#endif
    connection_start_reading(TO_CONN(conn));
    log_debug(LD_HANDSHAKE,"starting TLS handshake on fd %d", conn->_base.s);
    note_crypto_pk_op(receiving ? TLS_HANDSHAKE_S : TLS_HANDSHAKE_C);
  
 -  if (connection_tls_continue_handshake(conn) < 0) {
 -    return -1;
 +  IF_HAS_BUFFEREVENT(TO_CONN(conn), {
 +    /* ???? */;
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    if (connection_tls_continue_handshake(conn) < 0)
 +      return -1;
    }
    return 0;
  }
@@@ -1023,78 -956,6 +1023,78 @@@ connection_tls_continue_handshake(or_co
    return 0;
  }
  
 +#ifdef USE_BUFFEREVENTS
 +static void
 +connection_or_handle_event_cb(struct bufferevent *bufev, short event,
 +                              void *arg)
 +{
 +  struct or_connection_t *conn = TO_OR_CONN(arg);
 +
 +  /* XXXX cut-and-paste code; should become a function. */
 +  if (event & BEV_EVENT_CONNECTED) {
 +    if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
 +      if (tor_tls_finish_handshake(conn->tls) < 0) {
 +        log_warn(LD_OR, "Problem finishing handshake");
 +        connection_mark_for_close(TO_CONN(conn));
 +        return;
 +      }
 +    }
 +
 +    if (! tor_tls_used_v1_handshake(conn->tls)) {
 +      if (!tor_tls_is_server(conn->tls)) {
 +        if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
 +          conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
 +          tor_tls_unblock_renegotiation(conn->tls);
 +          if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) {
 +            log_warn(LD_OR, "Start_renegotiating went badly.");
 +            connection_mark_for_close(TO_CONN(conn));
 +          }
 +          tor_tls_unblock_renegotiation(conn->tls);
 +          return; /* ???? */
 +        }
 +      } else if (tor_tls_get_num_server_handshakes(conn->tls) == 1) {
 +        /* improved handshake, as a server. Only got one handshake, so
 +         * wait for the next one. */
 +        tor_tls_set_renegotiate_callback(conn->tls,
 +                                         connection_or_tls_renegotiated_cb,
 +                                         conn);
 +        conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING;
 +        /* return 0; */
 +        return; /* ???? */
 +      } else {
 +        const int handshakes = tor_tls_get_num_server_handshakes(conn->tls);
 +        tor_assert(handshakes >= 2);
 +        if (handshakes == 2) {
 +          /* improved handshake, as a server.  Two handshakes happened already,
 +           * so we treat renegotiation as done.
 +           */
 +          connection_or_tls_renegotiated_cb(conn->tls, conn);
 +        } else {
 +          log_warn(LD_OR, "More than two handshakes done on connection. "
 +                   "Closing.");
 +          connection_mark_for_close(TO_CONN(conn));
 +        }
 +        return;
 +      }
 +    }
 +    connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
 +    if (connection_tls_finish_handshake(conn) < 0)
 +      connection_mark_for_close(TO_CONN(conn)); /* ???? */
 +    return;
 +  }
 +
 +  if (event & BEV_EVENT_ERROR) {
 +    unsigned long err;
 +    while ((err = bufferevent_get_openssl_error(bufev))) {
 +      tor_tls_log_one_error(conn->tls, err, LOG_WARN, LD_OR,
 +                            "handshaking (with bufferevent)");
 +    }
 +  }
 +
 +  connection_handle_event_cb(bufev, event, arg);
 +}
 +#endif
 +
  /** Return 1 if we initiated this connection, or 0 if it started
   * out as an incoming connection.
   */
@@@ -1266,9 -1127,7 +1266,9 @@@ connection_tls_finish_handshake(or_conn
    char digest_rcvd[DIGEST_LEN];
    int started_here = connection_or_nonopen_was_started_here(conn);
  
 -  log_debug(LD_HANDSHAKE,"tls handshake with %s done. verifying.",
 +  log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done. verifying.",
 +            started_here?"outgoing":"incoming",
 +            conn,
              safe_str_client(conn->_base.address));
  
    directory_set_dirty();
@@@ -1349,7 -1208,7 +1349,7 @@@ connection_or_set_state_open(or_connect
      router_set_status(conn->identity_digest, 1);
    } else {
      /* only report it to the geoip module if it's not a known router */
 -    if (!router_get_by_digest(conn->identity_digest)) {
 +    if (!router_get_by_id_digest(conn->identity_digest)) {
        if (tor_addr_family(&TO_CONN(conn)->addr) == AF_INET) {
          /*XXXX IP6 support ipv6 geoip.*/
          uint32_t a = tor_addr_to_ipv4h(&TO_CONN(conn)->addr);
@@@ -1360,12 -1219,8 +1360,12 @@@
  
    or_handshake_state_free(conn->handshake_state);
    conn->handshake_state = NULL;
 +  IF_HAS_BUFFEREVENT(TO_CONN(conn), {
 +    connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    connection_start_reading(TO_CONN(conn));
 +  }
  
 -  connection_start_reading(TO_CONN(conn));
    circuit_n_conn_done(conn, 1); /* send the pending creates, if any. */
  
    return 0;
@@@ -1410,18 -1265,12 +1410,18 @@@ connection_or_write_var_cell_to_buf(con
      conn->timestamp_last_added_nonpadding = approx_time();
  }
  
 -/** See whether there's a variable-length cell waiting on <b>conn</b>'s
 +/** See whether there's a variable-length cell waiting on <b>or_conn</b>'s
   * inbuf.  Return values as for fetch_var_cell_from_buf(). */
  static int
 -connection_fetch_var_cell_from_buf(or_connection_t *conn, var_cell_t **out)
 +connection_fetch_var_cell_from_buf(or_connection_t *or_conn, var_cell_t **out)
  {
 -  return fetch_var_cell_from_buf(conn->_base.inbuf, out, conn->link_proto);
 +  connection_t *conn = TO_CONN(or_conn);
 +  IF_HAS_BUFFEREVENT(conn, {
 +    struct evbuffer *input = bufferevent_get_input(conn->bufev);
 +    return fetch_var_cell_from_evbuffer(input, out, or_conn->link_proto);
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    return fetch_var_cell_from_buf(conn->inbuf, out, or_conn->link_proto);
 +  }
  }
  
  /** Process cells from <b>conn</b>'s inbuf.
@@@ -1439,7 -1288,7 +1439,7 @@@ connection_or_process_cells_from_inbuf(
    while (1) {
      log_debug(LD_OR,
                "%d: starting, inbuf_datalen %d (%d pending in tls object).",
 -              conn->_base.s,(int)buf_datalen(conn->_base.inbuf),
 +              conn->_base.s,(int)connection_get_inbuf_len(TO_CONN(conn)),
                tor_tls_get_pending_bytes(conn->tls));
      if (connection_fetch_var_cell_from_buf(conn, &var_cell)) {
        if (!var_cell)
@@@ -1450,8 -1299,8 +1450,8 @@@
      } else {
        char buf[CELL_NETWORK_SIZE];
        cell_t cell;
 -      if (buf_datalen(conn->_base.inbuf) < CELL_NETWORK_SIZE) /* whole response
 -                                                                 available? */
 +      if (connection_get_inbuf_len(TO_CONN(conn))
 +          < CELL_NETWORK_SIZE) /* whole response available? */
          return 0; /* not yet */
  
        circuit_build_times_network_is_live(&circ_times);
@@@ -1538,7 -1387,7 +1538,7 @@@ connection_or_send_netinfo(or_connectio
  {
    cell_t cell;
    time_t now = time(NULL);
 -  routerinfo_t *me;
 +  const routerinfo_t *me;
    int len;
    uint8_t *out;
  
diff --combined src/or/dirvote.h
index b2b8d28,67540a3..97bed5e
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@@ -19,9 -19,6 +19,6 @@@
  /** Smallest allowable voting interval. */
  #define MIN_VOTE_INTERVAL 300
  
- /** Precision multiplier for the Bw weights */
- #define BW_WEIGHT_SCALE   10000
- 
  void dirvote_free_all(void);
  
  /* vote manipulation */
@@@ -63,7 -60,6 +60,7 @@@ const char *dirvote_get_pending_detache
  #define DGV_INCLUDE_PREVIOUS 4
  const cached_dir_t *dirvote_get_vote(const char *fp, int flags);
  void set_routerstatus_from_routerinfo(routerstatus_t *rs,
 +                                      node_t *node,
                                        routerinfo_t *ri, time_t now,
                                        int naming, int listbadexits,
                                        int listbaddirs);
diff --combined src/or/networkstatus.c
index 68dc9f6,687ac03..cccb1c0
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@@ -20,9 -20,7 +20,9 @@@
  #include "dirserv.h"
  #include "dirvote.h"
  #include "main.h"
 +#include "microdesc.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "relay.h"
  #include "router.h"
  #include "routerlist.h"
@@@ -46,19 -44,8 +46,19 @@@ static strmap_t *named_server_map = NUL
   * as unnamed for some server in the consensus. */
  static strmap_t *unnamed_server_map = NULL;
  
 -/** Most recently received and validated v3 consensus network status. */
 -static networkstatus_t *current_consensus = NULL;
 +/** Most recently received and validated v3 consensus network status,
 + * of whichever type we are using for our own circuits.  This will be the same
 + * as one of current_ns_consensus or current_md_consensus.
 + */
 +#define current_consensus current_ns_consensus
 +
 +/** Most recently received and validated v3 "ns"-flavored consensus network
 + * status. */
 +static networkstatus_t *current_ns_consensus = NULL;
 +
 +/** Most recently received and validated v3 "microdec"-flavored consensus
 + * network status. */
 +static networkstatus_t *current_md_consensus = NULL;
  
  /** A v3 consensus networkstatus that we've received, but which we don't
   * have enough certificates to be happy about. */
@@@ -107,8 -94,9 +107,8 @@@ voi
  networkstatus_reset_warnings(void)
  {
    if (current_consensus) {
 -    SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 -                      routerstatus_t *, rs,
 -                      rs->name_lookup_warned = 0);
 +    SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
 +                      node->name_lookup_warned = 0);
    }
  
    have_warned_about_old_version = 0;
@@@ -283,7 -271,6 +283,7 @@@ router_reload_consensus_networkstatus(v
    update_certificate_downloads(time(NULL));
  
    routers_update_all_from_networkstatus(time(NULL), 3);
 +  update_microdescs_from_networkstatus(time(NULL));
  
    return 0;
  }
@@@ -940,9 -927,10 +940,9 @@@ compare_digest_to_routerstatus_entry(co
    return memcmp(key, rs->identity_digest, DIGEST_LEN);
  }
  
 -/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
 - * NULL if none was found. */
 +/** As networkstatus_v2_find_entry, but do not return a const pointer */
  routerstatus_t *
 -networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
 +networkstatus_v2_find_mutable_entry(networkstatus_v2_t *ns, const char *digest)
  {
    return smartlist_bsearch(ns->entries, digest,
                             compare_digest_to_routerstatus_entry);
@@@ -950,29 -938,14 +950,29 @@@
  
  /** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
   * NULL if none was found. */
 +const routerstatus_t *
 +networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
 +{
 +  return networkstatus_v2_find_mutable_entry(ns, digest);
 +}
 +
 +/** As networkstatus_find_entry, but do not return a const pointer */
  routerstatus_t *
 -networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest)
 +networkstatus_vote_find_mutable_entry(networkstatus_t *ns, const char *digest)
  {
    return smartlist_bsearch(ns->routerstatus_list, digest,
                             compare_digest_to_routerstatus_entry);
  }
  
 -/*XXXX make this static once functions are moved into this file. */
 +/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
 + * NULL if none was found. */
 +const routerstatus_t *
 +networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest)
 +{
 +  return networkstatus_vote_find_mutable_entry(ns, digest);
 +}
 +
 +/*XXXX MOVE make this static once functions are moved into this file. */
  /** Search the routerstatuses in <b>ns</b> for one whose identity digest is
   * <b>digest</b>.  Return value and set *<b>found_out</b> as for
   * smartlist_bsearch_idx(). */
@@@ -994,37 -967,22 +994,37 @@@ networkstatus_get_v2_list(void
    return networkstatus_v2_list;
  }
  
 -/** Return the consensus view of the status of the router whose current
 - * <i>descriptor</i> digest is <b>digest</b>, or NULL if no such router is
 - * known. */
 +/* As router_get_consensus_status_by_descriptor_digest, but does not return
 + * a const pointer */
  routerstatus_t *
 -router_get_consensus_status_by_descriptor_digest(const char *digest)
 +router_get_mutable_consensus_status_by_descriptor_digest(
 +                                                 networkstatus_t *consensus,
 +                                                 const char *digest)
  {
 -  if (!current_consensus) return NULL;
 -  if (!current_consensus->desc_digest_map) {
 -    digestmap_t * m = current_consensus->desc_digest_map = digestmap_new();
 -    SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 +  if (!consensus)
 +    consensus = current_consensus;
 +  if (!consensus)
 +    return NULL;
 +  if (!consensus->desc_digest_map) {
 +    digestmap_t *m = consensus->desc_digest_map = digestmap_new();
 +    SMARTLIST_FOREACH(consensus->routerstatus_list,
                        routerstatus_t *, rs,
       {
         digestmap_set(m, rs->descriptor_digest, rs);
       });
    }
 -  return digestmap_get(current_consensus->desc_digest_map, digest);
 +  return digestmap_get(consensus->desc_digest_map, digest);
 +}
 +
 +/** Return the consensus view of the status of the router whose current
 + * <i>descriptor</i> digest in <b>consensus</b> is <b>digest</b>, or NULL if
 + * no such router is known. */
 +const routerstatus_t *
 +router_get_consensus_status_by_descriptor_digest(networkstatus_t *consensus,
 +                                                 const char *digest)
 +{
 +  return router_get_mutable_consensus_status_by_descriptor_digest(
 +                                                          consensus, digest);
  }
  
  /** Given the digest of a router descriptor, return its current download
@@@ -1033,10 -991,7 +1033,10 @@@ download_status_t 
  router_get_dl_status_by_descriptor_digest(const char *d)
  {
    routerstatus_t *rs;
 -  if ((rs = router_get_consensus_status_by_descriptor_digest(d)))
 +  if (!current_ns_consensus)
 +    return NULL;
 +  if ((rs = router_get_mutable_consensus_status_by_descriptor_digest(
 +                                              current_ns_consensus, d)))
      return &rs->dl_status;
    if (v2_download_status_map)
      return digestmap_get(v2_download_status_map, d);
@@@ -1044,9 -999,10 +1044,9 @@@
    return NULL;
  }
  
 -/** Return the consensus view of the status of the router whose identity
 - * digest is <b>digest</b>, or NULL if we don't know about any such router. */
 +/** As router_get_consensus_status_by_id, but do not return a const pointer */
  routerstatus_t *
 -router_get_consensus_status_by_id(const char *digest)
 +router_get_mutable_consensus_status_by_id(const char *digest)
  {
    if (!current_consensus)
      return NULL;
@@@ -1054,27 -1010,100 +1054,27 @@@
                             compare_digest_to_routerstatus_entry);
  }
  
 +/** Return the consensus view of the status of the router whose identity
 + * digest is <b>digest</b>, or NULL if we don't know about any such router. */
 +const routerstatus_t *
 +router_get_consensus_status_by_id(const char *digest)
 +{
 +  return router_get_mutable_consensus_status_by_id(digest);
 +}
 +
  /** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
   * the corresponding routerstatus_t, or NULL if none exists.  Warn the
   * user if <b>warn_if_unnamed</b> is set, and they have specified a router by
   * nickname, but the Named flag isn't set for that router. */
 -routerstatus_t *
 +const routerstatus_t *
  router_get_consensus_status_by_nickname(const char *nickname,
                                          int warn_if_unnamed)
  {
 -  char digest[DIGEST_LEN];
 -  routerstatus_t *best=NULL;
 -  smartlist_t *matches=NULL;
 -  const char *named_id=NULL;
 -
 -  if (!current_consensus || !nickname)
 -    return NULL;
 -
 -  /* Is this name really a hexadecimal identity digest? */
 -  if (nickname[0] == '$') {
 -    if (base16_decode(digest, DIGEST_LEN, nickname+1, strlen(nickname+1))<0)
 -      return NULL;
 -    return networkstatus_vote_find_entry(current_consensus, digest);
 -  } else if (strlen(nickname) == HEX_DIGEST_LEN &&
 -       (base16_decode(digest, DIGEST_LEN, nickname, strlen(nickname))==0)) {
 -    return networkstatus_vote_find_entry(current_consensus, digest);
 -  }
 -
 -  /* Is there a server that is Named with this name? */
 -  if (named_server_map)
 -    named_id = strmap_get_lc(named_server_map, nickname);
 -  if (named_id)
 -    return networkstatus_vote_find_entry(current_consensus, named_id);
 -
 -  /* Okay; is this name listed as Unnamed? */
 -  if (unnamed_server_map &&
 -      strmap_get_lc(unnamed_server_map, nickname)) {
 -    log_info(LD_GENERAL, "The name %s is listed as Unnamed; it is not the "
 -             "canonical name of any server we know.", escaped(nickname));
 +  const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
 +  if (node)
 +    return node->rs;
 +  else
      return NULL;
 -  }
 -
 -  /* This name is not canonical for any server; go through the list and
 -   * see who it matches. */
 -  /*XXXX This is inefficient; optimize it if it matters. */
 -  matches = smartlist_create();
 -  SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 -                    routerstatus_t *, lrs,
 -    {
 -      if (!strcasecmp(lrs->nickname, nickname)) {
 -        if (lrs->is_named) {
 -          tor_fragile_assert() /* This should never happen. */
 -          smartlist_free(matches);
 -          return lrs;
 -        } else {
 -          if (lrs->is_unnamed) {
 -            tor_fragile_assert(); /* nor should this. */
 -            smartlist_clear(matches);
 -            best=NULL;
 -            break;
 -          }
 -          smartlist_add(matches, lrs);
 -          best = lrs;
 -        }
 -      }
 -    });
 -
 -  if (smartlist_len(matches)>1 && warn_if_unnamed) {
 -    int any_unwarned=0;
 -    SMARTLIST_FOREACH(matches, routerstatus_t *, lrs,
 -      {
 -        if (! lrs->name_lookup_warned) {
 -          lrs->name_lookup_warned=1;
 -          any_unwarned=1;
 -        }
 -      });
 -    if (any_unwarned) {
 -      log_warn(LD_CONFIG,"There are multiple matches for the nickname \"%s\","
 -               " but none is listed as named by the directory authorities. "
 -               "Choosing one arbitrarily.", nickname);
 -    }
 -  } else if (warn_if_unnamed && best && !best->name_lookup_warned) {
 -    char fp[HEX_DIGEST_LEN+1];
 -    base16_encode(fp, sizeof(fp),
 -                  best->identity_digest, DIGEST_LEN);
 -    log_warn(LD_CONFIG,
 -         "When looking up a status, you specified a server \"%s\" by name, "
 -         "but the directory authorities do not have any key registered for "
 -         "this nickname -- so it could be used by any server, "
 -         "not just the one you meant. "
 -         "To make sure you get the same server in the future, refer to "
 -         "it by key, as \"$%s\".", nickname, fp);
 -    best->name_lookup_warned = 1;
 -  }
 -  smartlist_free(matches);
 -  return best;
  }
  
  /** Return the identity digest that's mapped to officially by
@@@ -1171,25 -1200,6 +1171,25 @@@ update_v2_networkstatus_cache_downloads
    }
  }
  
 +/** DOCDOC */
 +static int
 +we_want_to_fetch_flavor(or_options_t *options, int flavor)
 +{
 +  if (flavor < 0 || flavor > N_CONSENSUS_FLAVORS) {
 +    /* This flavor is crazy; we don't want it */
 +    /*XXXX handle unrecognized flavors later */
 +    return 0;
 +  }
 +  if (authdir_mode_v3(options) || directory_caches_dir_info(options)) {
 +    /* We want to serve all flavors to others, regardless if we would use
 +     * it ourselves. */
 +    return 1;
 +  }
 +  /* Otherwise, we want the flavor only if we want to use it to build
 +   * circuits. */
 +  return (flavor == USABLE_CONSENSUS_FLAVOR);
 +}
 +
  /** How many times will we try to fetch a consensus before we give up? */
  #define CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES 8
  /** How long will we hang onto a possibly live consensus for which we're
@@@ -1202,65 -1212,48 +1202,65 @@@ static voi
  update_consensus_networkstatus_downloads(time_t now)
  {
    int i;
 +  or_options_t *options = get_options();
 +
    if (!networkstatus_get_live_consensus(now))
      time_to_download_next_consensus = now; /* No live consensus? Get one now!*/
    if (time_to_download_next_consensus > now)
      return; /* Wait until the current consensus is older. */
 -  /* XXXXNM Microdescs: may need to download more types. */
 -  if (!download_status_is_ready(&consensus_dl_status[FLAV_NS], now,
 -                                CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
 -    return; /* We failed downloading a consensus too recently. */
 -  if (connection_get_by_type_purpose(CONN_TYPE_DIR,
 -                                     DIR_PURPOSE_FETCH_CONSENSUS))
 -    return; /* There's an in-progress download.*/
  
    for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
 -    consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i];
 +    /* XXXX need some way to download unknown flavors if we are caching. */
 +    const char *resource;
 +    consensus_waiting_for_certs_t *waiting;
 +
 +    if (! we_want_to_fetch_flavor(options, i))
 +      continue;
 +
 +    resource = i==FLAV_NS ? NULL : networkstatus_get_flavor_name(i);
 +
 +    if (!download_status_is_ready(&consensus_dl_status[i], now,
 +                                  CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
 +      continue; /* We failed downloading a consensus too recently. */
 +    if (connection_dir_get_by_purpose_and_resource(
 +                                DIR_PURPOSE_FETCH_CONSENSUS, resource))
 +      continue; /* There's an in-progress download.*/
 +
 +    waiting = &consensus_waiting_for_certs[i];
      if (waiting->consensus) {
        /* XXXX make sure this doesn't delay sane downloads. */
 -      if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now)
 -        return; /* We're still getting certs for this one. */
 -      else {
 +      if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
 +        continue; /* We're still getting certs for this one. */
 +      } else {
          if (!waiting->dl_failed) {
 -          download_status_failed(&consensus_dl_status[FLAV_NS], 0);
 +          download_status_failed(&consensus_dl_status[i], 0);
            waiting->dl_failed=1;
          }
        }
      }
 -  }
  
 -  log_info(LD_DIR, "Launching networkstatus consensus download.");
 -  directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
 -                               ROUTER_PURPOSE_GENERAL, NULL,
 -                               PDS_RETRY_IF_NO_SERVERS);
 +    log_info(LD_DIR, "Launching %s networkstatus consensus download.",
 +             networkstatus_get_flavor_name(i));
 +
 +    directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
 +                                 ROUTER_PURPOSE_GENERAL, resource,
 +                                 PDS_RETRY_IF_NO_SERVERS);
 +  }
  }
  
  /** Called when an attempt to download a consensus fails: note that the
   * failure occurred, and possibly retry. */
  void
 -networkstatus_consensus_download_failed(int status_code)
 +networkstatus_consensus_download_failed(int status_code, const char *flavname)
  {
 -  /* XXXXNM Microdescs: may need to handle more types. */
 -  download_status_failed(&consensus_dl_status[FLAV_NS], status_code);
 -  /* Retry immediately, if appropriate. */
 -  update_consensus_networkstatus_downloads(time(NULL));
 +  int flav = networkstatus_parse_flavor_name(flavname);
 +  if (flav >= 0) {
 +    tor_assert(flav < N_CONSENSUS_FLAVORS);
 +    /* XXXX handle unrecognized flavors */
 +    download_status_failed(&consensus_dl_status[flav], status_code);
 +    /* Retry immediately, if appropriate. */
 +    update_consensus_networkstatus_downloads(time(NULL));
 +  }
  }
  
  /** How long do we (as a cache) wait after a consensus becomes non-fresh
@@@ -1381,10 -1374,7 +1381,10 @@@ update_certificate_downloads(time_t now
                                      now);
    }
  
 -  authority_certs_fetch_missing(current_consensus, now);
 +  if (current_ns_consensus)
 +    authority_certs_fetch_missing(current_ns_consensus, now);
 +  if (current_md_consensus)
 +    authority_certs_fetch_missing(current_md_consensus, now);
  }
  
  /** Return 1 if we have a consensus but we don't have enough certificates
@@@ -1416,18 -1406,6 +1416,18 @@@ networkstatus_get_latest_consensus(void
    return current_consensus;
  }
  
 +/** DOCDOC */
 +networkstatus_t *
 +networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
 +{
 +  if (f == FLAV_NS)
 +    return current_ns_consensus;
 +  else if (f == FLAV_MICRODESC)
 +    return current_md_consensus;
 +  else
 +    tor_assert(0);
 +}
 +
  /** Return the most recent consensus that we have downloaded, or NULL if it is
   * no longer live. */
  networkstatus_t *
@@@ -1447,15 -1425,13 +1447,15 @@@ networkstatus_get_live_consensus(time_
  /** As networkstatus_get_live_consensus(), but is way more tolerant of expired
   * consensuses. */
  networkstatus_t *
 -networkstatus_get_reasonably_live_consensus(time_t now)
 +networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
  {
  #define REASONABLY_LIVE_TIME (24*60*60)
 -  if (current_consensus &&
 -      current_consensus->valid_after <= now &&
 -      now <= current_consensus->valid_until+REASONABLY_LIVE_TIME)
 -    return current_consensus;
 +  networkstatus_t *consensus =
 +    networkstatus_get_latest_consensus_by_flavor(flavor);
 +  if (consensus &&
 +      consensus->valid_after <= now &&
 +      now <= consensus->valid_until+REASONABLY_LIVE_TIME)
 +    return consensus;
    else
      return NULL;
  }
@@@ -1476,7 -1452,7 +1476,7 @@@ routerstatus_has_changed(const routerst
           a->is_exit != b->is_exit ||
           a->is_stable != b->is_stable ||
           a->is_fast != b->is_fast ||
 -         a->is_running != b->is_running ||
 +         a->is_flagged_running != b->is_flagged_running ||
           a->is_named != b->is_named ||
           a->is_unnamed != b->is_unnamed ||
           a->is_valid != b->is_valid ||
@@@ -1517,14 -1493,13 +1517,14 @@@ notify_control_networkstatus_changed(co
    }
    changed = smartlist_create();
  
 -  SMARTLIST_FOREACH_JOIN(old_c->routerstatus_list, routerstatus_t *, rs_old,
 -                         new_c->routerstatus_list, routerstatus_t *, rs_new,
 -                         memcmp(rs_old->identity_digest,
 -                                rs_new->identity_digest, DIGEST_LEN),
 -                         smartlist_add(changed, rs_new)) {
 +  SMARTLIST_FOREACH_JOIN(
 +                     old_c->routerstatus_list, const routerstatus_t *, rs_old,
 +                     new_c->routerstatus_list, const routerstatus_t *, rs_new,
 +                     memcmp(rs_old->identity_digest,
 +                            rs_new->identity_digest, DIGEST_LEN),
 +                     smartlist_add(changed, (void*) rs_new)) {
      if (routerstatus_has_changed(rs_old, rs_new))
 -      smartlist_add(changed, rs_new);
 +      smartlist_add(changed, (void*)rs_new);
    } SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new);
  
    control_event_networkstatus_changed(changed);
@@@ -1548,6 -1523,7 +1548,6 @@@ networkstatus_copy_old_consensus_info(n
                                  rs_new->identity_digest, DIGEST_LEN),
                           STMT_NIL) {
      /* Okay, so we're looking at the same identity. */
 -    rs_new->name_lookup_warned = rs_old->name_lookup_warned;
      rs_new->last_dir_503_at = rs_old->last_dir_503_at;
  
      if (!memcmp(rs_old->descriptor_digest, rs_new->descriptor_digest,
@@@ -1640,16 -1616,9 +1640,16 @@@ networkstatus_set_current_consensus(con
    if (!strcmp(flavor, "ns")) {
      consensus_fname = get_datadir_fname("cached-consensus");
      unverified_fname = get_datadir_fname("unverified-consensus");
 -    if (current_consensus) {
 -      current_digests = &current_consensus->digests;
 -      current_valid_after = current_consensus->valid_after;
 +    if (current_ns_consensus) {
 +      current_digests = &current_ns_consensus->digests;
 +      current_valid_after = current_ns_consensus->valid_after;
 +    }
 +  } else if (!strcmp(flavor, "microdesc")) {
 +    consensus_fname = get_datadir_fname("cached-microdesc-consensus");
 +    unverified_fname = get_datadir_fname("unverified-microdesc-consensus");
 +    if (current_md_consensus) {
 +      current_digests = &current_md_consensus->digests;
 +      current_valid_after = current_md_consensus->valid_after;
      }
    } else {
      cached_dir_t *cur;
@@@ -1735,27 -1704,15 +1735,27 @@@
  
    if (flav == USABLE_CONSENSUS_FLAVOR) {
      notify_control_networkstatus_changed(current_consensus, c);
 -
 -    if (current_consensus) {
 -      networkstatus_copy_old_consensus_info(c, current_consensus);
 -      networkstatus_vote_free(current_consensus);
 +  }
 +  if (flav == FLAV_NS) {
 +    if (current_ns_consensus) {
 +      networkstatus_copy_old_consensus_info(c, current_ns_consensus);
 +      networkstatus_vote_free(current_ns_consensus);
        /* Defensive programming : we should set current_consensus very soon,
         * but we're about to call some stuff in the meantime, and leaving this
         * dangling pointer around has proven to be trouble. */
 -       current_consensus = NULL;
 +      current_ns_consensus = NULL;
 +    }
 +    current_ns_consensus = c;
 +    free_consensus = 0; /* avoid free */
 +  } else if (flav == FLAV_MICRODESC) {
 +    if (current_md_consensus) {
 +      networkstatus_copy_old_consensus_info(c, current_md_consensus);
 +      networkstatus_vote_free(current_md_consensus);
 +      /* more defensive programming */
 +      current_md_consensus = NULL;
      }
 +    current_md_consensus = c;
 +    free_consensus = 0; /* avoid free */
    }
  
    waiting = &consensus_waiting_for_certs[flav];
@@@ -1781,11 -1738,11 +1781,11 @@@
    }
  
    if (flav == USABLE_CONSENSUS_FLAVOR) {
 -    current_consensus = c;
 -    free_consensus = 0; /* Prevent free. */
 -
      /* XXXXNM Microdescs: needs a non-ns variant. */
      update_consensus_networkstatus_fetch_time(now);
 +
 +    nodelist_set_consensus(current_consensus);
 +
      dirvote_recalculate_timing(options, now);
      routerstatus_list_update_named_server_map();
      cell_ewma_set_scale_factor(options, current_consensus);
@@@ -1807,11 -1764,11 +1807,11 @@@
      write_str_to_file(consensus_fname, consensus, 0);
    }
  
 -  if (ftime_definitely_before(now, current_consensus->valid_after)) {
 +  if (time_definitely_before(now, c->valid_after, 60)) {
      char tbuf[ISO_TIME_LEN+1];
      char dbuf[64];
 -    long delta = now - current_consensus->valid_after;
 -    format_iso_time(tbuf, current_consensus->valid_after);
 +    long delta = now - c->valid_after;
 +    format_iso_time(tbuf, c->valid_after);
      format_time_interval(dbuf, sizeof(dbuf), delta);
      log_warn(LD_GENERAL, "Our clock is %s behind the time published in the "
               "consensus network status document (%s GMT).  Tor needs an "
@@@ -1862,8 -1819,7 +1862,8 @@@ voi
  routers_update_all_from_networkstatus(time_t now, int dir_version)
  {
    routerlist_t *rl = router_get_routerlist();
 -  networkstatus_t *consensus = networkstatus_get_live_consensus(now);
 +  networkstatus_t *consensus = networkstatus_get_reasonably_live_consensus(now,
 +                                                                     FLAV_NS);
  
    if (networkstatus_v2_list_has_changed)
      download_status_map_update_from_v2_networkstatus();
@@@ -1935,7 -1891,7 +1935,7 @@@ download_status_map_update_from_v2_netw
  
    dl_status = digestmap_new();
    SMARTLIST_FOREACH_BEGIN(networkstatus_v2_list, networkstatus_v2_t *, ns) {
 -    SMARTLIST_FOREACH_BEGIN(ns->entries, routerstatus_t *, rs) {
 +    SMARTLIST_FOREACH_BEGIN(ns->entries, const routerstatus_t *, rs) {
        const char *d = rs->descriptor_digest;
        download_status_t *s;
        if (digestmap_get(dl_status, d))
@@@ -1963,8 -1919,7 +1963,8 @@@ routerstatus_list_update_named_server_m
    named_server_map = strmap_new();
    strmap_free(unnamed_server_map, NULL);
    unnamed_server_map = strmap_new();
 -  SMARTLIST_FOREACH(current_consensus->routerstatus_list, routerstatus_t *, rs,
 +  SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 +                                                   const routerstatus_t *, rs,
      {
        if (rs->is_named) {
          strmap_set_lc(named_server_map, rs->nickname,
@@@ -1986,6 -1941,7 +1986,6 @@@ routers_update_status_from_consensus_ne
    trusted_dir_server_t *ds;
    or_options_t *options = get_options();
    int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
 -  int namingdir = authdir && options->NamingAuthoritativeDir;
    networkstatus_t *ns = current_consensus;
    if (!ns || !smartlist_len(ns->routerstatus_list))
      return;
@@@ -1999,19 -1955,25 +1999,19 @@@
                           memcmp(rs->identity_digest,
                                 router->cache_info.identity_digest, DIGEST_LEN),
    {
 +#if 0
      /* We have no routerstatus for this router. Clear flags and skip it. */
 -    if (!namingdir)
 -      router->is_named = 0;
      if (!authdir) {
        if (router->purpose == ROUTER_PURPOSE_GENERAL)
          router_clear_status_flags(router);
      }
 +#endif
    }) {
      /* We have a routerstatus for this router. */
      const char *digest = router->cache_info.identity_digest;
  
      ds = router_get_trusteddirserver_by_digest(digest);
  
 -    if (!namingdir) {
 -      if (rs->is_named && !strcasecmp(router->nickname, rs->nickname))
 -        router->is_named = 1;
 -      else
 -        router->is_named = 0;
 -    }
      /* Is it the same descriptor, or only the same identity? */
      if (!memcmp(router->cache_info.signed_descriptor_digest,
                  rs->descriptor_digest, DIGEST_LEN)) {
@@@ -2019,17 -1981,28 +2019,17 @@@
          router->cache_info.last_listed_as_valid_until = ns->valid_until;
      }
  
 -    if (!authdir) {
 -      /* If we're not an authdir, believe others. */
 -      router->is_valid = rs->is_valid;
 -      router->is_running = rs->is_running;
 -      router->is_fast = rs->is_fast;
 -      router->is_stable = rs->is_stable;
 -      router->is_possible_guard = rs->is_possible_guard;
 -      router->is_exit = rs->is_exit;
 -      router->is_bad_directory = rs->is_bad_directory;
 -      router->is_bad_exit = rs->is_bad_exit;
 -      router->is_hs_dir = rs->is_hs_dir;
 -    } else {
 +    if (authdir) {
        /* If we _are_ an authority, we should check whether this router
         * is one that will cause us to need a reachability test. */
        routerinfo_t *old_router =
 -        router_get_by_digest(router->cache_info.identity_digest);
 +        router_get_mutable_by_digest(router->cache_info.identity_digest);
        if (old_router != router) {
          router->needs_retest_if_added =
            dirserv_should_launch_reachability_test(router, old_router);
        }
      }
 -    if (router->is_running && ds) {
 +    if (rs->is_flagged_running && ds) {
        download_status_reset(&ds->v2_ns_dl_status);
      }
      if (reset_failures) {
@@@ -2038,9 -2011,10 +2038,9 @@@
    } SMARTLIST_FOREACH_JOIN_END(rs, router);
  
    /* Now update last_listed_as_valid_until from v2 networkstatuses. */
 -  /* XXXX If this is slow, we need to rethink the code. */
    SMARTLIST_FOREACH(networkstatus_v2_list, networkstatus_v2_t *, ns, {
      time_t live_until = ns->published_on + V2_NETWORKSTATUS_ROUTER_LIFETIME;
 -    SMARTLIST_FOREACH_JOIN(ns->entries, routerstatus_t *, rs,
 +    SMARTLIST_FOREACH_JOIN(ns->entries, const routerstatus_t *, rs,
                           routers, routerinfo_t *, ri,
                           memcmp(rs->identity_digest,
                                  ri->cache_info.identity_digest, DIGEST_LEN),
@@@ -2061,7 -2035,7 +2061,7 @@@
  void
  signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
  {
 -  networkstatus_t *ns = current_consensus;
 +  networkstatus_t *ns = current_ns_consensus;
    if (!ns)
      return;
  
@@@ -2069,11 -2043,11 +2069,11 @@@
      char dummy[DIGEST_LEN];
      /* instantiates the digest map. */
      memset(dummy, 0, sizeof(dummy));
 -    router_get_consensus_status_by_descriptor_digest(dummy);
 +    router_get_consensus_status_by_descriptor_digest(ns, dummy);
    }
    SMARTLIST_FOREACH(descs, signed_descriptor_t *, d,
    {
 -    routerstatus_t *rs = digestmap_get(ns->desc_digest_map,
 +    const routerstatus_t *rs = digestmap_get(ns->desc_digest_map,
                                         d->signed_descriptor_digest);
      if (rs) {
        if (ns->valid_until > d->last_listed_as_valid_until)
@@@ -2086,7 -2060,7 +2086,7 @@@
   * return the result in a newly allocated string.  Used only by controller
   * interface (for now.) */
  char *
 -networkstatus_getinfo_helper_single(routerstatus_t *rs)
 +networkstatus_getinfo_helper_single(const routerstatus_t *rs)
  {
    char buf[RS_ENTRY_LEN+1];
    routerstatus_format_entry(buf, sizeof(buf), rs, NULL, NS_CONTROL_PORT);
@@@ -2119,9 -2093,6 +2119,9 @@@ networkstatus_getinfo_by_purpose(const 
  
    statuses = smartlist_create();
    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
 +    node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
 +    if (!node)
 +      continue;
      if (ri->cache_info.published_on < cutoff)
        continue;
      if (ri->purpose != purpose)
@@@ -2129,7 -2100,7 +2129,7 @@@
      if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE)
        dirserv_set_router_is_running(ri, now);
      /* then generate and write out status lines for each of them */
 -    set_routerstatus_from_routerinfo(&rs, ri, now, 0, 0, 0);
 +    set_routerstatus_from_routerinfo(&rs, node, ri, now, 0, 0, 0);
      smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs));
    });
  
@@@ -2154,32 -2125,52 +2154,52 @@@ networkstatus_dump_bridge_status_to_fil
    tor_free(status);
  }
  
- int32_t
+ static int32_t
  get_net_param_from_list(smartlist_t *net_params, const char *param_name,
-                         int default_val)
+                         int32_t default_val, int32_t min_val, int32_t max_val)
  {
+   int32_t res = default_val;
    size_t name_len = strlen(param_name);
  
+   tor_assert(max_val > min_val);
+   tor_assert(min_val <= default_val);
+   tor_assert(max_val >= default_val);
+ 
    SMARTLIST_FOREACH_BEGIN(net_params, const char *, p) {
      if (!strcmpstart(p, param_name) && p[name_len] == '=') {
        int ok=0;
        long v = tor_parse_long(p+name_len+1, 10, INT32_MIN,
                                INT32_MAX, &ok, NULL);
-       if (ok)
-         return (int32_t) v;
+       if (ok) {
+         res = (int32_t) v;
+         break;
+       }
      }
    } SMARTLIST_FOREACH_END(p);
  
-   return default_val;
+   if (res < min_val) {
+     log_warn(LD_DIR, "Consensus parameter %s is too small. Got %d, raising to "
+              "%d.", param_name, res, min_val);
+     res = min_val;
+   } else if (res > max_val) {
+     log_warn(LD_DIR, "Consensus parameter %s is too large. Got %d, capping to "
+              "%d.", param_name, res, max_val);
+     res = max_val;
+   }
+ 
+   return res;
  }
  
  /** Return the value of a integer parameter from the networkstatus <b>ns</b>
   * whose name is <b>param_name</b>.  If <b>ns</b> is NULL, try loading the
   * latest consensus ourselves. Return <b>default_val</b> if no latest
-  * consensus, or if it has no parameter called <b>param_name</b>. */
+  * consensus, or if it has no parameter called <b>param_name</b>.
+  * Make sure the value parsed from the consensus is at least
+  * <b>min_val</b> and at most <b>max_val</b> and raise/cap the parsed value
+  * if necessary. */
  int32_t
  networkstatus_get_param(networkstatus_t *ns, const char *param_name,
-                         int32_t default_val)
+                         int32_t default_val, int32_t min_val, int32_t max_val)
  {
    if (!ns) /* if they pass in null, go find it ourselves */
      ns = networkstatus_get_latest_consensus();
@@@ -2187,24 -2178,36 +2207,36 @@@
    if (!ns || !ns->net_params)
      return default_val;
  
-   return get_net_param_from_list(ns->net_params, param_name, default_val);
+   return get_net_param_from_list(ns->net_params, param_name,
+                                  default_val, min_val, max_val);
  }
  
  /** Return the value of a integer bw weight parameter from the networkstatus
   * <b>ns</b> whose name is <b>weight_name</b>.  If <b>ns</b> is NULL, try
   * loading the latest consensus ourselves. Return <b>default_val</b> if no
-  * latest consensus, or if it has no parameter called <b>param_name</b>. */
+  * latest consensus, or if it has no parameter called <b>weight_name</b>. */
  int32_t
  networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight_name,
-                         int32_t default_val)
+                             int32_t default_val)
  {
+   int32_t param;
+   int max;
    if (!ns) /* if they pass in null, go find it ourselves */
      ns = networkstatus_get_latest_consensus();
  
    if (!ns || !ns->weight_params)
      return default_val;
  
-   return get_net_param_from_list(ns->weight_params, weight_name, default_val);
+   max = circuit_build_times_get_bw_scale(ns);
+   param = get_net_param_from_list(ns->weight_params, weight_name,
+                                   default_val, -1,
+                                   BW_MAX_WEIGHT_SCALE);
+   if (param > max) {
+     log_warn(LD_DIR, "Value of consensus weight %s was too large, capping "
+              "to %d", weight_name, max);
+     param = max;
+   }
+   return param;
  }
  
  /** Return the name of the consensus flavor <b>flav</b> as used to identify
@@@ -2245,7 -2248,7 +2277,7 @@@ getinfo_helper_networkstatus(control_co
                               const char *question, char **answer,
                               const char **errmsg)
  {
 -  routerstatus_t *status;
 +  const routerstatus_t *status;
    (void) conn;
  
    if (!current_consensus) {
@@@ -2256,7 -2259,7 +2288,7 @@@
    if (!strcmp(question, "ns/all")) {
      smartlist_t *statuses = smartlist_create();
      SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 -                      routerstatus_t *, rs,
 +                      const routerstatus_t *, rs,
        {
          smartlist_add(statuses, networkstatus_getinfo_helper_single(rs));
        });
@@@ -2300,9 -2303,8 +2332,9 @@@ networkstatus_free_all(void
  
    digestmap_free(v2_download_status_map, _tor_free);
    v2_download_status_map = NULL;
 -  networkstatus_vote_free(current_consensus);
 -  current_consensus = NULL;
 +  networkstatus_vote_free(current_ns_consensus);
 +  networkstatus_vote_free(current_md_consensus);
 +  current_md_consensus = current_ns_consensus = NULL;
  
    for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
      consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i];
diff --combined src/or/networkstatus.h
index e01e6b5,ec2e8f8..38fac55
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@@ -38,34 -38,22 +38,34 @@@ int router_set_networkstatus_v2(const c
  void networkstatus_v2_list_clean(time_t now);
  int compare_digest_to_routerstatus_entry(const void *_key,
                                           const void **_member);
 -routerstatus_t *networkstatus_v2_find_entry(networkstatus_v2_t *ns,
 +const routerstatus_t *networkstatus_v2_find_entry(networkstatus_v2_t *ns,
                                           const char *digest);
 -routerstatus_t *networkstatus_vote_find_entry(networkstatus_t *ns,
 +const routerstatus_t *networkstatus_vote_find_entry(networkstatus_t *ns,
 +                                              const char *digest);
 +routerstatus_t *networkstatus_v2_find_mutable_entry(networkstatus_v2_t *ns,
 +                                        const char *digest);
 +routerstatus_t *networkstatus_vote_find_mutable_entry(networkstatus_t *ns,
                                                const char *digest);
  int networkstatus_vote_find_entry_idx(networkstatus_t *ns,
                                        const char *digest, int *found_out);
  const smartlist_t *networkstatus_get_v2_list(void);
  download_status_t *router_get_dl_status_by_descriptor_digest(const char *d);
 -routerstatus_t *router_get_consensus_status_by_id(const char *digest);
 -routerstatus_t *router_get_consensus_status_by_descriptor_digest(
 -                                                        const char *digest);
 -routerstatus_t *router_get_consensus_status_by_nickname(const char *nickname,
 -                                                       int warn_if_unnamed);
 +const routerstatus_t *router_get_consensus_status_by_id(const char *digest);
 +routerstatus_t *router_get_mutable_consensus_status_by_id(
 +                                   const char *digest);
 +const routerstatus_t *router_get_consensus_status_by_descriptor_digest(
 +                                   networkstatus_t *consensus,
 +                                   const char *digest);
 +routerstatus_t *router_get_mutable_consensus_status_by_descriptor_digest(
 +                                   networkstatus_t *consensus,
 +                                   const char *digest);
 +const routerstatus_t *router_get_consensus_status_by_nickname(
 +                                   const char *nickname,
 +                                   int warn_if_unnamed);
  const char *networkstatus_get_router_digest_by_nickname(const char *nickname);
  int networkstatus_nickname_is_unnamed(const char *nickname);
 -void networkstatus_consensus_download_failed(int status_code);
 +void networkstatus_consensus_download_failed(int status_code,
 +                                             const char *flavname);
  void update_consensus_networkstatus_fetch_time(time_t now);
  int should_delay_dir_fetches(or_options_t *options);
  void update_networkstatus_downloads(time_t now);
@@@ -73,11 -61,8 +73,11 @@@ void update_certificate_downloads(time_
  int consensus_is_waiting_for_certs(void);
  networkstatus_v2_t *networkstatus_v2_get_by_digest(const char *digest);
  networkstatus_t *networkstatus_get_latest_consensus(void);
 +networkstatus_t *networkstatus_get_latest_consensus_by_flavor(
 +                                                  consensus_flavor_t f);
  networkstatus_t *networkstatus_get_live_consensus(time_t now);
 -networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now);
 +networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
 +                                                             int flavor);
  #define NSSET_FROM_CACHE 1
  #define NSSET_WAS_WAITING_FOR_CERTS 2
  #define NSSET_DONT_DOWNLOAD_CERTS 4
@@@ -93,13 -78,12 +93,12 @@@ void routers_update_status_from_consens
  void signed_descs_update_status_from_consensus_networkstatus(
                                                           smartlist_t *descs);
  
 -char *networkstatus_getinfo_helper_single(routerstatus_t *rs);
 +char *networkstatus_getinfo_helper_single(const routerstatus_t *rs);
  char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now);
  void networkstatus_dump_bridge_status_to_file(time_t now);
- int32_t get_net_param_from_list(smartlist_t *net_params, const char *name,
-                                 int default_val);
  int32_t networkstatus_get_param(networkstatus_t *ns, const char *param_name,
-                                 int32_t default_val);
+                                 int32_t default_val, int32_t min_val,
+                                 int32_t max_val);
  int getinfo_helper_networkstatus(control_connection_t *conn,
                                   const char *question, char **answer,
                                   const char **errmsg);
diff --combined src/or/or.h
index b4ebe64,22c8498..9672b41
--- a/src/or/or.h
+++ b/src/or/or.h
@@@ -83,13 -83,6 +83,13 @@@
  #define snprintf _snprintf
  #endif
  
 +#ifdef USE_BUFFEREVENTS
 +#include <event2/bufferevent.h>
 +#include <event2/buffer.h>
 +#include <event2/util.h>
 +#endif
 +
 +#include "crypto.h"
  #include "tortls.h"
  #include "../common/torlog.h"
  #include "container.h"
@@@ -391,9 -384,7 +391,9 @@@ typedef enum 
  /** A connection to a hidden service directory server: download a v2 rendezvous
   * descriptor. */
  #define DIR_PURPOSE_FETCH_RENDDESC_V2 18
 -#define _DIR_PURPOSE_MAX 18
 +/** A connection to a directory server: download a microdescriptor. */
 +#define DIR_PURPOSE_FETCH_MICRODESC 19
 +#define _DIR_PURPOSE_MAX 19
  
  /** True iff <b>p</b> is a purpose corresponding to uploading data to a
   * directory server. */
@@@ -774,6 -765,8 +774,8 @@@ typedef enum 
  /** Initial value for both sides of a circuit transmission window when the
   * circuit is initialized.  Measured in cells. */
  #define CIRCWINDOW_START 1000
+ #define CIRCWINDOW_START_MIN 100
+ #define CIRCWINDOW_START_MAX 1000
  /** Amount to increment a circuit window when we get a circuit SENDME. */
  #define CIRCWINDOW_INCREMENT 100
  /** Initial value on both sides of a stream transmission window when the
@@@ -809,9 -802,6 +811,9 @@@
   * Tor 0.1.2.x is obsolete, we can remove this. */
  #define DEFAULT_CLIENT_NICKNAME "client"
  
 +/** Name chosen by routers that don't configure nicknames */
 +#define UNNAMED_ROUTER_NICKNAME "Unnamed"
 +
  /** Number of bytes in a SOCKS4 header. */
  #define SOCKS4_NETWORK_LEN 8
  
@@@ -857,8 -847,8 +859,8 @@@ typedef struct cell_t 
  typedef struct var_cell_t {
    uint8_t command;
    circid_t circ_id;
 -  uint16_t payload_len;
 -  uint8_t payload[1];
 +  uint16_t payload_len; /**< The actual length of <b>payload</b>. */
 +  uint8_t payload[FLEXIBLE_ARRAY_MEMBER];
  } var_cell_t;
  
  /** A cell as packed for writing to the network. */
@@@ -975,7 -965,6 +977,7 @@@ typedef struct connection_t 
    /** Our socket; -1 if this connection is closed, or has no socket. */
    evutil_socket_t s;
    int conn_array_index; /**< Index into the global connection array. */
 +
    struct event *read_event; /**< Libevent event structure. */
    struct event *write_event; /**< Libevent event structure. */
    buf_t *inbuf; /**< Buffer holding data read over this connection. */
@@@ -986,11 -975,6 +988,11 @@@
                                * read? */
    time_t timestamp_lastwritten; /**< When was the last time libevent said we
                                   * could write? */
 +
 +#ifdef USE_BUFFEREVENTS
 +  struct bufferevent *bufev; /**< A Libevent buffered IO structure. */
 +#endif
 +
    time_t timestamp_created; /**< When was this connection_t created? */
  
    /* XXXX_IP6 make this IPv6-capable */
@@@ -1090,16 -1074,10 +1092,16 @@@ typedef struct or_connection_t 
    /* bandwidth* and *_bucket only used by ORs in OPEN state: */
    int bandwidthrate; /**< Bytes/s added to the bucket. (OPEN ORs only.) */
    int bandwidthburst; /**< Max bucket size for this conn. (OPEN ORs only.) */
 +#ifndef USE_BUFFEREVENTS
    int read_bucket; /**< When this hits 0, stop receiving. Every second we
                      * add 'bandwidthrate' to this, capping it at
                      * bandwidthburst. (OPEN ORs only) */
    int write_bucket; /**< When this hits 0, stop writing. Like read_bucket. */
 +#else
 +  /** DOCDOC */
 +  /* XXXX we could share this among all connections. */
 +  struct ev_token_bucket_cfg *bucket_cfg;
 +#endif
    int n_circuits; /**< How many circuits use this connection as p_conn or
                     * n_conn ? */
  
@@@ -1203,13 -1181,8 +1205,13 @@@ typedef struct edge_connection_t 
  typedef struct dir_connection_t {
    connection_t _base;
  
 -  char *requested_resource; /**< Which 'resource' did we ask the directory
 -                             * for? */
 + /** Which 'resource' did we ask the directory for? This is typically the part
 +  * of the URL string that defines, relative to the directory conn purpose,
 +  * what thing we want.  For example, in router descriptor downloads by
 +  * descriptor digest, it contains "d/", then one ore more +-separated
 +  * fingerprints.
 +  **/
 +  char *requested_resource;
    unsigned int dirconn_direct:1; /**< Is this dirconn direct, or via Tor? */
  
    /* Used only for server sides of some dir connections, to implement
@@@ -1300,51 -1273,6 +1302,51 @@@ static INLINE control_connection_t *TO_
    return DOWNCAST(control_connection_t, c);
  }
  
 +/* Conditional macros to help write code that works whether bufferevents are
 +   disabled or not.
 +
 +   We can't just write:
 +      if (conn->bufev) {
 +        do bufferevent stuff;
 +      } else {
 +        do other stuff;
 +      }
 +   because the bufferevent stuff won't even compile unless we have a fairly
 +   new version of Libevent.  Instead, we say:
 +      IF_HAS_BUFFEREVENT(conn, { do_bufferevent_stuff } );
 +   or:
 +      IF_HAS_BUFFEREVENT(conn, {
 +        do bufferevent stuff;
 +      }) ELSE_IF_NO_BUFFEREVENT {
 +        do non-bufferevent stuff;
 +      }
 +   If we're compiling with bufferevent support, then the macros expand more or
 +   less to:
 +      if (conn->bufev) {
 +        do_bufferevent_stuff;
 +      } else {
 +        do non-bufferevent stuff;
 +      }
 +   and if we aren't using bufferevents, they expand more or less to:
 +      { do non-bufferevent stuff; }
 +*/
 +#ifdef USE_BUFFEREVENTS
 +#define HAS_BUFFEREVENT(c) (((c)->bufev) != NULL)
 +#define IF_HAS_BUFFEREVENT(c, stmt)                \
 +  if ((c)->bufev) do {                             \
 +      stmt ;                                       \
 +  } while (0)
 +#define ELSE_IF_NO_BUFFEREVENT ; else
 +#define IF_HAS_NO_BUFFEREVENT(c)                   \
 +  if (NULL == (c)->bufev)
 +#else
 +#define HAS_BUFFEREVENT(c) (0)
 +#define IF_HAS_BUFFEREVENT(c, stmt) (void)0
 +#define ELSE_IF_NO_BUFFEREVENT ;
 +#define IF_HAS_NO_BUFFEREVENT(c)                \
 +  if (1)
 +#endif
 +
  /** What action type does an address policy indicate: accept or reject? */
  typedef enum {
    ADDR_POLICY_ACCEPT=1,
@@@ -1508,49 -1436,59 +1510,49 @@@ typedef struct 
    char *contact_info; /**< Declared contact info for this router. */
    unsigned int is_hibernating:1; /**< Whether the router claims to be
                                    * hibernating */
 -  unsigned int has_old_dnsworkers:1; /**< Whether the router is using
 -                                      * dnsworker code. */
 -  unsigned int caches_extra_info:1; /**< Whether the router caches and serves
 -                                     * extrainfo documents. */
 -  unsigned int allow_single_hop_exits:1;  /**< Whether the router allows
 -                                     * single hop exits. */
 -
 -  /* local info */
 -  unsigned int is_running:1; /**< As far as we know, is this OR currently
 -                              * running? */
 -  unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
 -                               *  (For Authdir: Have we validated this OR?)
 -                               */
 -  unsigned int is_named:1; /**< Do we believe the nickname that this OR gives
 -                            * us? */
 -  unsigned int is_fast:1; /** Do we think this is a fast OR? */
 -  unsigned int is_stable:1; /** Do we think this is a stable OR? */
 -  unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
 -  unsigned int is_exit:1; /**< Do we think this is an OK exit? */
 -  unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
 -                               * or otherwise nasty? */
 -  unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
 -                                    * underpowered, or otherwise useless? */
 +  unsigned int caches_extra_info:1; /**< Whether the router says it caches and
 +                                     * serves extrainfo documents. */
 +  unsigned int allow_single_hop_exits:1;  /**< Whether the router says
 +                                           * it allows single hop exits. */
 +
    unsigned int wants_to_be_hs_dir:1; /**< True iff this router claims to be
                                        * a hidden service directory. */
 -  unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
 -                             * directory according to the authorities. */
    unsigned int policy_is_reject_star:1; /**< True iff the exit policy for this
                                           * router rejects everything. */
    /** True if, after we have added this router, we should re-launch
     * tests for it. */
    unsigned int needs_retest_if_added:1;
  
 -/** Tor can use this router for general positions in circuits. */
 +/** Tor can use this router for general positions in circuits; we got it
 + * from a directory server as usual, or we're an authority and a server
 + * uploaded it. */
  #define ROUTER_PURPOSE_GENERAL 0
 -/** Tor should avoid using this router for circuit-building. */
 +/** Tor should avoid using this router for circuit-building: we got it
 + * from a crontroller.  If the controller wants to use it, it'll have to
 + * ask for it by identity. */
  #define ROUTER_PURPOSE_CONTROLLER 1
 -/** Tor should use this router only for bridge positions in circuits. */
 +/** Tor should use this router only for bridge positions in circuits: we got
 + * it via a directory request from the bridge itself, or a bridge
 + * authority. x*/
  #define ROUTER_PURPOSE_BRIDGE 2
  /** Tor should not use this router; it was marked in cached-descriptors with
   * a purpose we didn't recognize. */
  #define ROUTER_PURPOSE_UNKNOWN 255
  
 -  uint8_t purpose; /** What positions in a circuit is this router good for? */
 +  /* In what way did we find out about this router?  One of ROUTER_PURPOSE_*.
 +   * Routers of different purposes are kept segregated and used for different
 +   * things; see notes on ROUTER_PURPOSE_* macros above.
 +   */
 +  uint8_t purpose;
  
    /* The below items are used only by authdirservers for
     * reachability testing. */
 +
    /** When was the last time we could reach this OR? */
    time_t last_reachable;
    /** When did we start testing reachability for this OR? */
    time_t testing_since;
 -  /** According to the geoip db what country is this router in? */
 -  country_t country;
 +
  } routerinfo_t;
  
  /** Information needed to keep and cache a signed extra-info document. */
@@@ -1576,9 -1514,8 +1578,9 @@@ typedef struct routerstatus_t 
                                        * has. */
    char identity_digest[DIGEST_LEN]; /**< Digest of the router's identity
                                       * key. */
 -  char descriptor_digest[DIGEST_LEN]; /**< Digest of the router's most recent
 -                                       * descriptor. */
 +  /** Digest of the router's most recent descriptor or microdescriptor.
 +   * If it's a descriptor, we only use the first DIGEST_LEN bytes. */
 +  char descriptor_digest[DIGEST256_LEN];
    uint32_t addr; /**< IPv4 address for this router. */
    uint16_t or_port; /**< OR port for this router. */
    uint16_t dir_port; /**< Directory port for this router. */
@@@ -1586,11 -1523,7 +1588,11 @@@
    unsigned int is_exit:1; /**< True iff this router is a good exit. */
    unsigned int is_stable:1; /**< True iff this router stays up a long time. */
    unsigned int is_fast:1; /**< True iff this router has good bandwidth. */
 -  unsigned int is_running:1; /**< True iff this router is up. */
 +  /** True iff this router is called 'running' in the consensus. We give it
 +   * this funny name so that we don't accidentally use this bit as a view of
 +   * whether we think the router is *currently* running.  If that's what you
 +   * want to know, look at is_running in node_t. */
 +  unsigned int is_flagged_running:1;
    unsigned int is_named:1; /**< True iff "nickname" belongs to this router. */
    unsigned int is_unnamed:1; /**< True iff "nickname" belongs to another
                                * router. */
@@@ -1642,31 -1575,15 +1644,31 @@@
     * from this authority.)  Applies in v2 networkstatus document only.
     */
    unsigned int need_to_mirror:1;
 -  unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
 -                                      * to this (unnamed) router by nickname?
 -                                      */
    time_t last_dir_503_at; /**< When did this router last tell us that it
                             * was too busy to serve directory info? */
    download_status_t dl_status;
  
  } routerstatus_t;
  
 +/** A single entry in a parsed policy summary, describing a range of ports. */
 +typedef struct short_policy_entry_t {
 +  uint16_t min_port, max_port;
 +} short_policy_entry_t;
 +
 +/** A short_poliy_t is the parsed version of a policy summary. */
 +typedef struct short_policy_t {
 +  /** True if the members of 'entries' are port ranges to accept; false if
 +   * they are port ranges to reject */
 +  unsigned int is_accept : 1;
 +  /** The actual number of values in 'entries'. */
 +  unsigned int n_entries : 31;
 +  /** An array of 0 or more short_policy_entry_t values, each describing a
 +   * range of ports that this policy accepts or rejects (depending on the
 +   * value of is_accept).
 +   */
 +  short_policy_entry_t entries[FLEXIBLE_ARRAY_MEMBER];
 +} short_policy_t;
 +
  /** A microdescriptor is the smallest amount of information needed to build a
   * circuit through a router.  They are generated by the directory authorities,
   * using information from the uploaded routerinfo documents.  They are not
@@@ -1708,83 -1625,15 +1710,83 @@@ typedef struct microdesc_t 
    crypto_pk_env_t *onion_pkey;
    /** As routerinfo_t.family */
    smartlist_t *family;
 -  /** Encoded exit policy summary */
 -  char *exitsummary; /**< exit policy summary -
 -                      * XXX this probably should not stay a string. */
 +  /** Exit policy summary */
 +  short_policy_t *exit_policy;
  } microdesc_t;
  
 +/** A node_t represents a Tor router.
 + *
 + * Specifically, a node_t is a Tor router as we are using it: a router that
 + * we are considering for circuits, connections, and so on.  A node_t is a
 + * thin wrapper around the routerstatus, routerinfo, and microdesc for a
 + * single wrapper, and provides a consistent interface for all of them.
 + *
 + * Also, a node_t has mutable state.  While a routerinfo, a routerstatus,
 + * and a microdesc have[*] only the information read from a router
 + * descriptor, a consensus entry, and a microdescriptor (respectively)...
 + * a node_t has flags based on *our own current opinion* of the node.
 + *
 + * [*] Actually, there is some leftover information in each that is mutable.
 + *  We should try to excise that.
 + */
 +typedef struct node_t {
 +  /* Indexing information */
 +
 +  /** Used to look up the node_t by its identity digest. */
 +  HT_ENTRY(node_t) ht_ent;
 +  /** Position of the node within the list of nodes */
 +  int nodelist_idx;
 +
 +  /** The identity digest of this node_t.  No more than one node_t per
 +   * identity may exist at a time. */
 +  char identity[DIGEST_LEN];
 +
 +  microdesc_t *md;
 +  routerinfo_t *ri;
 +  routerstatus_t *rs;
 +
 +  /* local info: copied from routerstatus, then possibly frobbed based
 +   * on experience.  Authorities set this stuff directly. */
 +
 +  unsigned int is_running:1; /**< As far as we know, is this OR currently
 +                              * running? */
 +  unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
 +                               *  (For Authdir: Have we validated this OR?)
 +                               */
 +  unsigned int is_fast:1; /** Do we think this is a fast OR? */
 +  unsigned int is_stable:1; /** Do we think this is a stable OR? */
 +  unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
 +  unsigned int is_exit:1; /**< Do we think this is an OK exit? */
 +  unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
 +                               * or otherwise nasty? */
 +  unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
 +                                    * underpowered, or otherwise useless? */
 +  unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
 +                             * directory according to the authorities. */
 +
 +  /* Local info: warning state. */
 +
 +  unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
 +                                      * to this (unnamed) router by nickname?
 +                                      */
 +
 +  /** Local info: we treat this node as if it rejects everything */
 +  unsigned int rejects_all:1;
 +
 +  /* Local info: derived. */
 +
 +  /** According to the geoip db what country is this router in? */
 +  country_t country;
 +} node_t;
 +
  /** How many times will we try to download a router's descriptor before giving
   * up? */
  #define MAX_ROUTERDESC_DOWNLOAD_FAILURES 8
  
 +/** How many times will we try to download a microdescriptor before giving
 + * up? */
 +#define MAX_MICRODESC_DOWNLOAD_FAILURES 8
 +
  /** Contents of a v2 (non-consensus, non-vote) network status object. */
  typedef struct networkstatus_v2_t {
    /** When did we receive the network-status document? */
@@@ -2260,12 -2109,10 +2262,12 @@@ typedef struct circuit_t 
      * length ONIONSKIN_CHALLENGE_LEN. */
    char *n_conn_onionskin;
  
 -  time_t timestamp_created; /**< When was this circuit created? */
 +  /** When was this circuit created?  We keep this timestamp with a higher
 +   * resolution than most so that the circuit-build-time tracking code can
 +   * get millisecond resolution. */
 +  struct timeval timestamp_created;
    time_t timestamp_dirty; /**< When the circuit was first used, or 0 if the
                             * circuit is clean. */
 -  struct timeval highres_created; /**< When exactly was the circuit created? */
  
    uint16_t marked_for_close; /**< Should we close this circuit at the end of
                                * the main loop? (If true, holds the line number
@@@ -2490,7 -2337,6 +2492,7 @@@ typedef struct 
  
    config_line_t *Logs; /**< New-style list of configuration lines
                          * for logs */
 +  int LogTimeGranularity; /**< Log resolution in milliseconds. */
  
    char *DebugLogFile; /**< Where to send verbose log messages. */
    char *DataDirectory; /**< OR only: where to store long-term data. */
@@@ -2747,8 -2593,7 +2749,8 @@@
  
    char *MyFamily; /**< Declared family for this OR. */
    config_line_t *NodeFamilies; /**< List of config lines for
 -                                       * node families */
 +                                * node families */
 +  smartlist_t *NodeFamilySets; /**< List of parsed NodeFamilies values. */
    config_line_t *AuthDirBadDir; /**< Address policy for descriptors to
                                   * mark as bad dir mirrors. */
    config_line_t *AuthDirBadExit; /**< Address policy for descriptors to
@@@ -2853,10 -2698,6 +2855,10 @@@
                         * possible. */
    int PreferTunneledDirConns; /**< If true, avoid dirservers that don't
                                 * support BEGIN_DIR, when possible. */
 +  int PortForwarding; /**< If true, use NAT-PMP or UPnP to automatically
 +                       * forward the DirPort and ORPort on the NAT device */
 +  char *PortForwardingHelper; /** < Filename or full path of the port
 +                                  forwarding helper executable */
    int AllowNonRFC953Hostnames; /**< If true, we allow connections to hostnames
                                  * with weird characters. */
    /** If true, we try resolving hostnames with weird characters. */
@@@ -2894,9 -2735,6 +2896,9 @@@
    /** If true, the user wants us to collect statistics on port usage. */
    int ExitPortStatistics;
  
 +  /** If true, the user wants us to collect connection statistics. */
 +  int ConnDirectionStatistics;
 +
    /** If true, the user wants us to collect cell statistics. */
    int CellStatistics;
  
@@@ -2989,12 -2827,6 +2991,12 @@@
     */
    double CircuitPriorityHalflife;
  
 +  /** If true, do not enable IOCP on windows with bufferevents, even if
 +   * we think we could. */
 +  int DisableIOCP;
 +  /** For testing only: will go away in 0.2.3.x. */
 +  int _UseFilteringSSLBufferevents;
 +
  } or_options_t;
  
  /** Persistent state for an onion router, as saved to disk. */
@@@ -3103,6 -2935,8 +3105,6 @@@ struct socks_request_t 
                                * every connection. */
  };
  
 -/* all the function prototypes go here */
 -
  /********************************* circuitbuild.c **********************/
  
  /** How many hops does a general-purpose circuit have by default? */
@@@ -3110,6 -2944,11 +3112,11 @@@
  
  /* Circuit Build Timeout "public" structures. */
  
+ /** Precision multiplier for the Bw weights */
+ #define BW_WEIGHT_SCALE   10000
+ #define BW_MIN_WEIGHT_SCALE 1
+ #define BW_MAX_WEIGHT_SCALE INT32_MAX
+ 
  /** Total size of the circuit timeout history to accumulate.
   * 1000 is approx 2.5 days worth of continual-use circuits. */
  #define CBT_NCIRCUITS_TO_OBSERVE 1000
@@@ -3119,6 -2958,8 +3126,8 @@@
  
  /** Number of modes to use in the weighted-avg computation of Xm */
  #define CBT_DEFAULT_NUM_XM_MODES 3
+ #define CBT_MIN_NUM_XM_MODES 1
+ #define CBT_MAX_NUM_XM_MODES 20
  
  /** A build_time_t is milliseconds */
  typedef uint32_t build_time_t;
@@@ -3140,12 -2981,16 +3149,16 @@@
   * build in terms of CDF quantile.
   */
  #define CBT_DEFAULT_CLOSE_QUANTILE 95
+ #define CBT_MIN_CLOSE_QUANTILE CBT_MIN_QUANTILE_CUTOFF
+ #define CBT_MAX_CLOSE_QUANTILE CBT_MAX_QUANTILE_CUTOFF
  
  /**
   * How many circuits count as recent when considering if the
   * connection has gone gimpy or changed.
   */
  #define CBT_DEFAULT_RECENT_CIRCUITS 20
+ #define CBT_MIN_RECENT_CIRCUITS 3
+ #define CBT_MAX_RECENT_CIRCUITS 1000
  
  /**
   * Maximum count of timeouts that finish the first hop in the past
@@@ -3156,25 -3001,37 +3169,37 @@@
   * gives us.
   */
  #define CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT (CBT_DEFAULT_RECENT_CIRCUITS*9/10)
+ #define CBT_MIN_MAX_RECENT_TIMEOUT_COUNT 3
+ #define CBT_MAX_MAX_RECENT_TIMEOUT_COUNT 10000
  
  /** Minimum circuits before estimating a timeout */
  #define CBT_DEFAULT_MIN_CIRCUITS_TO_OBSERVE 100
+ #define CBT_MIN_MIN_CIRCUITS_TO_OBSERVE 1
+ #define CBT_MAX_MIN_CIRCUITS_TO_OBSERVE 10000
  
  /** Cutoff percentile on the CDF for our timeout estimation. */
  #define CBT_DEFAULT_QUANTILE_CUTOFF 80
+ #define CBT_MIN_QUANTILE_CUTOFF 10
+ #define CBT_MAX_QUANTILE_CUTOFF 99
  double circuit_build_times_quantile_cutoff(void);
  
  /** How often in seconds should we build a test circuit */
  #define CBT_DEFAULT_TEST_FREQUENCY 60
+ #define CBT_MIN_TEST_FREQUENCY 1
+ #define CBT_MAX_TEST_FREQUENCY INT32_MAX
  
  /** Lowest allowable value for CircuitBuildTimeout in milliseconds */
  #define CBT_DEFAULT_TIMEOUT_MIN_VALUE (1500)
+ #define CBT_MIN_TIMEOUT_MIN_VALUE 500
+ #define CBT_MAX_TIMEOUT_MIN_VALUE INT32_MAX
  
  /** Initial circuit build timeout in milliseconds */
  #define CBT_DEFAULT_TIMEOUT_INITIAL_VALUE (60*1000)
+ #define CBT_MIN_TIMEOUT_INITIAL_VALUE CBT_MIN_TIMEOUT_MIN_VALUE
+ #define CBT_MAX_TIMEOUT_INITIAL_VALUE INT32_MAX
  int32_t circuit_build_times_initial_timeout(void);
  
- #if CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT < 1
+ #if CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT < CBT_MIN_MAX_RECENT_TIMEOUT_COUNT
  #error "RECENT_CIRCUITS is set too low."
  #endif
  
@@@ -3471,7 -3328,7 +3496,7 @@@ typedef enum 
    ADDR_POLICY_PROBABLY_ACCEPTED=1,
    /** Part of the address was unknown, but as far as we can tell, it was
     * rejected. */
 -  ADDR_POLICY_PROBABLY_REJECTED=2
 +  ADDR_POLICY_PROBABLY_REJECTED=2,
  } addr_policy_result_t;
  
  /********************************* rephist.c ***************************/
@@@ -3602,8 -3459,6 +3627,8 @@@ typedef struct trusted_dir_server_t 
   *  fetches to _any_ single directory server.]
   */
  #define PDS_NO_EXISTING_SERVERDESC_FETCH (1<<3)
 +#define PDS_NO_EXISTING_MICRODESC_FETCH (1<<4)
 +
  #define _PDS_PREFER_TUNNELED_DIR_CONNS (1<<16)
  
  /** Possible ways to weight routers when choosing one randomly.  See
@@@ -3621,8 -3476,7 +3646,8 @@@ typedef enum 
    CRN_NEED_GUARD = 1<<2,
    CRN_ALLOW_INVALID = 1<<3,
    /* XXXX not used, apparently. */
 -  CRN_WEIGHT_AS_EXIT = 1<<5
 +  CRN_WEIGHT_AS_EXIT = 1<<5,
 +  CRN_NEED_DESC = 1<<6
  } router_crn_flags_t;
  
  /** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */
diff --combined src/or/relay.c
index c2035b0,a6c2506..ec8e9ac
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@@ -24,7 -24,6 +24,7 @@@
  #include "main.h"
  #include "mempool.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "policies.h"
  #include "reasons.h"
  #include "relay.h"
@@@ -705,7 -704,7 +705,7 @@@ connection_ap_process_end_not_open
      edge_connection_t *conn, crypt_path_t *layer_hint)
  {
    struct in_addr in;
 -  routerinfo_t *exitrouter;
 +  node_t *exitrouter;
    int reason = *(cell->payload+RELAY_HEADER_SIZE);
    int control_reason = reason | END_STREAM_REASON_FLAG_REMOTE;
    (void) layer_hint; /* unused */
@@@ -713,12 -712,11 +713,12 @@@
    if (rh->length > 0 && edge_reason_is_retriable(reason) &&
        !connection_edge_is_rendezvous_stream(conn)  /* avoid retry if rend */
        ) {
 +    const char *chosen_exit_digest =
 +      circ->build_state->chosen_exit->identity_digest;
      log_info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
               safe_str(conn->socks_request->address),
               stream_end_reason_to_string(reason));
 -    exitrouter =
 -      router_get_by_digest(circ->build_state->chosen_exit->identity_digest);
 +    exitrouter = node_get_mutable_by_id(chosen_exit_digest);
      switch (reason) {
        case END_STREAM_REASON_EXITPOLICY:
          if (rh->length >= 5) {
@@@ -753,8 -751,8 +753,8 @@@
            log_info(LD_APP,
                   "Exitrouter '%s' seems to be more restrictive than its exit "
                   "policy. Not using this router as exit for now.",
 -                 exitrouter->nickname);
 -          policies_set_router_exitpolicy_to_reject_all(exitrouter);
 +                 node_get_nickname(exitrouter));
 +          policies_set_node_exitpolicy_to_reject_all(exitrouter);
          }
          /* rewrite it to an IP if we learned one. */
          if (addressmap_rewrite(conn->socks_request->address,
@@@ -817,7 -815,7 +817,7 @@@
        case END_STREAM_REASON_HIBERNATING:
        case END_STREAM_REASON_RESOURCELIMIT:
          if (exitrouter) {
 -          policies_set_router_exitpolicy_to_reject_all(exitrouter);
 +          policies_set_node_exitpolicy_to_reject_all(exitrouter);
          }
          if (conn->chosen_exit_optional) {
            /* stop wanting a specific exit */
@@@ -900,8 -898,12 +900,8 @@@ connection_edge_process_relay_cell_not_
        int ttl;
        if (!addr || (get_options()->ClientDNSRejectInternalAddresses &&
                      is_internal_IP(addr, 0))) {
 -        char buf[INET_NTOA_BUF_LEN];
 -        struct in_addr a;
 -        a.s_addr = htonl(addr);
 -        tor_inet_ntoa(&a, buf, sizeof(buf));
 -        log_info(LD_APP,
 -                 "...but it claims the IP address was %s. Closing.", buf);
 +        log_info(LD_APP, "...but it claims the IP address was %s. Closing.",
 +                 fmt_addr32(addr));
          connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
          connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
          return 0;
@@@ -978,8 -980,11 +978,8 @@@
        uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
        if (get_options()->ClientDNSRejectInternalAddresses &&
            is_internal_IP(addr, 0)) {
 -        char buf[INET_NTOA_BUF_LEN];
 -        struct in_addr a;
 -        a.s_addr = htonl(addr);
 -        tor_inet_ntoa(&a, buf, sizeof(buf));
 -        log_info(LD_APP,"Got a resolve with answer %s.  Rejecting.", buf);
 +        log_info(LD_APP,"Got a resolve with answer %s. Rejecting.",
 +                 fmt_addr32(addr));
          connection_ap_handshake_socks_resolved(conn,
                                                 RESOLVED_TYPE_ERROR_TRANSIENT,
                                                 0, NULL, 0, TIME_MAX);
@@@ -1144,7 -1149,8 +1144,7 @@@ connection_edge_process_relay_cell(cell
        if (!conn->_base.marked_for_close) {
          /* only mark it if not already marked. it's possible to
           * get the 'end' right around when the client hangs up on us. */
 -        connection_mark_for_close(TO_CONN(conn));
 -        conn->_base.hold_open_until_flushed = 1;
 +        connection_mark_and_flush(TO_CONN(conn));
        }
        return 0;
      case RELAY_COMMAND_EXTEND:
@@@ -1351,7 -1357,7 +1351,7 @@@ connection_edge_package_raw_inbuf(edge_
      return 0;
    }
  
 -  amount_to_process = buf_datalen(conn->_base.inbuf);
 +  amount_to_process = connection_get_inbuf_len(TO_CONN(conn));
  
    if (!amount_to_process)
      return 0;
@@@ -1370,7 -1376,7 +1370,7 @@@
    connection_fetch_from_buf(payload, length, TO_CONN(conn));
  
    log_debug(domain,"(%d) Packaging %d bytes (%d waiting).", conn->_base.s,
 -            (int)length, (int)buf_datalen(conn->_base.inbuf));
 +            (int)length, (int)connection_get_inbuf_len(TO_CONN(conn)));
  
    if (connection_edge_send_command(conn, RELAY_COMMAND_DATA,
                                     payload, length) < 0 )
@@@ -1522,7 -1528,7 +1522,7 @@@ circuit_resume_edge_reading_helper(edge
      if (!layer_hint || conn->cpath_layer == layer_hint) {
        connection_start_reading(TO_CONN(conn));
  
 -      if (buf_datalen(conn->_base.inbuf) > 0)
 +      if (connection_get_inbuf_len(TO_CONN(conn)) > 0)
          ++n_packaging_streams;
      }
    }
@@@ -1533,7 -1539,7 +1533,7 @@@
      if (!layer_hint || conn->cpath_layer == layer_hint) {
        connection_start_reading(TO_CONN(conn));
  
 -      if (buf_datalen(conn->_base.inbuf) > 0)
 +      if (connection_get_inbuf_len(TO_CONN(conn)) > 0)
          ++n_packaging_streams;
      }
    }
@@@ -1572,7 -1578,7 +1572,7 @@@
        }
  
        /* If there's still data to read, we'll be coming back to this stream. */
 -      if (buf_datalen(conn->_base.inbuf))
 +      if (connection_get_inbuf_len(TO_CONN(conn)))
            ++n_streams_left;
  
        /* If the circuit won't accept any more data, return without looking
@@@ -1993,9 -1999,9 +1993,9 @@@ cell_ewma_set_scale_factor(or_options_
    if (options && options->CircuitPriorityHalflife >= -EPSILON) {
      halflife = options->CircuitPriorityHalflife;
      source = "CircuitPriorityHalflife in configuration";
-   } else if (consensus &&
-              (halflife_ms = networkstatus_get_param(
-                    consensus, "CircuitPriorityHalflifeMsec", -1)) >= 0) {
+   } else if (consensus && (halflife_ms = networkstatus_get_param(
+                  consensus, "CircuitPriorityHalflifeMsec",
+                  -1, -1, INT32_MAX)) >= 0) {
      halflife = ((double)halflife_ms)/1000.0;
      source = "CircuitPriorityHalflifeMsec in consensus";
    } else {
@@@ -2236,7 -2242,7 +2236,7 @@@ set_streams_blocked_on_circ(circuit_t *
        edge->edge_blocked_on_circ = block;
      }
  
 -    if (!conn->read_event) {
 +    if (!conn->read_event && !HAS_BUFFEREVENT(conn)) {
        /* This connection is a placeholder for something; probably a DNS
         * request.  It can't actually stop or start reading.*/
        continue;
@@@ -2437,7 -2443,7 +2437,7 @@@ append_cell_to_circuit_queue(circuit_t 
      make_circuit_active_on_conn(circ, orconn);
    }
  
 -  if (! buf_datalen(orconn->_base.outbuf)) {
 +  if (! connection_get_outbuf_len(TO_CONN(orconn))) {
      /* There is no data at all waiting to be sent on the outbuf.  Add a
       * cell, so that we can notice when it gets flushed, flushed_some can
       * get called, and we can start putting more data onto the buffer then.
diff --combined src/or/router.c
index 1748652,26ac351..56f44cc
--- a/src/or/router.c
+++ b/src/or/router.c
@@@ -7,7 -7,6 +7,7 @@@
  #define ROUTER_PRIVATE
  
  #include "or.h"
 +#include "circuitbuild.h"
  #include "circuitlist.h"
  #include "circuituse.h"
  #include "config.h"
@@@ -20,7 -19,6 +20,7 @@@
  #include "hibernate.h"
  #include "main.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "policies.h"
  #include "relay.h"
  #include "rephist.h"
@@@ -152,8 -150,8 +152,8 @@@ assert_identity_keys_ok(void
    } else {
      /* assert that we have set the client and server keys to be unequal */
      if (server_identitykey)
 -       tor_assert(0!=crypto_pk_cmp_keys(client_identitykey,
 -                                        server_identitykey));
 +      tor_assert(0!=crypto_pk_cmp_keys(client_identitykey,
 +                                       server_identitykey));
    }
  }
  
@@@ -849,21 -847,18 +849,21 @@@ decide_to_advertise_dirport(or_options_
  void
  consider_testing_reachability(int test_or, int test_dir)
  {
 -  routerinfo_t *me = router_get_my_routerinfo();
 +  const routerinfo_t *me = router_get_my_routerinfo();
    int orport_reachable = check_whether_orport_reachable();
    tor_addr_t addr;
    if (!me)
      return;
  
    if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) {
 +    extend_info_t *ei;
      log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
               !orport_reachable ? "reachability" : "bandwidth",
               me->address, me->or_port);
 -    circuit_launch_by_router(CIRCUIT_PURPOSE_TESTING, me,
 -                             CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
 +    ei = extend_info_from_router(me);
 +    circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
 +                            CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
 +    extend_info_free(ei);
    }
  
    tor_addr_from_ipv4h(&addr, me->addr);
@@@ -887,7 -882,7 +887,7 @@@ voi
  router_orport_found_reachable(void)
  {
    if (!can_reach_or_port) {
 -    routerinfo_t *me = router_get_my_routerinfo();
 +    const routerinfo_t *me = router_get_my_routerinfo();
      log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from "
                 "the outside. Excellent.%s",
                 get_options()->_PublishServerDescriptor != NO_AUTHORITY ?
@@@ -910,7 -905,7 +910,7 @@@ voi
  router_dirport_found_reachable(void)
  {
    if (!can_reach_dir_port) {
 -    routerinfo_t *me = router_get_my_routerinfo();
 +    const routerinfo_t *me = router_get_my_routerinfo();
      log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable "
                 "from the outside. Excellent.");
      can_reach_dir_port = 1;
@@@ -1073,7 -1068,7 +1073,7 @@@ should_refuse_unknown_exits(or_options_
    if (options->RefuseUnknownExits_ != -1) {
      return options->RefuseUnknownExits_;
    } else {
-     return networkstatus_get_param(NULL, "refuseunknownexits", 1);
+     return networkstatus_get_param(NULL, "refuseunknownexits", 1, 0, 1);
    }
  }
  
@@@ -1181,7 -1176,7 +1181,7 @@@ static int desc_needs_upload = 0
  void
  router_upload_dir_desc_to_dirservers(int force)
  {
 -  routerinfo_t *ri;
 +  const routerinfo_t *ri;
    extrainfo_t *ei;
    char *msg;
    size_t desc_len, extra_len = 0, total_len;
@@@ -1275,7 -1270,7 +1275,7 @@@ router_extrainfo_digest_is_me(const cha
  
  /** A wrapper around router_digest_is_me(). */
  int
 -router_is_me(routerinfo_t *router)
 +router_is_me(const routerinfo_t *router)
  {
    return router_digest_is_me(router->cache_info.identity_digest);
  }
@@@ -1294,7 -1289,7 +1294,7 @@@ router_fingerprint_is_me(const char *fp
  
  /** Return a routerinfo for this OR, rebuilding a fresh one if
   * necessary.  Return NULL on error, or if called on an OP. */
 -routerinfo_t *
 +const routerinfo_t *
  router_get_my_routerinfo(void)
  {
    if (!server_mode(get_options()))
@@@ -1346,6 -1341,8 +1346,6 @@@ static int router_guess_address_from_di
  int
  router_pick_published_address(or_options_t *options, uint32_t *addr)
  {
 -  char buf[INET_NTOA_BUF_LEN];
 -  struct in_addr a;
    if (resolve_my_address(LOG_INFO, options, addr, NULL) < 0) {
      log_info(LD_CONFIG, "Could not determine our address locally. "
               "Checking if directory headers provide any hints.");
@@@ -1355,7 -1352,9 +1355,7 @@@
        return -1;
      }
    }
 -  a.s_addr = htonl(*addr);
 -  tor_inet_ntoa(&a, buf, sizeof(buf));
 -  log_info(LD_CONFIG,"Success: chose address '%s'.", buf);
 +  log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr));
    return 0;
  }
  
@@@ -1417,12 -1416,13 +1417,12 @@@ router_rebuild_descriptor(int force
    ri->policy_is_reject_star =
      policy_is_reject_star(ri->exit_policy);
  
 -  if (desc_routerinfo) { /* inherit values */
 -    ri->is_valid = desc_routerinfo->is_valid;
 -    ri->is_running = desc_routerinfo->is_running;
 -    ri->is_named = desc_routerinfo->is_named;
 -  }
 +#if 0
 +  /* XXXX NM NM I belive this is safe to remove */
    if (authdir_mode(options))
      ri->is_valid = ri->is_named = 1; /* believe in yourself */
 +#endif
 +
    if (options->MyFamily) {
      smartlist_t *family;
      if (!warned_nonexistent_family)
@@@ -1431,12 -1431,13 +1431,12 @@@
      ri->declared_family = smartlist_create();
      smartlist_split_string(family, options->MyFamily, ",",
        SPLIT_SKIP_SPACE|SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
 -    SMARTLIST_FOREACH(family, char *, name,
 -     {
 -       routerinfo_t *member;
 +    SMARTLIST_FOREACH_BEGIN(family, char *, name) {
 +       const node_t *member;
         if (!strcasecmp(name, options->Nickname))
 -         member = ri;
 +         goto skip; /* Don't list ourself, that's redundant */
         else
 -         member = router_get_by_nickname(name, 1);
 +         member = node_get_by_nickname(name, 1);
         if (!member) {
           int is_legal = is_legal_nickname_or_hexdigest(name);
           if (!smartlist_string_isin(warned_nonexistent_family, name) &&
@@@ -1456,21 -1457,19 +1456,21 @@@
             smartlist_add(ri->declared_family, name);
             name = NULL;
           }
 -       } else if (router_is_me(member)) {
 +       } else if (router_digest_is_me(member->identity)) {
           /* Don't list ourself in our own family; that's redundant */
 +         /* XXX shouldn't be possible */
         } else {
           char *fp = tor_malloc(HEX_DIGEST_LEN+2);
           fp[0] = '$';
           base16_encode(fp+1,HEX_DIGEST_LEN+1,
 -                       member->cache_info.identity_digest, DIGEST_LEN);
 +                       member->identity, DIGEST_LEN);
           smartlist_add(ri->declared_family, fp);
           if (smartlist_string_isin(warned_nonexistent_family, name))
             smartlist_string_remove(warned_nonexistent_family, name);
         }
 +    skip:
         tor_free(name);
 -     });
 +    } SMARTLIST_FOREACH_END(name);
  
      /* remove duplicates from the list */
      smartlist_sort_strings(ri->declared_family);
@@@ -1531,6 -1530,8 +1531,6 @@@
                           strlen(ri->cache_info.signed_descriptor_body),
                           ri->cache_info.signed_descriptor_digest);
  
 -  routerinfo_set_country(ri);
 -
    if (ei) {
      tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL));
    }
@@@ -2023,12 -2024,6 +2023,12 @@@ extrainfo_dump_to_string(char **s_out, 
    tor_free(bandwidth_usage);
    smartlist_add(chunks, pre);
  
 +  if (geoip_is_loaded()) {
 +    char *chunk=NULL;
 +    tor_asprintf(&chunk, "geoip-db-digest %s\n", geoip_db_digest());
 +    smartlist_add(chunks, chunk);
 +  }
 +
    if (options->ExtraInfoStatistics && write_stats_to_extrainfo) {
      log_info(LD_GENERAL, "Adding stats to extra-info descriptor.");
      if (options->DirReqStatistics &&
@@@ -2051,11 -2046,6 +2051,11 @@@
                          "exit-stats-end", now, &contents) > 0) {
        smartlist_add(chunks, contents);
      }
 +    if (options->ConnDirectionStatistics &&
 +        load_stats_file("stats"PATH_SEPARATOR"conn-stats",
 +                        "conn-bi-direct", now, &contents) > 0) {
 +      smartlist_add(chunks, contents);
 +    }
    }
  
    if (should_record_bridge_info(options) && write_stats_to_extrainfo) {
@@@ -2194,15 -2184,10 +2194,15 @@@ is_legal_hexdigest(const char *s
  void
  router_get_verbose_nickname(char *buf, const routerinfo_t *router)
  {
 +  const char *good_digest = networkstatus_get_router_digest_by_nickname(
 +                                                         router->nickname);
 +  int is_named = good_digest && !memcmp(good_digest,
 +                                        router->cache_info.identity_digest,
 +                                        DIGEST_LEN);
    buf[0] = '$';
    base16_encode(buf+1, HEX_DIGEST_LEN+1, router->cache_info.identity_digest,
                  DIGEST_LEN);
 -  buf[1+HEX_DIGEST_LEN] = router->is_named ? '=' : '~';
 +  buf[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
    strlcpy(buf+1+HEX_DIGEST_LEN+1, router->nickname, MAX_NICKNAME_LEN+1);
  }
  
diff --combined src/or/routerlist.c
index 96cc01b,47caebf..380e34b
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@@ -22,9 -22,7 +22,9 @@@
  #include "geoip.h"
  #include "hibernate.h"
  #include "main.h"
 +#include "microdesc.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "policies.h"
  #include "reasons.h"
  #include "rendcommon.h"
@@@ -39,21 -37,20 +39,21 @@@
  /****************************************************************************/
  
  /* static function prototypes */
 -static routerstatus_t *router_pick_directory_server_impl(
 +static const routerstatus_t *router_pick_directory_server_impl(
                                             authority_type_t auth, int flags);
 -static routerstatus_t *router_pick_trusteddirserver_impl(
 +static const routerstatus_t *router_pick_trusteddirserver_impl(
                            authority_type_t auth, int flags, int *n_busy_out);
  static void mark_all_trusteddirservers_up(void);
 -static int router_nickname_matches(routerinfo_t *router, const char *nickname);
 +static int router_nickname_matches(const routerinfo_t *router,
 +                                   const char *nickname);
 +static int node_nickname_matches(const node_t *router,
 +                                 const char *nickname);
  static void trusted_dir_server_free(trusted_dir_server_t *ds);
 -static void launch_router_descriptor_downloads(smartlist_t *downloadable,
 -                                               routerstatus_t *source,
 -                                               time_t now);
  static int signed_desc_digest_is_recognized(signed_descriptor_t *desc);
  static void update_router_have_minimum_dir_info(void);
 -static const char *signed_descriptor_get_body_impl(signed_descriptor_t *desc,
 -                                                   int with_annotations);
 +static const char *signed_descriptor_get_body_impl(
 +                                              const signed_descriptor_t *desc,
 +                                              int with_annotations);
  static void list_pending_downloads(digestmap_t *result,
                                     int purpose, const char *prefix);
  
@@@ -315,7 -312,6 +315,7 @@@ trusted_dirs_remove_old_certs(void
    time_t now = time(NULL);
  #define DEAD_CERT_LIFETIME (2*24*60*60)
  #define OLD_CERT_LIFETIME (7*24*60*60)
 +#define CERT_EXPIRY_SKEW (60*60)
    if (!trusted_dir_certs)
      return;
  
@@@ -332,7 -328,7 +332,7 @@@
          time_t cert_published;
          if (newest == cert)
            continue;
 -        expired = ftime_definitely_after(now, cert->expires);
 +        expired = time_definitely_after(now, cert->expires, CERT_EXPIRY_SKEW);
          cert_published = cert->cache_info.published_on;
          /* Store expired certs for 48 hours after a newer arrives;
           */
@@@ -524,7 -520,7 +524,7 @@@ authority_certs_fetch_missing(networkst
        continue;
      cl = get_cert_list(ds->v3_identity_digest);
      SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert, {
 -      if (!ftime_definitely_after(now, cert->expires)) {
 +      if (! time_definitely_after(now, cert->expires, CERT_EXPIRY_SKEW)) {
          /* It's not expired, and we weren't looking for something to
           * verify a consensus with.  Call it done. */
          download_status_reset(&cl->dl_status);
@@@ -604,7 -600,7 +604,7 @@@ router_should_rebuild_store(desc_store_
  /** Return the desc_store_t in <b>rl</b> that should be used to store
   * <b>sd</b>. */
  static INLINE desc_store_t *
 -desc_get_store(routerlist_t *rl, signed_descriptor_t *sd)
 +desc_get_store(routerlist_t *rl, const signed_descriptor_t *sd)
  {
    if (sd->is_extrainfo)
      return &rl->extrainfo_store;
@@@ -930,10 -926,10 +930,10 @@@ router_get_trusted_dir_servers(void
   * Don't pick an authority if any non-authority is viable; try to avoid using
   * servers that have returned 503 recently.
   */
 -routerstatus_t *
 +const routerstatus_t *
  router_pick_directory_server(authority_type_t type, int flags)
  {
 -  routerstatus_t *choice;
 +  const routerstatus_t *choice;
    if (get_options()->PreferTunneledDirConns)
      flags |= _PDS_PREFER_TUNNELED_DIR_CONNS;
  
@@@ -962,8 -958,8 +962,8 @@@ in
  router_get_my_share_of_directory_requests(double *v2_share_out,
                                            double *v3_share_out)
  {
 -  routerinfo_t *me = router_get_my_routerinfo();
 -  routerstatus_t *rs;
 +  const routerinfo_t *me = router_get_my_routerinfo();
 +  const routerstatus_t *rs;
    const int pds_flags = PDS_ALLOW_SELF|PDS_IGNORE_FASCISTFIREWALL;
    *v2_share_out = *v3_share_out = 0.0;
    if (!me)
@@@ -1036,10 -1032,10 +1036,10 @@@ trusteddirserver_get_by_v3_auth_digest(
  /** Try to find a running trusted dirserver.  Flags are as for
   * router_pick_directory_server.
   */
 -routerstatus_t *
 +const routerstatus_t *
  router_pick_trusteddirserver(authority_type_t type, int flags)
  {
 -  routerstatus_t *choice;
 +  const routerstatus_t *choice;
    int busy = 0;
    if (get_options()->PreferTunneledDirConns)
      flags |= _PDS_PREFER_TUNNELED_DIR_CONNS;
@@@ -1051,8 -1047,7 +1051,8 @@@
      /* If the reason that we got no server is that servers are "busy",
       * we must be excluding good servers because we already have serverdesc
       * fetches with them.  Do not mark down servers up because of this. */
 -    tor_assert((flags & PDS_NO_EXISTING_SERVERDESC_FETCH));
 +    tor_assert((flags & (PDS_NO_EXISTING_SERVERDESC_FETCH|
 +                         PDS_NO_EXISTING_MICRODESC_FETCH)));
      return NULL;
    }
  
@@@ -1072,10 -1067,10 +1072,10 @@@
   * If the _PDS_PREFER_TUNNELED_DIR_CONNS flag is set, prefer directory servers
   * that we can use with BEGINDIR.
   */
 -static routerstatus_t *
 +static const routerstatus_t *
  router_pick_directory_server_impl(authority_type_t type, int flags)
  {
 -  routerstatus_t *result;
 +  const node_t *result;
    smartlist_t *direct, *tunnel;
    smartlist_t *trusted_direct, *trusted_tunnel;
    smartlist_t *overloaded_direct, *overloaded_tunnel;
@@@ -1096,54 -1091,49 +1096,54 @@@
    overloaded_tunnel = smartlist_create();
  
    /* Find all the running dirservers we know about. */
 -  SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *,
 -                          status) {
 +  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
      int is_trusted;
 -    int is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
 +    int is_overloaded;
      tor_addr_t addr;
 -    if (!status->is_running || !status->dir_port || !status->is_valid)
 +    const routerstatus_t *status = node->rs;
 +    if (!status)
        continue;
 -    if (status->is_bad_directory)
 +
 +    if (!node->is_running || !status->dir_port || !node->is_valid)
 +      continue;
 +    if (node->is_bad_directory)
        continue;
 -    if (requireother && router_digest_is_me(status->identity_digest))
 +    if (requireother && router_digest_is_me(node->identity))
        continue;
      if (type & V3_AUTHORITY) {
        if (!(status->version_supports_v3_dir ||
 -            router_digest_is_trusted_dir_type(status->identity_digest,
 +            router_digest_is_trusted_dir_type(node->identity,
                                                V3_AUTHORITY)))
          continue;
      }
 -    is_trusted = router_digest_is_trusted_dir(status->identity_digest);
 -    if ((type & V2_AUTHORITY) && !(status->is_v2_dir || is_trusted))
 +    is_trusted = router_digest_is_trusted_dir(node->identity);
 +    if ((type & V2_AUTHORITY) && !(node->rs->is_v2_dir || is_trusted))
        continue;
      if ((type & EXTRAINFO_CACHE) &&
 -        !router_supports_extrainfo(status->identity_digest, 0))
 +        !router_supports_extrainfo(node->identity, 0))
        continue;
  
      /* XXXX IP6 proposal 118 */
 -    tor_addr_from_ipv4h(&addr, status->addr);
 +    tor_addr_from_ipv4h(&addr, node->rs->addr);
 +
 +    is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
  
      if (prefer_tunnel &&
          status->version_supports_begindir &&
          (!fascistfirewall ||
           fascist_firewall_allows_address_or(&addr, status->or_port)))
        smartlist_add(is_trusted ? trusted_tunnel :
 -                      is_overloaded ? overloaded_tunnel : tunnel, status);
 +                    is_overloaded ? overloaded_tunnel : tunnel, (void*)node);
      else if (!fascistfirewall ||
               fascist_firewall_allows_address_dir(&addr, status->dir_port))
        smartlist_add(is_trusted ? trusted_direct :
 -                      is_overloaded ? overloaded_direct : direct, status);
 -  } SMARTLIST_FOREACH_END(status);
 +                    is_overloaded ? overloaded_direct : direct, (void*)node);
 +  } SMARTLIST_FOREACH_END(node);
  
    if (smartlist_len(tunnel)) {
 -    result = routerstatus_sl_choose_by_bandwidth(tunnel, WEIGHT_FOR_DIR);
 +    result = node_sl_choose_by_bandwidth(tunnel, WEIGHT_FOR_DIR);
    } else if (smartlist_len(overloaded_tunnel)) {
 -    result = routerstatus_sl_choose_by_bandwidth(overloaded_tunnel,
 +    result = node_sl_choose_by_bandwidth(overloaded_tunnel,
                                                   WEIGHT_FOR_DIR);
    } else if (smartlist_len(trusted_tunnel)) {
      /* FFFF We don't distinguish between trusteds and overloaded trusteds
@@@ -1152,10 -1142,10 +1152,10 @@@
       * is a feature, but it could easily be a bug. -RD */
      result = smartlist_choose(trusted_tunnel);
    } else if (smartlist_len(direct)) {
 -    result = routerstatus_sl_choose_by_bandwidth(direct, WEIGHT_FOR_DIR);
 +    result = node_sl_choose_by_bandwidth(direct, WEIGHT_FOR_DIR);
    } else if (smartlist_len(overloaded_direct)) {
 -    result = routerstatus_sl_choose_by_bandwidth(overloaded_direct,
 -                                                 WEIGHT_FOR_DIR);
 +    result = node_sl_choose_by_bandwidth(overloaded_direct,
 +                                         WEIGHT_FOR_DIR);
    } else {
      result = smartlist_choose(trusted_direct);
    }
@@@ -1165,26 -1155,25 +1165,26 @@@
    smartlist_free(trusted_tunnel);
    smartlist_free(overloaded_direct);
    smartlist_free(overloaded_tunnel);
 -  return result;
 +  return result ? result->rs : NULL;
  }
  
  /** Choose randomly from among the trusted dirservers that are up.  Flags
   * are as for router_pick_directory_server_impl().
   */
 -static routerstatus_t *
 +static const routerstatus_t *
  router_pick_trusteddirserver_impl(authority_type_t type, int flags,
                                    int *n_busy_out)
  {
    smartlist_t *direct, *tunnel;
    smartlist_t *overloaded_direct, *overloaded_tunnel;
 -  routerinfo_t *me = router_get_my_routerinfo();
 -  routerstatus_t *result;
 +  const routerinfo_t *me = router_get_my_routerinfo();
 +  const routerstatus_t *result;
    time_t now = time(NULL);
    const int requireother = ! (flags & PDS_ALLOW_SELF);
    const int fascistfirewall = ! (flags & PDS_IGNORE_FASCISTFIREWALL);
    const int prefer_tunnel = (flags & _PDS_PREFER_TUNNELED_DIR_CONNS);
    const int no_serverdesc_fetching =(flags & PDS_NO_EXISTING_SERVERDESC_FETCH);
 +  const int no_microdesc_fetching =(flags & PDS_NO_EXISTING_MICRODESC_FETCH);
    int n_busy = 0;
  
    if (!trusted_dir_servers)
@@@ -1223,13 -1212,6 +1223,13 @@@
            continue;
          }
        }
 +      if (no_microdesc_fetching) {
 +        if (connection_get_by_type_addr_port_purpose(
 +             CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_MICRODESC)) {
 +          ++n_busy;
 +          continue;
 +        }
 +      }
  
        if (prefer_tunnel &&
            d->or_port &&
@@@ -1268,18 -1250,22 +1268,18 @@@
  static void
  mark_all_trusteddirservers_up(void)
  {
 -  if (routerlist) {
 -    SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
 -       if (router_digest_is_trusted_dir(router->cache_info.identity_digest) &&
 -         router->dir_port > 0) {
 -         router->is_running = 1;
 -       });
 -  }
 +  SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node, {
 +       if (router_digest_is_trusted_dir(node->identity))
 +         node->is_running = 1;
 +    });
    if (trusted_dir_servers) {
      SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, dir,
      {
        routerstatus_t *rs;
        dir->is_running = 1;
        download_status_reset(&dir->v2_ns_dl_status);
 -      rs = router_get_consensus_status_by_id(dir->digest);
 -      if (rs && !rs->is_running) {
 -        rs->is_running = 1;
 +      rs = router_get_mutable_consensus_status_by_id(dir->digest);
 +      if (rs) {
          rs->last_dir_503_at = 0;
          control_event_networkstatus_changed_single(rs);
        }
@@@ -1303,14 -1289,25 +1303,14 @@@ router_reset_status_download_failures(v
    mark_all_trusteddirservers_up();
  }
  
 -/** Return true iff router1 and router2 have the same /16 network. */
 +/** Return true iff router1 and router2 have similar enough network addresses
 + * that we should treat them as being in the same family */
  static INLINE int
 -routers_in_same_network_family(routerinfo_t *r1, routerinfo_t *r2)
 -{
 -  return (r1->addr & 0xffff0000) == (r2->addr & 0xffff0000);
 -}
 -
 -/** Look through the routerlist and identify routers that
 - * advertise the same /16 network address as <b>router</b>.
 - * Add each of them to <b>sl</b>.
 - */
 -static void
 -routerlist_add_network_family(smartlist_t *sl, routerinfo_t *router)
 +addrs_in_same_network_family(const tor_addr_t *a1,
 +                             const tor_addr_t *a2)
  {
 -  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, r,
 -  {
 -    if (router != r && routers_in_same_network_family(router, r))
 -      smartlist_add(sl, r);
 -  });
 +  /* XXXX MOVE ? */
 +  return 0 == tor_addr_compare_masked(a1, a2, 16, CMP_SEMANTIC);
  }
  
  /** Add all the family of <b>router</b> to the smartlist <b>sl</b>.
@@@ -1318,132 -1315,122 +1318,132 @@@
   * or pick more than one relay from a family for our entry guard list.
   */
  void
 -routerlist_add_family(smartlist_t *sl, routerinfo_t *router)
 +nodelist_add_node_family(smartlist_t *sl, const node_t *node)
  {
 -  routerinfo_t *r;
 -  config_line_t *cl;
 +  /* XXXX MOVE */
 +  const smartlist_t *all_nodes = nodelist_get_list();
 +  const smartlist_t *declared_family;
    or_options_t *options = get_options();
  
 -  /* First, add any routers with similar network addresses. */
 -  if (options->EnforceDistinctSubnets)
 -    routerlist_add_network_family(sl, router);
 +  tor_assert(node);
  
 -  if (router->declared_family) {
 -    /* Add every r such that router declares familyness with r, and r
 +  declared_family = node_get_declared_family(node);
 +
 +  /* First, add any nodes with similar network addresses. */
 +  if (options->EnforceDistinctSubnets) {
 +    tor_addr_t node_addr;
 +    node_get_addr(node, &node_addr);
 +
 +    SMARTLIST_FOREACH_BEGIN(all_nodes, const node_t *, node2) {
 +      tor_addr_t a;
 +      node_get_addr(node2, &a);
 +      if (addrs_in_same_network_family(&a, &node_addr))
 +        smartlist_add(sl, (void*)node2);
 +    } SMARTLIST_FOREACH_END(node2);
 +  }
 +
 +  /* Now, add all nodes in the declared_family of this node, if they
 +   * also declare this node to be in their family. */
 +  if (declared_family) {
 +    /* Add every r such that router declares familyness with node, and node
       * declares familyhood with router. */
 -    SMARTLIST_FOREACH(router->declared_family, const char *, n,
 -      {
 -        if (!(r = router_get_by_nickname(n, 0)))
 -          continue;
 -        if (!r->declared_family)
 -          continue;
 -        SMARTLIST_FOREACH(r->declared_family, const char *, n2,
 -          {
 -            if (router_nickname_matches(router, n2))
 -              smartlist_add(sl, r);
 -          });
 -      });
 +    SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) {
 +      const node_t *node2;
 +      const smartlist_t *family2;
 +      if (!(node2 = node_get_by_nickname(name, 0)))
 +        continue;
 +      if (!(family2 = node_get_declared_family(node2)))
 +        continue;
 +      SMARTLIST_FOREACH_BEGIN(family2, const char *, name2) {
 +          if (node_nickname_matches(node, name2)) {
 +            smartlist_add(sl, (void*)node2);
 +            break;
 +          }
 +      } SMARTLIST_FOREACH_END(name2);
 +    } SMARTLIST_FOREACH_END(name);
    }
  
    /* If the user declared any families locally, honor those too. */
 -  for (cl = options->NodeFamilies; cl; cl = cl->next) {
 -    if (router_nickname_is_in_list(router, cl->value)) {
 -      add_nickname_list_to_smartlist(sl, cl->value, 0);
 -    }
 +  if (options->NodeFamilySets) {
 +    SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, {
 +      if (routerset_contains_node(rs, node)) {
 +        routerset_get_all_nodes(sl, rs, 0);
 +      }
 +    });
 +  }
 +}
 +
 +/** Given a <b>router</b>, add every node_t in its family to <b>sl</b>.
 + *
 + * Note the type mismatch: This function takes a routerinfo, but adds nodes
 + * to the smartlist!
 + */
 +static void
 +routerlist_add_nodes_in_family(smartlist_t *sl, const routerinfo_t *router)
 +{
 +  /* XXXX MOVE ? */
 +  node_t fake_node;
 +  const node_t *node = node_get_by_id(router->cache_info.identity_digest);;
 +  if (node == NULL) {
 +    memset(&fake_node, 0, sizeof(fake_node));
 +    fake_node.ri = (routerinfo_t *)router;
 +    memcpy(fake_node.identity, router->cache_info.identity_digest, DIGEST_LEN);
 +    node = &fake_node;
    }
 +  nodelist_add_node_family(sl, node);
  }
  
 -/** Return true iff r is named by some nickname in <b>lst</b>. */
 +/** Return true iff <b>node</b> is named by some nickname in <b>lst</b>. */
  static INLINE int
 -router_in_nickname_smartlist(smartlist_t *lst, routerinfo_t *r)
 +node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node)
  {
 +  /* XXXX MOVE */
    if (!lst) return 0;
 -  SMARTLIST_FOREACH(lst, const char *, name,
 -    if (router_nickname_matches(r, name))
 -      return 1;);
 +  SMARTLIST_FOREACH(lst, const char *, name, {
 +    if (node_nickname_matches(node, name))
 +      return 1;
 +  });
    return 0;
  }
  
  /** Return true iff r1 and r2 are in the same family, but not the same
   * router. */
  int
 -routers_in_same_family(routerinfo_t *r1, routerinfo_t *r2)
 +nodes_in_same_family(const node_t *node1, const node_t *node2)
  {
 +  /* XXXX MOVE */
    or_options_t *options = get_options();
 -  config_line_t *cl;
 -
 -  if (options->EnforceDistinctSubnets && routers_in_same_network_family(r1,r2))
 -    return 1;
  
 -  if (router_in_nickname_smartlist(r1->declared_family, r2) &&
 -      router_in_nickname_smartlist(r2->declared_family, r1))
 -    return 1;
 -
 -  for (cl = options->NodeFamilies; cl; cl = cl->next) {
 -    if (router_nickname_is_in_list(r1, cl->value) &&
 -        router_nickname_is_in_list(r2, cl->value))
 +  /* Are they in the same family because of their addresses? */
 +  if (options->EnforceDistinctSubnets) {
 +    tor_addr_t a1, a2;
 +    node_get_addr(node1, &a1);
 +    node_get_addr(node2, &a2);
 +    if (addrs_in_same_network_family(&a1, &a2))
        return 1;
    }
 -  return 0;
 -}
  
 -/** Given a (possibly NULL) comma-and-whitespace separated list of nicknames,
 - * see which nicknames in <b>list</b> name routers in our routerlist, and add
 - * the routerinfos for those routers to <b>sl</b>.  If <b>must_be_running</b>,
 - * only include routers that we think are running.
 - * Warn if any non-Named routers are specified by nickname.
 - */
 -void
 -add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
 -                               int must_be_running)
 -{
 -  routerinfo_t *router;
 -  smartlist_t *nickname_list;
 -  int have_dir_info = router_have_minimum_dir_info();
 -
 -  if (!list)
 -    return; /* nothing to do */
 -  tor_assert(sl);
 -
 -  nickname_list = smartlist_create();
 -  if (!warned_nicknames)
 -    warned_nicknames = smartlist_create();
 +  /* Are they in the same family because the agree they are? */
 +  {
 +    const smartlist_t *f1, *f2;
 +    f1 = node_get_declared_family(node1);
 +    f2 = node_get_declared_family(node2);
 +    if (f1 && f2 &&
 +        node_in_nickname_smartlist(f1, node2) &&
 +        node_in_nickname_smartlist(f2, node1))
 +      return 1;
 +  }
  
 -  smartlist_split_string(nickname_list, list, ",",
 -                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
 +  /* Are they in the same option because the user says they are? */
 +  if (options->NodeFamilySets) {
 +    SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, {
 +        if (routerset_contains_node(rs, node1) &&
 +            routerset_contains_node(rs, node2))
 +          return 1;
 +      });
 +  }
  
 -  SMARTLIST_FOREACH(nickname_list, const char *, nick, {
 -    int warned;
 -    if (!is_legal_nickname_or_hexdigest(nick)) {
 -      log_warn(LD_CONFIG, "Nickname '%s' is misformed; skipping", nick);
 -      continue;
 -    }
 -    router = router_get_by_nickname(nick, 1);
 -    warned = smartlist_string_isin(warned_nicknames, nick);
 -    if (router) {
 -      if (!must_be_running || router->is_running) {
 -        smartlist_add(sl,router);
 -      }
 -    } else if (!router_get_consensus_status_by_nickname(nick,1)) {
 -      if (!warned) {
 -        log_fn(have_dir_info ? LOG_WARN : LOG_INFO, LD_CONFIG,
 -               "Nickname list includes '%s' which isn't a known router.",nick);
 -        smartlist_add(warned_nicknames, tor_strdup(nick));
 -      }
 -    }
 -  });
 -  SMARTLIST_FOREACH(nickname_list, char *, nick, tor_free(nick));
 -  smartlist_free(nickname_list);
 +  return 0;
  }
  
  /** Return 1 iff any member of the (possibly NULL) comma-separated list
@@@ -1451,7 -1438,7 +1451,7 @@@
   * return 0.
   */
  int
 -router_nickname_is_in_list(routerinfo_t *router, const char *list)
 +router_nickname_is_in_list(const routerinfo_t *router, const char *list)
  {
    smartlist_t *nickname_list;
    int v = 0;
@@@ -1470,32 -1457,34 +1470,32 @@@
    return v;
  }
  
 -/** Add every suitable router from our routerlist to <b>sl</b>, so that
 +/** Add every suitable node from our nodelist to <b>sl</b>, so that
   * we can pick a node for a circuit.
   */
  static void
 -router_add_running_routers_to_smartlist(smartlist_t *sl, int allow_invalid,
 -                                        int need_uptime, int need_capacity,
 -                                        int need_guard)
 -{
 -  if (!routerlist)
 -    return;
 +router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
 +                                      int need_uptime, int need_capacity,
 +                                      int need_guard, int need_desc)
 +{ /* XXXX MOVE */
 +  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
 +    if (!node->is_running ||
 +        (!node->is_valid && !allow_invalid))
 +      continue;
 +    if (need_desc && !(node->ri || (node->rs && node->md)))
 +      continue;
 +    if (node->ri && node->ri->purpose != ROUTER_PURPOSE_GENERAL)
 +      continue;
 +    if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
 +      continue;
  
 -  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
 -  {
 -    if (router->is_running &&
 -        router->purpose == ROUTER_PURPOSE_GENERAL &&
 -        (router->is_valid || allow_invalid) &&
 -        !router_is_unreliable(router, need_uptime,
 -                              need_capacity, need_guard)) {
 -      /* If it's running, and it's suitable according to the
 -       * other flags we had in mind */
 -      smartlist_add(sl, router);
 -    }
 -  });
 +    smartlist_add(sl, (void *)node);
 +  } SMARTLIST_FOREACH_END(node);
  }
  
  /** Look through the routerlist until we find a router that has my key.
   Return it. */
 -routerinfo_t *
 +const routerinfo_t *
  routerlist_find_my_routerinfo(void)
  {
    if (!routerlist)
@@@ -1513,9 -1502,9 +1513,9 @@@
   * that allows exit to this address:port, or return NULL if there
   * isn't a good one.
   */
 -routerinfo_t *
 +const node_t *
  router_find_exact_exit_enclave(const char *address, uint16_t port)
 -{
 +{/*XXXX MOVE*/
    uint32_t addr;
    struct in_addr in;
    tor_addr_t a;
@@@ -1526,12 -1515,13 +1526,12 @@@
  
    tor_addr_from_ipv4h(&a, addr);
  
 -  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
 -  {
 -    if (router->addr == addr &&
 -        router->is_running &&
 -        compare_tor_addr_to_addr_policy(&a, port, router->exit_policy) ==
 +  SMARTLIST_FOREACH(nodelist_get_list(), const node_t *, node, {
 +    if (node_get_addr_ipv4h(node) == addr &&
 +        node->is_running &&
 +        compare_tor_addr_to_node_policy(&a, port, node) ==
            ADDR_POLICY_ACCEPTED)
 -      return router;
 +      return node;
    });
    return NULL;
  }
@@@ -1543,14 -1533,14 +1543,14 @@@
   * If <b>need_guard</b>, we require that the router is a possible entry guard.
   */
  int
 -router_is_unreliable(routerinfo_t *router, int need_uptime,
 -                     int need_capacity, int need_guard)
 +node_is_unreliable(const node_t *node, int need_uptime,
 +                   int need_capacity, int need_guard)
  {
 -  if (need_uptime && !router->is_stable)
 +  if (need_uptime && !node->is_stable)
      return 1;
 -  if (need_capacity && !router->is_fast)
 +  if (need_capacity && !node->is_fast)
      return 1;
 -  if (need_guard && !router->is_possible_guard)
 +  if (need_guard && !node->is_possible_guard)
      return 1;
    return 0;
  }
@@@ -1558,7 -1548,7 +1558,7 @@@
  /** Return the smaller of the router's configured BandwidthRate
   * and its advertised capacity. */
  uint32_t
 -router_get_advertised_bandwidth(routerinfo_t *router)
 +router_get_advertised_bandwidth(const routerinfo_t *router)
  {
    if (router->bandwidthcapacity < router->bandwidthrate)
      return router->bandwidthcapacity;
@@@ -1572,7 -1562,7 +1572,7 @@@
  /** Return the smaller of the router's configured BandwidthRate
   * and its advertised capacity, capped by max-believe-bw. */
  uint32_t
 -router_get_advertised_bandwidth_capped(routerinfo_t *router)
 +router_get_advertised_bandwidth_capped(const routerinfo_t *router)
  {
    uint32_t result = router->bandwidthcapacity;
    if (result > router->bandwidthrate)
@@@ -1614,10 -1604,13 +1614,10 @@@ kb_to_bytes(uint32_t bw
  }
  
  /** Helper function:
 - * choose a random element of smartlist <b>sl</b>, weighted by
 + * choose a random element of smartlist <b>sl</b> of nodes, weighted by
   * the advertised bandwidth of each element using the consensus
   * bandwidth weights.
   *
 - * If <b>statuses</b> is zero, then <b>sl</b> is a list of
 - * routerinfo_t's. Otherwise it's a list of routerstatus_t's.
 - *
   * If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all
   * nodes' bandwidth equally regardless of their Exit status, since there may
   * be some in the list because they exit to obscure ports. If
@@@ -1627,9 -1620,10 +1627,9 @@@
   * guard node: consider all guard's bandwidth equally. Otherwise, weight
   * guards proportionally less.
   */
 -static void *
 -smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
 -                                      bandwidth_weight_rule_t rule,
 -                                      int statuses)
 +static const node_t *
 +smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl,
 +                                           bandwidth_weight_rule_t rule)
  {
    int64_t weight_scale;
    int64_t rand_bw;
@@@ -1656,8 -1650,7 +1656,7 @@@
      return NULL;
    }
  
-   weight_scale = networkstatus_get_param(NULL, "bwweightscale",
-                                          BW_WEIGHT_SCALE);
+   weight_scale = circuit_build_times_get_bw_scale(NULL);
  
    if (rule == WEIGHT_FOR_GUARD) {
      Wg = networkstatus_get_bw_weight(NULL, "Wgg", -1);
@@@ -1724,14 -1717,15 +1723,14 @@@
    bandwidths = tor_malloc_zero(sizeof(double)*smartlist_len(sl));
  
    // Cycle through smartlist and total the bandwidth.
 -  for (i = 0; i < (unsigned)smartlist_len(sl); ++i) {
 +  SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
      int is_exit = 0, is_guard = 0, is_dir = 0, this_bw = 0, is_me = 0;
      double weight = 1;
 -    if (statuses) {
 -      routerstatus_t *status = smartlist_get(sl, i);
 -      is_exit = status->is_exit;
 -      is_guard = status->is_possible_guard;
 -      is_dir = (status->dir_port != 0);
 -      if (!status->has_bandwidth) {
 +    is_exit = node->is_exit;
 +    is_guard = node->is_possible_guard;
 +    is_dir = node_is_dir(node);
 +    if (node->rs) {
 +      if (!node->rs->has_bandwidth) {
          tor_free(bandwidths);
          /* This should never happen, unless all the authorites downgrade
           * to 0.2.0 or rogue routerstatuses get inserted into our consensus. */
@@@ -1740,17 -1734,26 +1739,17 @@@
                   "old router selection algorithm.");
          return NULL;
        }
 -      this_bw = kb_to_bytes(status->bandwidth);
 -      if (router_digest_is_me(status->identity_digest))
 -        is_me = 1;
 +      this_bw = kb_to_bytes(node->rs->bandwidth);
 +    } else if (node->ri) {
 +      /* bridge or other descriptor not in our consensus */
 +      this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
 +      have_unknown = 1;
      } else {
 -      routerstatus_t *rs;
 -      routerinfo_t *router = smartlist_get(sl, i);
 -      rs = router_get_consensus_status_by_id(
 -             router->cache_info.identity_digest);
 -      is_exit = router->is_exit;
 -      is_guard = router->is_possible_guard;
 -      is_dir = (router->dir_port != 0);
 -      if (rs && rs->has_bandwidth) {
 -        this_bw = kb_to_bytes(rs->bandwidth);
 -      } else { /* bridge or other descriptor not in our consensus */
 -        this_bw = bridge_get_advertised_bandwidth_bounded(router);
 -        have_unknown = 1;
 -      }
 -      if (router_digest_is_me(router->cache_info.identity_digest))
 -        is_me = 1;
 +      /* We can't use this one. */
 +      continue;
      }
 +    is_me = router_digest_is_me(node->identity);
 +
      if (is_guard && is_exit) {
        weight = (is_dir ? Wdb*Wd : Wd);
      } else if (is_guard) {
@@@ -1761,11 -1764,11 +1760,11 @@@
        weight = (is_dir ? Wmb*Wm : Wm);
      }
  
 -    bandwidths[i] = weight*this_bw;
 +    bandwidths[node_sl_idx] = weight*this_bw;
      weighted_bw += weight*this_bw;
      if (is_me)
        sl_last_weighted_bw_of_me = weight*this_bw;
 -  }
 +  } SMARTLIST_FOREACH_END(node);
  
    /* XXXX022 this is a kludge to expose these values. */
    sl_last_total_weighted_bw = weighted_bw;
@@@ -1813,9 -1816,12 +1812,9 @@@
  }
  
  /** Helper function:
 - * choose a random element of smartlist <b>sl</b>, weighted by
 + * choose a random node_t element of smartlist <b>sl</b>, weighted by
   * the advertised bandwidth of each element.
   *
 - * If <b>statuses</b> is zero, then <b>sl</b> is a list of
 - * routerinfo_t's. Otherwise it's a list of routerstatus_t's.
 - *
   * If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all
   * nodes' bandwidth equally regardless of their Exit status, since there may
   * be some in the list because they exit to obscure ports. If
@@@ -1825,11 -1831,13 +1824,11 @@@
   * guard node: consider all guard's bandwidth equally. Otherwise, weight
   * guards proportionally less.
   */
 -static void *
 -smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
 -                              int statuses)
 +static const node_t *
 +smartlist_choose_node_by_bandwidth(smartlist_t *sl,
 +                                   bandwidth_weight_rule_t rule)
  {
 -  unsigned int i;
 -  routerinfo_t *router;
 -  routerstatus_t *status=NULL;
 +  unsigned i;
    int32_t *bandwidths;
    int is_exit;
    int is_guard;
@@@ -1870,34 -1878,49 +1869,34 @@@
    guard_bits = bitarray_init_zero(smartlist_len(sl));
  
    /* Iterate over all the routerinfo_t or routerstatus_t, and */
 -  for (i = 0; i < (unsigned)smartlist_len(sl); ++i) {
 +  SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
      /* first, learn what bandwidth we think i has */
      int is_known = 1;
      int32_t flags = 0;
      uint32_t this_bw = 0;
 -    if (statuses) {
 -      status = smartlist_get(sl, i);
 -      if (router_digest_is_me(status->identity_digest))
 -        me_idx = i;
 -      router = router_get_by_digest(status->identity_digest);
 -      is_exit = status->is_exit;
 -      is_guard = status->is_possible_guard;
 -      if (status->has_bandwidth) {
 -        this_bw = kb_to_bytes(status->bandwidth);
 +    i = node_sl_idx;
 +
 +    if (router_digest_is_me(node->identity))
 +      me_idx = node_sl_idx;
 +
 +    is_exit = node->is_exit;
 +    is_guard = node->is_possible_guard;
 +    if (node->rs) {
 +      if (node->rs->has_bandwidth) {
 +        this_bw = kb_to_bytes(node->rs->bandwidth);
        } else { /* guess */
          /* XXX022 once consensuses always list bandwidths, we can take
           * this guessing business out. -RD */
          is_known = 0;
 -        flags = status->is_fast ? 1 : 0;
 +        flags = node->rs->is_fast ? 1 : 0;
          flags |= is_exit ? 2 : 0;
          flags |= is_guard ? 4 : 0;
        }
 -    } else {
 -      routerstatus_t *rs;
 -      router = smartlist_get(sl, i);
 -      rs = router_get_consensus_status_by_id(
 -             router->cache_info.identity_digest);
 -      if (router_digest_is_me(router->cache_info.identity_digest))
 -        me_idx = i;
 -      is_exit = router->is_exit;
 -      is_guard = router->is_possible_guard;
 -      if (rs && rs->has_bandwidth) {
 -        this_bw = kb_to_bytes(rs->bandwidth);
 -      } else if (rs) { /* guess; don't trust the descriptor */
 -        /* XXX022 once consensuses always list bandwidths, we can take
 -         * this guessing business out. -RD */
 -        is_known = 0;
 -        flags = router->is_fast ? 1 : 0;
 -        flags |= is_exit ? 2 : 0;
 -        flags |= is_guard ? 4 : 0;
 -      } else /* bridge or other descriptor not in our consensus */
 -        this_bw = bridge_get_advertised_bandwidth_bounded(router);
 +    } else if (node->ri) {
 +      /* Must be a bridge if we're willing to use it */
 +      this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
      }
 +
      if (is_exit)
        bitarray_set(exit_bits, i);
      if (is_guard)
@@@ -1917,9 -1940,9 +1916,9 @@@
          total_nonexit_bw += this_bw;
      } else {
        ++n_unknown;
 -      bandwidths[i] = -flags;
 +      bandwidths[node_sl_idx] = -flags;
      }
 -  }
 +  } SMARTLIST_FOREACH_END(node);
  
    /* Now, fill in the unknown values. */
    if (n_unknown) {
@@@ -2061,23 -2084,40 +2060,23 @@@
    return smartlist_get(sl, i);
  }
  
 -/** Choose a random element of router list <b>sl</b>, weighted by
 - * the advertised bandwidth of each router.
 - */
 -routerinfo_t *
 -routerlist_sl_choose_by_bandwidth(smartlist_t *sl,
 -                                  bandwidth_weight_rule_t rule)
 -{
 -  routerinfo_t *ret;
 -  if ((ret = smartlist_choose_by_bandwidth_weights(sl, rule, 0))) {
 -    return ret;
 -  } else {
 -    return smartlist_choose_by_bandwidth(sl, rule, 0);
 -  }
 -}
 -
  /** Choose a random element of status list <b>sl</b>, weighted by
 - * the advertised bandwidth of each status.
 - */
 -routerstatus_t *
 -routerstatus_sl_choose_by_bandwidth(smartlist_t *sl,
 -                                    bandwidth_weight_rule_t rule)
 -{
 -  /* We are choosing neither exit nor guard here. Weight accordingly. */
 -  routerstatus_t *ret;
 -  if ((ret = smartlist_choose_by_bandwidth_weights(sl, rule, 1))) {
 + * the advertised bandwidth of each node */
 +const node_t *
 +node_sl_choose_by_bandwidth(smartlist_t *sl,
 +                            bandwidth_weight_rule_t rule)
 +{ /*XXXX MOVE */
 +  const node_t *ret;
 +  if ((ret = smartlist_choose_node_by_bandwidth_weights(sl, rule))) {
      return ret;
    } else {
 -    return smartlist_choose_by_bandwidth(sl, rule, 1);
 +    return smartlist_choose_node_by_bandwidth(sl, rule);
    }
  }
  
 -/** Return a random running router from the routerlist. Never
 - * pick a node whose routerinfo is in
 - * <b>excludedsmartlist</b>, or whose routerinfo matches <b>excludedset</b>,
 +/** Return a random running node from the nodelist. Never
 + * pick a node that is in
 + * <b>excludedsmartlist</b>, or which matches <b>excludedset</b>,
   * even if they are the only nodes available.
   * If <b>CRN_NEED_UPTIME</b> is set in flags and any router has more than
   * a minimum uptime, return one of those.
@@@ -2089,26 -2129,21 +2088,26 @@@
   * If <b>CRN_WEIGHT_AS_EXIT</b> is set in flags, we weight bandwidths as if
   * picking an exit node, otherwise we weight bandwidths for picking a relay
   * node (that is, possibly discounting exit nodes).
 + * If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that
 + * have a routerinfo or microdescriptor -- that is, enough info to be
 + * used to build a circuit.
   */
 -routerinfo_t *
 +const node_t *
  router_choose_random_node(smartlist_t *excludedsmartlist,
                            routerset_t *excludedset,
                            router_crn_flags_t flags)
 -{
 +{ /* XXXX MOVE */
    const int need_uptime = (flags & CRN_NEED_UPTIME) != 0;
    const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0;
    const int need_guard = (flags & CRN_NEED_GUARD) != 0;
    const int allow_invalid = (flags & CRN_ALLOW_INVALID) != 0;
    const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0;
 +  const int need_desc = (flags & CRN_NEED_DESC) != 0;
  
    smartlist_t *sl=smartlist_create(),
 -              *excludednodes=smartlist_create();
 -  routerinfo_t *choice = NULL, *r;
 +    *excludednodes=smartlist_create();
 +  const node_t *choice = NULL;
 +  const routerinfo_t *r;
    bandwidth_weight_rule_t rule;
  
    tor_assert(!(weight_for_exit && need_guard));
@@@ -2118,30 -2153,29 +2117,30 @@@
    /* Exclude relays that allow single hop exit circuits, if the user
     * wants to (such relays might be risky) */
    if (get_options()->ExcludeSingleHopRelays) {
 -    routerlist_t *rl = router_get_routerlist();
 -    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
 -      if (r->allow_single_hop_exits) {
 -        smartlist_add(excludednodes, r);
 +    SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
 +      if (node_allows_single_hop_exits(node)) {
 +        smartlist_add(excludednodes, node);
        });
    }
  
    if ((r = routerlist_find_my_routerinfo())) {
 -    smartlist_add(excludednodes, r);
 -    routerlist_add_family(excludednodes, r);
 +    const node_t *me = node_get_by_id(r->cache_info.identity_digest);
 +    if (me)
 +      smartlist_add(excludednodes, (void *)me);
 +    routerlist_add_nodes_in_family(excludednodes, r);
    }
  
 -  router_add_running_routers_to_smartlist(sl, allow_invalid,
 -                                          need_uptime, need_capacity,
 -                                          need_guard);
 +  router_add_running_nodes_to_smartlist(sl, allow_invalid,
 +                                        need_uptime, need_capacity,
 +                                        need_guard, need_desc);
    smartlist_subtract(sl,excludednodes);
    if (excludedsmartlist)
      smartlist_subtract(sl,excludedsmartlist);
    if (excludedset)
 -    routerset_subtract_routers(sl,excludedset);
 +    routerset_subtract_nodes(sl,excludedset);
  
    // Always weight by bandwidth
 -  choice = routerlist_sl_choose_by_bandwidth(sl, rule);
 +  choice = node_sl_choose_by_bandwidth(sl, rule);
  
    smartlist_free(sl);
    if (!choice && (need_uptime || need_capacity || need_guard)) {
@@@ -2164,88 -2198,35 +2163,88 @@@
    return choice;
  }
  
 -/** Helper: Return true iff the <b>identity_digest</b> and <b>nickname</b>
 - * combination of a router, encoded in hexadecimal, matches <b>hexdigest</b>
 - * (which is optionally prefixed with a single dollar sign).  Return false if
 - * <b>hexdigest</b> is malformed, or it doesn't match.  */
 -static INLINE int
 -hex_digest_matches(const char *hexdigest, const char *identity_digest,
 -                   const char *nickname, int is_named)
 +/** Helper: given an extended nickname in <b>hexdigest</b> try to decode it.
 + * Return 0 on success, -1 on failure.  Store the result into the
 + * DIGEST_LEN-byte buffer at <b>digest_out</b>, the single character at
 + * <b>nickname_qualifier_char_out</b>, and the MAXNICKNAME_LEN+1-byte buffer
 + * at <b>nickname_out</b>.
 + *
 + * The recognized format is:
 + *   HexName = Dollar? HexDigest NamePart?
 + *   Dollar = '?'
 + *   HexDigest = HexChar*20
 + *   HexChar = 'a'..'f' | 'A'..'F' | '0'..'9'
 + *   NamePart = QualChar Name
 + *   QualChar = '=' | '~'
 + *   Name = NameChar*(1..MAX_NICKNAME_LEN)
 + *   NameChar = Any ASCII alphanumeric character
 + */
 +int
 +hex_digest_nickname_decode(const char *hexdigest,
 +                           char *digest_out,
 +                           char *nickname_qualifier_char_out,
 +                           char *nickname_out)
  {
 -  char digest[DIGEST_LEN];
    size_t len;
 +
    tor_assert(hexdigest);
    if (hexdigest[0] == '$')
      ++hexdigest;
  
    len = strlen(hexdigest);
 -  if (len < HEX_DIGEST_LEN)
 +  if (len < HEX_DIGEST_LEN) {
 +    return -1;
 +  } else if (len > HEX_DIGEST_LEN && (hexdigest[HEX_DIGEST_LEN] == '=' ||
 +                                    hexdigest[HEX_DIGEST_LEN] == '~') &&
 +           len <= HEX_DIGEST_LEN+1+MAX_NICKNAME_LEN) {
 +    *nickname_qualifier_char_out = hexdigest[HEX_DIGEST_LEN];
 +    strlcpy(nickname_out, hexdigest+HEX_DIGEST_LEN+1 , MAX_NICKNAME_LEN+1);
 +  } else if (len == HEX_DIGEST_LEN) {
 +    ;
 +  } else {
 +    return -1;
 +  }
 +
 +  if (base16_decode(digest_out, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
 +    return -1;
 +  return 0;
 +}
 +
 +/** Helper: Return true iff the <b>identity_digest</b> and <b>nickname</b>
 + * combination of a router, encoded in hexadecimal, matches <b>hexdigest</b>
 + * (which is optionally prefixed with a single dollar sign).  Return false if
 + * <b>hexdigest</b> is malformed, or it doesn't match.  */
 +static int
 +hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest,
 +                            const char *nickname, int is_named)
 +{
 +  char digest[DIGEST_LEN];
 +  char nn_char='\0';
 +  char nn_buf[MAX_NICKNAME_LEN+1];
 +
 +  if (hex_digest_nickname_decode(hexdigest, digest, &nn_char, nn_buf) == -1)
      return 0;
 -  else if (len > HEX_DIGEST_LEN &&
 -           (hexdigest[HEX_DIGEST_LEN] == '=' ||
 -            hexdigest[HEX_DIGEST_LEN] == '~')) {
 -    if (strcasecmp(hexdigest+HEX_DIGEST_LEN+1, nickname))
 +
 +  if (nn_char == '=' || nn_char == '~') {
 +    if (strcasecmp(nn_buf, nickname))
        return 0;
 -    if (hexdigest[HEX_DIGEST_LEN] == '=' && !is_named)
 +    if (nn_char == '=' && !is_named)
        return 0;
    }
  
 -  if (base16_decode(digest, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
 -    return 0;
 -  return (!memcmp(digest, identity_digest, DIGEST_LEN));
 +  return !memcmp(digest, identity_digest, DIGEST_LEN);
 +}
 +
 +/* Return true iff <b>router</b> is listed as named in the current
 + * consensus. */
 +static int
 +router_is_named(const routerinfo_t *router)
 +{
 +  const char *digest =
 +    networkstatus_get_router_digest_by_nickname(router->nickname);
 +
 +  return (digest &&
 +          !memcmp(digest, router->cache_info.identity_digest, DIGEST_LEN));
  }
  
  /** Return true iff the digest of <b>router</b>'s identity key,
@@@ -2253,12 -2234,10 +2252,12 @@@
   * optionally prefixed with a single dollar sign).  Return false if
   * <b>hexdigest</b> is malformed, or it doesn't match.  */
  static INLINE int
 -router_hex_digest_matches(routerinfo_t *router, const char *hexdigest)
 +router_hex_digest_matches(const routerinfo_t *router, const char *hexdigest)
  {
 -  return hex_digest_matches(hexdigest, router->cache_info.identity_digest,
 -                            router->nickname, router->is_named);
 +  return hex_digest_nickname_matches(hexdigest,
 +                                     router->cache_info.identity_digest,
 +                                     router->nickname,
 +                                     router_is_named(router));
  }
  
  /** Return true if <b>router</b>'s nickname matches <b>nickname</b>
@@@ -2266,43 -2245,20 +2265,43 @@@
   * matches a hexadecimal value stored in <b>nickname</b>.  Return
   * false otherwise. */
  static int
 -router_nickname_matches(routerinfo_t *router, const char *nickname)
 +router_nickname_matches(const routerinfo_t *router, const char *nickname)
  {
    if (nickname[0]!='$' && !strcasecmp(router->nickname, nickname))
      return 1;
    return router_hex_digest_matches(router, nickname);
  }
  
 +/** Return true if <b>node</b>'s nickname matches <b>nickname</b>
 + * (case-insensitive), or if <b>node's</b> identity key digest
 + * matches a hexadecimal value stored in <b>nickname</b>.  Return
 + * false otherwise. */
 +static int
 +node_nickname_matches(const node_t *node, const char *nickname)
 +{
 +  const char *n = node_get_nickname(node);
 +  if (n && nickname[0]!='$' && !strcasecmp(n, nickname))
 +    return 1;
 +  return hex_digest_nickname_matches(nickname,
 +                                     node->identity,
 +                                     n,
 +                                     node_is_named(node));
 +}
 +
  /** Return the router in our routerlist whose (case-insensitive)
   * nickname or (case-sensitive) hexadecimal key digest is
   * <b>nickname</b>.  Return NULL if no such router is known.
   */
 -routerinfo_t *
 +const routerinfo_t *
  router_get_by_nickname(const char *nickname, int warn_if_unnamed)
  {
 +#if 1
 +  const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
 +  if (node)
 +    return node->ri;
 +  else
 +    return NULL;
 +#else
    int maybedigest;
    char digest[DIGEST_LEN];
    routerinfo_t *best_match=NULL;
@@@ -2348,14 -2304,15 +2347,14 @@@
      if (warn_if_unnamed && n_matches > 1) {
        smartlist_t *fps = smartlist_create();
        int any_unwarned = 0;
 -      SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
 -        {
 +      SMARTLIST_FOREACH_BEGIN(routerlist->routers, routerinfo_t *, router) {
            routerstatus_t *rs;
            char *desc;
            size_t dlen;
            char fp[HEX_DIGEST_LEN+1];
            if (strcasecmp(router->nickname, nickname))
              continue;
 -          rs = router_get_consensus_status_by_id(
 +          rs = router_get_mutable_consensus_status_by_id(
                                            router->cache_info.identity_digest);
            if (rs && !rs->name_lookup_warned) {
              rs->name_lookup_warned = 1;
@@@ -2368,7 -2325,7 +2367,7 @@@
            tor_snprintf(desc, dlen, "\"$%s\" for the one at %s:%d",
                         fp, router->address, router->or_port);
            smartlist_add(fps, desc);
 -        });
 +      } SMARTLIST_FOREACH_END(router);
        if (any_unwarned) {
          char *alternatives = smartlist_join_strings(fps, "; ",0,NULL);
          log_warn(LD_CONFIG,
@@@ -2381,7 -2338,7 +2380,7 @@@
        SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp));
        smartlist_free(fps);
      } else if (warn_if_unnamed) {
 -      routerstatus_t *rs = router_get_consensus_status_by_id(
 +      routerstatus_t *rs = router_get_mutable_consensus_status_by_id(
            best_match->cache_info.identity_digest);
        if (rs && !rs->name_lookup_warned) {
          char fp[HEX_DIGEST_LEN+1];
@@@ -2397,8 -2354,8 +2396,8 @@@
      }
      return best_match;
    }
 -
    return NULL;
 +#endif
  }
  
  /** Try to find a routerinfo for <b>digest</b>. If we don't have one,
@@@ -2407,7 -2364,7 +2406,7 @@@
  int
  router_digest_version_as_new_as(const char *digest, const char *cutoff)
  {
 -  routerinfo_t *router = router_get_by_digest(digest);
 +  const routerinfo_t *router = router_get_by_id_digest(digest);
    if (!router)
      return 1;
    return tor_version_as_new_as(router->platform, cutoff);
@@@ -2461,20 -2418,44 +2460,20 @@@ hexdigest_to_digest(const char *hexdige
  
  /** Return the router in our routerlist whose hexadecimal key digest
   * is <b>hexdigest</b>.  Return NULL if no such router is known. */
 -routerinfo_t *
 +const routerinfo_t *
  router_get_by_hexdigest(const char *hexdigest)
  {
 -  char digest[DIGEST_LEN];
 -  size_t len;
 -  routerinfo_t *ri;
 -
 -  tor_assert(hexdigest);
 -  if (!routerlist)
 -    return NULL;
 -  if (hexdigest[0]=='$')
 -    ++hexdigest;
 -  len = strlen(hexdigest);
 -  if (hexdigest_to_digest(hexdigest, digest) < 0)
 +  if (is_legal_nickname(hexdigest))
      return NULL;
  
 -  ri = router_get_by_digest(digest);
 -
 -  if (ri && len > HEX_DIGEST_LEN) {
 -    if (hexdigest[HEX_DIGEST_LEN] == '=') {
 -      if (strcasecmp(ri->nickname, hexdigest+HEX_DIGEST_LEN+1) ||
 -          !ri->is_named)
 -        return NULL;
 -    } else if (hexdigest[HEX_DIGEST_LEN] == '~') {
 -      if (strcasecmp(ri->nickname, hexdigest+HEX_DIGEST_LEN+1))
 -        return NULL;
 -    } else {
 -      return NULL;
 -    }
 -  }
 -
 -  return ri;
 +  /* It's not a legal nickname, so it must be a hexdigest or nothing. */
 +  return router_get_by_nickname(hexdigest, 1);
  }
  
 -/** Return the router in our routerlist whose 20-byte key digest
 - * is <b>digest</b>.  Return NULL if no such router is known. */
 +/** As router_get_by_id_digest,but return a pointer that you're allowed to
 + * modify */
  routerinfo_t *
 -router_get_by_digest(const char *digest)
 +router_get_mutable_by_digest(const char *digest)
  {
    tor_assert(digest);
  
@@@ -2485,14 -2466,6 +2484,14 @@@
    return rimap_get(routerlist->identity_map, digest);
  }
  
 +/** Return the router in our routerlist whose 20-byte key digest
 + * is <b>digest</b>.  Return NULL if no such router is known. */
 +const routerinfo_t *
 +router_get_by_id_digest(const char *digest)
 +{
 +  return router_get_mutable_by_digest(digest);
 +}
 +
  /** Return the router in our routerlist whose 20-byte descriptor
   * is <b>digest</b>.  Return NULL if no such router is known. */
  signed_descriptor_t *
@@@ -2543,7 -2516,7 +2542,7 @@@ extrainfo_get_by_descriptor_digest(cons
   * The caller must not free the string returned.
   */
  static const char *
 -signed_descriptor_get_body_impl(signed_descriptor_t *desc,
 +signed_descriptor_get_body_impl(const signed_descriptor_t *desc,
                                  int with_annotations)
  {
    const char *r = NULL;
@@@ -2592,7 -2565,7 +2591,7 @@@
   * The caller must not free the string returned.
   */
  const char *
 -signed_descriptor_get_body(signed_descriptor_t *desc)
 +signed_descriptor_get_body(const signed_descriptor_t *desc)
  {
    return signed_descriptor_get_body_impl(desc, 0);
  }
@@@ -2600,7 -2573,7 +2599,7 @@@
  /** As signed_descriptor_get_body(), but points to the beginning of the
   * annotations section rather than the beginning of the descriptor. */
  const char *
 -signed_descriptor_get_annotations(signed_descriptor_t *desc)
 +signed_descriptor_get_annotations(const signed_descriptor_t *desc)
  {
    return signed_descriptor_get_body_impl(desc, 1);
  }
@@@ -2653,6 -2626,7 +2652,6 @@@ routerinfo_free(routerinfo_t *router
    }
    addr_policy_list_free(router->exit_policy);
  
 -  /* XXXX Remove if this turns out to affect performance. */
    memset(router, 77, sizeof(routerinfo_t));
  
    tor_free(router);
@@@ -2667,6 -2641,7 +2666,6 @@@ extrainfo_free(extrainfo_t *extrainfo
    tor_free(extrainfo->cache_info.signed_descriptor_body);
    tor_free(extrainfo->pending_sig);
  
 -  /* XXXX remove this if it turns out to slow us down. */
    memset(extrainfo, 88, sizeof(extrainfo_t)); /* debug bad memory usage */
    tor_free(extrainfo);
  }
@@@ -2680,6 -2655,7 +2679,6 @@@ signed_descriptor_free(signed_descripto
  
    tor_free(sd->signed_descriptor_body);
  
 -  /* XXXX remove this once more bugs go away. */
    memset(sd, 99, sizeof(signed_descriptor_t)); /* Debug bad mem usage */
    tor_free(sd);
  }
@@@ -2785,7 -2761,8 +2784,7 @@@ routerlist_insert(routerlist_t *rl, rou
    routerinfo_t *ri_old;
    signed_descriptor_t *sd_old;
    {
 -    /* XXXX Remove if this slows us down. */
 -    routerinfo_t *ri_generated = router_get_my_routerinfo();
 +    const routerinfo_t *ri_generated = router_get_my_routerinfo();
      tor_assert(ri_generated != ri);
    }
    tor_assert(ri->cache_info.routerlist_index == -1);
@@@ -2807,7 -2784,6 +2806,7 @@@
                &ri->cache_info);
    smartlist_add(rl->routers, ri);
    ri->cache_info.routerlist_index = smartlist_len(rl->routers) - 1;
 +  nodelist_add_routerinfo(ri);
    router_dir_info_changed();
  #ifdef DEBUG_ROUTERLIST
    routerlist_assert_ok(rl);
@@@ -2828,6 -2804,7 +2827,6 @@@ extrainfo_insert(routerlist_t *rl, extr
    extrainfo_t *ei_tmp;
  
    {
 -    /* XXXX remove this code if it slows us down. */
      extrainfo_t *ei_generated = router_get_my_extrainfo();
      tor_assert(ei_generated != ei);
    }
@@@ -2873,7 -2850,8 +2872,7 @@@ static voi
  routerlist_insert_old(routerlist_t *rl, routerinfo_t *ri)
  {
    {
 -    /* XXXX remove this code if it slows us down. */
 -    routerinfo_t *ri_generated = router_get_my_routerinfo();
 +    const routerinfo_t *ri_generated = router_get_my_routerinfo();
      tor_assert(ri_generated != ri);
    }
    tor_assert(ri->cache_info.routerlist_index == -1);
@@@ -2913,8 -2891,6 +2912,8 @@@ routerlist_remove(routerlist_t *rl, rou
    tor_assert(0 <= idx && idx < smartlist_len(rl->routers));
    tor_assert(smartlist_get(rl->routers, idx) == ri);
  
 +  nodelist_remove_routerinfo(ri);
 +
    /* make sure the rephist module knows that it's not running */
    rep_hist_note_router_unreachable(ri->cache_info.identity_digest, now);
  
@@@ -3026,7 -3002,8 +3025,7 @@@ routerlist_replace(routerlist_t *rl, ro
    routerinfo_t *ri_tmp;
    extrainfo_t *ei_tmp;
    {
 -    /* XXXX Remove this if it turns out to slow us down. */
 -    routerinfo_t *ri_generated = router_get_my_routerinfo();
 +    const routerinfo_t *ri_generated = router_get_my_routerinfo();
      tor_assert(ri_generated != ri_new);
    }
    tor_assert(ri_old != ri_new);
@@@ -3036,9 -3013,6 +3035,9 @@@
    tor_assert(0 <= idx && idx < smartlist_len(rl->routers));
    tor_assert(smartlist_get(rl->routers, idx) == ri_old);
  
 +  nodelist_remove_routerinfo(ri_old);
 +  nodelist_add_routerinfo(ri_new);
 +
    router_dir_info_changed();
    if (idx >= 0) {
      smartlist_set(rl->routers, idx, ri_new);
@@@ -3185,27 -3159,28 +3184,27 @@@ routerlist_reset_warnings(void
  void
  router_set_status(const char *digest, int up)
  {
 -  routerinfo_t *router;
 -  routerstatus_t *status;
 +  node_t *node;
    tor_assert(digest);
  
    SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, d,
                      if (!memcmp(d->digest, digest, DIGEST_LEN))
                        d->is_running = up);
  
 -  router = router_get_by_digest(digest);
 -  if (router) {
 -    log_debug(LD_DIR,"Marking router '%s/%s' as %s.",
 -              router->nickname, router->address, up ? "up" : "down");
 -    if (!up && router_is_me(router) && !we_are_hibernating())
 +  node = node_get_mutable_by_id(digest);
 +  if (node) {
 +#if 0
 +    char buf[MAX_VERBOSE_NICKNAME_LEN+1];
 +    node_get_verbose_nickname(node,buf);
 +    log_debug(LD_DIR,"Marking router %s as %s.",
 +              buf, up ? "up" : "down");
 +#endif
 +    if (!up && node_is_me(node) && !we_are_hibernating())
        log_warn(LD_NET, "We just marked ourself as down. Are your external "
                 "addresses reachable?");
 -    router->is_running = up;
 -  }
 -  status = router_get_consensus_status_by_id(digest);
 -  if (status && status->is_running != up) {
 -    status->is_running = up;
 -    control_event_networkstatus_changed_single(status);
 +    node->is_running = up;
    }
 +
    router_dir_info_changed();
  }
  
@@@ -3248,7 -3223,7 +3247,7 @@@ router_add_to_routerlist(routerinfo_t *
  
    id_digest = router->cache_info.identity_digest;
  
 -  old_router = router_get_by_digest(id_digest);
 +  old_router = router_get_mutable_by_digest(id_digest);
  
    /* Make sure that we haven't already got this exact descriptor. */
    if (sdmap_get(routerlist->desc_digest_map,
@@@ -3279,12 -3254,12 +3278,12 @@@
  
    if (authdir) {
      if (authdir_wants_to_reject_router(router, msg,
 -                                       !from_cache && !from_fetch)) {
 +                                       !from_cache && !from_fetch,
 +                                       &authdir_believes_valid)) {
        tor_assert(*msg);
        routerinfo_free(router);
        return ROUTER_AUTHDIR_REJECTS;
      }
 -    authdir_believes_valid = router->is_valid;
    } else if (from_fetch) {
      /* Only check the descriptor digest against the network statuses when
       * we are receiving in response to a fetch. */
@@@ -3311,15 -3286,14 +3310,15 @@@
    SMARTLIST_FOREACH(networkstatus_v2_list, networkstatus_v2_t *, ns,
    {
      routerstatus_t *rs =
 -      networkstatus_v2_find_entry(ns, id_digest);
 +      networkstatus_v2_find_mutable_entry(ns, id_digest);
      if (rs && !memcmp(rs->descriptor_digest,
                        router->cache_info.signed_descriptor_digest,
                        DIGEST_LEN))
        rs->need_to_mirror = 0;
    });
    if (consensus) {
 -    routerstatus_t *rs = networkstatus_vote_find_entry(consensus, id_digest);
 +    routerstatus_t *rs = networkstatus_vote_find_mutable_entry(
 +                                                     consensus, id_digest);
      if (rs && !memcmp(rs->descriptor_digest,
                        router->cache_info.signed_descriptor_digest,
                        DIGEST_LEN)) {
@@@ -3919,7 -3893,7 +3918,7 @@@ router_load_extrainfo_from_string(cons
  static int
  signed_desc_digest_is_recognized(signed_descriptor_t *desc)
  {
 -  routerstatus_t *rs;
 +  const routerstatus_t *rs;
    networkstatus_t *consensus = networkstatus_get_latest_consensus();
    int caches = directory_caches_dir_info(get_options());
    const smartlist_t *networkstatus_v2_list = networkstatus_get_v2_list();
@@@ -3952,34 -3926,33 +3951,34 @@@ routerlist_retry_directory_downloads(ti
    router_reset_descriptor_download_failures();
    update_networkstatus_downloads(now);
    update_router_descriptor_downloads(now);
 +  update_microdesc_downloads(now);
  }
  
 -/** Return 1 if all running sufficiently-stable routers will reject
 +/** Return 1 if all running sufficiently-stable routers we can use will reject
   * addr:port, return 0 if any might accept it. */
  int
 -router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port,
 -                                          int need_uptime)
 -{
 +router_exit_policy_all_nodes_reject(uint32_t addr, uint16_t port,
 +                                    int need_uptime)
 +{ /* XXXX MOVE */
    addr_policy_result_t r;
 -  if (!routerlist) return 1;
  
 -  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
 -  {
 -    if (router->is_running &&
 -        !router_is_unreliable(router, need_uptime, 0, 0)) {
 -      r = compare_addr_to_addr_policy(addr, port, router->exit_policy);
 +  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
 +    if (node->is_running &&
 +        !node_is_unreliable(node, need_uptime, 0, 0)) {
 +
 +      r = compare_addr_to_node_policy(addr, port, node);
 +
        if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED)
          return 0; /* this one could be ok. good enough. */
      }
 -  });
 +  } SMARTLIST_FOREACH_END(node);
    return 1; /* all will reject. */
  }
  
  /** Return true iff <b>router</b> does not permit exit streams.
   */
  int
 -router_exit_policy_rejects_all(routerinfo_t *router)
 +router_exit_policy_rejects_all(const routerinfo_t *router)
  {
    return router->policy_is_reject_star;
  }
@@@ -4114,9 -4087,7 +4113,9 @@@ any_trusted_dir_is_v1_authority(void
  /** For every current directory connection whose purpose is <b>purpose</b>,
   * and where the resource being downloaded begins with <b>prefix</b>, split
   * rest of the resource into base16 fingerprints, decode them, and set the
 - * corresponding elements of <b>result</b> to a nonzero value. */
 + * corresponding elements of <b>result</b> to a nonzero value.
 + * DOCDOC purpose==microdesc
 + */
  static void
  list_pending_downloads(digestmap_t *result,
                         int purpose, const char *prefix)
@@@ -4124,23 -4095,20 +4123,23 @@@
    const size_t p_len = strlen(prefix);
    smartlist_t *tmp = smartlist_create();
    smartlist_t *conns = get_connection_array();
 +  int flags = DSR_HEX;
 +  if (purpose == DIR_PURPOSE_FETCH_MICRODESC)
 +    flags = DSR_DIGEST256|DSR_BASE64;
  
    tor_assert(result);
  
 -  SMARTLIST_FOREACH(conns, connection_t *, conn,
 -  {
 +  SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
      if (conn->type == CONN_TYPE_DIR &&
          conn->purpose == purpose &&
          !conn->marked_for_close) {
        const char *resource = TO_DIR_CONN(conn)->requested_resource;
        if (!strcmpstart(resource, prefix))
          dir_split_resource_into_fingerprints(resource + p_len,
 -                                             tmp, NULL, DSR_HEX);
 +                                             tmp, NULL, flags);
      }
 -  });
 +  } SMARTLIST_FOREACH_END(conn);
 +
    SMARTLIST_FOREACH(tmp, char *, d,
                      {
                        digestmap_set(result, d, (void*)1);
@@@ -4160,21 -4128,13 +4159,21 @@@ list_pending_descriptor_downloads(diges
    list_pending_downloads(result, purpose, "d/");
  }
  
 -/** Launch downloads for all the descriptors whose digests are listed
 - * as digests[i] for lo <= i < hi.  (Lo and hi may be out of range.)
 - * If <b>source</b> is given, download from <b>source</b>; otherwise,
 - * download from an appropriate random directory server.
 +/** DOCDOC */
 +/*XXXX NM should use digest256, if one comes into being. */
 +void
 +list_pending_microdesc_downloads(digestmap_t *result)
 +{
 +  list_pending_downloads(result, DIR_PURPOSE_FETCH_MICRODESC, "d/");
 +}
 +
 +/** Launch downloads for all the descriptors whose digests or digests256
 + * are listed as digests[i] for lo <= i < hi.  (Lo and hi may be out of
 + * range.)  If <b>source</b> is given, download from <b>source</b>;
 + * otherwise, download from an appropriate random directory server.
   */
  static void
 -initiate_descriptor_downloads(routerstatus_t *source,
 +initiate_descriptor_downloads(const routerstatus_t *source,
                                int purpose,
                                smartlist_t *digests,
                                int lo, int hi, int pds_flags)
@@@ -4182,20 -4142,6 +4181,20 @@@
    int i, n = hi-lo;
    char *resource, *cp;
    size_t r_len;
 +
 +  int digest_len = DIGEST_LEN, enc_digest_len = HEX_DIGEST_LEN;
 +  char sep = '+';
 +  int b64_256 = 0;
 +
 +  if (purpose == DIR_PURPOSE_FETCH_MICRODESC) {
 +    /* Microdescriptors are downloaded by "-"-separated base64-encoded
 +     * 256-bit digests. */
 +    digest_len = DIGEST256_LEN;
 +    enc_digest_len = BASE64_DIGEST256_LEN;
 +    sep = '-';
 +    b64_256 = 1;
 +  }
 +
    if (n <= 0)
      return;
    if (lo < 0)
@@@ -4203,19 -4149,15 +4202,19 @@@
    if (hi > smartlist_len(digests))
      hi = smartlist_len(digests);
  
 -  r_len = 8 + (HEX_DIGEST_LEN+1)*n;
 +  r_len = 8 + (enc_digest_len+1)*n;
    cp = resource = tor_malloc(r_len);
    memcpy(cp, "d/", 2);
    cp += 2;
    for (i = lo; i < hi; ++i) {
 -    base16_encode(cp, r_len-(cp-resource),
 -                  smartlist_get(digests,i), DIGEST_LEN);
 -    cp += HEX_DIGEST_LEN;
 -    *cp++ = '+';
 +    if (b64_256) {
 +      digest256_to_base64(cp, smartlist_get(digests, i));
 +    } else {
 +      base16_encode(cp, r_len-(cp-resource),
 +                    smartlist_get(digests,i), digest_len);
 +    }
 +    cp += enc_digest_len;
 +    *cp++ = sep;
    }
    memcpy(cp-1, ".z", 3);
  
@@@ -4238,7 -4180,7 +4237,7 @@@
  static INLINE int
  client_would_use_router(routerstatus_t *rs, time_t now, or_options_t *options)
  {
 -  if (!rs->is_running && !options->FetchUselessDescriptors) {
 +  if (!rs->is_flagged_running && !options->FetchUselessDescriptors) {
      /* If we had this router descriptor, we wouldn't even bother using it.
       * But, if we want to have a complete list, fetch it anyway. */
      return 0;
@@@ -4262,7 -4204,6 +4261,7 @@@
   *   So use 96 because it's a nice number.
   */
  #define MAX_DL_PER_REQUEST 96
 +#define MAX_MICRODESC_DL_PER_REQUEST 92
  /** Don't split our requests so finely that we are requesting fewer than
   * this number per server. */
  #define MIN_DL_PER_REQUEST 4
@@@ -4277,33 -4218,21 +4276,33 @@@
   * them until they have more, or until this amount of time has passed. */
  #define MAX_CLIENT_INTERVAL_WITHOUT_REQUEST (10*60)
  
 -/** Given a list of router descriptor digests in <b>downloadable</b>, decide
 - * whether to delay fetching until we have more.  If we don't want to delay,
 - * launch one or more requests to the appropriate directory authorities. */
 -static void
 -launch_router_descriptor_downloads(smartlist_t *downloadable,
 -                                   routerstatus_t *source, time_t now)
 +/** Given a <b>purpose</b> (FETCH_MICRODESC or FETCH_SERVERDESC) and a list of
 + * router descriptor digests or microdescriptor digest256s in
 + * <b>downloadable</b>, decide whether to delay fetching until we have more.
 + * If we don't want to delay, launch one or more requests to the appropriate
 + * directory authorities.
 + */
 +void
 +launch_descriptor_downloads(int purpose,
 +                            smartlist_t *downloadable,
 +                            const routerstatus_t *source, time_t now)
  {
    int should_delay = 0, n_downloadable;
    or_options_t *options = get_options();
 +  const char *descname;
 +
 +  tor_assert(purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
 +             purpose == DIR_PURPOSE_FETCH_MICRODESC);
 +
 +  descname = (purpose == DIR_PURPOSE_FETCH_SERVERDESC) ?
 +    "routerdesc" : "microdesc";
  
    n_downloadable = smartlist_len(downloadable);
    if (!directory_fetches_dir_info_early(options)) {
      if (n_downloadable >= MAX_DL_TO_DELAY) {
        log_debug(LD_DIR,
 -             "There are enough downloadable routerdescs to launch requests.");
 +                "There are enough downloadable %ss to launch requests.",
 +                descname);
        should_delay = 0;
      } else {
        should_delay = (last_routerdesc_download_attempted +
@@@ -4311,15 -4240,13 +4310,15 @@@
        if (!should_delay && n_downloadable) {
          if (last_routerdesc_download_attempted) {
            log_info(LD_DIR,
 -                   "There are not many downloadable routerdescs, but we've "
 +                   "There are not many downloadable %ss, but we've "
                     "been waiting long enough (%d seconds). Downloading.",
 +                   descname,
                     (int)(now-last_routerdesc_download_attempted));
          } else {
            log_info(LD_DIR,
 -                 "There are not many downloadable routerdescs, but we haven't "
 -                 "tried downloading descriptors recently. Downloading.");
 +                   "There are not many downloadable %ss, but we haven't "
 +                   "tried downloading descriptors recently. Downloading.",
 +                   descname);
          }
        }
      }
@@@ -4346,19 -4273,12 +4345,19 @@@
         * update_router_descriptor_downloads() later on, once the connections
         * have succeeded or failed.
         */
 -      pds_flags |= PDS_NO_EXISTING_SERVERDESC_FETCH;
 +      pds_flags |= (purpose == DIR_PURPOSE_FETCH_MICRODESC) ?
 +        PDS_NO_EXISTING_MICRODESC_FETCH :
 +        PDS_NO_EXISTING_SERVERDESC_FETCH;
      }
  
      n_per_request = CEIL_DIV(n_downloadable, MIN_REQUESTS);
 -    if (n_per_request > MAX_DL_PER_REQUEST)
 -      n_per_request = MAX_DL_PER_REQUEST;
 +    if (purpose == DIR_PURPOSE_FETCH_MICRODESC) {
 +      if (n_per_request > MAX_MICRODESC_DL_PER_REQUEST)
 +        n_per_request = MAX_MICRODESC_DL_PER_REQUEST;
 +    } else {
 +      if (n_per_request > MAX_DL_PER_REQUEST)
 +        n_per_request = MAX_DL_PER_REQUEST;
 +    }
      if (n_per_request < MIN_DL_PER_REQUEST)
        n_per_request = MIN_DL_PER_REQUEST;
  
@@@ -4373,7 -4293,7 +4372,7 @@@
               req_plural, n_downloadable, rtr_plural, n_per_request);
      smartlist_sort_digests(downloadable);
      for (i=0; i < n_downloadable; i += n_per_request) {
 -      initiate_descriptor_downloads(source, DIR_PURPOSE_FETCH_SERVERDESC,
 +      initiate_descriptor_downloads(source, purpose,
                                      downloadable, i, i+n_per_request,
                                      pds_flags);
      }
@@@ -4563,14 -4483,15 +4562,14 @@@ update_consensus_router_descriptor_down
  
    map = digestmap_new();
    list_pending_descriptor_downloads(map, 0);
 -  SMARTLIST_FOREACH(consensus->routerstatus_list, void *, rsp,
 -    {
 +  SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, void *, rsp) {
        routerstatus_t *rs =
          is_vote ? &(((vote_routerstatus_t *)rsp)->status) : rsp;
        signed_descriptor_t *sd;
        if ((sd = router_get_by_descriptor_digest(rs->descriptor_digest))) {
 -        routerinfo_t *ri;
 +        const routerinfo_t *ri;
          ++n_have;
 -        if (!(ri = router_get_by_digest(rs->identity_digest)) ||
 +        if (!(ri = router_get_by_id_digest(rs->identity_digest)) ||
              memcmp(ri->cache_info.signed_descriptor_digest,
                     sd->signed_descriptor_digest, DIGEST_LEN)) {
            /* We have a descriptor with this digest, but either there is no
@@@ -4603,8 -4524,7 +4602,8 @@@
        if (is_vote && source) {
          char time_bufnew[ISO_TIME_LEN+1];
          char time_bufold[ISO_TIME_LEN+1];
 -        routerinfo_t *oldrouter = router_get_by_digest(rs->identity_digest);
 +        const routerinfo_t *oldrouter;
 +        oldrouter = router_get_by_id_digest(rs->identity_digest);
          format_iso_time(time_bufnew, rs->published_on);
          if (oldrouter)
            format_iso_time(time_bufold, oldrouter->cache_info.published_on);
@@@ -4614,7 -4534,7 +4613,7 @@@
                   source->nickname, oldrouter ? "known" : "unknown");
        }
        smartlist_add(downloadable, rs->descriptor_digest);
 -    });
 +  } SMARTLIST_FOREACH_END(rsp);
  
    if (!authdir_mode_handles_descs(options, ROUTER_PURPOSE_GENERAL)
        && smartlist_len(no_longer_old)) {
@@@ -4646,8 -4566,7 +4645,8 @@@
             smartlist_len(downloadable), n_delayed, n_have, n_in_oldrouters,
             n_would_reject, n_wouldnt_use, n_inprogress);
  
 -  launch_router_descriptor_downloads(downloadable, source, now);
 +  launch_descriptor_downloads(DIR_PURPOSE_FETCH_SERVERDESC,
 +                              downloadable, source, now);
  
    digestmap_free(map, NULL);
   done:
@@@ -4672,13 -4591,10 +4671,13 @@@ update_router_descriptor_downloads(time
    if (directory_fetches_dir_info_early(options)) {
      update_router_descriptor_cache_downloads_v2(now);
    }
 +
    update_consensus_router_descriptor_downloads(now, 0,
 -    networkstatus_get_reasonably_live_consensus(now));
 +                  networkstatus_get_reasonably_live_consensus(now, FLAV_NS));
  
    /* XXXX021 we could be smarter here; see notes on bug 652. */
 +  /* XXXX NM Microdescs: if we're not fetching microdescriptors, we need
 +   * to make something else invoke this. */
    /* If we're a server that doesn't have a configured address, we rely on
     * directory fetches to learn when our address changes.  So if we haven't
     * tried to get any routerdescs in a long time, try a dummy fetch now. */
@@@ -4725,7 -4641,7 +4724,7 @@@ update_extrainfo_downloads(time_t now
          sd = &((routerinfo_t*)smartlist_get(lst, i))->cache_info;
        if (sd->is_extrainfo)
          continue; /* This should never happen. */
 -      if (old_routers && !router_get_by_digest(sd->identity_digest))
 +      if (old_routers && !router_get_by_id_digest(sd->identity_digest))
          continue; /* Couldn't check the signature if we got it. */
        if (sd->extrainfo_is_bogus)
          continue;
@@@ -4826,7 -4742,7 +4825,7 @@@ count_usable_descriptors(int *num_prese
  
    SMARTLIST_FOREACH(consensus->routerstatus_list, routerstatus_t *, rs,
       {
 -       if (in_set && ! routerset_contains_routerstatus(in_set, rs))
 +       if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
           continue;
         if (client_would_use_router(rs, now, options)) {
           ++*num_usable; /* the consensus says we want it. */
@@@ -4849,7 -4765,7 +4848,7 @@@ count_loading_descriptors_progress(void
    int num_present = 0, num_usable=0;
    time_t now = time(NULL);
    const networkstatus_t *consensus =
 -    networkstatus_get_reasonably_live_consensus(now);
 +    networkstatus_get_reasonably_live_consensus(now, FLAV_NS);
    double fraction;
  
    if (!consensus)
@@@ -4879,7 -4795,7 +4878,7 @@@ update_router_have_minimum_dir_info(voi
    int res;
    or_options_t *options = get_options();
    const networkstatus_t *consensus =
 -    networkstatus_get_reasonably_live_consensus(now);
 +    networkstatus_get_reasonably_live_consensus(now, FLAV_NS);
  
    if (!consensus) {
      if (!networkstatus_get_latest_consensus())
@@@ -4989,7 -4905,7 +4988,7 @@@ router_reset_descriptor_download_failur
   * would not cause a recent (post 0.1.1.6) dirserver to republish.
   */
  int
 -router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2)
 +router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2)
  {
    time_t r1pub, r2pub;
    long time_difference;
@@@ -4997,7 -4913,7 +4996,7 @@@
  
    /* r1 should be the one that was published first. */
    if (r1->cache_info.published_on > r2->cache_info.published_on) {
 -    routerinfo_t *ri_tmp = r2;
 +    const routerinfo_t *ri_tmp = r2;
      r2 = r1;
      r1 = ri_tmp;
    }
@@@ -5016,6 -4932,7 +5015,6 @@@
        (r1->contact_info && r2->contact_info &&
         strcasecmp(r1->contact_info, r2->contact_info)) ||
        r1->is_hibernating != r2->is_hibernating ||
 -      r1->has_old_dnsworkers != r2->has_old_dnsworkers ||
        cmp_addr_policies(r1->exit_policy, r2->exit_policy))
      return 0;
    if ((r1->declared_family == NULL) != (r2->declared_family == NULL))
@@@ -5070,8 -4987,7 +5069,8 @@@
   * incompatibility (if any).
   **/
  int
 -routerinfo_incompatible_with_extrainfo(routerinfo_t *ri, extrainfo_t *ei,
 +routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
 +                                       extrainfo_t *ei,
                                         signed_descriptor_t *sd,
                                         const char **msg)
  {
@@@ -5079,7 -4995,7 +5078,7 @@@
    tor_assert(ri);
    tor_assert(ei);
    if (!sd)
 -    sd = &ri->cache_info;
 +    sd = (signed_descriptor_t*)&ri->cache_info;
  
    if (ei->bad_sig) {
      if (msg) *msg = "Extrainfo signature was bad, or signed with wrong key.";
@@@ -5143,7 -5059,7 +5142,7 @@@
  /** Assert that the internal representation of <b>rl</b> is
   * self-consistent. */
  void
 -routerlist_assert_ok(routerlist_t *rl)
 +routerlist_assert_ok(const routerlist_t *rl)
  {
    routerinfo_t *r2;
    signed_descriptor_t *sd2;
@@@ -5233,7 -5149,7 +5232,7 @@@
   * If <b>router</b> is NULL, it just frees its internal memory and returns.
   */
  const char *
 -esc_router_info(routerinfo_t *router)
 +esc_router_info(const routerinfo_t *router)
  {
    static char *info=NULL;
    char *esc_contact, *esc_platform;
@@@ -5335,6 -5251,38 +5334,6 @@@ routerset_get_countryname(const char *c
    return country;
  }
  
 -#if 0
 -/** Add the GeoIP database's integer index (+1) of a valid two-character
 - * country code to the routerset's <b>countries</b> bitarray. Return the
 - * integer index if the country code is valid, -1 otherwise.*/
 -static int
 -routerset_add_country(const char *c)
 -{
 -  char country[3];
 -  country_t cc;
 -
 -  /* XXXX: Country codes must be of the form \{[a-z\?]{2}\} but this accepts
 -     \{[.]{2}\}. Do we need to be strict? -RH */
 -  /* Nope; if the country code is bad, we'll get 0 when we look it up. */
 -
 -  if (!geoip_is_loaded()) {
 -    log(LOG_WARN, LD_CONFIG, "GeoIP database not loaded: Cannot add country"
 -                             "entry %s, ignoring.", c);
 -    return -1;
 -  }
 -
 -  memcpy(country, c+1, 2);
 -  country[2] = '\0';
 -  tor_strlower(country);
 -
 -  if ((cc=geoip_get_country(country))==-1) {
 -    log(LOG_WARN, LD_CONFIG, "Country code '%s' is not valid, ignoring.",
 -        country);
 -  }
 -  return cc;
 -}
 -#endif
 -
  /** Update the routerset's <b>countries</b> bitarray_t. Called whenever
   * the GeoIP database is reloaded.
   */
@@@ -5436,7 -5384,7 +5435,7 @@@ refresh_all_country_info(void
    if (options->_ExcludeExitNodesUnion)
      routerset_refresh_countries(options->_ExcludeExitNodesUnion);
  
 -  routerlist_refresh_countries();
 +  nodelist_refresh_countries();
  }
  
  /** Add all members of the set <b>source</b> to <b>target</b>. */
@@@ -5486,10 -5434,11 +5485,10 @@@ routerset_is_empty(const routerset_t *s
  static int
  routerset_contains(const routerset_t *set, const tor_addr_t *addr,
                     uint16_t orport,
 -                   const char *nickname, const char *id_digest, int is_named,
 +                   const char *nickname, const char *id_digest,
                     country_t country)
  {
    if (!set || !set->list) return 0;
 -  (void) is_named; /* not supported */
    if (nickname && strmap_get_lc(set->names, nickname))
      return 4;
    if (id_digest && digestmap_get(set->digests, id_digest))
@@@ -5517,14 -5466,13 +5516,14 @@@ routerset_contains_extendinfo(const rou
                              ei->port,
                              ei->nickname,
                              ei->identity_digest,
                              -1 /*country*/);
  }
  
 -/** Return true iff <b>ri</b> is in <b>set</b>. */
 +/** Return true iff <b>ri</b> is in <b>set</b>.  If country is <b>-1</b>, we
 + * look up the country. */
  int
 -routerset_contains_router(const routerset_t *set, routerinfo_t *ri)
 +routerset_contains_router(const routerset_t *set, const routerinfo_t *ri,
 +                          country_t country)
  {
    tor_addr_t addr;
    tor_addr_from_ipv4h(&addr, ri->addr);
@@@ -5533,15 -5481,13 +5532,15 @@@
                              ri->or_port,
                              ri->nickname,
                              ri->cache_info.identity_digest,
 -                            ri->is_named,
 -                            ri->country);
 +                            country);
  }
  
 -/** Return true iff <b>rs</b> is in <b>set</b>. */
 +/** Return true iff <b>rs</b> is in <b>set</b>.  If country is <b>-1</b>, we
 + * look up the country. */
  int
 -routerset_contains_routerstatus(const routerset_t *set, routerstatus_t *rs)
 +routerset_contains_routerstatus(const routerset_t *set,
 +                                const routerstatus_t *rs,
 +                                country_t country)
  {
    tor_addr_t addr;
    tor_addr_from_ipv4h(&addr, rs->addr);
@@@ -5550,55 -5496,46 +5549,55 @@@
                              rs->or_port,
                              rs->nickname,
                              rs->identity_digest,
 -                            rs->is_named,
 -                            -1);
 +                            country);
 +}
 +
 +/** Return true iff <b>node</b> is in <b>set</b>. */
 +int
 +routerset_contains_node(const routerset_t *set, const node_t *node)
 +{
 +  if (node->rs)
 +    return routerset_contains_routerstatus(set, node->rs, node->country);
 +  else if (node->ri)
 +    return routerset_contains_router(set, node->ri, node->country);
 +  else
 +    return 0;
  }
  
 -/** Add every known routerinfo_t that is a member of <b>routerset</b> to
 +/** Add every known node_t that is a member of <b>routerset</b> to
   * <b>out</b>.  If <b>running_only</b>, only add the running ones. */
  void
 -routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset,
 -                          int running_only)
 -{
 +routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
 +                        int running_only)
 +{ /* XXXX MOVE */
    tor_assert(out);
    if (!routerset || !routerset->list)
      return;
 -  if (!warned_nicknames)
 -    warned_nicknames = smartlist_create();
 -  if (routerset_is_list(routerset)) {
  
 +  if (routerset_is_list(routerset)) {
      /* No routers are specified by type; all are given by name or digest.
       * we can do a lookup in O(len(list)). */
      SMARTLIST_FOREACH(routerset->list, const char *, name, {
 -        routerinfo_t *router = router_get_by_nickname(name, 1);
 -        if (router) {
 -          if (!running_only || router->is_running)
 -            smartlist_add(out, router);
 +        const node_t *node = node_get_by_nickname(name, 1);
 +        if (node) {
 +          if (!running_only || node->is_running)
 +            smartlist_add(out, (void*)node);
          }
      });
    } else {
      /* We need to iterate over the routerlist to get all the ones of the
       * right kind. */
 -    routerlist_t *rl = router_get_routerlist();
 -    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, router, {
 -        if (running_only && !router->is_running)
 +    smartlist_t *nodes = nodelist_get_list();
 +    SMARTLIST_FOREACH(nodes, const node_t *, node, {
 +        if (running_only && !node->is_running)
            continue;
 -        if (routerset_contains_router(routerset, router))
 -          smartlist_add(out, router);
 +        if (routerset_contains_node(routerset, node))
 +          smartlist_add(out, (void*)node);
      });
    }
  }
  
 -/** Add to <b>target</b> every routerinfo_t from <b>source</b> except:
 +/** Add to <b>target</b> every node_t from <b>source</b> except:
   *
   * 1) Don't add it if <b>include</b> is non-empty and the relay isn't in
   * <b>include</b>; and
@@@ -5607,39 -5544,39 +5606,39 @@@
   * 3) If <b>running_only</b>, don't add non-running routers.
   */
  void
 -routersets_get_disjunction(smartlist_t *target,
 +routersets_get_node_disjunction(smartlist_t *target,
                             const smartlist_t *source,
                             const routerset_t *include,
                             const routerset_t *exclude, int running_only)
  {
 -  SMARTLIST_FOREACH(source, routerinfo_t *, router, {
 +  SMARTLIST_FOREACH(source, const node_t *, node, {
      int include_result;
 -    if (running_only && !router->is_running)
 +    if (running_only && !node->is_running)
        continue;
      if (!routerset_is_empty(include))
 -      include_result = routerset_contains_router(include, router);
 +      include_result = routerset_contains_node(include, node);
      else
        include_result = 1;
  
      if (include_result) {
 -      int exclude_result = routerset_contains_router(exclude, router);
 +      int exclude_result = routerset_contains_node(exclude, node);
        if (include_result >= exclude_result)
 -        smartlist_add(target, router);
 +        smartlist_add(target, (void*)node);
      }
    });
  }
  
 -/** Remove every routerinfo_t from <b>lst</b> that is in <b>routerset</b>. */
 +/** Remove every node_t from <b>lst</b> that is in <b>routerset</b>. */
  void
 -routerset_subtract_routers(smartlist_t *lst, const routerset_t *routerset)
 -{
 +routerset_subtract_nodes(smartlist_t *lst, const routerset_t *routerset)
 +{ /*XXXX MOVE ? */
    tor_assert(lst);
    if (!routerset)
      return;
 -  SMARTLIST_FOREACH(lst, routerinfo_t *, r, {
 -      if (routerset_contains_router(routerset, r)) {
 +  SMARTLIST_FOREACH(lst, const node_t *, node, {
 +      if (routerset_contains_node(routerset, node)) {
          //log_debug(LD_DIR, "Subtracting %s",r->nickname);
 -        SMARTLIST_DEL_CURRENT(lst, r);
 +        SMARTLIST_DEL_CURRENT(lst, node);
        }
      });
  }
@@@ -5700,23 -5637,18 +5699,23 @@@ routerset_free(routerset_t *routerset
  /** Refresh the country code of <b>ri</b>.  This function MUST be called on
   * each router when the GeoIP database is reloaded, and on all new routers. */
  void
 -routerinfo_set_country(routerinfo_t *ri)
 +node_set_country(node_t *node)
  {
 -  ri->country = geoip_get_country_by_ip(ri->addr);
 +  if (node->rs)
 +    node->country = geoip_get_country_by_ip(node->rs->addr);
 +  else if (node->ri)
 +    node->country = geoip_get_country_by_ip(node->ri->addr);
 +  else
 +    node->country = -1;
  }
  
  /** Set the country code of all routers in the routerlist. */
  void
 -routerlist_refresh_countries(void)
 +nodelist_refresh_countries(void) /* MOVE */
  {
 -  routerlist_t *rl = router_get_routerlist();
 -  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
 -                    routerinfo_set_country(ri));
 +  smartlist_t *nodes = nodelist_get_list();
 +  SMARTLIST_FOREACH(nodes, node_t *, node,
 +                    node_set_country(node));
  }
  
  /** Determine the routers that are responsible for <b>id</b> (binary) and
@@@ -5765,9 -5697,9 +5764,9 @@@ hid_serv_get_responsible_directories(sm
  int
  hid_serv_acting_as_directory(void)
  {
 -  routerinfo_t *me = router_get_my_routerinfo();
 +  const routerinfo_t *me = router_get_my_routerinfo();
    networkstatus_t *c;
 -  routerstatus_t *rs;
 +  const routerstatus_t *rs;
    if (!me)
      return 0;
    if (!get_options()->HidServDirectoryV2) {
@@@ -5799,7 -5731,7 +5798,7 @@@
  int
  hid_serv_responsible_for_desc_id(const char *query)
  {
 -  routerinfo_t *me;
 +  const routerinfo_t *me;
    routerstatus_t *last_rs;
    const char *my_id, *last_id;
    int result;
diff --combined src/or/routerparse.c
index fbceda1,08f81d9..2bd370a
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@@ -11,6 -11,7 +11,7 @@@
  
  #include "or.h"
  #include "config.h"
+ #include "circuitbuild.h"
  #include "dirserv.h"
  #include "dirvote.h"
  #include "policies.h"
@@@ -69,6 -70,7 +70,6 @@@ typedef enum 
    K_V,
    K_W,
    K_M,
 -  K_EVENTDNS,
    K_EXTRA_INFO,
    K_EXTRA_INFO_DIGEST,
    K_CACHES_EXTRA_INFO,
@@@ -285,6 -287,7 +286,6 @@@ static token_rule_t routerdesc_token_ta
  
    T01("family",              K_FAMILY,              ARGS,    NO_OBJ ),
    T01("caches-extra-info",   K_CACHES_EXTRA_INFO,   NO_ARGS, NO_OBJ ),
 -  T01("eventdns",            K_EVENTDNS,            ARGS,    NO_OBJ ),
  
    T0N("opt",                 K_OPT,             CONCAT_ARGS, OBJ_OK ),
    T1( "bandwidth",           K_BANDWIDTH,           GE(3),   NO_OBJ ),
@@@ -1354,6 -1357,7 +1355,6 @@@ router_parse_entry_from_string(const ch
    tor_assert(tok->n_args >= 5);
  
    router = tor_malloc_zero(sizeof(routerinfo_t));
 -  router->country = -1;
    router->cache_info.routerlist_index = -1;
    router->cache_info.annotations_len = s-start_of_annotations + prepend_len;
    router->cache_info.signed_descriptor_len = end-s;
@@@ -1494,6 -1498,13 +1495,6 @@@
      router->contact_info = tor_strdup(tok->args[0]);
    }
  
 -  if ((tok = find_opt_by_keyword(tokens, K_EVENTDNS))) {
 -    router->has_old_dnsworkers = tok->n_args && !strcmp(tok->args[0], "0");
 -  } else if (router->platform) {
 -    if (! tor_version_as_new_as(router->platform, "0.1.2.2-alpha"))
 -      router->has_old_dnsworkers = 1;
 -  }
 -
    exit_policy_tokens = find_all_exitpolicy(tokens);
    if (!smartlist_len(exit_policy_tokens)) {
      log_warn(LD_DIR, "No exit policy tokens in descriptor.");
@@@ -1552,6 -1563,8 +1553,6 @@@
                              "router descriptor") < 0)
      goto err;
  
 -  routerinfo_set_country(router);
 -
    if (!router->or_port) {
      log_warn(LD_DIR,"or_port unreadable or 0. Failing.");
      goto err;
@@@ -1941,7 -1954,6 +1942,7 @@@ routerstatus_parse_entry_from_string(me
  
    if (!consensus_method)
      flav = FLAV_NS;
 +  tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC);
  
    eos = find_start_of_next_routerstatus(*s);
  
@@@ -1954,16 -1966,15 +1955,16 @@@
      goto err;
    }
    tok = find_by_keyword(tokens, K_R);
 -  tor_assert(tok->n_args >= 7);
 +  tor_assert(tok->n_args >= 7); /* guaranteed by GE(7) in K_R setup */
    if (flav == FLAV_NS) {
      if (tok->n_args < 8) {
        log_warn(LD_DIR, "Too few arguments to r");
        goto err;
      }
 -  } else {
 -    offset = -1;
 +  } else if (flav == FLAV_MICRODESC) {
 +    offset = -1; /* There is no identity digest */
    }
 +
    if (vote_rs) {
      rs = &vote_rs->status;
    } else {
@@@ -2037,7 -2048,7 +2038,7 @@@
        else if (!strcmp(tok->args[i], "Fast"))
          rs->is_fast = 1;
        else if (!strcmp(tok->args[i], "Running"))
 -        rs->is_running = 1;
 +        rs->is_flagged_running = 1;
        else if (!strcmp(tok->args[i], "Named"))
          rs->is_named = 1;
        else if (!strcmp(tok->args[i], "Valid"))
@@@ -2139,16 -2150,6 +2140,16 @@@
          vote_rs->microdesc = line;
        }
      } SMARTLIST_FOREACH_END(t);
 +  } else if (flav == FLAV_MICRODESC) {
 +    tok = find_opt_by_keyword(tokens, K_M);
 +    if (tok) {
 +      tor_assert(tok->n_args);
 +      if (digest256_from_base64(rs->descriptor_digest, tok->args[0])) {
 +        log_warn(LD_DIR, "Error decoding microdescriptor digest %s",
 +                 escaped(tok->args[0]));
 +        goto err;
 +      }
 +    }
    }
  
    if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
@@@ -2373,7 -2374,7 +2374,7 @@@ networkstatus_verify_bw_weights(network
    const char *casename = NULL;
    int valid = 1;
  
-   weight_scale = networkstatus_get_param(ns, "bwweightscale", BW_WEIGHT_SCALE);
+   weight_scale = circuit_build_times_get_bw_scale(ns);
    Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
    Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
    Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
@@@ -4323,7 -4324,7 +4324,7 @@@ microdescs_parse_from_string(const cha
      }
  
      if ((tok = find_opt_by_keyword(tokens, K_P))) {
 -      md->exitsummary = tor_strdup(tok->args[0]);
 +      md->exit_policy = parse_short_policy(tok->args[0]);
      }
  
      crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);
diff --combined src/test/test_dir.c
index 836de94,1f3beb4..161eda9
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@@ -609,8 -609,11 +609,11 @@@ test_dir_param_voting(void
                           "abcd=20 c=60 cw=500 x-yz=-9 zzzzz=101", NULL, 0, 0);
    smartlist_split_string(vote4.net_params,
                           "ab=900 abcd=200 c=1 cw=51 x-yz=100", NULL, 0, 0);
-   test_eq(100, networkstatus_get_param(&vote4, "x-yz", 50));
-   test_eq(222, networkstatus_get_param(&vote4, "foobar", 222));
+   test_eq(100, networkstatus_get_param(&vote4, "x-yz", 50, 0, 300));
+   test_eq(222, networkstatus_get_param(&vote4, "foobar", 222, 0, 300));
+   test_eq(80, networkstatus_get_param(&vote4, "ab", 12, 0, 80));
+   test_eq(-8, networkstatus_get_param(&vote4, "ab", -12, -100, -8));
+   test_eq(0, networkstatus_get_param(&vote4, "foobar", 0, -100, 8));
  
    smartlist_add(votes, &vote1);
    smartlist_add(votes, &vote2);
@@@ -800,7 -803,7 +803,7 @@@ test_dir_v3_networkstatus(void
    rs->or_port = 443;
    rs->dir_port = 8000;
    /* all flags but running cleared */
 -  rs->is_running = 1;
 +  rs->is_flagged_running = 1;
    smartlist_add(vote->routerstatus_list, vrs);
    test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
  
@@@ -815,7 -818,7 +818,7 @@@
    rs->addr = 0x99009901;
    rs->or_port = 443;
    rs->dir_port = 0;
 -  rs->is_exit = rs->is_stable = rs->is_fast = rs->is_running =
 +  rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running =
      rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
    smartlist_add(vote->routerstatus_list, vrs);
    test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
@@@ -832,8 -835,7 +835,8 @@@
    rs->or_port = 400;
    rs->dir_port = 9999;
    rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
 -    rs->is_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
 +    rs->is_flagged_running = rs->is_valid = rs->is_v2_dir =
 +    rs->is_possible_guard = 1;
    smartlist_add(vote->routerstatus_list, vrs);
    test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
  
@@@ -1073,8 -1075,7 +1076,8 @@@
    test_assert(!rs->is_fast);
    test_assert(!rs->is_possible_guard);
    test_assert(!rs->is_stable);
 -  test_assert(rs->is_running); /* If it wasn't running it wouldn't be here */
 +  /* (If it wasn't running it wouldn't be here) */
 +  test_assert(rs->is_flagged_running);
    test_assert(!rs->is_v2_dir);
    test_assert(!rs->is_valid);
    test_assert(!rs->is_named);
@@@ -1096,7 -1097,7 +1099,7 @@@
    test_assert(rs->is_fast);
    test_assert(rs->is_possible_guard);
    test_assert(rs->is_stable);
 -  test_assert(rs->is_running);
 +  test_assert(rs->is_flagged_running);
    test_assert(rs->is_v2_dir);
    test_assert(rs->is_valid);
    test_assert(!rs->is_named);



More information about the tor-commits mailing list