[tor-commits] [tor/master] Merge remote branch 'origin/maint-0.2.2'

nickm at torproject.org nickm at torproject.org
Mon Mar 14 20:34:11 UTC 2011


commit e91a8c5589be1f562563a73c6e866a3b84afee87
Merge: f1c365b 0588330
Author: Nick Mathewson <nickm at torproject.org>
Date:   Mon Mar 14 16:34:33 2011 -0400

    Merge remote branch 'origin/maint-0.2.2'
    
    Resolved nontrivial conflict around rewrite_x_address_for_bridge and
    learned_bridge_descriptor.  Now, since leanred_bridge_descriptor works
    on nodes, we must make sure that rewrite_node_address_for_bridge also
    works on nodes.
    
    Conflicts:
    	src/or/circuitbuild.c

 changes/bug2510       |    8 +++++++
 changes/bug2511       |    6 +++++
 src/or/circuitbuild.c |   55 +++++++++++++++++++++++++++++++++++++++++++++++-
 src/or/or.h           |    1 +
 src/or/routerlist.c   |   17 ++++++++++++++-
 5 files changed, 84 insertions(+), 3 deletions(-)

diff --combined src/or/circuitbuild.c
index 7ad3b07,a54f232..e2b5f13
--- 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);
@@@ -1514,9 -1513,10 +1514,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)
@@@ -1526,8 -1526,10 +1526,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);
@@@ -1539,9 -1541,9 +1539,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] = '$';
@@@ -1610,28 -1612,31 +1610,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;
      }
@@@ -1901,7 -1906,7 +1901,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,
@@@ -1973,7 -1978,7 +1973,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;
@@@ -1989,7 -1994,7 +1989,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
@@@ -2023,7 -2028,7 +2023,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);
@@@ -2036,7 -2041,7 +2036,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
@@@ -2499,12 -2504,12 +2499,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 &&
@@@ -2512,10 -2517,10 +2512,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,
@@@ -2533,12 -2538,24 +2533,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;
  }
@@@ -2572,12 -2589,12 +2572,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;
  
@@@ -2587,10 -2604,7 +2587,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;
    }
@@@ -2623,17 -2637,18 +2623,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();
  
@@@ -2654,11 -2669,10 +2654,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
@@@ -2666,15 -2680,13 +2666,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
@@@ -2683,19 -2695,18 +2683,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);
@@@ -2703,10 -2714,11 +2703,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]);
@@@ -2714,7 -2726,7 +2714,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. */
@@@ -2731,7 -2743,7 +2731,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,
@@@ -2742,19 -2754,18 +2742,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 {
@@@ -2773,7 -2784,7 +2773,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.",
@@@ -2785,29 -2796,28 +2785,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);
@@@ -2819,9 -2829,9 +2819,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,
@@@ -2841,12 -2851,12 +2841,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)
@@@ -2859,7 -2869,7 +2859,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;
@@@ -2940,12 -2950,13 +2940,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;
@@@ -2956,15 -2967,14 +2956,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;
@@@ -3015,30 -3025,35 +3015,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;
  }
@@@ -3066,31 -3081,31 +3066,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);
      }
    }
  
@@@ -3113,45 -3128,44 +3113,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);
          }
        });
    }
@@@ -3207,18 -3221,14 +3207,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) {
@@@ -3277,7 -3287,7 +3277,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);
@@@ -3286,29 -3296,6 +3286,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)
@@@ -3339,12 -3326,12 +3339,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
@@@ -3366,8 -3353,9 +3366,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];
@@@ -3376,17 -3364,16 +3376,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) {
@@@ -3430,7 -3417,7 +3430,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;
@@@ -3444,11 -3431,11 +3444,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);
  
@@@ -3462,36 -3449,33 +3462,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. */
@@@ -3589,15 -3573,15 +3589,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;
@@@ -3606,15 -3590,14 +3606,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
@@@ -3629,7 -3612,7 +3629,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
@@@ -3783,7 -3766,7 +3783,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;
@@@ -3804,7 -3787,7 +3804,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",
@@@ -3921,7 -3904,7 +3921,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;
@@@ -3962,7 -3945,7 +3962,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);
  
@@@ -3982,22 -3965,22 +3982,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);
@@@ -4006,9 -3989,9 +4006,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);
      }
    });
  
