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


Mon Feb 28 22:06:02 UTC 2011


commit 1b8f2ef5505a3ee4d824abf70141e1ad8b48e66c
Merge: b7b01d9 ed87738
Author: Nick Mathewson <nickm at torproject.org>
Date:   Sat Jan 15 12:03:44 2011 -0500

    Merge remote branch 'origin/maint-0.2.2'

 changes/bug2332        |    4 +++
 changes/tolen_asserts  |    8 +++++++
 src/common/crypto.c    |   53 ++++++++++++++++++++++++++++++++++++++----------
 src/common/crypto.h    |   12 ++++++----
 src/or/config.c        |    4 +-
 src/or/dnsserv.c       |    2 +-
 src/or/networkstatus.c |    1 +
 src/or/onion.c         |    2 +
 src/or/rendclient.c    |    1 +
 src/or/rendservice.c   |    6 +++-
 src/or/routerlist.c    |    3 +-
 src/or/routerparse.c   |   18 ++++++++++------
 src/test/test_crypto.c |   32 +++++++++++++++++-----------
 13 files changed, 104 insertions(+), 42 deletions(-)

diff --combined src/common/crypto.h
index 8fcc58c,29ba36c..b44efcc
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@@ -123,23 -123,25 +123,25 @@@ crypto_pk_env_t *crypto_pk_dup_key(cryp
  crypto_pk_env_t *crypto_pk_copy_full(crypto_pk_env_t *orig);
  int crypto_pk_key_is_private(const crypto_pk_env_t *key);
  
- int crypto_pk_public_encrypt(crypto_pk_env_t *env, char *to,
+ int crypto_pk_public_encrypt(crypto_pk_env_t *env, char *to, size_t tolen,
                               const char *from, size_t fromlen, int padding);
- int crypto_pk_private_decrypt(crypto_pk_env_t *env, char *to,
+ int crypto_pk_private_decrypt(crypto_pk_env_t *env, char *to, size_t tolen,
                                const char *from, size_t fromlen,
                                int padding, int warnOnFailure);
- int crypto_pk_public_checksig(crypto_pk_env_t *env, char *to,
+ int crypto_pk_public_checksig(crypto_pk_env_t *env, char *to, size_t tolen,
                                const char *from, size_t fromlen);
  int crypto_pk_public_checksig_digest(crypto_pk_env_t *env, const char *data,
                                 size_t datalen, const char *sig, size_t siglen);
- int crypto_pk_private_sign(crypto_pk_env_t *env, char *to,
+ int crypto_pk_private_sign(crypto_pk_env_t *env, char *to, size_t tolen,
                             const char *from, size_t fromlen);
- int crypto_pk_private_sign_digest(crypto_pk_env_t *env, char *to,
+ int crypto_pk_private_sign_digest(crypto_pk_env_t *env, char *to, size_t tolen,
                                    const char *from, size_t fromlen);
  int crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, char *to,
+                                     size_t tolen,
                                      const char *from, size_t fromlen,
                                      int padding, int force);
  int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, char *to,
+                                      size_t tolen,
                                       const char *from, size_t fromlen,
                                       int padding, int warnOnFailure);
  
@@@ -238,8 -240,7 +240,8 @@@ void secret_to_key(char *key_out, size_
                     size_t secret_len, const char *s2k_specifier);
  
  #ifdef CRYPTO_PRIVATE
 -/* Prototypes for private functions only used by tortls.c and crypto.c */
 +/* Prototypes for private functions only used by tortls.c, crypto.c, and the
 + * unit tests. */
  struct rsa_st;
  struct evp_pkey_st;
  struct dh_st;
diff --combined src/or/config.c
index 00a01f5,b124db1..c7376a2
--- a/src/or/config.c
+++ b/src/or/config.c
@@@ -44,8 -44,6 +44,8 @@@ typedef enum config_type_t 
    CONFIG_TYPE_FILENAME,     /**< A filename: some prefixes get expanded. */
    CONFIG_TYPE_UINT,         /**< A non-negative integer less than MAX_INT */
    CONFIG_TYPE_INTERVAL,     /**< A number of seconds, with optional units*/
 +  CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional
 +                              * units */
    CONFIG_TYPE_MEMUNIT,      /**< A number of bytes, with optional units*/
    CONFIG_TYPE_DOUBLE,       /**< A floating-point value */
    CONFIG_TYPE_BOOL,         /**< A boolean value, expressed as 0 or 1. */
@@@ -200,7 -198,6 +200,7 @@@ static config_var_t _option_vars[] = 
    V(ClientOnly,                  BOOL,     "0"),
    V(ConsensusParams,             STRING,   NULL),
    V(ConnLimit,                   UINT,     "1000"),
 +  V(ConnDirectionStatistics,     BOOL,     "0"),
    V(ConstrainedSockets,          BOOL,     "0"),
    V(ConstrainedSockSize,         MEMUNIT,  "8192"),
    V(ContactInfo,                 STRING,   NULL),
@@@ -224,10 -221,9 +224,10 @@@
    OBSOLETE("DirRecordUsageGranularity"),
    OBSOLETE("DirRecordUsageRetainIPs"),
    OBSOLETE("DirRecordUsageSaveInterval"),
 -  V(DirReqStatistics,            BOOL,     "0"),
 +  V(DirReqStatistics,            BOOL,     "1"),
    VAR("DirServer",               LINELIST, DirServers, NULL),
    V(DisableAllSwap,              BOOL,     "0"),
 +  V(DisableIOCP,                 BOOL,     "1"),
    V(DNSPort,                     UINT,     "0"),
    V(DNSListenAddress,            LINELIST, NULL),
    V(DownloadExtraInfo,           BOOL,     "0"),
@@@ -242,7 -238,7 +242,7 @@@
    V(ExitPolicy,                  LINELIST, NULL),
    V(ExitPolicyRejectPrivate,     BOOL,     "1"),
    V(ExitPortStatistics,          BOOL,     "0"),
 -  V(ExtraInfoStatistics,         BOOL,     "0"),
 +  V(ExtraInfoStatistics,         BOOL,     "1"),
  
  #if defined (WINCE)
    V(FallbackNetworkstatusFile,   FILENAME, "fallback-consensus"),
@@@ -294,7 -290,6 +294,7 @@@
    OBSOLETE("LinkPadding"),
    OBSOLETE("LogLevel"),
    OBSOLETE("LogFile"),
 +  V(LogTimeGranularity,          MSEC_INTERVAL, "1 second"),
    V(LongLivedPorts,              CSV,
                           "21,22,706,1863,5050,5190,5222,5223,6667,6697,8300"),
    VAR("MapAddress",              LINELIST, AddressMap,           NULL),
@@@ -311,7 -306,7 +311,7 @@@
    V(WarnUnsafeSocks,              BOOL,     "1"),
    OBSOLETE("NoPublish"),
    VAR("NodeFamily",              LINELIST, NodeFamilies,         NULL),
 -  V(NumCPUs,                     UINT,     "1"),
 +  V(NumCPUs,                     UINT,     "0"),
    V(NumEntryGuards,              UINT,     "3"),
    V(ORListenAddress,             LINELIST, NULL),
    V(ORPort,                      UINT,     "0"),
@@@ -321,8 -316,6 +321,8 @@@
    V(PerConnBWRate,               MEMUNIT,  "0"),
    V(PidFile,                     STRING,   NULL),
    V(TestingTorNetwork,           BOOL,     "0"),
 +  V(PortForwarding,              BOOL,     "0"),
 +  V(PortForwardingHelper,        FILENAME, "tor-fw-helper"),
    V(PreferTunneledDirConns,      BOOL,     "1"),
    V(ProtocolWarnings,            BOOL,     "0"),
    V(PublishServerDescriptor,     CSV,      "1"),
@@@ -391,7 -384,6 +391,7 @@@
    VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
    V(VirtualAddrNetwork,          STRING,   "127.192.0.0/10"),
    V(WarnPlaintextPorts,          CSV,      "23,109,110,143"),
 +  V(_UseFilteringSSLBufferevents, BOOL,    "0"),
    VAR("__ReloadTorrcOnSIGHUP",   BOOL,  ReloadTorrcOnSIGHUP,      "1"),
    VAR("__AllDirActionsPrivate",  BOOL,  AllDirActionsPrivate,     "0"),
    VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"),
@@@ -563,9 -555,8 +563,9 @@@ static int is_listening_on_low_port(uin
                                      const config_line_t *listen_options);
  
  static uint64_t config_parse_memunit(const char *s, int *ok);
 +static int config_parse_msec_interval(const char *s, int *ok);
  static int config_parse_interval(const char *s, int *ok);
 -static void init_libevent(void);
 +static void init_libevent(const or_options_t *options);
  static int opt_streq(const char *s1, const char *s2);
  
  /** Magic value for or_options_t. */
@@@ -699,11 -690,6 +699,11 @@@ or_options_free(or_options_t *options
      return;
  
    routerset_free(options->_ExcludeExitNodesUnion);
 +  if (options->NodeFamilySets) {
 +    SMARTLIST_FOREACH(options->NodeFamilySets, routerset_t *,
 +                      rs, routerset_free(rs));
 +    smartlist_free(options->NodeFamilySets);
 +  }
    config_free(&options_format, options);
  }
  
@@@ -971,7 -957,7 +971,7 @@@ options_act_reversible(or_options_t *ol
      /* Set up libevent.  (We need to do this before we can register the
       * listeners as listeners.) */
      if (running_tor && !libevent_initialized) {
 -      init_libevent();
 +      init_libevent(options);
        libevent_initialized = 1;
      }
  
@@@ -1247,17 -1233,6 +1247,17 @@@ options_act(or_options_t *old_options
    if (accounting_is_enabled(options))
      configure_accounting(time(NULL));
  
 +#ifdef USE_BUFFEREVENTS
 +  /* If we're using the bufferevents implementation and our rate limits
 +   * changed, we need to tell the rate-limiting system about it. */
 +  if (!old_options ||
 +      old_options->BandwidthRate != options->BandwidthRate ||
 +      old_options->BandwidthBurst != options->BandwidthBurst ||
 +      old_options->RelayBandwidthRate != options->RelayBandwidthRate ||
 +      old_options->RelayBandwidthBurst != options->RelayBandwidthBurst)
 +    connection_bucket_init();
 +#endif
 +
    /* parse RefuseUnknownExits tristate */
    if (!strcmp(options->RefuseUnknownExits, "0"))
      options->RefuseUnknownExits_ = 0;
@@@ -1369,52 -1344,44 +1369,52 @@@
      tor_free(actual_fname);
    }
  
 -  if (options->DirReqStatistics && !geoip_is_loaded()) {
 -    /* Check if GeoIP database could be loaded. */
 -    log_warn(LD_CONFIG, "Configured to measure directory request "
 -             "statistics, but no GeoIP database found!");
 -    return -1;
 -  }
 -
 -  if (options->EntryStatistics) {
 -    if (should_record_bridge_info(options)) {
 -      /* Don't allow measuring statistics on entry guards when configured
 -       * as bridge. */
 -      log_warn(LD_CONFIG, "Bridges cannot be configured to measure "
 -               "additional GeoIP statistics as entry guards.");
 -      return -1;
 -    } else if (!geoip_is_loaded()) {
 -      /* Check if GeoIP database could be loaded. */
 -      log_warn(LD_CONFIG, "Configured to measure entry node statistics, "
 -               "but no GeoIP database found!");
 -      return -1;
 -    }
 -  }
 -
    if (options->CellStatistics || options->DirReqStatistics ||
 -      options->EntryStatistics || options->ExitPortStatistics) {
 +      options->EntryStatistics || options->ExitPortStatistics ||
 +      options->ConnDirectionStatistics) {
      time_t now = time(NULL);
 +    int print_notice = 0;
      if ((!old_options || !old_options->CellStatistics) &&
 -        options->CellStatistics)
 +        options->CellStatistics) {
        rep_hist_buffer_stats_init(now);
 +      print_notice = 1;
 +    }
      if ((!old_options || !old_options->DirReqStatistics) &&
 -        options->DirReqStatistics)
 -      geoip_dirreq_stats_init(now);
 +        options->DirReqStatistics) {
 +      if (geoip_is_loaded()) {
 +        geoip_dirreq_stats_init(now);
 +        print_notice = 1;
 +      } else {
 +        options->DirReqStatistics = 0;
 +        log_notice(LD_CONFIG, "Configured to measure directory request "
 +                              "statistics, but no GeoIP database found! "
 +                              "Please specify a GeoIP database using the "
 +                              "GeoIPFile option!");
 +      }
 +    }
      if ((!old_options || !old_options->EntryStatistics) &&
 -        options->EntryStatistics)
 -      geoip_entry_stats_init(now);
 +        options->EntryStatistics && !should_record_bridge_info(options)) {
 +      if (geoip_is_loaded()) {
 +        geoip_entry_stats_init(now);
 +        print_notice = 1;
 +      } else {
 +        options->EntryStatistics = 0;
 +        log_notice(LD_CONFIG, "Configured to measure entry node "
 +                              "statistics, but no GeoIP database found! "
 +                              "Please specify a GeoIP database using the "
 +                              "GeoIPFile option!");
 +      }
 +    }
      if ((!old_options || !old_options->ExitPortStatistics) &&
 -        options->ExitPortStatistics)
 +        options->ExitPortStatistics) {
        rep_hist_exit_stats_init(now);
 -    if (!old_options)
 +      print_notice = 1;
 +    }
 +    if ((!old_options || !old_options->ConnDirectionStatistics) &&
 +        options->ConnDirectionStatistics) {
 +      rep_hist_conn_stats_init(now);
 +    }
 +    if (print_notice)
        log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
                   "the *-stats files that will first be written to the "
                   "data directory in 24 hours from now.");
@@@ -1432,9 -1399,6 +1432,9 @@@
    if (old_options && old_options->ExitPortStatistics &&
        !options->ExitPortStatistics)
      rep_hist_exit_stats_term();
 +  if (old_options && old_options->ConnDirectionStatistics &&
 +      !options->ConnDirectionStatistics)
 +    rep_hist_conn_stats_term();
  
    /* Check if we need to parse and add the EntryNodes config option. */
    if (options->EntryNodes &&
@@@ -1740,18 -1704,6 +1740,18 @@@ config_assign_value(config_format_t *fm
      break;
    }
  
 +  case CONFIG_TYPE_MSEC_INTERVAL: {
 +    i = config_parse_msec_interval(c->value, &ok);
 +    if (!ok) {
 +      tor_asprintf(msg,
 +          "Msec interval '%s %s' is malformed or out of bounds.",
 +          c->key, c->value);
 +      return -1;
 +    }
 +    *(int *)lvalue = i;
 +    break;
 +  }
 +
    case CONFIG_TYPE_MEMUNIT: {
      uint64_t u64 = config_parse_memunit(c->value, &ok);
      if (!ok) {
@@@ -2039,7 -1991,6 +2039,7 @@@ get_assigned_option(config_format_t *fm
        escape_val = 0; /* Can't need escape. */
        break;
      case CONFIG_TYPE_INTERVAL:
 +    case CONFIG_TYPE_MSEC_INTERVAL:
      case CONFIG_TYPE_UINT:
        /* This means every or_options_t uint or bool element
         * needs to be an int. Not, say, a uint16_t or char. */
@@@ -2267,7 -2218,6 +2267,7 @@@ option_clear(config_format_t *fmt, or_o
        *(time_t*)lvalue = 0;
        break;
      case CONFIG_TYPE_INTERVAL:
 +    case CONFIG_TYPE_MSEC_INTERVAL:
      case CONFIG_TYPE_UINT:
      case CONFIG_TYPE_BOOL:
        *(int*)lvalue = 0;
@@@ -2374,7 -2324,7 +2374,7 @@@ resolve_my_address(int warn_severity, o
    int explicit_ip=1;
    int explicit_hostname=1;
    int from_interface=0;
 -  char tmpbuf[INET_NTOA_BUF_LEN];
 +  char *addr_string = NULL;
    const char *address = options->Address;
    int notice_severity = warn_severity <= LOG_NOTICE ?
                            LOG_NOTICE : warn_severity;
@@@ -2416,43 -2366,48 +2416,43 @@@
          return -1;
        }
        from_interface = 1;
 -      in.s_addr = htonl(interface_ip);
 -      tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
 +      addr = interface_ip;
        log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for "
 -             "local interface. Using that.", tmpbuf);
 +             "local interface. Using that.", fmt_addr32(addr));
        strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
      } else { /* resolved hostname into addr */
 -      in.s_addr = htonl(addr);
 -
        if (!explicit_hostname &&
 -          is_internal_IP(ntohl(in.s_addr), 0)) {
 +          is_internal_IP(addr, 0)) {
          uint32_t interface_ip;
  
 -        tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
          log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' "
 -               "resolves to a private IP address (%s).  Trying something "
 -               "else.", hostname, tmpbuf);
 +               "resolves to a private IP address (%s). Trying something "
 +               "else.", hostname, fmt_addr32(addr));
  
          if (get_interface_address(warn_severity, &interface_ip)) {
            log_fn(warn_severity, LD_CONFIG,
                   "Could not get local interface IP address. Too bad.");
          } else if (is_internal_IP(interface_ip, 0)) {
 -          struct in_addr in2;
 -          in2.s_addr = htonl(interface_ip);
 -          tor_inet_ntoa(&in2,tmpbuf,sizeof(tmpbuf));
            log_fn(notice_severity, LD_CONFIG,
                   "Interface IP address '%s' is a private address too. "
 -                 "Ignoring.", tmpbuf);
 +                 "Ignoring.", fmt_addr32(interface_ip));
          } else {
            from_interface = 1;
 -          in.s_addr = htonl(interface_ip);
 -          tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
 +          addr = interface_ip;
            log_fn(notice_severity, LD_CONFIG,
                   "Learned IP address '%s' for local interface."
 -                 " Using that.", tmpbuf);
 +                 " Using that.", fmt_addr32(addr));
            strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
          }
        }
      }
 +  } else {
 +    addr = ntohl(in.s_addr); /* set addr so that addr_string is not
 +                              * illformed */
    }
  
 -  tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
 -  if (is_internal_IP(ntohl(in.s_addr), 0)) {
 +  addr_string = tor_dup_ip(addr);
 +  if (is_internal_IP(addr, 0)) {
      /* make sure we're ok with publishing an internal IP */
      if (!options->DirServers && !options->AlternateDirAuthority) {
        /* if they are using the default dirservers, disallow internal IPs
@@@ -2460,8 -2415,7 +2460,8 @@@
        log_fn(warn_severity, LD_CONFIG,
               "Address '%s' resolves to private IP address '%s'. "
               "Tor servers that use the default DirServers must have public "
 -             "IP addresses.", hostname, tmpbuf);
 +             "IP addresses.", hostname, addr_string);
 +      tor_free(addr_string);
        return -1;
      }
      if (!explicit_ip) {
@@@ -2469,20 -2423,19 +2469,20 @@@
         * they're using an internal address. */
        log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private "
               "IP address '%s'. Please set the Address config option to be "
 -             "the IP address you want to use.", hostname, tmpbuf);
 +             "the IP address you want to use.", hostname, addr_string);
 +      tor_free(addr_string);
        return -1;
      }
    }
  
 -  log_debug(LD_CONFIG, "Resolved Address to '%s'.", tmpbuf);
 -  *addr_out = ntohl(in.s_addr);
 +  log_debug(LD_CONFIG, "Resolved Address to '%s'.", fmt_addr32(addr));
 +  *addr_out = addr;
    if (last_resolved_addr && last_resolved_addr != *addr_out) {
      /* Leave this as a notice, regardless of the requested severity,
       * at least until dynamic IP address support becomes bulletproof. */
      log_notice(LD_NET,
                 "Your IP address seems to have changed to %s. Updating.",
 -               tmpbuf);
 +               addr_string);
      ip_address_changed(0);
    }
    if (last_resolved_addr != *addr_out) {
@@@ -2501,12 -2454,11 +2501,12 @@@
      }
      control_event_server_status(LOG_NOTICE,
                                  "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s %s%s",
 -                                tmpbuf, method, h?"HOSTNAME=":"", h);
 +                                addr_string, method, h?"HOSTNAME=":"", h);
    }
    last_resolved_addr = *addr_out;
    if (hostname_out)
      *hostname_out = tor_strdup(hostname);
 +  tor_free(addr_string);
    return 0;
  }
  