@@@ -4017,8 -4000,8 +4017,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 */
@@@ -4029,7 -4012,7 +4029,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);
@@@ -4067,21 -4050,20 +4067,21 @@@ 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) {
 -    routerlist_add_family(exit_family, chosen_exit);
 +    nodelist_add_node_family(exit_family, chosen_exit);
      consider_exit_family = 1;
    }
  
@@@ -4097,17 -4079,18 +4097,17 @@@
  
   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 (r == chosen_exit)
 +      if (node == chosen_exit)
          continue; /* don't pick the same node for entry and exit */
 -      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 */
@@@ -4120,7 -4103,7 +4120,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
@@@ -4130,7 -4113,7 +4130,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
@@@ -4150,8 -4133,8 +4150,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
@@@ -4161,16 -4144,16 +4161,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. */
@@@ -4184,16 -4167,16 +4184,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.
@@@ -4440,7 -4423,7 +4440,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";
@@@ -4451,9 -4434,9 +4451,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);
@@@ -4530,7 -4513,7 +4530,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);
@@@ -4540,7 -4523,7 +4540,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;
  }
@@@ -4688,6 -4671,29 +4688,55 @@@ fetch_bridge_descriptors(or_options_t *
    SMARTLIST_FOREACH_END(bridge);
  }
  
+ /** If our <b>bridge</b> is configured to be a different address than
 - * the bridge gives in its routerinfo <b>ri</b>, rewrite the routerinfo
++ * the bridge gives in <b>node</b>, rewrite the routerinfo
+  * we received to use the address we meant to use. Now we handle
+  * multihomed bridges better.
+  */
+ static void
 -rewrite_routerinfo_address_for_bridge(bridge_info_t *bridge, routerinfo_t *ri)
++rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
+ {
++  /* XXXX move this function. */
++  /* XXXX overridden addresses should really live in the node_t, so that the
++   *   routerinfo_t and the microdesc_t can be immutable.  But we can only
++   *   do that safely if
++   */
+   tor_addr_t addr;
 -  tor_addr_from_ipv4h(&addr, ri->addr);
+ 
 -  if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
 -      bridge->port == ri->or_port)
 -    return; /* they match, so no need to do anything */
++  if (node->ri) {
++    routerinfo_t *ri = node->ri;
++    tor_addr_from_ipv4h(&addr, ri->addr);
++
++    if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
++        bridge->port == ri->or_port) {
++      /* they match, so no need to do anything */
++    } else {
++      ri->addr = tor_addr_to_ipv4h(&bridge->addr);
++      tor_free(ri->address);
++      ri->address = tor_dup_ip(ri->addr);
++      ri->or_port = bridge->port;
++      log_info(LD_DIR,
++               "Adjusted bridge '%s' to match configured address %s:%d.",
++               ri->nickname, ri->address, ri->or_port);
++    }
++  }
++  if (node->rs) {
++    routerstatus_t *rs = node->rs;
++    tor_addr_from_ipv4h(&addr, rs->addr);
+ 
 -  ri->addr = tor_addr_to_ipv4h(&bridge->addr);
 -  tor_free(ri->address);
 -  ri->address = tor_dup_ip(ri->addr);
 -  ri->or_port = bridge->port;
 -  log_info(LD_DIR, "Adjusted bridge '%s' to match configured address %s:%d.",
 -           ri->nickname, ri->address, ri->or_port);
++    if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
++        bridge->port == rs->or_port) {
++      /* they match, so no need to do anything */
++    } else {
++      rs->addr = tor_addr_to_ipv4h(&bridge->addr);
++      rs->or_port = bridge->port;
++      log_info(LD_DIR,
++               "Adjusted bridge '%s' to match configured address %s:%d.",
++               rs->nickname, fmt_addr(&bridge->addr), rs->or_port);
++    }
++  }
+ }
+ 
  /** We just learned a descriptor for a bridge. See if that
   * digest is in our entry guard list, and add it if not. */
  void
@@@ -4699,17 -4705,16 +4748,19 @@@ 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;
++      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);
  
-       node = node_get_by_id(ri->cache_info.identity_digest);
 -      rewrite_routerinfo_address_for_bridge(bridge, ri);
++      node = node_get_mutable_by_id(ri->cache_info.identity_digest);
 +      tor_assert(node);
++      rewrite_node_address_for_bridge(bridge, node);
 +      add_an_entry_guard(node, 1);
+ 
 -      add_an_entry_guard(ri, 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
@@@ -4764,18 -4769,19 +4815,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
@@@ -4784,15 -4790,15 +4835,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/or.h
index 323c472,910bf8d..553afbd
--- 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. */
@@@ -814,9 -805,6 +814,9 @@@ typedef enum 
   * 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
  
@@@ -862,8 -850,8 +862,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. */
@@@ -980,7 -968,6 +980,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. */
@@@ -991,11 -978,6 +991,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 */
@@@ -1095,16 -1077,10 +1095,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 ? */
  
@@@ -1212,13 -1188,8 +1212,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
@@@ -1309,51 -1280,6 +1309,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,
@@@ -1517,49 -1443,59 +1517,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. */
@@@ -1585,9 -1521,8 +1585,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. */
@@@ -1595,11 -1530,7 +1595,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. */
@@@ -1651,31 -1582,15 +1651,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
@@@ -1717,83 -1632,15 +1717,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? */
@@@ -2269,12 -2116,10 +2269,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
@@@ -2499,7 -2344,6 +2499,7 @@@ typedef struct 
  
    config_line_t *Logs; /**< New-style list of configuration lines
                          * for logs */
 +  int LogTimeGranularity; /**< Log resolution in milliseconds. */
  
    int LogMessageDomains; /**< Boolean: Should we log the domain(s) in which
                            * each log message occurs? */
@@@ -2722,9 -2566,6 +2722,9 @@@
                                 * authorizations for hidden services */
    char *ContactInfo; /**< Contact info to be published in the directory. */
  
 +  int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds
 +                        * have passed. */
 +
    char *HTTPProxy; /**< hostname[:port] to use as http proxy, if any. */
    tor_addr_t HTTPProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */
    uint16_t HTTPProxyPort; /**< Parsed port for http proxy, if any. */
@@@ -2762,8 -2603,7 +2762,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
@@@ -2868,10 -2708,6 +2868,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. */
@@@ -2909,9 -2745,6 +2909,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;
  
@@@ -3008,12 -2841,6 +3008,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;
 +
    /** Set to true if the TestingTorNetwork configuration option is set.
     * This is used so that options_validate() has a chance to realize that
     * the defaults have changed. */
@@@ -3133,6 -2960,8 +3133,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? */
@@@ -3524,7 -3353,7 +3524,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 ***************************/
@@@ -3655,8 -3484,6 +3655,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
@@@ -3674,8 -3501,7 +3674,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() */
@@@ -3687,6 -3513,7 +3687,7 @@@ typedef enum was_router_added_t 
    ROUTER_NOT_IN_CONSENSUS = -3,
    ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS = -4,
    ROUTER_AUTHDIR_REJECTS = -5,
+   ROUTER_WAS_NOT_WANTED = -6
  } was_router_added_t;
  
  /********************************* routerparse.c ************************/
diff --combined src/or/routerlist.c
index 66066f6,4421d5c..1205fd1
--- 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;
@@@ -1723,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 && !status->is_bad_exit;
 -      is_guard = status->is_possible_guard;
 -      is_dir = (status->dir_port != 0);
 -      if (!status->has_bandwidth) {
 +    is_exit = node->is_exit && ! node->is_bad_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. */
@@@ -1739,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 && !router->is_bad_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) {
@@@ -1760,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;
@@@ -1812,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
@@@ -1824,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;
@@@ -1869,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)
@@@ -1916,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) {
@@@ -2060,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.
@@@ -2088,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));
@@@ -2117,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)) {
@@@ -2163,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,
@@@ -2252,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>
@@@ -2265,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;
@@@ -2347,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;
@@@ -2367,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,
@@@ -2380,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];
@@@ -2396,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,
@@@ -2406,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);
@@@ -2460,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);
  
@@@ -2484,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 *
@@@ -2542,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;
@@@ -2591,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);
  }
@@@ -2599,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);
  }
@@@ -2652,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);
@@@ -2666,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);
  }
@@@ -2679,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);
  }
@@@ -2784,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);
@@@ -2806,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);
@@@ -2827,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);
    }
@@@ -2872,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);
@@@ -2912,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);
  