@@@ -3111,24 -3063,17 +3111,24 @@@ options_validate(or_options_t *old_opti
      routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeNodes);
    }
  
 +  if (options->NodeFamilies) {
 +    options->NodeFamilySets = smartlist_create();
 +    for (cl = options->NodeFamilies; cl; cl = cl->next) {
 +      routerset_t *rs = routerset_new();
 +      if (routerset_parse(rs, cl->value, cl->key) == 0) {
 +        smartlist_add(options->NodeFamilySets, rs);
 +      } else {
 +        routerset_free(rs);
 +      }
 +    }
 +  }
 +
    if (options->ExcludeNodes && options->StrictNodes) {
      COMPLAIN("You have asked to exclude certain relays from all positions "
               "in your circuits. Expect hidden services and other Tor "
               "features to be broken in unpredictable ways.");
    }
  
 -  if (options->EntryNodes && !routerset_is_list(options->EntryNodes)) {
 -    /* XXXX fix this; see entry_guards_prepend_from_config(). */
 -    REJECT("IPs or countries are not yet supported in EntryNodes.");
 -  }
 -
    if (options->AuthoritativeDir) {
      if (!options->ContactInfo && !options->TestingTorNetwork)
        REJECT("Authoritative directory servers must set ContactInfo");
@@@ -3589,12 -3534,8 +3589,12 @@@
    if (check_nickname_list(options->MyFamily, "MyFamily", msg))
      return -1;
    for (cl = options->NodeFamilies; cl; cl = cl->next) {
 -    if (check_nickname_list(cl->value, "NodeFamily", msg))
 +    routerset_t *rs = routerset_new();
 +    if (routerset_parse(rs, cl->value, cl->key)) {
 +      routerset_free(rs);
        return -1;
 +    }
 +    routerset_free(rs);
    }
  
    if (validate_addr_policies(options, msg) < 0)
@@@ -4374,35 -4315,6 +4374,35 @@@ options_init_logs(or_options_t *options
                 options->RunAsDaemon;
  #endif
  
 +  if (options->LogTimeGranularity <= 0) {
 +    log_warn(LD_CONFIG, "Log time granularity '%d' has to be positive.",
 +             options->LogTimeGranularity);
 +    return -1;
 +  } else if (1000 % options->LogTimeGranularity != 0 &&
 +             options->LogTimeGranularity % 1000 != 0) {
 +    int granularity = options->LogTimeGranularity;
 +    if (granularity < 40) {
 +      do granularity++;
 +      while (1000 % granularity != 0);
 +    } else if (granularity < 1000) {
 +      granularity = 1000 / granularity;
 +      while (1000 % granularity != 0)
 +        granularity--;
 +      granularity = 1000 / granularity;
 +    } else {
 +      granularity = 1000 * ((granularity / 1000) + 1);
 +    }
 +    log_warn(LD_CONFIG, "Log time granularity '%d' has to be either a "
 +                        "divisor or a multiple of 1 second. Changing to "
 +                        "'%d'.",
 +             options->LogTimeGranularity, granularity);
 +    if (!validate_only)
 +      set_log_time_granularity(granularity);
 +  } else {
 +    if (!validate_only)
 +      set_log_time_granularity(options->LogTimeGranularity);
 +  }
 +
    ok = 1;
    elts = smartlist_create();
  
@@@ -4891,26 -4803,6 +4891,26 @@@ static struct unit_table_t time_units[
    { NULL, 0 },
  };
  
 +/** Table to map the names of time units to the number of milliseconds
 + * they contain. */
 +static struct unit_table_t time_msec_units[] = {
 +  { "",         1 },
 +  { "msec",     1 },
 +  { "millisecond", 1 },
 +  { "milliseconds", 1 },
 +  { "second",   1000 },
 +  { "seconds",  1000 },
 +  { "minute",   60*1000 },
 +  { "minutes",  60*1000 },
 +  { "hour",     60*60*1000 },
 +  { "hours",    60*60*1000 },
 +  { "day",      24*60*60*1000 },
 +  { "days",     24*60*60*1000 },
 +  { "week",     7*24*60*60*1000 },
 +  { "weeks",    7*24*60*60*1000 },
 +  { NULL, 0 },
 +};
 +
  /** Parse a string <b>val</b> containing a number, zero or more
   * spaces, and an optional unit string.  If the unit appears in the
   * table <b>u</b>, then multiply the number by the unit multiplier.
@@@ -4974,25 -4866,6 +4974,25 @@@ config_parse_memunit(const char *s, in
    return u;
  }
  
 +/** Parse a string in the format "number unit", where unit is a unit of
 + * time in milliseconds.  On success, set *<b>ok</b> to true and return
 + * the number of milliseconds in the provided interval.  Otherwise, set
 + * *<b>ok</b> to 0 and return -1. */
 +static int
 +config_parse_msec_interval(const char *s, int *ok)
 +{
 +  uint64_t r;
 +  r = config_parse_units(s, time_msec_units, ok);
 +  if (!ok)
 +    return -1;
 +  if (r > INT_MAX) {
 +    log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
 +    *ok = 0;
 +    return -1;
 +  }
 +  return (int)r;
 +}
 +
  /** Parse a string in the format "number unit", where unit is a unit of time.
   * On success, set *<b>ok</b> to true and return the number of seconds in
   * the provided interval.  Otherwise, set *<b>ok</b> to 0 and return -1.
@@@ -5012,29 -4885,13 +5012,29 @@@ config_parse_interval(const char *s, in
    return (int)r;
  }
  
 +/** Return the number of cpus configured in <b>options</b>.  If we are
 + * told to auto-detect the number of cpus, return the auto-detected number. */
 +int
 +get_num_cpus(const or_options_t *options)
 +{
 +  if (options->NumCPUs == 0) {
 +    int n = compute_num_cpus();
 +    return (n >= 1) ? n : 1;
 +  } else {
 +    return options->NumCPUs;
 +  }
 +}
 +
  /**
   * Initialize the libevent library.
   */
  static void
 -init_libevent(void)
 +init_libevent(const or_options_t *options)
  {
    const char *badness=NULL;
 +  tor_libevent_cfg cfg;
 +
 +  tor_assert(options);
  
    configure_libevent_logging();
    /* If the kernel complains that some method (say, epoll) doesn't
@@@ -5044,11 -4901,7 +5044,11 @@@
  
    tor_check_libevent_header_compatibility();
  
 -  tor_libevent_initialize();
 +  memset(&cfg, 0, sizeof(cfg));
 +  cfg.disable_iocp = options->DisableIOCP;
 +  cfg.num_cpus = get_num_cpus(options);
 +
 +  tor_libevent_initialize(&cfg);
  
    suppress_libevent_log_msg(NULL);
  
@@@ -5329,8 -5182,8 +5329,8 @@@ or_state_save(time_t now
    tor_free(state);
    fname = get_datadir_fname("state");
    if (write_str_to_file(fname, contents, 0)<0) {
-     log_warn(LD_FS, "Unable to write state to file \"%s\"; will try later",
-              fname);
+     log_warn(LD_FS, "Unable to write state to file \"%s\"; "
+              "will try again later", fname);
      global_state->LastWritten = -1;
      tor_free(fname);
      tor_free(contents);
@@@ -5389,7 -5242,6 +5389,7 @@@ getinfo_helper_config(control_connectio
          case CONFIG_TYPE_FILENAME: type = "Filename"; break;
          case CONFIG_TYPE_UINT: type = "Integer"; break;
          case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break;
 +        case CONFIG_TYPE_MSEC_INTERVAL: type = "TimeMsecInterval"; break;
          case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break;
          case CONFIG_TYPE_DOUBLE: type = "Float"; break;
          case CONFIG_TYPE_BOOL: type = "Boolean"; break;
diff --combined src/or/networkstatus.c
index 405db12,dfc3a45..68dc9f6
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@@ -20,9 -20,7 +20,9 @@@
  #include "dirserv.h"
  #include "dirvote.h"
  #include "main.h"
 +#include "microdesc.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "relay.h"
  #include "router.h"
  #include "routerlist.h"
@@@ -46,19 -44,8 +46,19 @@@ static strmap_t *named_server_map = NUL
   * as unnamed for some server in the consensus. */
  static strmap_t *unnamed_server_map = NULL;
  
 -/** Most recently received and validated v3 consensus network status. */
 -static networkstatus_t *current_consensus = NULL;
 +/** Most recently received and validated v3 consensus network status,
 + * of whichever type we are using for our own circuits.  This will be the same
 + * as one of current_ns_consensus or current_md_consensus.
 + */
 +#define current_consensus current_ns_consensus
 +
 +/** Most recently received and validated v3 "ns"-flavored consensus network
 + * status. */
 +static networkstatus_t *current_ns_consensus = NULL;
 +
 +/** Most recently received and validated v3 "microdec"-flavored consensus
 + * network status. */
 +static networkstatus_t *current_md_consensus = NULL;
  
  /** A v3 consensus networkstatus that we've received, but which we don't
   * have enough certificates to be happy about. */
@@@ -107,8 -94,9 +107,8 @@@ voi
  networkstatus_reset_warnings(void)
  {
    if (current_consensus) {
 -    SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 -                      routerstatus_t *, rs,
 -                      rs->name_lookup_warned = 0);
 +    SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
 +                      node->name_lookup_warned = 0);
    }
  
    have_warned_about_old_version = 0;
@@@ -283,7 -271,6 +283,7 @@@ router_reload_consensus_networkstatus(v
    update_certificate_downloads(time(NULL));
  
    routers_update_all_from_networkstatus(time(NULL), 3);
 +  update_microdescs_from_networkstatus(time(NULL));
  
    return 0;
  }
@@@ -452,6 -439,7 +452,7 @@@ networkstatus_check_document_signature(
    signed_digest = tor_malloc(signed_digest_len);
    if (crypto_pk_public_checksig(cert->signing_key,
                                  signed_digest,
+                                 signed_digest_len,
                                  sig->signature,
                                  sig->signature_len) < dlen ||
        memcmp(signed_digest, consensus->digests.d[sig->alg], dlen)) {
@@@ -939,9 -927,10 +940,9 @@@ compare_digest_to_routerstatus_entry(co
    return memcmp(key, rs->identity_digest, DIGEST_LEN);
  }
  
 -/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
 - * NULL if none was found. */
 +/** As networkstatus_v2_find_entry, but do not return a const pointer */
  routerstatus_t *
 -networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
 +networkstatus_v2_find_mutable_entry(networkstatus_v2_t *ns, const char *digest)
  {
    return smartlist_bsearch(ns->entries, digest,
                             compare_digest_to_routerstatus_entry);
@@@ -949,29 -938,14 +950,29 @@@
  
  /** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
   * NULL if none was found. */
 +const routerstatus_t *
 +networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
 +{
 +  return networkstatus_v2_find_mutable_entry(ns, digest);
 +}
 +
 +/** As networkstatus_find_entry, but do not return a const pointer */
  routerstatus_t *
 -networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest)
 +networkstatus_vote_find_mutable_entry(networkstatus_t *ns, const char *digest)
  {
    return smartlist_bsearch(ns->routerstatus_list, digest,
                             compare_digest_to_routerstatus_entry);
  }
  
 -/*XXXX make this static once functions are moved into this file. */
 +/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
 + * NULL if none was found. */
 +const routerstatus_t *
 +networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest)
 +{
 +  return networkstatus_vote_find_mutable_entry(ns, digest);
 +}
 +
 +/*XXXX MOVE make this static once functions are moved into this file. */
  /** Search the routerstatuses in <b>ns</b> for one whose identity digest is
   * <b>digest</b>.  Return value and set *<b>found_out</b> as for
   * smartlist_bsearch_idx(). */
@@@ -993,37 -967,22 +994,37 @@@ networkstatus_get_v2_list(void
    return networkstatus_v2_list;
  }
  
 -/** Return the consensus view of the status of the router whose current
 - * <i>descriptor</i> digest is <b>digest</b>, or NULL if no such router is
 - * known. */
 +/* As router_get_consensus_status_by_descriptor_digest, but does not return
 + * a const pointer */
  routerstatus_t *
 -router_get_consensus_status_by_descriptor_digest(const char *digest)
 +router_get_mutable_consensus_status_by_descriptor_digest(
 +                                                 networkstatus_t *consensus,
 +                                                 const char *digest)
  {
 -  if (!current_consensus) return NULL;
 -  if (!current_consensus->desc_digest_map) {
 -    digestmap_t * m = current_consensus->desc_digest_map = digestmap_new();
 -    SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 +  if (!consensus)
 +    consensus = current_consensus;
 +  if (!consensus)
 +    return NULL;
 +  if (!consensus->desc_digest_map) {
 +    digestmap_t *m = consensus->desc_digest_map = digestmap_new();
 +    SMARTLIST_FOREACH(consensus->routerstatus_list,
                        routerstatus_t *, rs,
       {
         digestmap_set(m, rs->descriptor_digest, rs);
       });
    }
 -  return digestmap_get(current_consensus->desc_digest_map, digest);
 +  return digestmap_get(consensus->desc_digest_map, digest);
 +}
 +
 +/** Return the consensus view of the status of the router whose current
 + * <i>descriptor</i> digest in <b>consensus</b> is <b>digest</b>, or NULL if
 + * no such router is known. */
 +const routerstatus_t *
 +router_get_consensus_status_by_descriptor_digest(networkstatus_t *consensus,
 +                                                 const char *digest)
 +{
 +  return router_get_mutable_consensus_status_by_descriptor_digest(
 +                                                          consensus, digest);
  }
  
  /** Given the digest of a router descriptor, return its current download
@@@ -1032,10 -991,7 +1033,10 @@@ download_status_t 
  router_get_dl_status_by_descriptor_digest(const char *d)
  {
    routerstatus_t *rs;
 -  if ((rs = router_get_consensus_status_by_descriptor_digest(d)))
 +  if (!current_ns_consensus)
 +    return NULL;
 +  if ((rs = router_get_mutable_consensus_status_by_descriptor_digest(
 +                                              current_ns_consensus, d)))
      return &rs->dl_status;
    if (v2_download_status_map)
      return digestmap_get(v2_download_status_map, d);
@@@ -1043,9 -999,10 +1044,9 @@@
    return NULL;
  }
  
 -/** Return the consensus view of the status of the router whose identity
 - * digest is <b>digest</b>, or NULL if we don't know about any such router. */
 +/** As router_get_consensus_status_by_id, but do not return a const pointer */
  routerstatus_t *
 -router_get_consensus_status_by_id(const char *digest)
 +router_get_mutable_consensus_status_by_id(const char *digest)
  {
    if (!current_consensus)
      return NULL;
@@@ -1053,27 -1010,100 +1054,27 @@@
                             compare_digest_to_routerstatus_entry);
  }
  
 +/** Return the consensus view of the status of the router whose identity
 + * digest is <b>digest</b>, or NULL if we don't know about any such router. */
 +const routerstatus_t *
 +router_get_consensus_status_by_id(const char *digest)
 +{
 +  return router_get_mutable_consensus_status_by_id(digest);
 +}
 +
  /** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
   * the corresponding routerstatus_t, or NULL if none exists.  Warn the
   * user if <b>warn_if_unnamed</b> is set, and they have specified a router by
   * nickname, but the Named flag isn't set for that router. */
 -routerstatus_t *
 +const routerstatus_t *
  router_get_consensus_status_by_nickname(const char *nickname,
                                          int warn_if_unnamed)
  {
 -  char digest[DIGEST_LEN];
 -  routerstatus_t *best=NULL;
 -  smartlist_t *matches=NULL;
 -  const char *named_id=NULL;
 -
 -  if (!current_consensus || !nickname)
 -    return NULL;
 -
 -  /* Is this name really a hexadecimal identity digest? */
 -  if (nickname[0] == '$') {
 -    if (base16_decode(digest, DIGEST_LEN, nickname+1, strlen(nickname+1))<0)
 -      return NULL;
 -    return networkstatus_vote_find_entry(current_consensus, digest);
 -  } else if (strlen(nickname) == HEX_DIGEST_LEN &&
 -       (base16_decode(digest, DIGEST_LEN, nickname, strlen(nickname))==0)) {
 -    return networkstatus_vote_find_entry(current_consensus, digest);
 -  }
 -
 -  /* Is there a server that is Named with this name? */
 -  if (named_server_map)
 -    named_id = strmap_get_lc(named_server_map, nickname);
 -  if (named_id)
 -    return networkstatus_vote_find_entry(current_consensus, named_id);
 -
 -  /* Okay; is this name listed as Unnamed? */
 -  if (unnamed_server_map &&
 -      strmap_get_lc(unnamed_server_map, nickname)) {
 -    log_info(LD_GENERAL, "The name %s is listed as Unnamed; it is not the "
 -             "canonical name of any server we know.", escaped(nickname));
 +  const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
 +  if (node)
 +    return node->rs;
 +  else
      return NULL;
 -  }
 -
 -  /* This name is not canonical for any server; go through the list and
 -   * see who it matches. */
 -  /*XXXX This is inefficient; optimize it if it matters. */
 -  matches = smartlist_create();
 -  SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 -                    routerstatus_t *, lrs,
 -    {
 -      if (!strcasecmp(lrs->nickname, nickname)) {
 -        if (lrs->is_named) {
 -          tor_fragile_assert() /* This should never happen. */
 -          smartlist_free(matches);
 -          return lrs;
 -        } else {
 -          if (lrs->is_unnamed) {
 -            tor_fragile_assert(); /* nor should this. */
 -            smartlist_clear(matches);
 -            best=NULL;
 -            break;
 -          }
 -          smartlist_add(matches, lrs);
 -          best = lrs;
 -        }
 -      }
 -    });
 -
 -  if (smartlist_len(matches)>1 && warn_if_unnamed) {
 -    int any_unwarned=0;
 -    SMARTLIST_FOREACH(matches, routerstatus_t *, lrs,
 -      {
 -        if (! lrs->name_lookup_warned) {
 -          lrs->name_lookup_warned=1;
 -          any_unwarned=1;
 -        }
 -      });
 -    if (any_unwarned) {
 -      log_warn(LD_CONFIG,"There are multiple matches for the nickname \"%s\","
 -               " but none is listed as named by the directory authorities. "
 -               "Choosing one arbitrarily.", nickname);
 -    }
 -  } else if (warn_if_unnamed && best && !best->name_lookup_warned) {
 -    char fp[HEX_DIGEST_LEN+1];
 -    base16_encode(fp, sizeof(fp),
 -                  best->identity_digest, DIGEST_LEN);
 -    log_warn(LD_CONFIG,
 -         "When looking up a status, you specified a server \"%s\" by name, "
 -         "but the directory authorities do not have any key registered for "
 -         "this nickname -- so it could be used by any server, "
 -         "not just the one you meant. "
 -         "To make sure you get the same server in the future, refer to "
 -         "it by key, as \"$%s\".", nickname, fp);
 -    best->name_lookup_warned = 1;
 -  }
 -  smartlist_free(matches);
 -  return best;
  }
  
  /** Return the identity digest that's mapped to officially by
@@@ -1170,25 -1200,6 +1171,25 @@@ update_v2_networkstatus_cache_downloads
    }
  }
  
 +/** DOCDOC */
 +static int
 +we_want_to_fetch_flavor(or_options_t *options, int flavor)
 +{
 +  if (flavor < 0 || flavor > N_CONSENSUS_FLAVORS) {
 +    /* This flavor is crazy; we don't want it */
 +    /*XXXX handle unrecognized flavors later */
 +    return 0;
 +  }
 +  if (authdir_mode_v3(options) || directory_caches_dir_info(options)) {
 +    /* We want to serve all flavors to others, regardless if we would use
 +     * it ourselves. */
 +    return 1;
 +  }
 +  /* Otherwise, we want the flavor only if we want to use it to build
 +   * circuits. */
 +  return (flavor == USABLE_CONSENSUS_FLAVOR);
 +}
 +
  /** How many times will we try to fetch a consensus before we give up? */
  #define CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES 8
  /** How long will we hang onto a possibly live consensus for which we're
@@@ -1201,65 -1212,48 +1202,65 @@@ static voi
  update_consensus_networkstatus_downloads(time_t now)
  {
    int i;
 +  or_options_t *options = get_options();
 +
    if (!networkstatus_get_live_consensus(now))
      time_to_download_next_consensus = now; /* No live consensus? Get one now!*/
    if (time_to_download_next_consensus > now)
      return; /* Wait until the current consensus is older. */
 -  /* XXXXNM Microdescs: may need to download more types. */
 -  if (!download_status_is_ready(&consensus_dl_status[FLAV_NS], now,
 -                                CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
 -    return; /* We failed downloading a consensus too recently. */
 -  if (connection_get_by_type_purpose(CONN_TYPE_DIR,
 -                                     DIR_PURPOSE_FETCH_CONSENSUS))
 -    return; /* There's an in-progress download.*/
  
    for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
 -    consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i];
 +    /* XXXX need some way to download unknown flavors if we are caching. */
 +    const char *resource;
 +    consensus_waiting_for_certs_t *waiting;
 +
 +    if (! we_want_to_fetch_flavor(options, i))
 +      continue;
 +
 +    resource = i==FLAV_NS ? NULL : networkstatus_get_flavor_name(i);
 +
 +    if (!download_status_is_ready(&consensus_dl_status[i], now,
 +                                  CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
 +      continue; /* We failed downloading a consensus too recently. */
 +    if (connection_dir_get_by_purpose_and_resource(
 +                                DIR_PURPOSE_FETCH_CONSENSUS, resource))
 +      continue; /* There's an in-progress download.*/
 +
 +    waiting = &consensus_waiting_for_certs[i];
      if (waiting->consensus) {
        /* XXXX make sure this doesn't delay sane downloads. */
 -      if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now)
 -        return; /* We're still getting certs for this one. */
 -      else {
 +      if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
 +        continue; /* We're still getting certs for this one. */
 +      } else {
          if (!waiting->dl_failed) {
 -          download_status_failed(&consensus_dl_status[FLAV_NS], 0);
 +          download_status_failed(&consensus_dl_status[i], 0);
            waiting->dl_failed=1;
          }
        }
      }
 -  }
  
 -  log_info(LD_DIR, "Launching networkstatus consensus download.");
 -  directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
 -                               ROUTER_PURPOSE_GENERAL, NULL,
 -                               PDS_RETRY_IF_NO_SERVERS);
 +    log_info(LD_DIR, "Launching %s networkstatus consensus download.",
 +             networkstatus_get_flavor_name(i));
 +
 +    directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
 +                                 ROUTER_PURPOSE_GENERAL, resource,
 +                                 PDS_RETRY_IF_NO_SERVERS);
 +  }
  }
  
  /** Called when an attempt to download a consensus fails: note that the
   * failure occurred, and possibly retry. */
  void
 -networkstatus_consensus_download_failed(int status_code)
 +networkstatus_consensus_download_failed(int status_code, const char *flavname)
  {
 -  /* XXXXNM Microdescs: may need to handle more types. */
 -  download_status_failed(&consensus_dl_status[FLAV_NS], status_code);
 -  /* Retry immediately, if appropriate. */
 -  update_consensus_networkstatus_downloads(time(NULL));
 +  int flav = networkstatus_parse_flavor_name(flavname);
 +  if (flav >= 0) {
 +    tor_assert(flav < N_CONSENSUS_FLAVORS);
 +    /* XXXX handle unrecognized flavors */
 +    download_status_failed(&consensus_dl_status[flav], status_code);
 +    /* Retry immediately, if appropriate. */
 +    update_consensus_networkstatus_downloads(time(NULL));
 +  }
  }
  
  /** How long do we (as a cache) wait after a consensus becomes non-fresh
@@@ -1380,10 -1374,7 +1381,10 @@@ update_certificate_downloads(time_t now
                                      now);
    }
  
 -  authority_certs_fetch_missing(current_consensus, now);
 +  if (current_ns_consensus)
 +    authority_certs_fetch_missing(current_ns_consensus, now);
 +  if (current_md_consensus)
 +    authority_certs_fetch_missing(current_md_consensus, now);
  }
  
  /** Return 1 if we have a consensus but we don't have enough certificates
@@@ -1415,18 -1406,6 +1416,18 @@@ networkstatus_get_latest_consensus(void
    return current_consensus;
  }
  
 +/** DOCDOC */
 +networkstatus_t *
 +networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
 +{
 +  if (f == FLAV_NS)
 +    return current_ns_consensus;
 +  else if (f == FLAV_MICRODESC)
 +    return current_md_consensus;
 +  else
 +    tor_assert(0);
 +}
 +
  /** Return the most recent consensus that we have downloaded, or NULL if it is
   * no longer live. */
  networkstatus_t *