@@@ -3025,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);
@@@ -3035,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);
@@@ -3184,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();
  }
  
@@@ -3233,7 -3209,8 +3233,8 @@@ router_add_to_routerlist(routerinfo_t *
                           int from_cache, int from_fetch)
  {
    const char *id_digest;
-   int authdir = authdir_mode_handles_descs(get_options(), router->purpose);
+   or_options_t *options = get_options();
+   int authdir = authdir_mode_handles_descs(options, router->purpose);
    int authdir_believes_valid = 0;
    routerinfo_t *old_router;
    networkstatus_t *consensus = networkstatus_get_latest_consensus();
@@@ -3247,7 -3224,7 +3248,7 @@@
  
    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,
@@@ -3278,12 -3255,12 +3279,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. */
@@@ -3310,15 -3287,14 +3311,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)) {
@@@ -3339,6 -3315,20 +3340,20 @@@
      return ROUTER_NOT_IN_CONSENSUS;
    }
  
+   /* If we're reading a bridge descriptor from our cache, and we don't
+    * recognize it as one of our currently configured bridges, drop the
+    * descriptor. Otherwise we could end up using it as one of our entry
+    * guards even if it isn't in our Bridge config lines. */
+   if (router->purpose == ROUTER_PURPOSE_BRIDGE && from_cache &&
+       !authdir_mode_bridge(options) &&
+       !routerinfo_is_a_configured_bridge(router)) {
+     log_info(LD_DIR, "Dropping bridge descriptor for '%s' because we have "
+              "no bridge configured at that address.", router->nickname);
+     *msg = "Router descriptor was not a configured bridge.";
+     routerinfo_free(router);
+     return ROUTER_WAS_NOT_WANTED;
+   }
+ 
    /* If we have a router with the same identity key, choose the newer one. */
    if (old_router) {
      if (!in_consensus && (router->cache_info.published_on <=
@@@ -3918,7 -3908,7 +3933,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();
@@@ -3951,34 -3941,33 +3966,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;
  }
@@@ -4113,9 -4102,7 +4128,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)
@@@ -4123,23 -4110,20 +4138,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);
@@@ -4159,21 -4143,13 +4174,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)
@@@ -4181,20 -4157,6 +4196,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)
@@@ -4202,19 -4164,15 +4217,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);
  
@@@ -4237,7 -4195,7 +4252,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;
@@@ -4261,7 -4219,6 +4276,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
@@@ -4276,33 -4233,21 +4291,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 +
@@@ -4310,15 -4255,13 +4325,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);
          }
        }
      }
@@@ -4345,19 -4288,12 +4360,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;
  
@@@ -4372,7 -4308,7 +4387,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);
      }
@@@ -4562,14 -4498,15 +4577,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
@@@ -4602,8 -4539,7 +4617,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);
@@@ -4613,7 -4549,7 +4628,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)) {
@@@ -4645,8 -4581,7 +4660,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:
@@@ -4671,13 -4606,10 +4686,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. */
@@@ -4724,7 -4656,7 +4739,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;
@@@ -4825,7 -4757,7 +4840,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. */
@@@ -4848,7 -4780,7 +4863,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)
@@@ -4878,7 -4810,7 +4893,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())
@@@ -4988,7 -4920,7 +5003,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;
@@@ -4996,7 -4928,7 +5011,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;
    }
@@@ -5015,6 -4947,7 +5030,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))
@@@ -5069,8 -5002,7 +5084,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)
  {
@@@ -5078,7 -5010,7 +5093,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.";
@@@ -5142,7 -5074,7 +5157,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;
@@@ -5232,7 -5164,7 +5247,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;
@@@ -5334,6 -5266,38 +5349,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.
   */
@@@ -5435,7 -5399,7 +5450,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>. */
@@@ -5485,10 -5449,11 +5500,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))
@@@ -5516,14 -5481,13 +5531,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);
@@@ -5532,15 -5496,13 +5547,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);
@@@ -5549,55 -5511,46 +5564,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
@@@ -5606,39 -5559,39 +5621,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);
        }
      });
  }
@@@ -5699,23 -5652,18 +5714,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
@@@ -5764,9 -5712,9 +5779,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) {
@@@ -5798,7 -5746,7 +5813,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;



More information about the tor-commits mailing list