@@@ -1446,15 -1425,13 +1447,15 @@@ networkstatus_get_live_consensus(time_
  /** As networkstatus_get_live_consensus(), but is way more tolerant of expired
   * consensuses. */
  networkstatus_t *
 -networkstatus_get_reasonably_live_consensus(time_t now)
 +networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
  {
  #define REASONABLY_LIVE_TIME (24*60*60)
 -  if (current_consensus &&
 -      current_consensus->valid_after <= now &&
 -      now <= current_consensus->valid_until+REASONABLY_LIVE_TIME)
 -    return current_consensus;
 +  networkstatus_t *consensus =
 +    networkstatus_get_latest_consensus_by_flavor(flavor);
 +  if (consensus &&
 +      consensus->valid_after <= now &&
 +      now <= consensus->valid_until+REASONABLY_LIVE_TIME)
 +    return consensus;
    else
      return NULL;
  }
@@@ -1475,7 -1452,7 +1476,7 @@@ routerstatus_has_changed(const routerst
           a->is_exit != b->is_exit ||
           a->is_stable != b->is_stable ||
           a->is_fast != b->is_fast ||
 -         a->is_running != b->is_running ||
 +         a->is_flagged_running != b->is_flagged_running ||
           a->is_named != b->is_named ||
           a->is_unnamed != b->is_unnamed ||
           a->is_valid != b->is_valid ||
@@@ -1516,14 -1493,13 +1517,14 @@@ notify_control_networkstatus_changed(co
    }
    changed = smartlist_create();
  
 -  SMARTLIST_FOREACH_JOIN(old_c->routerstatus_list, routerstatus_t *, rs_old,
 -                         new_c->routerstatus_list, routerstatus_t *, rs_new,
 -                         memcmp(rs_old->identity_digest,
 -                                rs_new->identity_digest, DIGEST_LEN),
 -                         smartlist_add(changed, rs_new)) {
 +  SMARTLIST_FOREACH_JOIN(
 +                     old_c->routerstatus_list, const routerstatus_t *, rs_old,
 +                     new_c->routerstatus_list, const routerstatus_t *, rs_new,
 +                     memcmp(rs_old->identity_digest,
 +                            rs_new->identity_digest, DIGEST_LEN),
 +                     smartlist_add(changed, (void*) rs_new)) {
      if (routerstatus_has_changed(rs_old, rs_new))
 -      smartlist_add(changed, rs_new);
 +      smartlist_add(changed, (void*)rs_new);
    } SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new);
  
    control_event_networkstatus_changed(changed);
@@@ -1547,6 -1523,7 +1548,6 @@@ networkstatus_copy_old_consensus_info(n
                                  rs_new->identity_digest, DIGEST_LEN),
                           STMT_NIL) {
      /* Okay, so we're looking at the same identity. */
 -    rs_new->name_lookup_warned = rs_old->name_lookup_warned;
      rs_new->last_dir_503_at = rs_old->last_dir_503_at;
  
      if (!memcmp(rs_old->descriptor_digest, rs_new->descriptor_digest,
@@@ -1639,16 -1616,9 +1640,16 @@@ networkstatus_set_current_consensus(con
    if (!strcmp(flavor, "ns")) {
      consensus_fname = get_datadir_fname("cached-consensus");
      unverified_fname = get_datadir_fname("unverified-consensus");
 -    if (current_consensus) {
 -      current_digests = &current_consensus->digests;
 -      current_valid_after = current_consensus->valid_after;
 +    if (current_ns_consensus) {
 +      current_digests = &current_ns_consensus->digests;
 +      current_valid_after = current_ns_consensus->valid_after;
 +    }
 +  } else if (!strcmp(flavor, "microdesc")) {
 +    consensus_fname = get_datadir_fname("cached-microdesc-consensus");
 +    unverified_fname = get_datadir_fname("unverified-microdesc-consensus");
 +    if (current_md_consensus) {
 +      current_digests = &current_md_consensus->digests;
 +      current_valid_after = current_md_consensus->valid_after;
      }
    } else {
      cached_dir_t *cur;
@@@ -1734,27 -1704,15 +1735,27 @@@
  
    if (flav == USABLE_CONSENSUS_FLAVOR) {
      notify_control_networkstatus_changed(current_consensus, c);
 -
 -    if (current_consensus) {
 -      networkstatus_copy_old_consensus_info(c, current_consensus);
 -      networkstatus_vote_free(current_consensus);
 +  }
 +  if (flav == FLAV_NS) {
 +    if (current_ns_consensus) {
 +      networkstatus_copy_old_consensus_info(c, current_ns_consensus);
 +      networkstatus_vote_free(current_ns_consensus);
        /* Defensive programming : we should set current_consensus very soon,
         * but we're about to call some stuff in the meantime, and leaving this
         * dangling pointer around has proven to be trouble. */
 -       current_consensus = NULL;
 +      current_ns_consensus = NULL;
 +    }
 +    current_ns_consensus = c;
 +    free_consensus = 0; /* avoid free */
 +  } else if (flav == FLAV_MICRODESC) {
 +    if (current_md_consensus) {
 +      networkstatus_copy_old_consensus_info(c, current_md_consensus);
 +      networkstatus_vote_free(current_md_consensus);
 +      /* more defensive programming */
 +      current_md_consensus = NULL;
      }
 +    current_md_consensus = c;
 +    free_consensus = 0; /* avoid free */
    }
  
    waiting = &consensus_waiting_for_certs[flav];
@@@ -1780,11 -1738,11 +1781,11 @@@
    }
  
    if (flav == USABLE_CONSENSUS_FLAVOR) {
 -    current_consensus = c;
 -    free_consensus = 0; /* Prevent free. */
 -
      /* XXXXNM Microdescs: needs a non-ns variant. */
      update_consensus_networkstatus_fetch_time(now);
 +
 +    nodelist_set_consensus(current_consensus);
 +
      dirvote_recalculate_timing(options, now);
      routerstatus_list_update_named_server_map();
      cell_ewma_set_scale_factor(options, current_consensus);
@@@ -1806,11 -1764,11 +1807,11 @@@
      write_str_to_file(consensus_fname, consensus, 0);
    }
  
 -  if (ftime_definitely_before(now, current_consensus->valid_after)) {
 +  if (time_definitely_before(now, c->valid_after, 60)) {
      char tbuf[ISO_TIME_LEN+1];
      char dbuf[64];
 -    long delta = now - current_consensus->valid_after;
 -    format_iso_time(tbuf, current_consensus->valid_after);
 +    long delta = now - c->valid_after;
 +    format_iso_time(tbuf, c->valid_after);
      format_time_interval(dbuf, sizeof(dbuf), delta);
      log_warn(LD_GENERAL, "Our clock is %s behind the time published in the "
               "consensus network status document (%s GMT).  Tor needs an "
@@@ -1861,8 -1819,7 +1862,8 @@@ voi
  routers_update_all_from_networkstatus(time_t now, int dir_version)
  {
    routerlist_t *rl = router_get_routerlist();
 -  networkstatus_t *consensus = networkstatus_get_live_consensus(now);
 +  networkstatus_t *consensus = networkstatus_get_reasonably_live_consensus(now,
 +                                                                     FLAV_NS);
  
    if (networkstatus_v2_list_has_changed)
      download_status_map_update_from_v2_networkstatus();
@@@ -1934,7 -1891,7 +1935,7 @@@ download_status_map_update_from_v2_netw
  
    dl_status = digestmap_new();
    SMARTLIST_FOREACH_BEGIN(networkstatus_v2_list, networkstatus_v2_t *, ns) {
 -    SMARTLIST_FOREACH_BEGIN(ns->entries, routerstatus_t *, rs) {
 +    SMARTLIST_FOREACH_BEGIN(ns->entries, const routerstatus_t *, rs) {
        const char *d = rs->descriptor_digest;
        download_status_t *s;
        if (digestmap_get(dl_status, d))
@@@ -1962,8 -1919,7 +1963,8 @@@ routerstatus_list_update_named_server_m
    named_server_map = strmap_new();
    strmap_free(unnamed_server_map, NULL);
    unnamed_server_map = strmap_new();
 -  SMARTLIST_FOREACH(current_consensus->routerstatus_list, routerstatus_t *, rs,
 +  SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 +                                                   const routerstatus_t *, rs,
      {
        if (rs->is_named) {
          strmap_set_lc(named_server_map, rs->nickname,
@@@ -1985,6 -1941,7 +1986,6 @@@ routers_update_status_from_consensus_ne
    trusted_dir_server_t *ds;
    or_options_t *options = get_options();
    int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
 -  int namingdir = authdir && options->NamingAuthoritativeDir;
    networkstatus_t *ns = current_consensus;
    if (!ns || !smartlist_len(ns->routerstatus_list))
      return;
@@@ -1998,19 -1955,25 +1999,19 @@@
                           memcmp(rs->identity_digest,
                                 router->cache_info.identity_digest, DIGEST_LEN),
    {
 +#if 0
      /* We have no routerstatus for this router. Clear flags and skip it. */
 -    if (!namingdir)
 -      router->is_named = 0;
      if (!authdir) {
        if (router->purpose == ROUTER_PURPOSE_GENERAL)
          router_clear_status_flags(router);
      }
 +#endif
    }) {
      /* We have a routerstatus for this router. */
      const char *digest = router->cache_info.identity_digest;
  
      ds = router_get_trusteddirserver_by_digest(digest);
  
 -    if (!namingdir) {
 -      if (rs->is_named && !strcasecmp(router->nickname, rs->nickname))
 -        router->is_named = 1;
 -      else
 -        router->is_named = 0;
 -    }
      /* Is it the same descriptor, or only the same identity? */
      if (!memcmp(router->cache_info.signed_descriptor_digest,
                  rs->descriptor_digest, DIGEST_LEN)) {
@@@ -2018,17 -1981,28 +2019,17 @@@
          router->cache_info.last_listed_as_valid_until = ns->valid_until;
      }
  
 -    if (!authdir) {
 -      /* If we're not an authdir, believe others. */
 -      router->is_valid = rs->is_valid;
 -      router->is_running = rs->is_running;
 -      router->is_fast = rs->is_fast;
 -      router->is_stable = rs->is_stable;
 -      router->is_possible_guard = rs->is_possible_guard;
 -      router->is_exit = rs->is_exit;
 -      router->is_bad_directory = rs->is_bad_directory;
 -      router->is_bad_exit = rs->is_bad_exit;
 -      router->is_hs_dir = rs->is_hs_dir;
 -    } else {
 +    if (authdir) {
        /* If we _are_ an authority, we should check whether this router
         * is one that will cause us to need a reachability test. */
        routerinfo_t *old_router =
 -        router_get_by_digest(router->cache_info.identity_digest);
 +        router_get_mutable_by_digest(router->cache_info.identity_digest);
        if (old_router != router) {
          router->needs_retest_if_added =
            dirserv_should_launch_reachability_test(router, old_router);
        }
      }
 -    if (router->is_running && ds) {
 +    if (rs->is_flagged_running && ds) {
        download_status_reset(&ds->v2_ns_dl_status);
      }
      if (reset_failures) {
@@@ -2037,9 -2011,10 +2038,9 @@@
    } SMARTLIST_FOREACH_JOIN_END(rs, router);
  
    /* Now update last_listed_as_valid_until from v2 networkstatuses. */
 -  /* XXXX If this is slow, we need to rethink the code. */
    SMARTLIST_FOREACH(networkstatus_v2_list, networkstatus_v2_t *, ns, {
      time_t live_until = ns->published_on + V2_NETWORKSTATUS_ROUTER_LIFETIME;
 -    SMARTLIST_FOREACH_JOIN(ns->entries, routerstatus_t *, rs,
 +    SMARTLIST_FOREACH_JOIN(ns->entries, const routerstatus_t *, rs,
                           routers, routerinfo_t *, ri,
                           memcmp(rs->identity_digest,
                                  ri->cache_info.identity_digest, DIGEST_LEN),
@@@ -2060,7 -2035,7 +2061,7 @@@
  void
  signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
  {
 -  networkstatus_t *ns = current_consensus;
 +  networkstatus_t *ns = current_ns_consensus;
    if (!ns)
      return;
  
@@@ -2068,11 -2043,11 +2069,11 @@@
      char dummy[DIGEST_LEN];
      /* instantiates the digest map. */
      memset(dummy, 0, sizeof(dummy));
 -    router_get_consensus_status_by_descriptor_digest(dummy);
 +    router_get_consensus_status_by_descriptor_digest(ns, dummy);
    }
    SMARTLIST_FOREACH(descs, signed_descriptor_t *, d,
    {
 -    routerstatus_t *rs = digestmap_get(ns->desc_digest_map,
 +    const routerstatus_t *rs = digestmap_get(ns->desc_digest_map,
                                         d->signed_descriptor_digest);
      if (rs) {
        if (ns->valid_until > d->last_listed_as_valid_until)
@@@ -2085,7 -2060,7 +2086,7 @@@
   * return the result in a newly allocated string.  Used only by controller
   * interface (for now.) */
  char *
 -networkstatus_getinfo_helper_single(routerstatus_t *rs)
 +networkstatus_getinfo_helper_single(const routerstatus_t *rs)
  {
    char buf[RS_ENTRY_LEN+1];
    routerstatus_format_entry(buf, sizeof(buf), rs, NULL, NS_CONTROL_PORT);
@@@ -2118,9 -2093,6 +2119,9 @@@ networkstatus_getinfo_by_purpose(const 
  
    statuses = smartlist_create();
    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
 +    node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
 +    if (!node)
 +      continue;
      if (ri->cache_info.published_on < cutoff)
        continue;
      if (ri->purpose != purpose)
@@@ -2128,7 -2100,7 +2129,7 @@@
      if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE)
        dirserv_set_router_is_running(ri, now);
      /* then generate and write out status lines for each of them */
 -    set_routerstatus_from_routerinfo(&rs, ri, now, 0, 0, 0);
 +    set_routerstatus_from_routerinfo(&rs, node, ri, now, 0, 0, 0);
      smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs));
    });
  
@@@ -2244,7 -2216,7 +2245,7 @@@ getinfo_helper_networkstatus(control_co
                               const char *question, char **answer,
                               const char **errmsg)
  {
 -  routerstatus_t *status;
 +  const routerstatus_t *status;
    (void) conn;
  
    if (!current_consensus) {
@@@ -2255,7 -2227,7 +2256,7 @@@
    if (!strcmp(question, "ns/all")) {
      smartlist_t *statuses = smartlist_create();
      SMARTLIST_FOREACH(current_consensus->routerstatus_list,
 -                      routerstatus_t *, rs,
 +                      const routerstatus_t *, rs,
        {
          smartlist_add(statuses, networkstatus_getinfo_helper_single(rs));
        });
@@@ -2299,9 -2271,8 +2300,9 @@@ networkstatus_free_all(void
  
    digestmap_free(v2_download_status_map, _tor_free);
    v2_download_status_map = NULL;
 -  networkstatus_vote_free(current_consensus);
 -  current_consensus = NULL;
 +  networkstatus_vote_free(current_ns_consensus);
 +  networkstatus_vote_free(current_md_consensus);
 +  current_md_consensus = current_ns_consensus = NULL;
  
    for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
      consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i];
diff --combined src/or/rendclient.c
index 88c9fd7,b8526b6..1907d5a
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@@ -16,7 -16,6 +16,7 @@@
  #include "connection_edge.h"
  #include "directory.h"
  #include "main.h"
 +#include "nodelist.h"
  #include "relay.h"
  #include "rendclient.h"
  #include "rendcommon.h"
@@@ -185,6 -184,7 +185,7 @@@ rend_client_send_introduction(origin_ci
    /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
     * to avoid buffer overflows? */
    r = crypto_pk_public_hybrid_encrypt(intro_key, payload+DIGEST_LEN,
+                                       sizeof(payload)-DIGEST_LEN,
                                        tmp,
                                        (int)(dh_offset+DH_KEY_LEN),
                                        PK_PKCS1_OAEP_PADDING, 0);
@@@ -415,7 -415,7 +416,7 @@@ directory_get_from_hs_dir(const char *d
    SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, {
      if (lookup_last_hid_serv_request(dir, desc_id_base32, 0, 0) +
              REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
 -        !router_get_by_digest(dir->identity_digest))
 +        !router_get_by_id_digest(dir->identity_digest))
        SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
    });
  
@@@ -740,6 -740,7 +741,6 @@@ rend_client_get_random_intro(const rend
    int i;
    rend_cache_entry_t *entry;
    rend_intro_point_t *intro;
 -  routerinfo_t *router;
  
    if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) {
      log_warn(LD_REND,
@@@ -756,12 -757,11 +757,12 @@@
    intro = smartlist_get(entry->parsed->intro_nodes, i);
    /* Do we need to look up the router or is the extend info complete? */
    if (!intro->extend_info->onion_key) {
 +    const node_t *node;
      if (tor_digest_is_zero(intro->extend_info->identity_digest))
 -      router = router_get_by_hexdigest(intro->extend_info->nickname);
 +      node = node_get_by_hex_id(intro->extend_info->nickname);
      else
 -      router = router_get_by_digest(intro->extend_info->identity_digest);
 -    if (!router) {
 +      node = node_get_by_id(intro->extend_info->identity_digest);
 +    if (!node) {
        log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
                 intro->extend_info->nickname);
        rend_intro_point_free(intro);
@@@ -769,7 -769,7 +770,7 @@@
        goto again;
      }
      extend_info_free(intro->extend_info);
 -    intro->extend_info = extend_info_from_router(router);
 +    intro->extend_info = extend_info_from_node(node);
    }
    return extend_info_dup(intro->extend_info);
  }
diff --combined src/or/rendservice.c
index 9f775ef,1d64cf4..9f364b0
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@@ -14,7 -14,6 +14,7 @@@
  #include "config.h"
  #include "directory.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "rendclient.h"
  #include "rendcommon.h"
  #include "rendservice.h"
@@@ -929,7 -928,8 +929,8 @@@ rend_service_introduce(origin_circuit_
    /* Next N bytes is encrypted with service key */
    note_crypto_pk_op(REND_SERVER);
    r = crypto_pk_private_hybrid_decrypt(
-        intro_key,buf,(char*)(request+DIGEST_LEN),request_len-DIGEST_LEN,
+        intro_key,buf,sizeof(buf),
+        (char*)(request+DIGEST_LEN),request_len-DIGEST_LEN,
         PK_PKCS1_OAEP_PADDING,1);
    if (r<0) {
      log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 cell.");
@@@ -1002,7 -1002,7 +1003,7 @@@
    } else {
      char *rp_nickname;
      size_t nickname_field_len;
 -    routerinfo_t *router;
 +    const node_t *node;
      int version;
      if (*buf == 1) {
        rp_nickname = buf+1;
@@@ -1029,8 -1029,8 +1030,8 @@@
      len -= nickname_field_len;
      len -= rp_nickname - buf; /* also remove header space used by version, if
                                 * any */
 -    router = router_get_by_nickname(rp_nickname, 0);
 -    if (!router) {
 +    node = node_get_by_nickname(rp_nickname, 0);
 +    if (!node) {
        log_info(LD_REND, "Couldn't find router %s named in introduce2 cell.",
                 escaped_safe_str_client(rp_nickname));
        /* XXXX Add a no-such-router reason? */
@@@ -1038,7 -1038,7 +1039,7 @@@
        goto err;
      }
  
 -    extend_info = extend_info_from_router(router);
 +    extend_info = extend_info_from_node(node);
    }
  
    if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
@@@ -1366,7 -1366,8 +1367,8 @@@ rend_service_intro_has_opened(origin_ci
      goto err;
    len += 20;
    note_crypto_pk_op(REND_SERVER);
-   r = crypto_pk_private_sign_digest(intro_key, buf+len, buf, len);
+   r = crypto_pk_private_sign_digest(intro_key, buf+len, sizeof(buf)-len,
+                                     buf, len);
    if (r<0) {
      log_warn(LD_BUG, "Internal error: couldn't sign introduction request.");
      reason = END_CIRC_REASON_INTERNAL;
@@@ -1579,7 -1580,7 +1581,7 @@@ directory_post_to_hs_dir(rend_service_d
                                  hs_dir->identity_digest))
          /* Don't upload descriptor if we succeeded in doing so last time. */
          continue;
 -      if (!router_get_by_digest(hs_dir->identity_digest)) {
 +      if (!router_get_by_id_digest(hs_dir->identity_digest)) {
          log_info(LD_REND, "Not sending publish request for v2 descriptor to "
                            "hidden service directory '%s'; we don't have its "
                            "router descriptor. Queuing for later upload.",
@@@ -1756,19 -1757,19 +1758,19 @@@ voi
  rend_services_introduce(void)
  {
    int i,j,r;
 -  routerinfo_t *router;
 +  const node_t *node;
    rend_service_t *service;
    rend_intro_point_t *intro;
    int changed, prev_intro_nodes;
 -  smartlist_t *intro_routers;
 +  smartlist_t *intro_nodes;
    time_t now;
    or_options_t *options = get_options();
  
 -  intro_routers = smartlist_create();
 +  intro_nodes = smartlist_create();
    now = time(NULL);
  
    for (i=0; i < smartlist_len(rend_service_list); ++i) {
 -    smartlist_clear(intro_routers);
 +    smartlist_clear(intro_nodes);
      service = smartlist_get(rend_service_list, i);
  
      tor_assert(service);
@@@ -1788,8 -1789,8 +1790,8 @@@
         service. */
      for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
        intro = smartlist_get(service->intro_nodes, j);
 -      router = router_get_by_digest(intro->extend_info->identity_digest);
 -      if (!router || !find_intro_circuit(intro, service->pk_digest)) {
 +      node = node_get_by_id(intro->extend_info->identity_digest);
 +      if (!node || !find_intro_circuit(intro, service->pk_digest)) {
          log_info(LD_REND,"Giving up on %s as intro point for %s.",
                   intro->extend_info->nickname, service->service_id);
          if (service->desc) {
@@@ -1808,8 -1809,8 +1810,8 @@@
          smartlist_del(service->intro_nodes,j--);
          changed = 1;
        }
 -      if (router)
 -        smartlist_add(intro_routers, router);
 +      if (node)
 +        smartlist_add(intro_nodes, (void*)node);
      }
  
      /* We have enough intro points, and the intro points we thought we had were
@@@ -1838,26 -1839,26 +1840,26 @@@
  #define NUM_INTRO_POINTS_INIT (NUM_INTRO_POINTS + 2)
      for (j=prev_intro_nodes; j < (prev_intro_nodes == 0 ?
               NUM_INTRO_POINTS_INIT : NUM_INTRO_POINTS); ++j) {
 -      router_crn_flags_t flags = CRN_NEED_UPTIME;
 +      router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC;
        if (get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION)
          flags |= CRN_ALLOW_INVALID;
 -      router = router_choose_random_node(intro_routers,
 -                                         options->ExcludeNodes, flags);
 -      if (!router) {
 +      node = router_choose_random_node(intro_nodes,
 +                                       options->ExcludeNodes, flags);
 +      if (!node) {
          log_warn(LD_REND,
                   "Could only establish %d introduction points for %s.",
                   smartlist_len(service->intro_nodes), service->service_id);
          break;
        }
        changed = 1;
 -      smartlist_add(intro_routers, router);
 +      smartlist_add(intro_nodes, (void*)node);
        intro = tor_malloc_zero(sizeof(rend_intro_point_t));
 -      intro->extend_info = extend_info_from_router(router);
 +      intro->extend_info = extend_info_from_node(node);
        intro->intro_key = crypto_new_pk_env();
        tor_assert(!crypto_pk_generate_key(intro->intro_key));
        smartlist_add(service->intro_nodes, intro);
        log_info(LD_REND, "Picked router %s as an intro point for %s.",
 -               router->nickname, service->service_id);
 +               node_get_nickname(node), service->service_id);
      }
  
      /* If there's no need to launch new circuits, stop here. */
@@@ -1874,7 -1875,7 +1876,7 @@@
        }
      }
    }
 -  smartlist_free(intro_routers);
 +  smartlist_free(intro_nodes);
  }
  
  /** Regenerate and upload rendezvous service descriptors for all
diff --combined src/or/routerlist.c
index 539e540,e29b4c4..96cc01b
--- 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;
@@@ -1724,14 -1718,15 +1724,14 @@@
    bandwidths = tor_malloc_zero(sizeof(double)*smartlist_len(sl));
  
    // Cycle through smartlist and total the bandwidth.
 -  for (i = 0; i < (unsigned)smartlist_len(sl); ++i) {
 +  SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
      int is_exit = 0, is_guard = 0, is_dir = 0, this_bw = 0, is_me = 0;
      double weight = 1;
 -    if (statuses) {
 -      routerstatus_t *status = smartlist_get(sl, i);
 -      is_exit = status->is_exit;
 -      is_guard = status->is_possible_guard;
 -      is_dir = (status->dir_port != 0);
 -      if (!status->has_bandwidth) {
 +    is_exit = node->is_exit;
 +    is_guard = node->is_possible_guard;
 +    is_dir = node_is_dir(node);
 +    if (node->rs) {
 +      if (!node->rs->has_bandwidth) {
          tor_free(bandwidths);
          /* This should never happen, unless all the authorites downgrade
           * to 0.2.0 or rogue routerstatuses get inserted into our consensus. */
@@@ -1740,17 -1735,26 +1740,17 @@@
                   "old router selection algorithm.");
          return NULL;
        }
 -      this_bw = kb_to_bytes(status->bandwidth);
 -      if (router_digest_is_me(status->identity_digest))
 -        is_me = 1;
 +      this_bw = kb_to_bytes(node->rs->bandwidth);
 +    } else if (node->ri) {
 +      /* bridge or other descriptor not in our consensus */
 +      this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
 +      have_unknown = 1;
      } else {
 -      routerstatus_t *rs;
 -      routerinfo_t *router = smartlist_get(sl, i);
 -      rs = router_get_consensus_status_by_id(
 -             router->cache_info.identity_digest);
 -      is_exit = router->is_exit;
 -      is_guard = router->is_possible_guard;
 -      is_dir = (router->dir_port != 0);
 -      if (rs && rs->has_bandwidth) {
 -        this_bw = kb_to_bytes(rs->bandwidth);
 -      } else { /* bridge or other descriptor not in our consensus */
 -        this_bw = bridge_get_advertised_bandwidth_bounded(router);
 -        have_unknown = 1;
 -      }
 -      if (router_digest_is_me(router->cache_info.identity_digest))
 -        is_me = 1;
 +      /* We can't use this one. */
 +      continue;
      }
 +    is_me = router_digest_is_me(node->identity);
 +
      if (is_guard && is_exit) {
        weight = (is_dir ? Wdb*Wd : Wd);
      } else if (is_guard) {
@@@ -1761,11 -1765,11 +1761,11 @@@
        weight = (is_dir ? Wmb*Wm : Wm);
      }
  
 -    bandwidths[i] = weight*this_bw;
 +    bandwidths[node_sl_idx] = weight*this_bw;
      weighted_bw += weight*this_bw;
      if (is_me)
        sl_last_weighted_bw_of_me = weight*this_bw;
 -  }
 +  } SMARTLIST_FOREACH_END(node);
  
    /* XXXX022 this is a kludge to expose these values. */
    sl_last_total_weighted_bw = weighted_bw;
@@@ -1813,9 -1817,12 +1813,9 @@@
  }
  
  /** Helper function:
 - * choose a random element of smartlist <b>sl</b>, weighted by
 + * choose a random node_t element of smartlist <b>sl</b>, weighted by
   * the advertised bandwidth of each element.
   *
 - * If <b>statuses</b> is zero, then <b>sl</b> is a list of
 - * routerinfo_t's. Otherwise it's a list of routerstatus_t's.
 - *
   * If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all
   * nodes' bandwidth equally regardless of their Exit status, since there may
   * be some in the list because they exit to obscure ports. If
@@@ -1825,11 -1832,13 +1825,11 @@@
   * guard node: consider all guard's bandwidth equally. Otherwise, weight
   * guards proportionally less.
   */
 -static void *
 -smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
 -                              int statuses)
 +static const node_t *
 +smartlist_choose_node_by_bandwidth(smartlist_t *sl,
 +                                   bandwidth_weight_rule_t rule)
  {
 -  unsigned int i;
 -  routerinfo_t *router;
 -  routerstatus_t *status=NULL;
 +  unsigned i;
    int32_t *bandwidths;
    int is_exit;
    int is_guard;
@@@ -1870,34 -1879,49 +1870,34 @@@
    guard_bits = bitarray_init_zero(smartlist_len(sl));
  
    /* Iterate over all the routerinfo_t or routerstatus_t, and */
 -  for (i = 0; i < (unsigned)smartlist_len(sl); ++i) {
 +  SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
      /* first, learn what bandwidth we think i has */
      int is_known = 1;
      int32_t flags = 0;
      uint32_t this_bw = 0;
 -    if (statuses) {
 -      status = smartlist_get(sl, i);
 -      if (router_digest_is_me(status->identity_digest))
 -        me_idx = i;
 -      router = router_get_by_digest(status->identity_digest);
 -      is_exit = status->is_exit;
 -      is_guard = status->is_possible_guard;
 -      if (status->has_bandwidth) {
 -        this_bw = kb_to_bytes(status->bandwidth);
 +    i = node_sl_idx;
 +
 +    if (router_digest_is_me(node->identity))
 +      me_idx = node_sl_idx;
 +
 +    is_exit = node->is_exit;
 +    is_guard = node->is_possible_guard;
 +    if (node->rs) {
 +      if (node->rs->has_bandwidth) {
 +        this_bw = kb_to_bytes(node->rs->bandwidth);
        } else { /* guess */
          /* XXX022 once consensuses always list bandwidths, we can take
           * this guessing business out. -RD */
          is_known = 0;
 -        flags = status->is_fast ? 1 : 0;
 +        flags = node->rs->is_fast ? 1 : 0;
          flags |= is_exit ? 2 : 0;
          flags |= is_guard ? 4 : 0;
        }
 -    } else {
 -      routerstatus_t *rs;
 -      router = smartlist_get(sl, i);
 -      rs = router_get_consensus_status_by_id(
 -             router->cache_info.identity_digest);
 -      if (router_digest_is_me(router->cache_info.identity_digest))
 -        me_idx = i;
 -      is_exit = router->is_exit;
 -      is_guard = router->is_possible_guard;
 -      if (rs && rs->has_bandwidth) {
 -        this_bw = kb_to_bytes(rs->bandwidth);
 -      } else if (rs) { /* guess; don't trust the descriptor */
 -        /* XXX022 once consensuses always list bandwidths, we can take
 -         * this guessing business out. -RD */
 -        is_known = 0;
 -        flags = router->is_fast ? 1 : 0;
 -        flags |= is_exit ? 2 : 0;
 -        flags |= is_guard ? 4 : 0;
 -      } else /* bridge or other descriptor not in our consensus */
 -        this_bw = bridge_get_advertised_bandwidth_bounded(router);
 +    } else if (node->ri) {
 +      /* Must be a bridge if we're willing to use it */
 +      this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
      }
 +
      if (is_exit)
        bitarray_set(exit_bits, i);
      if (is_guard)
@@@ -1917,9 -1941,9 +1917,9 @@@
          total_nonexit_bw += this_bw;
      } else {
        ++n_unknown;
 -      bandwidths[i] = -flags;
 +      bandwidths[node_sl_idx] = -flags;
      }
 -  }
 +  } SMARTLIST_FOREACH_END(node);
  
    /* Now, fill in the unknown values. */
    if (n_unknown) {
@@@ -2061,23 -2085,40 +2061,23 @@@
    return smartlist_get(sl, i);
  }
  
 -/** Choose a random element of router list <b>sl</b>, weighted by
 - * the advertised bandwidth of each router.
 - */
 -routerinfo_t *
 -routerlist_sl_choose_by_bandwidth(smartlist_t *sl,
 -                                  bandwidth_weight_rule_t rule)
 -{
 -  routerinfo_t *ret;
 -  if ((ret = smartlist_choose_by_bandwidth_weights(sl, rule, 0))) {
 -    return ret;
 -  } else {
 -    return smartlist_choose_by_bandwidth(sl, rule, 0);
 -  }
 -}
 -
  /** Choose a random element of status list <b>sl</b>, weighted by
 - * the advertised bandwidth of each status.
 - */
 -routerstatus_t *
 -routerstatus_sl_choose_by_bandwidth(smartlist_t *sl,
 -                                    bandwidth_weight_rule_t rule)
 -{
 -  /* We are choosing neither exit nor guard here. Weight accordingly. */
 -  routerstatus_t *ret;
 -  if ((ret = smartlist_choose_by_bandwidth_weights(sl, rule, 1))) {
 + * the advertised bandwidth of each node */
 +const node_t *
 +node_sl_choose_by_bandwidth(smartlist_t *sl,
 +                            bandwidth_weight_rule_t rule)
 +{ /*XXXX MOVE */
 +  const node_t *ret;
 +  if ((ret = smartlist_choose_node_by_bandwidth_weights(sl, rule))) {
      return ret;
    } else {
 -    return smartlist_choose_by_bandwidth(sl, rule, 1);
 +    return smartlist_choose_node_by_bandwidth(sl, rule);
    }
  }
  
 -/** Return a random running router from the routerlist. Never
 - * pick a node whose routerinfo is in
 - * <b>excludedsmartlist</b>, or whose routerinfo matches <b>excludedset</b>,
 +/** Return a random running node from the nodelist. Never
 + * pick a node that is in
 + * <b>excludedsmartlist</b>, or which matches <b>excludedset</b>,
   * even if they are the only nodes available.
   * If <b>CRN_NEED_UPTIME</b> is set in flags and any router has more than
   * a minimum uptime, return one of those.
@@@ -2089,26 -2130,21 +2089,26 @@@
   * If <b>CRN_WEIGHT_AS_EXIT</b> is set in flags, we weight bandwidths as if
   * picking an exit node, otherwise we weight bandwidths for picking a relay
   * node (that is, possibly discounting exit nodes).
 + * If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that
 + * have a routerinfo or microdescriptor -- that is, enough info to be
 + * used to build a circuit.
   */
 -routerinfo_t *
 +const node_t *
  router_choose_random_node(smartlist_t *excludedsmartlist,
                            routerset_t *excludedset,
                            router_crn_flags_t flags)
 -{
 +{ /* XXXX MOVE */
    const int need_uptime = (flags & CRN_NEED_UPTIME) != 0;
    const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0;
    const int need_guard = (flags & CRN_NEED_GUARD) != 0;
    const int allow_invalid = (flags & CRN_ALLOW_INVALID) != 0;
    const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0;
 +  const int need_desc = (flags & CRN_NEED_DESC) != 0;
  
    smartlist_t *sl=smartlist_create(),
 -              *excludednodes=smartlist_create();
 -  routerinfo_t *choice = NULL, *r;
 +    *excludednodes=smartlist_create();
 +  const node_t *choice = NULL;
 +  const routerinfo_t *r;
    bandwidth_weight_rule_t rule;
  
    tor_assert(!(weight_for_exit && need_guard));
@@@ -2118,30 -2154,29 +2118,30 @@@
    /* Exclude relays that allow single hop exit circuits, if the user
     * wants to (such relays might be risky) */
    if (get_options()->ExcludeSingleHopRelays) {
 -    routerlist_t *rl = router_get_routerlist();
 -    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
 -      if (r->allow_single_hop_exits) {
 -        smartlist_add(excludednodes, r);
 +    SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
 +      if (node_allows_single_hop_exits(node)) {
 +        smartlist_add(excludednodes, node);
        });
    }
  
    if ((r = routerlist_find_my_routerinfo())) {
 -    smartlist_add(excludednodes, r);
 -    routerlist_add_family(excludednodes, r);
 +    const node_t *me = node_get_by_id(r->cache_info.identity_digest);
 +    if (me)
 +      smartlist_add(excludednodes, (void *)me);
 +    routerlist_add_nodes_in_family(excludednodes, r);
    }
  
 -  router_add_running_routers_to_smartlist(sl, allow_invalid,
 -                                          need_uptime, need_capacity,
 -                                          need_guard);
 +  router_add_running_nodes_to_smartlist(sl, allow_invalid,
 +                                        need_uptime, need_capacity,
 +                                        need_guard, need_desc);
    smartlist_subtract(sl,excludednodes);
    if (excludedsmartlist)
      smartlist_subtract(sl,excludedsmartlist);
    if (excludedset)
 -    routerset_subtract_routers(sl,excludedset);
 +    routerset_subtract_nodes(sl,excludedset);
  
    // Always weight by bandwidth
 -  choice = routerlist_sl_choose_by_bandwidth(sl, rule);
 +  choice = node_sl_choose_by_bandwidth(sl, rule);
  
    smartlist_free(sl);
    if (!choice && (need_uptime || need_capacity || need_guard)) {
@@@ -2164,88 -2199,35 +2164,88 @@@
    return choice;
  }
  
 -/** Helper: Return true iff the <b>identity_digest</b> and <b>nickname</b>
 - * combination of a router, encoded in hexadecimal, matches <b>hexdigest</b>
 - * (which is optionally prefixed with a single dollar sign).  Return false if
 - * <b>hexdigest</b> is malformed, or it doesn't match.  */
 -static INLINE int
 -hex_digest_matches(const char *hexdigest, const char *identity_digest,
 -                   const char *nickname, int is_named)
 +/** Helper: given an extended nickname in <b>hexdigest</b> try to decode it.
 + * Return 0 on success, -1 on failure.  Store the result into the
 + * DIGEST_LEN-byte buffer at <b>digest_out</b>, the single character at
 + * <b>nickname_qualifier_char_out</b>, and the MAXNICKNAME_LEN+1-byte buffer
 + * at <b>nickname_out</b>.
 + *
 + * The recognized format is:
 + *   HexName = Dollar? HexDigest NamePart?
 + *   Dollar = '?'
 + *   HexDigest = HexChar*20
 + *   HexChar = 'a'..'f' | 'A'..'F' | '0'..'9'
 + *   NamePart = QualChar Name
 + *   QualChar = '=' | '~'
 + *   Name = NameChar*(1..MAX_NICKNAME_LEN)
 + *   NameChar = Any ASCII alphanumeric character
 + */
 +int
 +hex_digest_nickname_decode(const char *hexdigest,
 +                           char *digest_out,
 +                           char *nickname_qualifier_char_out,
 +                           char *nickname_out)
  {
 -  char digest[DIGEST_LEN];
    size_t len;
 +
    tor_assert(hexdigest);
    if (hexdigest[0] == '$')
      ++hexdigest;
  
    len = strlen(hexdigest);
 -  if (len < HEX_DIGEST_LEN)
 +  if (len < HEX_DIGEST_LEN) {
 +    return -1;
 +  } else if (len > HEX_DIGEST_LEN && (hexdigest[HEX_DIGEST_LEN] == '=' ||
 +                                    hexdigest[HEX_DIGEST_LEN] == '~') &&
 +           len <= HEX_DIGEST_LEN+1+MAX_NICKNAME_LEN) {
 +    *nickname_qualifier_char_out = hexdigest[HEX_DIGEST_LEN];
 +    strlcpy(nickname_out, hexdigest+HEX_DIGEST_LEN+1 , MAX_NICKNAME_LEN+1);
 +  } else if (len == HEX_DIGEST_LEN) {
 +    ;
 +  } else {
 +    return -1;
 +  }
 +
 +  if (base16_decode(digest_out, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
 +    return -1;
 +  return 0;
 +}
 +
 +/** Helper: Return true iff the <b>identity_digest</b> and <b>nickname</b>
 + * combination of a router, encoded in hexadecimal, matches <b>hexdigest</b>
 + * (which is optionally prefixed with a single dollar sign).  Return false if
 + * <b>hexdigest</b> is malformed, or it doesn't match.  */
 +static int
 +hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest,
 +                            const char *nickname, int is_named)
 +{
 +  char digest[DIGEST_LEN];
 +  char nn_char='\0';
 +  char nn_buf[MAX_NICKNAME_LEN+1];
 +
 +  if (hex_digest_nickname_decode(hexdigest, digest, &nn_char, nn_buf) == -1)
      return 0;
 -  else if (len > HEX_DIGEST_LEN &&
 -           (hexdigest[HEX_DIGEST_LEN] == '=' ||
 -            hexdigest[HEX_DIGEST_LEN] == '~')) {
 -    if (strcasecmp(hexdigest+HEX_DIGEST_LEN+1, nickname))
 +
 +  if (nn_char == '=' || nn_char == '~') {
 +    if (strcasecmp(nn_buf, nickname))
        return 0;
 -    if (hexdigest[HEX_DIGEST_LEN] == '=' && !is_named)
 +    if (nn_char == '=' && !is_named)
        return 0;
    }
  
 -  if (base16_decode(digest, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
 -    return 0;
 -  return (!memcmp(digest, identity_digest, DIGEST_LEN));
 +  return !memcmp(digest, identity_digest, DIGEST_LEN);
 +}
 +
 +/* Return true iff <b>router</b> is listed as named in the current
 + * consensus. */
 +static int
 +router_is_named(const routerinfo_t *router)
 +{
 +  const char *digest =
 +    networkstatus_get_router_digest_by_nickname(router->nickname);
 +
 +  return (digest &&
 +          !memcmp(digest, router->cache_info.identity_digest, DIGEST_LEN));
  }
  
  /** Return true iff the digest of <b>router</b>'s identity key,
@@@ -2253,12 -2235,10 +2253,12 @@@
   * optionally prefixed with a single dollar sign).  Return false if
   * <b>hexdigest</b> is malformed, or it doesn't match.  */
  static INLINE int
 -router_hex_digest_matches(routerinfo_t *router, const char *hexdigest)
 +router_hex_digest_matches(const routerinfo_t *router, const char *hexdigest)
  {
 -  return hex_digest_matches(hexdigest, router->cache_info.identity_digest,
 -                            router->nickname, router->is_named);
 +  return hex_digest_nickname_matches(hexdigest,
 +                                     router->cache_info.identity_digest,
 +                                     router->nickname,
 +                                     router_is_named(router));
  }
  
  /** Return true if <b>router</b>'s nickname matches <b>nickname</b>
@@@ -2266,43 -2246,20 +2266,43 @@@
   * matches a hexadecimal value stored in <b>nickname</b>.  Return
   * false otherwise. */
  static int
 -router_nickname_matches(routerinfo_t *router, const char *nickname)
 +router_nickname_matches(const routerinfo_t *router, const char *nickname)
  {
    if (nickname[0]!='$' && !strcasecmp(router->nickname, nickname))
      return 1;
    return router_hex_digest_matches(router, nickname);
  }
  
 +/** Return true if <b>node</b>'s nickname matches <b>nickname</b>
 + * (case-insensitive), or if <b>node's</b> identity key digest
 + * matches a hexadecimal value stored in <b>nickname</b>.  Return
 + * false otherwise. */
 +static int
 +node_nickname_matches(const node_t *node, const char *nickname)
 +{
 +  const char *n = node_get_nickname(node);
 +  if (n && nickname[0]!='$' && !strcasecmp(n, nickname))
 +    return 1;
 +  return hex_digest_nickname_matches(nickname,
 +                                     node->identity,
 +                                     n,
 +                                     node_is_named(node));
 +}
 +
  /** Return the router in our routerlist whose (case-insensitive)
   * nickname or (case-sensitive) hexadecimal key digest is
   * <b>nickname</b>.  Return NULL if no such router is known.
   */
 -routerinfo_t *
 +const routerinfo_t *
  router_get_by_nickname(const char *nickname, int warn_if_unnamed)
  {
 +#if 1
 +  const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
 +  if (node)
 +    return node->ri;
 +  else
 +    return NULL;
 +#else
    int maybedigest;
    char digest[DIGEST_LEN];
    routerinfo_t *best_match=NULL;
@@@ -2348,14 -2305,15 +2348,14 @@@
      if (warn_if_unnamed && n_matches > 1) {
        smartlist_t *fps = smartlist_create();
        int any_unwarned = 0;
 -      SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
 -        {
 +      SMARTLIST_FOREACH_BEGIN(routerlist->routers, routerinfo_t *, router) {
            routerstatus_t *rs;
            char *desc;
            size_t dlen;
            char fp[HEX_DIGEST_LEN+1];
            if (strcasecmp(router->nickname, nickname))
              continue;
 -          rs = router_get_consensus_status_by_id(
 +          rs = router_get_mutable_consensus_status_by_id(
                                            router->cache_info.identity_digest);
            if (rs && !rs->name_lookup_warned) {
              rs->name_lookup_warned = 1;
@@@ -2368,7 -2326,7 +2368,7 @@@
            tor_snprintf(desc, dlen, "\"$%s\" for the one at %s:%d",
                         fp, router->address, router->or_port);
            smartlist_add(fps, desc);
 -        });
 +      } SMARTLIST_FOREACH_END(router);
        if (any_unwarned) {
          char *alternatives = smartlist_join_strings(fps, "; ",0,NULL);
          log_warn(LD_CONFIG,
@@@ -2381,7 -2339,7 +2381,7 @@@
        SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp));
        smartlist_free(fps);
      } else if (warn_if_unnamed) {
 -      routerstatus_t *rs = router_get_consensus_status_by_id(
 +      routerstatus_t *rs = router_get_mutable_consensus_status_by_id(
            best_match->cache_info.identity_digest);
        if (rs && !rs->name_lookup_warned) {
          char fp[HEX_DIGEST_LEN+1];
@@@ -2397,8 -2355,8 +2397,8 @@@
      }
      return best_match;
    }
 -
    return NULL;
 +#endif
  }
  
  /** Try to find a routerinfo for <b>digest</b>. If we don't have one,
@@@ -2407,7 -2365,7 +2407,7 @@@
  int
  router_digest_version_as_new_as(const char *digest, const char *cutoff)
  {
 -  routerinfo_t *router = router_get_by_digest(digest);
 +  const routerinfo_t *router = router_get_by_id_digest(digest);
    if (!router)
      return 1;
    return tor_version_as_new_as(router->platform, cutoff);
@@@ -2461,20 -2419,44 +2461,20 @@@ hexdigest_to_digest(const char *hexdige
  
  /** Return the router in our routerlist whose hexadecimal key digest
   * is <b>hexdigest</b>.  Return NULL if no such router is known. */
 -routerinfo_t *
 +const routerinfo_t *
  router_get_by_hexdigest(const char *hexdigest)
  {
 -  char digest[DIGEST_LEN];
 -  size_t len;
 -  routerinfo_t *ri;
 -
 -  tor_assert(hexdigest);
 -  if (!routerlist)
 -    return NULL;
 -  if (hexdigest[0]=='$')
 -    ++hexdigest;
 -  len = strlen(hexdigest);
 -  if (hexdigest_to_digest(hexdigest, digest) < 0)
 +  if (is_legal_nickname(hexdigest))
      return NULL;
  
 -  ri = router_get_by_digest(digest);
 -
 -  if (ri && len > HEX_DIGEST_LEN) {
 -    if (hexdigest[HEX_DIGEST_LEN] == '=') {
 -      if (strcasecmp(ri->nickname, hexdigest+HEX_DIGEST_LEN+1) ||
 -          !ri->is_named)
 -        return NULL;
 -    } else if (hexdigest[HEX_DIGEST_LEN] == '~') {
 -      if (strcasecmp(ri->nickname, hexdigest+HEX_DIGEST_LEN+1))
 -        return NULL;
 -    } else {
 -      return NULL;
 -    }
 -  }
 -
 -  return ri;
 +  /* It's not a legal nickname, so it must be a hexdigest or nothing. */
 +  return router_get_by_nickname(hexdigest, 1);
  }
  
 -/** Return the router in our routerlist whose 20-byte key digest
 - * is <b>digest</b>.  Return NULL if no such router is known. */
 +/** As router_get_by_id_digest,but return a pointer that you're allowed to
 + * modify */
  routerinfo_t *
 -router_get_by_digest(const char *digest)
 +router_get_mutable_by_digest(const char *digest)
  {
    tor_assert(digest);
  
@@@ -2485,14 -2467,6 +2485,14 @@@
    return rimap_get(routerlist->identity_map, digest);
  }
  
 +/** Return the router in our routerlist whose 20-byte key digest
 + * is <b>digest</b>.  Return NULL if no such router is known. */
 +const routerinfo_t *
 +router_get_by_id_digest(const char *digest)
 +{
 +  return router_get_mutable_by_digest(digest);
 +}
 +
  /** Return the router in our routerlist whose 20-byte descriptor
   * is <b>digest</b>.  Return NULL if no such router is known. */
  signed_descriptor_t *
@@@ -2543,7 -2517,7 +2543,7 @@@ extrainfo_get_by_descriptor_digest(cons
   * The caller must not free the string returned.
   */
  static const char *
 -signed_descriptor_get_body_impl(signed_descriptor_t *desc,
 +signed_descriptor_get_body_impl(const signed_descriptor_t *desc,
                                  int with_annotations)
  {
    const char *r = NULL;
@@@ -2592,7 -2566,7 +2592,7 @@@
   * The caller must not free the string returned.
   */
  const char *
 -signed_descriptor_get_body(signed_descriptor_t *desc)
 +signed_descriptor_get_body(const signed_descriptor_t *desc)
  {
    return signed_descriptor_get_body_impl(desc, 0);
  }
@@@ -2600,7 -2574,7 +2600,7 @@@
  /** As signed_descriptor_get_body(), but points to the beginning of the
   * annotations section rather than the beginning of the descriptor. */
  const char *
 -signed_descriptor_get_annotations(signed_descriptor_t *desc)
 +signed_descriptor_get_annotations(const signed_descriptor_t *desc)
  {
    return signed_descriptor_get_body_impl(desc, 1);
  }
@@@ -2653,6 -2627,7 +2653,6 @@@ routerinfo_free(routerinfo_t *router
    }
    addr_policy_list_free(router->exit_policy);
  
 -  /* XXXX Remove if this turns out to affect performance. */
    memset(router, 77, sizeof(routerinfo_t));
  
    tor_free(router);
@@@ -2667,6 -2642,7 +2667,6 @@@ extrainfo_free(extrainfo_t *extrainfo
    tor_free(extrainfo->cache_info.signed_descriptor_body);
    tor_free(extrainfo->pending_sig);
  
 -  /* XXXX remove this if it turns out to slow us down. */
    memset(extrainfo, 88, sizeof(extrainfo_t)); /* debug bad memory usage */
    tor_free(extrainfo);
  }
@@@ -2680,6 -2656,7 +2680,6 @@@ signed_descriptor_free(signed_descripto
  
    tor_free(sd->signed_descriptor_body);
  
 -  /* XXXX remove this once more bugs go away. */
    memset(sd, 99, sizeof(signed_descriptor_t)); /* Debug bad mem usage */
    tor_free(sd);
  }
@@@ -2785,7 -2762,8 +2785,7 @@@ routerlist_insert(routerlist_t *rl, rou
    routerinfo_t *ri_old;
    signed_descriptor_t *sd_old;
    {
 -    /* XXXX Remove if this slows us down. */
 -    routerinfo_t *ri_generated = router_get_my_routerinfo();
 +    const routerinfo_t *ri_generated = router_get_my_routerinfo();
      tor_assert(ri_generated != ri);
    }
    tor_assert(ri->cache_info.routerlist_index == -1);
@@@ -2807,7 -2785,6 +2807,7 @@@
                &ri->cache_info);
    smartlist_add(rl->routers, ri);
    ri->cache_info.routerlist_index = smartlist_len(rl->routers) - 1;
 +  nodelist_add_routerinfo(ri);
    router_dir_info_changed();
  #ifdef DEBUG_ROUTERLIST
    routerlist_assert_ok(rl);
@@@ -2828,6 -2805,7 +2828,6 @@@ extrainfo_insert(routerlist_t *rl, extr
    extrainfo_t *ei_tmp;
  
    {
 -    /* XXXX remove this code if it slows us down. */
      extrainfo_t *ei_generated = router_get_my_extrainfo();
      tor_assert(ei_generated != ei);
    }
@@@ -2873,7 -2851,8 +2873,7 @@@ static voi
  routerlist_insert_old(routerlist_t *rl, routerinfo_t *ri)
  {
    {
 -    /* XXXX remove this code if it slows us down. */
 -    routerinfo_t *ri_generated = router_get_my_routerinfo();
 +    const routerinfo_t *ri_generated = router_get_my_routerinfo();
      tor_assert(ri_generated != ri);
    }
    tor_assert(ri->cache_info.routerlist_index == -1);
@@@ -2913,8 -2892,6 +2913,8 @@@ routerlist_remove(routerlist_t *rl, rou
    tor_assert(0 <= idx && idx < smartlist_len(rl->routers));
    tor_assert(smartlist_get(rl->routers, idx) == ri);
  
 +  nodelist_remove_routerinfo(ri);
 +
    /* make sure the rephist module knows that it's not running */
    rep_hist_note_router_unreachable(ri->cache_info.identity_digest, now);
  
@@@ -3026,7 -3003,8 +3026,7 @@@ routerlist_replace(routerlist_t *rl, ro
    routerinfo_t *ri_tmp;
    extrainfo_t *ei_tmp;
    {
 -    /* XXXX Remove this if it turns out to slow us down. */
 -    routerinfo_t *ri_generated = router_get_my_routerinfo();
 +    const routerinfo_t *ri_generated = router_get_my_routerinfo();
      tor_assert(ri_generated != ri_new);
    }
    tor_assert(ri_old != ri_new);
@@@ -3036,9 -3014,6 +3036,9 @@@
    tor_assert(0 <= idx && idx < smartlist_len(rl->routers));
    tor_assert(smartlist_get(rl->routers, idx) == ri_old);
  
 +  nodelist_remove_routerinfo(ri_old);
 +  nodelist_add_routerinfo(ri_new);
 +
    router_dir_info_changed();
    if (idx >= 0) {
      smartlist_set(rl->routers, idx, ri_new);
@@@ -3185,27 -3160,28 +3185,27 @@@ routerlist_reset_warnings(void
  void
  router_set_status(const char *digest, int up)
  {
 -  routerinfo_t *router;
 -  routerstatus_t *status;
 +  node_t *node;
    tor_assert(digest);
  
    SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, d,
                      if (!memcmp(d->digest, digest, DIGEST_LEN))
                        d->is_running = up);
  
 -  router = router_get_by_digest(digest);
 -  if (router) {
 -    log_debug(LD_DIR,"Marking router '%s/%s' as %s.",
 -              router->nickname, router->address, up ? "up" : "down");
 -    if (!up && router_is_me(router) && !we_are_hibernating())
 +  node = node_get_mutable_by_id(digest);
 +  if (node) {
 +#if 0
 +    char buf[MAX_VERBOSE_NICKNAME_LEN+1];
 +    node_get_verbose_nickname(node,buf);
 +    log_debug(LD_DIR,"Marking router %s as %s.",
 +              buf, up ? "up" : "down");
 +#endif
 +    if (!up && node_is_me(node) && !we_are_hibernating())
        log_warn(LD_NET, "We just marked ourself as down. Are your external "
                 "addresses reachable?");
 -    router->is_running = up;
 -  }
 -  status = router_get_consensus_status_by_id(digest);
 -  if (status && status->is_running != up) {
 -    status->is_running = up;
 -    control_event_networkstatus_changed_single(status);
 +    node->is_running = up;
    }
 +
    router_dir_info_changed();
  }
  
@@@ -3248,7 -3224,7 +3248,7 @@@ router_add_to_routerlist(routerinfo_t *
  
    id_digest = router->cache_info.identity_digest;
  
 -  old_router = router_get_by_digest(id_digest);
 +  old_router = router_get_mutable_by_digest(id_digest);
  
    /* Make sure that we haven't already got this exact descriptor. */
    if (sdmap_get(routerlist->desc_digest_map,
@@@ -3279,12 -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. */
@@@ -3311,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)) {
@@@ -3919,7 -3894,7 +3919,7 @@@ router_load_extrainfo_from_string(cons
  static int
  signed_desc_digest_is_recognized(signed_descriptor_t *desc)
  {
 -  routerstatus_t *rs;
 +  const routerstatus_t *rs;
    networkstatus_t *consensus = networkstatus_get_latest_consensus();
    int caches = directory_caches_dir_info(get_options());
    const smartlist_t *networkstatus_v2_list = networkstatus_get_v2_list();
@@@ -3952,34 -3927,33 +3952,34 @@@ routerlist_retry_directory_downloads(ti
    router_reset_descriptor_download_failures();
    update_networkstatus_downloads(now);
    update_router_descriptor_downloads(now);
 +  update_microdesc_downloads(now);
  }
  
 -/** Return 1 if all running sufficiently-stable routers will reject
 +/** Return 1 if all running sufficiently-stable routers we can use will reject
   * addr:port, return 0 if any might accept it. */
  int
 -router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port,
 -                                          int need_uptime)
 -{
 +router_exit_policy_all_nodes_reject(uint32_t addr, uint16_t port,
 +                                    int need_uptime)
 +{ /* XXXX MOVE */
    addr_policy_result_t r;
 -  if (!routerlist) return 1;
  
 -  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
 -  {
 -    if (router->is_running &&
 -        !router_is_unreliable(router, need_uptime, 0, 0)) {
 -      r = compare_addr_to_addr_policy(addr, port, router->exit_policy);
 +  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
 +    if (node->is_running &&
 +        !node_is_unreliable(node, need_uptime, 0, 0)) {
 +
 +      r = compare_addr_to_node_policy(addr, port, node);
 +
        if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED)
          return 0; /* this one could be ok. good enough. */
      }
 -  });
 +  } SMARTLIST_FOREACH_END(node);
    return 1; /* all will reject. */
  }
  
  /** Return true iff <b>router</b> does not permit exit streams.
   */
  int
 -router_exit_policy_rejects_all(routerinfo_t *router)
 +router_exit_policy_rejects_all(const routerinfo_t *router)
  {
    return router->policy_is_reject_star;
  }
@@@ -4114,9 -4088,7 +4114,9 @@@ any_trusted_dir_is_v1_authority(void
  /** For every current directory connection whose purpose is <b>purpose</b>,
   * and where the resource being downloaded begins with <b>prefix</b>, split
   * rest of the resource into base16 fingerprints, decode them, and set the
 - * corresponding elements of <b>result</b> to a nonzero value. */
 + * corresponding elements of <b>result</b> to a nonzero value.
 + * DOCDOC purpose==microdesc
 + */
  static void
  list_pending_downloads(digestmap_t *result,
                         int purpose, const char *prefix)
@@@ -4124,23 -4096,20 +4124,23 @@@
    const size_t p_len = strlen(prefix);
    smartlist_t *tmp = smartlist_create();
    smartlist_t *conns = get_connection_array();
 +  int flags = DSR_HEX;
 +  if (purpose == DIR_PURPOSE_FETCH_MICRODESC)
 +    flags = DSR_DIGEST256|DSR_BASE64;
  
    tor_assert(result);
  
 -  SMARTLIST_FOREACH(conns, connection_t *, conn,
 -  {
 +  SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
      if (conn->type == CONN_TYPE_DIR &&
          conn->purpose == purpose &&
          !conn->marked_for_close) {
        const char *resource = TO_DIR_CONN(conn)->requested_resource;
        if (!strcmpstart(resource, prefix))
          dir_split_resource_into_fingerprints(resource + p_len,
 -                                             tmp, NULL, DSR_HEX);
 +                                             tmp, NULL, flags);
      }
 -  });
 +  } SMARTLIST_FOREACH_END(conn);
 +
    SMARTLIST_FOREACH(tmp, char *, d,
                      {
                        digestmap_set(result, d, (void*)1);
@@@ -4160,21 -4129,13 +4160,21 @@@ list_pending_descriptor_downloads(diges
    list_pending_downloads(result, purpose, "d/");
  }
  
 -/** Launch downloads for all the descriptors whose digests are listed
 - * as digests[i] for lo <= i < hi.  (Lo and hi may be out of range.)
 - * If <b>source</b> is given, download from <b>source</b>; otherwise,
 - * download from an appropriate random directory server.
 +/** DOCDOC */
 +/*XXXX NM should use digest256, if one comes into being. */
 +void
 +list_pending_microdesc_downloads(digestmap_t *result)
 +{
 +  list_pending_downloads(result, DIR_PURPOSE_FETCH_MICRODESC, "d/");
 +}
 +
 +/** Launch downloads for all the descriptors whose digests or digests256
 + * are listed as digests[i] for lo <= i < hi.  (Lo and hi may be out of
 + * range.)  If <b>source</b> is given, download from <b>source</b>;
 + * otherwise, download from an appropriate random directory server.
   */
  static void
 -initiate_descriptor_downloads(routerstatus_t *source,
 +initiate_descriptor_downloads(const routerstatus_t *source,
                                int purpose,
                                smartlist_t *digests,
                                int lo, int hi, int pds_flags)
@@@ -4182,20 -4143,6 +4182,20 @@@
    int i, n = hi-lo;
    char *resource, *cp;
    size_t r_len;
 +
 +  int digest_len = DIGEST_LEN, enc_digest_len = HEX_DIGEST_LEN;
 +  char sep = '+';
 +  int b64_256 = 0;
 +
 +  if (purpose == DIR_PURPOSE_FETCH_MICRODESC) {
 +    /* Microdescriptors are downloaded by "-"-separated base64-encoded
 +     * 256-bit digests. */
 +    digest_len = DIGEST256_LEN;
 +    enc_digest_len = BASE64_DIGEST256_LEN;
 +    sep = '-';
 +    b64_256 = 1;
 +  }
 +
    if (n <= 0)
      return;
    if (lo < 0)
@@@ -4203,19 -4150,15 +4203,19 @@@
    if (hi > smartlist_len(digests))
      hi = smartlist_len(digests);
  
 -  r_len = 8 + (HEX_DIGEST_LEN+1)*n;
 +  r_len = 8 + (enc_digest_len+1)*n;
    cp = resource = tor_malloc(r_len);
    memcpy(cp, "d/", 2);
    cp += 2;
    for (i = lo; i < hi; ++i) {
 -    base16_encode(cp, r_len-(cp-resource),
 -                  smartlist_get(digests,i), DIGEST_LEN);
 -    cp += HEX_DIGEST_LEN;
 -    *cp++ = '+';
 +    if (b64_256) {
 +      digest256_to_base64(cp, smartlist_get(digests, i));
 +    } else {
 +      base16_encode(cp, r_len-(cp-resource),
 +                    smartlist_get(digests,i), digest_len);
 +    }
 +    cp += enc_digest_len;
 +    *cp++ = sep;
    }
    memcpy(cp-1, ".z", 3);
  
@@@ -4238,7 -4181,7 +4238,7 @@@
  static INLINE int
  client_would_use_router(routerstatus_t *rs, time_t now, or_options_t *options)
  {
 -  if (!rs->is_running && !options->FetchUselessDescriptors) {
 +  if (!rs->is_flagged_running && !options->FetchUselessDescriptors) {
      /* If we had this router descriptor, we wouldn't even bother using it.
       * But, if we want to have a complete list, fetch it anyway. */
      return 0;
@@@ -4262,7 -4205,6 +4262,7 @@@
   *   So use 96 because it's a nice number.
   */
  #define MAX_DL_PER_REQUEST 96
 +#define MAX_MICRODESC_DL_PER_REQUEST 92
  /** Don't split our requests so finely that we are requesting fewer than
   * this number per server. */
  #define MIN_DL_PER_REQUEST 4
@@@ -4277,33 -4219,21 +4277,33 @@@
   * them until they have more, or until this amount of time has passed. */
  #define MAX_CLIENT_INTERVAL_WITHOUT_REQUEST (10*60)
  
 -/** Given a list of router descriptor digests in <b>downloadable</b>, decide
 - * whether to delay fetching until we have more.  If we don't want to delay,
 - * launch one or more requests to the appropriate directory authorities. */
 -static void
 -launch_router_descriptor_downloads(smartlist_t *downloadable,
 -                                   routerstatus_t *source, time_t now)
 +/** Given a <b>purpose</b> (FETCH_MICRODESC or FETCH_SERVERDESC) and a list of
 + * router descriptor digests or microdescriptor digest256s in
 + * <b>downloadable</b>, decide whether to delay fetching until we have more.
 + * If we don't want to delay, launch one or more requests to the appropriate
 + * directory authorities.
 + */
 +void
 +launch_descriptor_downloads(int purpose,
 +                            smartlist_t *downloadable,
 +                            const routerstatus_t *source, time_t now)
  {
    int should_delay = 0, n_downloadable;
    or_options_t *options = get_options();
 +  const char *descname;
 +
 +  tor_assert(purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
 +             purpose == DIR_PURPOSE_FETCH_MICRODESC);
 +
 +  descname = (purpose == DIR_PURPOSE_FETCH_SERVERDESC) ?
 +    "routerdesc" : "microdesc";
  
    n_downloadable = smartlist_len(downloadable);
    if (!directory_fetches_dir_info_early(options)) {
      if (n_downloadable >= MAX_DL_TO_DELAY) {
        log_debug(LD_DIR,
 -             "There are enough downloadable routerdescs to launch requests.");
 +                "There are enough downloadable %ss to launch requests.",
 +                descname);
        should_delay = 0;
      } else {
        should_delay = (last_routerdesc_download_attempted +
@@@ -4311,15 -4241,13 +4311,15 @@@
        if (!should_delay && n_downloadable) {
          if (last_routerdesc_download_attempted) {
            log_info(LD_DIR,
 -                   "There are not many downloadable routerdescs, but we've "
 +                   "There are not many downloadable %ss, but we've "
                     "been waiting long enough (%d seconds). Downloading.",
 +                   descname,
                     (int)(now-last_routerdesc_download_attempted));
          } else {
            log_info(LD_DIR,
 -                 "There are not many downloadable routerdescs, but we haven't "
 -                 "tried downloading descriptors recently. Downloading.");
 +                   "There are not many downloadable %ss, but we haven't "
 +                   "tried downloading descriptors recently. Downloading.",
 +                   descname);
          }
        }
      }
@@@ -4346,19 -4274,12 +4346,19 @@@
         * update_router_descriptor_downloads() later on, once the connections
         * have succeeded or failed.
         */
 -      pds_flags |= PDS_NO_EXISTING_SERVERDESC_FETCH;
 +      pds_flags |= (purpose == DIR_PURPOSE_FETCH_MICRODESC) ?
 +        PDS_NO_EXISTING_MICRODESC_FETCH :
 +        PDS_NO_EXISTING_SERVERDESC_FETCH;
      }
  
      n_per_request = CEIL_DIV(n_downloadable, MIN_REQUESTS);
 -    if (n_per_request > MAX_DL_PER_REQUEST)
 -      n_per_request = MAX_DL_PER_REQUEST;
 +    if (purpose == DIR_PURPOSE_FETCH_MICRODESC) {
 +      if (n_per_request > MAX_MICRODESC_DL_PER_REQUEST)
 +        n_per_request = MAX_MICRODESC_DL_PER_REQUEST;
 +    } else {
 +      if (n_per_request > MAX_DL_PER_REQUEST)
 +        n_per_request = MAX_DL_PER_REQUEST;
 +    }
      if (n_per_request < MIN_DL_PER_REQUEST)
        n_per_request = MIN_DL_PER_REQUEST;
  
@@@ -4373,7 -4294,7 +4373,7 @@@
               req_plural, n_downloadable, rtr_plural, n_per_request);
      smartlist_sort_digests(downloadable);
      for (i=0; i < n_downloadable; i += n_per_request) {
 -      initiate_descriptor_downloads(source, DIR_PURPOSE_FETCH_SERVERDESC,
 +      initiate_descriptor_downloads(source, purpose,
                                      downloadable, i, i+n_per_request,
                                      pds_flags);
      }
@@@ -4563,14 -4484,15 +4563,14 @@@ update_consensus_router_descriptor_down
  
    map = digestmap_new();
    list_pending_descriptor_downloads(map, 0);
 -  SMARTLIST_FOREACH(consensus->routerstatus_list, void *, rsp,
 -    {
 +  SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, void *, rsp) {
        routerstatus_t *rs =
          is_vote ? &(((vote_routerstatus_t *)rsp)->status) : rsp;
        signed_descriptor_t *sd;
        if ((sd = router_get_by_descriptor_digest(rs->descriptor_digest))) {
 -        routerinfo_t *ri;
 +        const routerinfo_t *ri;
          ++n_have;
 -        if (!(ri = router_get_by_digest(rs->identity_digest)) ||
 +        if (!(ri = router_get_by_id_digest(rs->identity_digest)) ||
              memcmp(ri->cache_info.signed_descriptor_digest,
                     sd->signed_descriptor_digest, DIGEST_LEN)) {
            /* We have a descriptor with this digest, but either there is no
@@@ -4603,8 -4525,7 +4603,8 @@@
        if (is_vote && source) {
          char time_bufnew[ISO_TIME_LEN+1];
          char time_bufold[ISO_TIME_LEN+1];
 -        routerinfo_t *oldrouter = router_get_by_digest(rs->identity_digest);
 +        const routerinfo_t *oldrouter;
 +        oldrouter = router_get_by_id_digest(rs->identity_digest);
          format_iso_time(time_bufnew, rs->published_on);
          if (oldrouter)
            format_iso_time(time_bufold, oldrouter->cache_info.published_on);
@@@ -4614,7 -4535,7 +4614,7 @@@
                   source->nickname, oldrouter ? "known" : "unknown");
        }
        smartlist_add(downloadable, rs->descriptor_digest);
 -    });
 +  } SMARTLIST_FOREACH_END(rsp);
  
    if (!authdir_mode_handles_descs(options, ROUTER_PURPOSE_GENERAL)
        && smartlist_len(no_longer_old)) {
@@@ -4646,8 -4567,7 +4646,8 @@@
             smartlist_len(downloadable), n_delayed, n_have, n_in_oldrouters,
             n_would_reject, n_wouldnt_use, n_inprogress);
  
 -  launch_router_descriptor_downloads(downloadable, source, now);
 +  launch_descriptor_downloads(DIR_PURPOSE_FETCH_SERVERDESC,
 +                              downloadable, source, now);
  
    digestmap_free(map, NULL);
   done:
@@@ -4672,13 -4592,10 +4672,13 @@@ update_router_descriptor_downloads(time
    if (directory_fetches_dir_info_early(options)) {
      update_router_descriptor_cache_downloads_v2(now);
    }
 +
    update_consensus_router_descriptor_downloads(now, 0,
 -    networkstatus_get_reasonably_live_consensus(now));
 +                  networkstatus_get_reasonably_live_consensus(now, FLAV_NS));
  
    /* XXXX021 we could be smarter here; see notes on bug 652. */
 +  /* XXXX NM Microdescs: if we're not fetching microdescriptors, we need
 +   * to make something else invoke this. */
    /* If we're a server that doesn't have a configured address, we rely on
     * directory fetches to learn when our address changes.  So if we haven't
     * tried to get any routerdescs in a long time, try a dummy fetch now. */
@@@ -4725,7 -4642,7 +4725,7 @@@ update_extrainfo_downloads(time_t now
          sd = &((routerinfo_t*)smartlist_get(lst, i))->cache_info;
        if (sd->is_extrainfo)
          continue; /* This should never happen. */
 -      if (old_routers && !router_get_by_digest(sd->identity_digest))
 +      if (old_routers && !router_get_by_id_digest(sd->identity_digest))
          continue; /* Couldn't check the signature if we got it. */
        if (sd->extrainfo_is_bogus)
          continue;
@@@ -4826,7 -4743,7 +4826,7 @@@ count_usable_descriptors(int *num_prese
  
    SMARTLIST_FOREACH(consensus->routerstatus_list, routerstatus_t *, rs,
       {
 -       if (in_set && ! routerset_contains_routerstatus(in_set, rs))
 +       if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
           continue;
         if (client_would_use_router(rs, now, options)) {
           ++*num_usable; /* the consensus says we want it. */
@@@ -4849,7 -4766,7 +4849,7 @@@ count_loading_descriptors_progress(void
    int num_present = 0, num_usable=0;
    time_t now = time(NULL);
    const networkstatus_t *consensus =
 -    networkstatus_get_reasonably_live_consensus(now);
 +    networkstatus_get_reasonably_live_consensus(now, FLAV_NS);
    double fraction;
  
    if (!consensus)
@@@ -4879,7 -4796,7 +4879,7 @@@ update_router_have_minimum_dir_info(voi
    int res;
    or_options_t *options = get_options();
    const networkstatus_t *consensus =
 -    networkstatus_get_reasonably_live_consensus(now);
 +    networkstatus_get_reasonably_live_consensus(now, FLAV_NS);
  
    if (!consensus) {
      if (!networkstatus_get_latest_consensus())
@@@ -4989,7 -4906,7 +4989,7 @@@ router_reset_descriptor_download_failur
   * would not cause a recent (post 0.1.1.6) dirserver to republish.
   */
  int
 -router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2)
 +router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2)
  {
    time_t r1pub, r2pub;
    long time_difference;
@@@ -4997,7 -4914,7 +4997,7 @@@
  
    /* r1 should be the one that was published first. */
    if (r1->cache_info.published_on > r2->cache_info.published_on) {
 -    routerinfo_t *ri_tmp = r2;
 +    const routerinfo_t *ri_tmp = r2;
      r2 = r1;
      r1 = ri_tmp;
    }
@@@ -5016,6 -4933,7 +5016,6 @@@
        (r1->contact_info && r2->contact_info &&
         strcasecmp(r1->contact_info, r2->contact_info)) ||
        r1->is_hibernating != r2->is_hibernating ||
 -      r1->has_old_dnsworkers != r2->has_old_dnsworkers ||
        cmp_addr_policies(r1->exit_policy, r2->exit_policy))
      return 0;
    if ((r1->declared_family == NULL) != (r2->declared_family == NULL))
@@@ -5070,8 -4988,7 +5070,8 @@@
   * incompatibility (if any).
   **/
  int
 -routerinfo_incompatible_with_extrainfo(routerinfo_t *ri, extrainfo_t *ei,
 +routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
 +                                       extrainfo_t *ei,
                                         signed_descriptor_t *sd,
                                         const char **msg)
  {
@@@ -5079,7 -4996,7 +5079,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.";
@@@ -5099,7 -5016,8 +5099,8 @@@
  
    if (ei->pending_sig) {
      char signed_digest[128];
-     if (crypto_pk_public_checksig(ri->identity_pkey, signed_digest,
+     if (crypto_pk_public_checksig(ri->identity_pkey,
+                        signed_digest, sizeof(signed_digest),
                         ei->pending_sig, ei->pending_sig_len) != DIGEST_LEN ||
          memcmp(signed_digest, ei->cache_info.signed_descriptor_digest,
                 DIGEST_LEN)) {
@@@ -5142,7 -5060,7 +5143,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 -5150,7 +5233,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 -5252,38 +5335,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 -5385,7 +5436,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 -5435,11 +5486,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 -5467,13 +5517,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 -5482,13 +5533,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 -5497,46 +5550,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 -5545,39 +5607,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 -5638,18 +5700,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 -5698,9 +5765,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 -5732,7 +5799,7 @@@
  int
  hid_serv_responsible_for_desc_id(const char *query)
  {
 -  routerinfo_t *me;
 +  const routerinfo_t *me;
    routerstatus_t *last_rs;
    const char *my_id, *last_id;
    int result;
diff --combined src/or/routerparse.c
index 1822517,66d024e..56d86bd
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@@ -69,6 -69,7 +69,6 @@@ typedef enum 
    K_V,
    K_W,
    K_M,
 -  K_EVENTDNS,
    K_EXTRA_INFO,
    K_EXTRA_INFO_DIGEST,
    K_CACHES_EXTRA_INFO,
@@@ -285,6 -286,7 +285,6 @@@ static token_rule_t routerdesc_token_ta
  
    T01("family",              K_FAMILY,              ARGS,    NO_OBJ ),
    T01("caches-extra-info",   K_CACHES_EXTRA_INFO,   NO_ARGS, NO_OBJ ),
 -  T01("eventdns",            K_EVENTDNS,            ARGS,    NO_OBJ ),
  
    T0N("opt",                 K_OPT,             CONCAT_ARGS, OBJ_OK ),
    T1( "bandwidth",           K_BANDWIDTH,           GE(3),   NO_OBJ ),
@@@ -700,11 -702,13 +700,13 @@@ router_append_dirobj_signature(char *bu
                                 size_t digest_len, crypto_pk_env_t *private_key)
  {
    char *signature;
-   size_t i;
+   size_t i, keysize;
    int siglen;
  
-   signature = tor_malloc(crypto_pk_keysize(private_key));
-   siglen = crypto_pk_private_sign(private_key, signature, digest, digest_len);
+   keysize = crypto_pk_keysize(private_key);
+   signature = tor_malloc(keysize);
+   siglen = crypto_pk_private_sign(private_key, signature, keysize,
+                                   digest, digest_len);
    if (siglen < 0) {
      log_warn(LD_BUG,"Couldn't sign digest.");
      goto err;
@@@ -1057,6 -1061,7 +1059,7 @@@ check_signature_token(const char *diges
                        const char *doctype)
  {
    char *signed_digest;
+   size_t keysize;
    const int check_authority = (flags & CST_CHECK_AUTHORITY);
    const int check_objtype = ! (flags & CST_NO_CHECK_OBJTYPE);
  
@@@ -1078,10 -1083,11 +1081,11 @@@
      }
    }
  
-   signed_digest = tor_malloc(tok->object_size);
-   if (crypto_pk_public_checksig(pkey, signed_digest, tok->object_body,
-                                 tok->object_size)
-       < digest_len) {
+   keysize = crypto_pk_keysize(pkey);
+   signed_digest = tor_malloc(keysize);
+   if (crypto_pk_public_checksig(pkey, signed_digest, keysize,
+                                 tok->object_body, tok->object_size)
+       < DIGEST_LEN) {
      log_warn(LD_DIR, "Error reading %s: invalid signature.", doctype);
      tor_free(signed_digest);
      return -1;
@@@ -1350,6 -1356,7 +1354,6 @@@ router_parse_entry_from_string(const ch
    tor_assert(tok->n_args >= 5);
  
    router = tor_malloc_zero(sizeof(routerinfo_t));
 -  router->country = -1;
    router->cache_info.routerlist_index = -1;
    router->cache_info.annotations_len = s-start_of_annotations + prepend_len;
    router->cache_info.signed_descriptor_len = end-s;
@@@ -1490,6 -1497,13 +1494,6 @@@
      router->contact_info = tor_strdup(tok->args[0]);
    }
  
 -  if ((tok = find_opt_by_keyword(tokens, K_EVENTDNS))) {
 -    router->has_old_dnsworkers = tok->n_args && !strcmp(tok->args[0], "0");
 -  } else if (router->platform) {
 -    if (! tor_version_as_new_as(router->platform, "0.1.2.2-alpha"))
 -      router->has_old_dnsworkers = 1;
 -  }
 -
    exit_policy_tokens = find_all_exitpolicy(tokens);
    if (!smartlist_len(exit_policy_tokens)) {
      log_warn(LD_DIR, "No exit policy tokens in descriptor.");
@@@ -1548,6 -1562,8 +1552,6 @@@
                              "router descriptor") < 0)
      goto err;
  
 -  routerinfo_set_country(router);
 -
    if (!router->or_port) {
      log_warn(LD_DIR,"or_port unreadable or 0. Failing.");
      goto err;
@@@ -1937,7 -1953,6 +1941,7 @@@ routerstatus_parse_entry_from_string(me
  
    if (!consensus_method)
      flav = FLAV_NS;
 +  tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC);
  
    eos = find_start_of_next_routerstatus(*s);
  
@@@ -1950,16 -1965,15 +1954,16 @@@
      goto err;
    }
    tok = find_by_keyword(tokens, K_R);
 -  tor_assert(tok->n_args >= 7);
 +  tor_assert(tok->n_args >= 7); /* guaranteed by GE(7) in K_R setup */
    if (flav == FLAV_NS) {
      if (tok->n_args < 8) {
        log_warn(LD_DIR, "Too few arguments to r");
        goto err;
      }
 -  } else {
 -    offset = -1;
 +  } else if (flav == FLAV_MICRODESC) {
 +    offset = -1; /* There is no identity digest */
    }
 +
    if (vote_rs) {
      rs = &vote_rs->status;
    } else {
@@@ -2033,7 -2047,7 +2037,7 @@@
        else if (!strcmp(tok->args[i], "Fast"))
          rs->is_fast = 1;
        else if (!strcmp(tok->args[i], "Running"))
 -        rs->is_running = 1;
 +        rs->is_flagged_running = 1;
        else if (!strcmp(tok->args[i], "Named"))
          rs->is_named = 1;
        else if (!strcmp(tok->args[i], "Valid"))
@@@ -2135,16 -2149,6 +2139,16 @@@
          vote_rs->microdesc = line;
        }
      } SMARTLIST_FOREACH_END(t);
 +  } else if (flav == FLAV_MICRODESC) {
 +    tok = find_opt_by_keyword(tokens, K_M);
 +    if (tok) {
 +      tor_assert(tok->n_args);
 +      if (digest256_from_base64(rs->descriptor_digest, tok->args[0])) {
 +        log_warn(LD_DIR, "Error decoding microdescriptor digest %s",
 +                 escaped(tok->args[0]));
 +        goto err;
 +      }
 +    }
    }
  
    if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
@@@ -4311,7 -4315,7 +4315,7 @@@ microdescs_parse_from_string(const cha
      }
  
      if ((tok = find_opt_by_keyword(tokens, K_P))) {
 -      md->exitsummary = tor_strdup(tok->args[0]);
 +      md->exit_policy = parse_short_policy(tok->args[0]);
      }
  
      crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);



More information about the tor-commits mailing list