瀏覽代碼

Merge branch 'nodes'

Nick Mathewson 13 年之前
父節點
當前提交
8c837db38f

+ 10 - 0
changes/nodelist

@@ -0,0 +1,10 @@
+  o Code refactorings
+    - Unified our node-listing and selecting logic.  We had at least
+      two major ways to look at the question of "which Tor servers do
+      we know about": our list of router descriptors, and the current
+      consensus.  We're adding a third in microdescriptors.  Having
+      so many systems without an abstraction layer over them was
+      hurting the codebase.  Now, we have a new "node_t" abstraction
+      that presents a consistent interface to a client's view of
+      a Tor node, and holds (nearly) all of the mutable state
+      formerly in routerinfo_t and routerstatus_t.

+ 4 - 0
doc/nodefamily_routerset

@@ -0,0 +1,4 @@
+  o Minor features
+    - The NodeFamily option -- which let you declare that you want to
+      consider nodes to be part of a family whether they list themselves
+      that way or not -- now allows IP address ranges and country codes.

+ 2 - 1
doc/tor.1.txt

@@ -571,7 +571,8 @@ The following options are useful only for clients (that is, if
     constitute a "family" of similar or co-administered servers, so never use
     any two of them in the same circuit. Defining a NodeFamily is only needed
     when a server doesn't list the family itself (with MyFamily). This option
-    can be used multiple times.
+    can be used multiple times.  In addition to nodes, you can also list
+    IP address and ranges and country codes in {curly braces}.
 
 **EnforceDistinctSubnets** **0**|**1**::
     If 1, Tor will not put two servers whose IP addresses are "too close" on

+ 1 - 1
src/common/address.c

@@ -311,7 +311,7 @@ tor_addr_is_internal(const tor_addr_t *addr, int for_listening)
  *  brackets.
  */
 const char *
-tor_addr_to_str(char *dest, const tor_addr_t *addr, int len, int decorate)
+tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate)
 {
   const char *ptr;
   tor_assert(addr && dest);

+ 1 - 1
src/common/address.h

@@ -144,7 +144,7 @@ int tor_addr_port_parse(const char *s, tor_addr_t *addr_out,
 int tor_addr_parse_mask_ports(const char *s,
                               tor_addr_t *addr_out, maskbits_t *mask_out,
                               uint16_t *port_min_out, uint16_t *port_max_out);
-const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, int len,
+const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len,
                              int decorate);
 int tor_addr_from_str(tor_addr_t *addr, const char *src);
 void tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src);

+ 0 - 1
src/common/container.c

@@ -268,7 +268,6 @@ smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2)
 
 /** Remove the <b>idx</b>th element of sl; if idx is not the last
  * element, swap the last element of sl into the <b>idx</b>th space.
- * Return the old value of the <b>idx</b>th element.
  */
 void
 smartlist_del(smartlist_t *sl, int idx)

+ 1 - 1
src/common/util.c

@@ -934,7 +934,7 @@ esc_for_log(const char *s)
   char *result, *outp;
   size_t len = 3;
   if (!s) {
-    return tor_strdup("");
+    return tor_strdup("(null)");
   }
 
   for (cp = s; *cp; ++cp) {

+ 2 - 1
src/or/Makefile.am

@@ -21,7 +21,7 @@ libtor_a_SOURCES = buffers.c circuitbuild.c circuitlist.c \
 	cpuworker.c directory.c dirserv.c dirvote.c \
 	dns.c dnsserv.c geoip.c hibernate.c main.c $(tor_platform_source) \
 	microdesc.c \
-	networkstatus.c onion.c policies.c \
+	networkstatus.c nodelist.c onion.c policies.c \
 	reasons.c relay.c rendcommon.c rendclient.c rendmid.c \
 	rendservice.c rephist.c router.c routerlist.c routerparse.c \
 	$(evdns_source) config_codedigest.c
@@ -56,6 +56,7 @@ noinst_HEADERS = buffers.h circuitbuild.h circuitlist.h circuituse.h \
 	command.h config.h connection_edge.h connection.h connection_or.h \
 	control.h cpuworker.h directory.h dirserv.h dirvote.h dns.h \
 	dnsserv.h geoip.h hibernate.h main.h microdesc.h networkstatus.h \
+	nodelist.h \
 	ntmain.h onion.h policies.h reasons.h relay.h rendclient.h \
 	rendcommon.h rendmid.h rendservice.h rephist.h router.h routerlist.h \
 	routerparse.h or.h eventdns.h eventdns_tor.h micro-revision.i

文件差異過大導致無法顯示
+ 225 - 199
src/or/circuitbuild.c


+ 7 - 6
src/or/circuitbuild.h

@@ -44,10 +44,11 @@ void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
 extend_info_t *extend_info_alloc(const char *nickname, const char *digest,
                                  crypto_pk_env_t *onion_key,
                                  const tor_addr_t *addr, uint16_t port);
-extend_info_t *extend_info_from_router(routerinfo_t *r);
+extend_info_t *extend_info_from_router(const routerinfo_t *r);
+extend_info_t *extend_info_from_node(const node_t *node);
 extend_info_t *extend_info_dup(extend_info_t *info);
 void extend_info_free(extend_info_t *info);
-routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
+const node_t *build_state_get_exit_node(cpath_build_state_t *state);
 const char *build_state_get_exit_nickname(cpath_build_state_t *state);
 
 void entry_guards_compute_status(or_options_t *options, time_t now);
@@ -55,7 +56,7 @@ int entry_guard_register_connect_status(const char *digest, int succeeded,
                                         int mark_relay_status, time_t now);
 void entry_nodes_should_be_added(void);
 int entry_list_is_constrained(or_options_t *options);
-routerinfo_t *choose_random_entry(cpath_build_state_t *state);
+const node_t *choose_random_entry(cpath_build_state_t *state);
 int entry_guards_parse_state(or_state_t *state, int set, char **msg);
 void entry_guards_update_state(or_state_t *state);
 int getinfo_helper_entry_guards(control_connection_t *conn,
@@ -63,9 +64,9 @@ int getinfo_helper_entry_guards(control_connection_t *conn,
                                 const char **errmsg);
 
 void clear_bridge_list(void);
-int routerinfo_is_a_configured_bridge(routerinfo_t *ri);
-void
-learned_router_identity(tor_addr_t *addr, uint16_t port, const char *digest);
+int routerinfo_is_a_configured_bridge(const routerinfo_t *ri);
+void learned_router_identity(tor_addr_t *addr, uint16_t port,
+                             const char *digest);
 void bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
                             char *digest);
 void retry_bridge_descriptor_fetch_directly(const char *digest);

+ 5 - 4
src/or/circuitlist.c

@@ -19,6 +19,7 @@
 #include "connection_or.h"
 #include "control.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "onion.h"
 #include "relay.h"
 #include "rendclient.h"
@@ -946,15 +947,15 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
         if (info) {
           /* need to make sure we don't duplicate hops */
           crypt_path_t *hop = circ->cpath;
-          routerinfo_t *ri1 = router_get_by_digest(info->identity_digest);
+          const node_t *ri1 = node_get_by_id(info->identity_digest);
           do {
-            routerinfo_t *ri2;
+            const node_t *ri2;
             if (!memcmp(hop->extend_info->identity_digest,
                         info->identity_digest, DIGEST_LEN))
               goto next;
             if (ri1 &&
-                (ri2 = router_get_by_digest(hop->extend_info->identity_digest))
-                && routers_in_same_family(ri1, ri2))
+                (ri2 = node_get_by_id(hop->extend_info->identity_digest))
+                && nodes_in_same_family(ri1, ri2))
               goto next;
             hop=hop->next;
           } while (hop!=circ->cpath);

+ 30 - 37
src/or/circuituse.c

@@ -17,6 +17,7 @@
 #include "connection.h"
 #include "connection_edge.h"
 #include "control.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "rendclient.h"
 #include "rendcommon.h"
@@ -43,7 +44,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
                       int need_uptime, int need_internal,
                       time_t now)
 {
-  routerinfo_t *exitrouter;
+  const node_t *exitnode;
   cpath_build_state_t *build_state;
   tor_assert(circ);
   tor_assert(conn);
@@ -85,7 +86,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
    * of the one we meant to finish at.
    */
   build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
-  exitrouter = build_state_get_exit_router(build_state);
+  exitnode = build_state_get_exit_node(build_state);
 
   if (need_uptime && !build_state->need_uptime)
     return 0;
@@ -93,7 +94,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
     return 0;
 
   if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
-    if (!exitrouter && !build_state->onehop_tunnel) {
+    if (!exitnode && !build_state->onehop_tunnel) {
       log_debug(LD_CIRC,"Not considering circuit with unknown router.");
       return 0; /* this circuit is screwed and doesn't know it yet,
                  * or is a rendezvous circuit. */
@@ -127,7 +128,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
         return 0;
       }
     }
-    if (exitrouter && !connection_ap_can_use_exit(conn, exitrouter, 0)) {
+    if (exitnode && !connection_ap_can_use_exit(conn, exitnode, 0)) {
       /* can't exit from this router */
       return 0;
     }
@@ -473,7 +474,7 @@ circuit_stream_is_being_handled(edge_connection_t *conn,
                                 uint16_t port, int min)
 {
   circuit_t *circ;
-  routerinfo_t *exitrouter;
+  const node_t *exitnode;
   int num=0;
   time_t now = time(NULL);
   int need_uptime = smartlist_string_num_isin(get_options()->LongLivedPorts,
@@ -489,14 +490,14 @@ circuit_stream_is_being_handled(edge_connection_t *conn,
       if (build_state->is_internal || build_state->onehop_tunnel)
         continue;
 
-      exitrouter = build_state_get_exit_router(build_state);
-      if (exitrouter && (!need_uptime || build_state->need_uptime)) {
+      exitnode = build_state_get_exit_node(build_state);
+      if (exitnode && (!need_uptime || build_state->need_uptime)) {
         int ok;
         if (conn) {
-          ok = connection_ap_can_use_exit(conn, exitrouter, 0);
+          ok = connection_ap_can_use_exit(conn, exitnode, 0);
         } else {
-          addr_policy_result_t r = compare_addr_to_addr_policy(
-              0, port, exitrouter->exit_policy);
+          addr_policy_result_t r;
+          r = compare_addr_to_node_policy(0, port, exitnode);
           ok = r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED;
         }
         if (ok) {
@@ -563,7 +564,7 @@ circuit_predict_and_launch_new(void)
     log_info(LD_CIRC,
              "Have %d clean circs (%d internal), need another exit circ.",
              num, num_internal);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+    circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
     return;
   }
 
@@ -575,7 +576,7 @@ circuit_predict_and_launch_new(void)
              "Have %d clean circs (%d internal), need another internal "
              "circ for my hidden service.",
              num, num_internal);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+    circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
     return;
   }
 
@@ -593,7 +594,7 @@ circuit_predict_and_launch_new(void)
              "Have %d clean circs (%d uptime-internal, %d internal), need"
              " another hidden service circ.",
              num, num_uptime_internal, num_internal);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+    circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
     return;
   }
 
@@ -606,7 +607,7 @@ circuit_predict_and_launch_new(void)
     flags = CIRCLAUNCH_NEED_CAPACITY;
     log_info(LD_CIRC,
              "Have %d clean circs need another buildtime test circ.", num);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+    circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
     return;
   }
 }
@@ -644,7 +645,7 @@ circuit_build_needed_circs(time_t now)
         circ &&
         circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) {
       log_fn(LOG_INFO,"Creating a new testing circuit.");
-      circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0);
+      circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, 0);
     }
 #endif
   }
@@ -1076,17 +1077,9 @@ static int did_circs_fail_last_period = 0;
 /** Launch a new circuit; see circuit_launch_by_extend_info() for
  * details on arguments. */
 origin_circuit_t *
-circuit_launch_by_router(uint8_t purpose,
-                         routerinfo_t *exit, int flags)
+circuit_launch(uint8_t purpose, int flags)
 {
-  origin_circuit_t *circ;
-  extend_info_t *info = NULL;
-  if (exit)
-    info = extend_info_from_router(exit);
-  circ = circuit_launch_by_extend_info(purpose, info, flags);
-
-  extend_info_free(info);
-  return circ;
+  return circuit_launch_by_extend_info(purpose, NULL, flags);
 }
 
 /** Launch a new circuit with purpose <b>purpose</b> and exit node
@@ -1256,9 +1249,9 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
       uint32_t addr = 0;
       if (tor_inet_aton(conn->socks_request->address, &in))
         addr = ntohl(in.s_addr);
-      if (router_exit_policy_all_routers_reject(addr,
-                                                conn->socks_request->port,
-                                                need_uptime)) {
+      if (router_exit_policy_all_nodes_reject(addr,
+                                              conn->socks_request->port,
+                                              need_uptime)) {
         log_notice(LD_APP,
                    "No Tor server allows exit to %s:%d. Rejecting.",
                    safe_str_client(conn->socks_request->address),
@@ -1267,9 +1260,9 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
       }
     } else {
       /* XXXX022 Duplicates checks in connection_ap_handshake_attach_circuit */
-      routerinfo_t *router = router_get_by_nickname(conn->chosen_exit_name, 1);
+      const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
       int opt = conn->chosen_exit_optional;
-      if (router && !connection_ap_can_use_exit(conn, router, 0)) {
+      if (node && !connection_ap_can_use_exit(conn, node, 0)) {
         log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
                "Requested exit point '%s' would refuse request. %s.",
                conn->chosen_exit_name, opt ? "Trying others" : "Closing");
@@ -1317,11 +1310,11 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
      */
     if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
       if (conn->chosen_exit_name) {
-        routerinfo_t *r;
+        const node_t *r;
         int opt = conn->chosen_exit_optional;
-        r = router_get_by_nickname(conn->chosen_exit_name, 1);
-        if (r) {
-          extend_info = extend_info_from_router(r);
+        r = node_get_by_nickname(conn->chosen_exit_name, 1);
+        if (r && node_has_descriptor(r)) {
+          extend_info = extend_info_from_node(r);
         } else {
           log_debug(LD_DIR, "considering %d, %s",
                     want_onehop, conn->chosen_exit_name);
@@ -1571,9 +1564,9 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
     origin_circuit_t *circ=NULL;
 
     if (conn->chosen_exit_name) {
-      routerinfo_t *router = router_get_by_nickname(conn->chosen_exit_name, 1);
+      const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
       int opt = conn->chosen_exit_optional;
-      if (!router && !want_onehop) {
+      if (!node && !want_onehop) {
         /* We ran into this warning when trying to extend a circuit to a
          * hidden service directory for which we didn't have a router
          * descriptor. See flyspray task 767 for more details. We should
@@ -1589,7 +1582,7 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
         }
         return -1;
       }
-      if (router && !connection_ap_can_use_exit(conn, router, 0)) {
+      if (node && !connection_ap_can_use_exit(conn, node, 0)) {
         log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
                "Requested exit point '%s' would refuse request. %s.",
                conn->chosen_exit_name, opt ? "Trying others" : "Closing");

+ 1 - 2
src/or/circuituse.h

@@ -41,8 +41,7 @@ void circuit_build_failed(origin_circuit_t *circ);
 origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
                                                 extend_info_t *info,
                                                 int flags);
-origin_circuit_t *circuit_launch_by_router(uint8_t purpose,
-                                           routerinfo_t *exit, int flags);
+origin_circuit_t *circuit_launch(uint8_t purpose, int flags);
 void circuit_reset_failure_count(int timeout);
 int connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
                                                   origin_circuit_t *circ,

+ 7 - 3
src/or/command.c

@@ -25,6 +25,7 @@
 #include "control.h"
 #include "cpuworker.h"
 #include "hibernate.h"
+#include "nodelist.h"
 #include "onion.h"
 #include "relay.h"
 #include "router.h"
@@ -267,15 +268,18 @@ command_process_create_cell(cell_t *cell, or_connection_t *conn)
   }
 
   if (circuit_id_in_use_on_orconn(cell->circ_id, conn)) {
-    routerinfo_t *router = router_get_by_digest(conn->identity_digest);
+    const node_t *node = node_get_by_id(conn->identity_digest);
     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
            "Received CREATE cell (circID %d) for known circ. "
            "Dropping (age %d).",
            cell->circ_id, (int)(time(NULL) - conn->_base.timestamp_created));
-    if (router)
+    if (node) {
+      char *p = esc_for_log(node_get_platform(node));
       log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
              "Details: nickname \"%s\", platform %s.",
-             router->nickname, escaped(router->platform));
+             node_get_nickname(node), p);
+      tor_free(p);
+    }
     return;
   }
 

+ 22 - 1
src/or/config.c

@@ -691,6 +691,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);
 }
 
@@ -3084,6 +3089,18 @@ options_validate(or_options_t *old_options, or_options_t *options,
     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 "
@@ -3549,8 +3566,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
   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)

+ 18 - 18
src/or/connection_edge.c

@@ -23,6 +23,7 @@
 #include "dirserv.h"
 #include "hibernate.h"
 #include "main.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "reasons.h"
 #include "relay.h"
@@ -586,7 +587,7 @@ void
 circuit_discard_optional_exit_enclaves(extend_info_t *info)
 {
   edge_connection_t *edge_conn;
-  routerinfo_t *r1, *r2;
+  const node_t *r1, *r2;
 
   smartlist_t *conns = get_connection_array();
   SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
@@ -598,8 +599,8 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
     if (!edge_conn->chosen_exit_optional &&
         !edge_conn->chosen_exit_retries)
       continue;
-    r1 = router_get_by_nickname(edge_conn->chosen_exit_name, 0);
-    r2 = router_get_by_nickname(info->nickname, 0);
+    r1 = node_get_by_nickname(edge_conn->chosen_exit_name, 0);
+    r2 = node_get_by_nickname(info->nickname, 0);
     if (!r1 || !r2 || r1 != r2)
       continue;
     tor_assert(edge_conn->socks_request);
@@ -1574,12 +1575,12 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
         return -1;
       }
     } else {
-      routerinfo_t *r;
+      const node_t *r;
       conn->chosen_exit_name = tor_strdup(socks->address);
-      r = router_get_by_nickname(conn->chosen_exit_name, 1);
+      r = node_get_by_nickname(conn->chosen_exit_name, 1);
       *socks->address = 0;
       if (r) {
-        strlcpy(socks->address, r->address, sizeof(socks->address));
+        node_get_address_string(r, socks->address, sizeof(socks->address));
       } else {
         log_warn(LD_APP,
                  "Unrecognized server in exit address '%s.exit'. Refusing.",
@@ -1630,16 +1631,16 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
 
       if (!conn->use_begindir && !conn->chosen_exit_name && !circ) {
         /* see if we can find a suitable enclave exit */
-        routerinfo_t *r =
+        const node_t *r =
           router_find_exact_exit_enclave(socks->address, socks->port);
         if (r) {
           log_info(LD_APP,
                    "Redirecting address %s to exit at enclave router %s",
-                   safe_str_client(socks->address), r->nickname);
+                   safe_str_client(socks->address), node_get_nickname(r));
           /* use the hex digest, not nickname, in case there are two
              routers with this nickname */
           conn->chosen_exit_name =
-            tor_strdup(hex_str(r->cache_info.identity_digest, DIGEST_LEN));
+            tor_strdup(hex_str(r->identity, DIGEST_LEN));
           conn->chosen_exit_optional = 1;
         }
       }
@@ -2895,7 +2896,7 @@ connection_edge_is_rendezvous_stream(edge_connection_t *conn)
  * this relay, return 0.
  */
 int
-connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit,
+connection_ap_can_use_exit(edge_connection_t *conn, const node_t *exit,
                            int excluded_means_no)
 {
   or_options_t *options = get_options();
@@ -2909,10 +2910,10 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit,
    * make sure the exit node of the existing circuit matches exactly.
    */
   if (conn->chosen_exit_name) {
-    routerinfo_t *chosen_exit =
-      router_get_by_nickname(conn->chosen_exit_name, 1);
-    if (!chosen_exit || memcmp(chosen_exit->cache_info.identity_digest,
-                               exit->cache_info.identity_digest, DIGEST_LEN)) {
+    const node_t *chosen_exit =
+      node_get_by_nickname(conn->chosen_exit_name, 1);
+    if (!chosen_exit || memcmp(chosen_exit->identity,
+                               exit->identity, DIGEST_LEN)) {
       /* doesn't match */
 //      log_debug(LD_APP,"Requested node '%s', considering node '%s'. No.",
 //                conn->chosen_exit_name, exit->nickname);
@@ -2927,8 +2928,7 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit,
     addr_policy_result_t r;
     if (tor_inet_aton(conn->socks_request->address, &in))
       addr = ntohl(in.s_addr);
-    r = compare_addr_to_addr_policy(addr, conn->socks_request->port,
-                                    exit->exit_policy);
+    r = compare_addr_to_node_policy(addr, conn->socks_request->port, exit);
     if (r == ADDR_POLICY_REJECTED)
       return 0; /* We know the address, and the exit policy rejects it. */
     if (r == ADDR_POLICY_PROBABLY_REJECTED && !conn->chosen_exit_name)
@@ -2937,12 +2937,12 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit,
                  * this node, err on the side of caution. */
   } else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
     /* Don't send DNS requests to non-exit servers by default. */
-    if (!conn->chosen_exit_name && policy_is_reject_star(exit->exit_policy))
+    if (!conn->chosen_exit_name && node_exit_policy_rejects_all(exit))
       return 0;
   }
   if (options->_ExcludeExitNodesUnion &&
       (options->StrictNodes || excluded_means_no) &&
-      routerset_contains_router(options->_ExcludeExitNodesUnion, exit)) {
+      routerset_contains_node(options->_ExcludeExitNodesUnion, exit)) {
     /* If we are trying to avoid this node as exit, and we have StrictNodes
      * set, then this is not a suitable exit. Refuse it.
      *

+ 2 - 1
src/or/connection_edge.h

@@ -47,7 +47,8 @@ int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
 int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
 void connection_exit_connect(edge_connection_t *conn);
 int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
-int connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit,
+int connection_ap_can_use_exit(edge_connection_t *conn,
+                               const node_t *exit,
                                int excluded_means_no);
 void connection_ap_expire_beginning(void);
 void connection_ap_attach_pending(void);

+ 10 - 7
src/or/connection_or.c

@@ -22,6 +22,7 @@
 #include "geoip.h"
 #include "main.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "reasons.h"
 #include "relay.h"
 #include "rephist.h"
@@ -446,7 +447,7 @@ connection_or_init_conn_from_address(or_connection_t *conn,
                                      const char *id_digest,
                                      int started_here)
 {
-  routerinfo_t *r = router_get_by_digest(id_digest);
+  const node_t *r = node_get_by_id(id_digest);
   connection_or_set_identity_digest(conn, id_digest);
   connection_or_update_token_buckets_helper(conn, 1, get_options());
 
@@ -454,8 +455,10 @@ connection_or_init_conn_from_address(or_connection_t *conn,
   tor_addr_copy(&conn->_base.addr, addr);
   tor_addr_copy(&conn->real_addr, addr);
   if (r) {
+    tor_addr_t node_addr;
+    node_get_addr(r, &node_addr);
     /* XXXX proposal 118 will make this more complex. */
-    if (tor_addr_eq_ipv4h(&conn->_base.addr, r->addr))
+    if (tor_addr_eq(&conn->_base.addr, &node_addr))
       conn->is_canonical = 1;
     if (!started_here) {
       /* Override the addr/port, so our log messages will make sense.
@@ -468,12 +471,12 @@ connection_or_init_conn_from_address(or_connection_t *conn,
        * right IP address and port 56244, that wouldn't be as helpful. now we
        * log the "right" port too, so we know if it's moria1 or moria2.
        */
-      tor_addr_from_ipv4h(&conn->_base.addr, r->addr);
-      conn->_base.port = r->or_port;
+      tor_addr_copy(&conn->_base.addr, &node_addr);
+      conn->_base.port = node_get_orport(r);
     }
-    conn->nickname = tor_strdup(r->nickname);
+    conn->nickname = tor_strdup(node_get_nickname(r));
     tor_free(conn->_base.address);
-    conn->_base.address = tor_strdup(r->address);
+    conn->_base.address = tor_dup_addr(&node_addr);
   } else {
     const char *n;
     /* If we're an authoritative directory server, we may know a
@@ -1527,7 +1530,7 @@ connection_or_send_netinfo(or_connection_t *conn)
 {
   cell_t cell;
   time_t now = time(NULL);
-  routerinfo_t *me;
+  const routerinfo_t *me;
   int len;
   char *out;
 

+ 41 - 33
src/or/control.c

@@ -26,6 +26,7 @@
 #include "hibernate.h"
 #include "main.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "reasons.h"
 #include "router.h"
@@ -1344,7 +1345,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
   } else if (!strcmp(question, "dir-usage")) {
     *answer = directory_dump_request_log();
   } else if (!strcmp(question, "fingerprint")) {
-    routerinfo_t *me = router_get_my_routerinfo();
+    const routerinfo_t *me = router_get_my_routerinfo();
     if (!me) {
       *errmsg = "No routerdesc known; am I really a server?";
       return -1;
@@ -1366,8 +1367,9 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
  * NOTE: <b>ri_body</b> is as returned by signed_descriptor_get_body: it might
  * not be NUL-terminated. */
 static char *
-munge_extrainfo_into_routerinfo(const char *ri_body, signed_descriptor_t *ri,
-                                signed_descriptor_t *ei)
+munge_extrainfo_into_routerinfo(const char *ri_body,
+                                const signed_descriptor_t *ri,
+                                const signed_descriptor_t *ei)
 {
   char *out = NULL, *outp;
   int i;
@@ -1412,16 +1414,17 @@ getinfo_helper_dir(control_connection_t *control_conn,
                    const char *question, char **answer,
                    const char **errmsg)
 {
+  const routerinfo_t *ri;
   (void) control_conn;
   if (!strcmpstart(question, "desc/id/")) {
-    routerinfo_t *ri = router_get_by_hexdigest(question+strlen("desc/id/"));
+    ri = router_get_by_hexdigest(question+strlen("desc/id/"));
     if (ri) {
       const char *body = signed_descriptor_get_body(&ri->cache_info);
       if (body)
         *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
     }
   } else if (!strcmpstart(question, "desc/name/")) {
-    routerinfo_t *ri = router_get_by_nickname(question+strlen("desc/name/"),1);
+    ri = router_get_by_nickname(question+strlen("desc/name/"),1);
     if (ri) {
       const char *body = signed_descriptor_get_body(&ri->cache_info);
       if (body)
@@ -1431,7 +1434,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
     routerlist_t *routerlist = router_get_routerlist();
     smartlist_t *sl = smartlist_create();
     if (routerlist && routerlist->routers) {
-      SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
+      SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, ri,
       {
         const char *body = signed_descriptor_get_body(&ri->cache_info);
         if (body)
@@ -1447,7 +1450,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
     routerlist_t *routerlist = router_get_routerlist();
     smartlist_t *sl = smartlist_create();
     if (routerlist && routerlist->routers) {
-      SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
+      SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, ri,
       {
         const char *body = signed_descriptor_get_body(&ri->cache_info);
         signed_descriptor_t *ei = extrainfo_get_by_descriptor_digest(
@@ -1465,8 +1468,8 @@ getinfo_helper_dir(control_connection_t *control_conn,
     SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
     smartlist_free(sl);
   } else if (!strcmpstart(question, "desc-annotations/id/")) {
-    routerinfo_t *ri = router_get_by_hexdigest(question+
-                                               strlen("desc-annotations/id/"));
+    ri = router_get_by_hexdigest(question+
+                                 strlen("desc-annotations/id/"));
     if (ri) {
       const char *annotations =
         signed_descriptor_get_annotations(&ri->cache_info);
@@ -2105,7 +2108,7 @@ static int
 handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
                              const char *body)
 {
-  smartlist_t *router_nicknames=NULL, *routers=NULL;
+  smartlist_t *router_nicknames=NULL, *nodes=NULL;
   origin_circuit_t *circ = NULL;
   int zero_circ;
   uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL;
@@ -2136,8 +2139,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
     if ((smartlist_len(args) == 1) ||
         (smartlist_len(args) >= 2 && is_keyval_pair(smartlist_get(args, 1)))) {
       // "EXTENDCIRCUIT 0" || EXTENDCIRCUIT 0 foo=bar"
-      circ = circuit_launch_by_router(intended_purpose, NULL,
-                                      CIRCLAUNCH_NEED_CAPACITY);
+      circ = circuit_launch(intended_purpose, CIRCLAUNCH_NEED_CAPACITY);
       if (!circ) {
         connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
       } else {
@@ -2165,17 +2167,21 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
   SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
   smartlist_free(args);
 
-  routers = smartlist_create();
+  nodes = smartlist_create();
   SMARTLIST_FOREACH(router_nicknames, const char *, n,
   {
-    routerinfo_t *r = router_get_by_nickname(n, 1);
-    if (!r) {
+    const node_t *node = node_get_by_nickname(n, 1);
+    if (!node) {
       connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n);
       goto done;
     }
-    smartlist_add(routers, r);
+    if (!node_has_descriptor(node)) {
+      connection_printf_to_buf(conn, "552 descriptor for \"%s\"\r\n", n);
+      goto done;
+    }
+    smartlist_add(nodes, (void*)node);
   });
-  if (!smartlist_len(routers)) {
+  if (!smartlist_len(nodes)) {
     connection_write_str_to_buf("512 No router names provided\r\n", conn);
     goto done;
   }
@@ -2186,9 +2192,10 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
   }
 
   /* now circ refers to something that is ready to be extended */
-  SMARTLIST_FOREACH(routers, routerinfo_t *, r,
+  SMARTLIST_FOREACH(nodes, const node_t *, node,
   {
-    extend_info_t *info = extend_info_from_router(r);
+    extend_info_t *info = extend_info_from_node(node);
+    tor_assert(info); /* True, since node_has_descriptor(node) == true */
     circuit_append_new_exit(circ, info);
     extend_info_free(info);
   });
@@ -2222,7 +2229,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
  done:
   SMARTLIST_FOREACH(router_nicknames, char *, n, tor_free(n));
   smartlist_free(router_nicknames);
-  smartlist_free(routers);
+  smartlist_free(nodes);
   return 0;
 }
 
@@ -2338,16 +2345,17 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
   }
   /* Is this a single hop circuit? */
   if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) {
-    routerinfo_t *r = NULL;
-    char* exit_digest;
+    const node_t *node = NULL;
+    char *exit_digest;
     if (circ->build_state &&
         circ->build_state->chosen_exit &&
         !tor_digest_is_zero(circ->build_state->chosen_exit->identity_digest)) {
       exit_digest = circ->build_state->chosen_exit->identity_digest;
-      r = router_get_by_digest(exit_digest);
+      node = node_get_by_id(exit_digest);
     }
     /* Do both the client and relay allow one-hop exit circuits? */
-    if (!r || !r->allow_single_hop_exits ||
+    if (!node ||
+        !node_allows_single_hop_exits(node) ||
         !get_options()->AllowSingleHopCircuits) {
       connection_write_str_to_buf(
       "551 Can't attach stream to this one-hop circuit.\r\n", conn);
@@ -3173,10 +3181,10 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
 static void
 orconn_target_get_name(char *name, size_t len, or_connection_t *conn)
 {
-  routerinfo_t *ri = router_get_by_digest(conn->identity_digest);
-  if (ri) {
+  const node_t *node = node_get_by_id(conn->identity_digest);
+  if (node) {
     tor_assert(len > MAX_VERBOSE_NICKNAME_LEN);
-    router_get_verbose_nickname(name, ri);
+    node_get_verbose_nickname(node, name);
   } else if (! tor_digest_is_zero(conn->identity_digest)) {
     name[0] = '$';
     base16_encode(name+1, len-1, conn->identity_digest,
@@ -3493,7 +3501,7 @@ control_event_networkstatus_changed_helper(smartlist_t *statuses,
   smartlist_add(strs, tor_strdup("650+"));
   smartlist_add(strs, tor_strdup(event_string));
   smartlist_add(strs, tor_strdup("\r\n"));
-  SMARTLIST_FOREACH(statuses, routerstatus_t *, rs,
+  SMARTLIST_FOREACH(statuses, const routerstatus_t *, rs,
     {
       s = networkstatus_getinfo_helper_single(rs);
       if (!s) continue;
@@ -3584,7 +3592,7 @@ control_event_buildtimeout_set(const circuit_build_times_t *cbt,
 /** Called when a single local_routerstatus_t has changed: Sends an NS event
  * to any controller that cares. */
 int
-control_event_networkstatus_changed_single(routerstatus_t *rs)
+control_event_networkstatus_changed_single(const routerstatus_t *rs)
 {
   smartlist_t *statuses;
   int r;
@@ -3593,7 +3601,7 @@ control_event_networkstatus_changed_single(routerstatus_t *rs)
     return 0;
 
   statuses = smartlist_create();
-  smartlist_add(statuses, rs);
+  smartlist_add(statuses, (void*)rs);
   r = control_event_networkstatus_changed(statuses);
   smartlist_free(statuses);
   return r;
@@ -3718,9 +3726,9 @@ control_event_guard(const char *nickname, const char *digest,
 
   {
     char buf[MAX_VERBOSE_NICKNAME_LEN+1];
-    routerinfo_t *ri = router_get_by_digest(digest);
-    if (ri) {
-      router_get_verbose_nickname(buf, ri);
+    const node_t *node = node_get_by_id(digest);
+    if (node) {
+      node_get_verbose_nickname(node, buf);
     } else {
       tor_snprintf(buf, sizeof(buf), "$%s~%s", hbuf, nickname);
     }

+ 1 - 1
src/or/control.h

@@ -53,7 +53,7 @@ int control_event_my_descriptor_changed(void);
 int control_event_networkstatus_changed(smartlist_t *statuses);
 
 int control_event_newconsensus(const networkstatus_t *consensus);
-int control_event_networkstatus_changed_single(routerstatus_t *rs);
+int control_event_networkstatus_changed_single(const routerstatus_t *rs);
 int control_event_general_status(int severity, const char *format, ...)
   CHECK_PRINTF(2,3);
 int control_event_client_status(int severity, const char *format, ...)

+ 27 - 20
src/or/directory.c

@@ -17,6 +17,7 @@
 #include "main.h"
 #include "microdesc.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "rendclient.h"
 #include "rendcommon.h"
@@ -219,17 +220,19 @@ dir_conn_purpose_to_string(int purpose)
 int
 router_supports_extrainfo(const char *identity_digest, int is_authority)
 {
-  routerinfo_t *ri = router_get_by_digest(identity_digest);
+  const node_t *node = node_get_by_id(identity_digest);
 
-  if (ri) {
-    if (ri->caches_extra_info)
+  if (node && node->ri) {
+    if (node->ri->caches_extra_info)
       return 1;
-    if (is_authority && ri->platform &&
-        tor_version_as_new_as(ri->platform, "Tor 0.2.0.0-alpha-dev (r10070)"))
+    if (is_authority && node->ri->platform &&
+        tor_version_as_new_as(node->ri->platform,
+                              "Tor 0.2.0.0-alpha-dev (r10070)"))
       return 1;
   }
   if (is_authority) {
-    routerstatus_t *rs = router_get_consensus_status_by_id(identity_digest);
+    const routerstatus_t *rs =
+      router_get_consensus_status_by_id(identity_digest);
     if (rs && rs->version_supports_extrainfo_upload)
       return 1;
   }
@@ -328,7 +331,7 @@ void
 directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
                              const char *resource, int pds_flags)
 {
-  routerstatus_t *rs = NULL;
+  const routerstatus_t *rs = NULL;
   or_options_t *options = get_options();
   int prefer_authority = directory_fetches_from_authorities(options);
   int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose);
@@ -400,10 +403,12 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
        * possible directory question. This won't be true forever. -RD */
       /* It certainly is not true with conditional consensus downloading,
        * so, for now, never assume the server supports that. */
-      routerinfo_t *ri = choose_random_entry(NULL);
-      if (ri) {
+      const node_t *node = choose_random_entry(NULL);
+      if (node && node->ri) {
+        /* every bridge has a routerinfo. */
         tor_addr_t addr;
-        tor_addr_from_ipv4h(&addr, ri->addr);
+        routerinfo_t *ri = node->ri;
+        node_get_addr(node, &addr);
         directory_initiate_command(ri->address, &addr,
                                    ri->or_port, 0,
                                    0, /* don't use conditional consensus url */
@@ -512,7 +517,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose,
 /** Same as directory_initiate_command_routerstatus(), but accepts
  * rendezvous data to fetch a hidden service descriptor. */
 void
-directory_initiate_command_routerstatus_rend(routerstatus_t *status,
+directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
                                              uint8_t dir_purpose,
                                              uint8_t router_purpose,
                                              int anonymized_connection,
@@ -522,18 +527,19 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status,
                                              time_t if_modified_since,
                                              const rend_data_t *rend_query)
 {
-  routerinfo_t *router;
+  const node_t *node;
   char address_buf[INET_NTOA_BUF_LEN+1];
   struct in_addr in;
   const char *address;
   tor_addr_t addr;
-  router = router_get_by_digest(status->identity_digest);
-  if (!router && anonymized_connection) {
+  node = node_get_by_id(status->identity_digest);
+  if (!node && anonymized_connection) {
     log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we "
                      "don't have its router descriptor.", status->nickname);
     return;
-  } else if (router) {
-    address = router->address;
+  } else if (node) {
+    node_get_address_string(node, address_buf, sizeof(address_buf));
+    address = address_buf;
   } else {
     in.s_addr = htonl(status->addr);
     tor_inet_ntoa(&in, address_buf, sizeof(address_buf));
@@ -566,7 +572,7 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status,
  * want to fetch.
  */
 void
-directory_initiate_command_routerstatus(routerstatus_t *status,
+directory_initiate_command_routerstatus(const routerstatus_t *status,
                                         uint8_t dir_purpose,
                                         uint8_t router_purpose,
                                         int anonymized_connection,
@@ -590,7 +596,7 @@ directory_conn_is_self_reachability_test(dir_connection_t *conn)
 {
   if (conn->requested_resource &&
       !strcmpstart(conn->requested_resource,"authority")) {
-    routerinfo_t *me = router_get_my_routerinfo();
+    const routerinfo_t *me = router_get_my_routerinfo();
     if (me &&
         router_digest_is_me(conn->identity_digest) &&
         tor_addr_eq_ipv4h(&conn->_base.addr, me->addr) && /*XXXX prop 118*/
@@ -1597,7 +1603,8 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
                "'%s:%d'. I'll try again soon.",
                status_code, escaped(reason), conn->_base.address,
                conn->_base.port);
-      if ((rs = router_get_consensus_status_by_id(conn->identity_digest)))
+      rs = router_get_mutable_consensus_status_by_id(conn->identity_digest);
+      if (rs)
         rs->last_dir_503_at = now;
       if ((ds = router_get_trusteddirserver_by_digest(conn->identity_digest)))
         ds->fake_status.last_dir_503_at = now;
@@ -3692,7 +3699,7 @@ dir_microdesc_download_failed(smartlist_t *failed,
   if (! consensus)
     return;
   SMARTLIST_FOREACH_BEGIN(failed, const char *, d) {
-    rs = router_get_consensus_status_by_descriptor_digest(consensus, d);
+    rs = router_get_mutable_consensus_status_by_descriptor_digest(consensus,d);
     if (!rs)
       continue;
     dls = &rs->dl_status;

+ 2 - 2
src/or/directory.h

@@ -23,7 +23,7 @@ void directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
 void directory_get_from_all_authorities(uint8_t dir_purpose,
                                         uint8_t router_purpose,
                                         const char *resource);
-void directory_initiate_command_routerstatus(routerstatus_t *status,
+void directory_initiate_command_routerstatus(const routerstatus_t *status,
                                              uint8_t dir_purpose,
                                              uint8_t router_purpose,
                                              int anonymized_connection,
@@ -31,7 +31,7 @@ void directory_initiate_command_routerstatus(routerstatus_t *status,
                                              const char *payload,
                                              size_t payload_len,
                                              time_t if_modified_since);
-void directory_initiate_command_routerstatus_rend(routerstatus_t *status,
+void directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
                                                   uint8_t dir_purpose,
                                                   uint8_t router_purpose,
                                                   int anonymized_connection,

+ 144 - 109
src/or/dirserv.c

@@ -16,6 +16,7 @@
 #include "hibernate.h"
 #include "microdesc.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "rephist.h"
 #include "router.h"
@@ -65,8 +66,6 @@ static char *format_versions_list(config_line_t *ln);
 struct authdir_config_t;
 static int add_fingerprint_to_dir(const char *nickname, const char *fp,
                                   struct authdir_config_t *list);
-static uint32_t dirserv_router_get_status(const routerinfo_t *router,
-                                          const char **msg);
 static uint32_t
 dirserv_get_status_impl(const char *fp, const char *nickname,
                         const char *address,
@@ -74,7 +73,8 @@ dirserv_get_status_impl(const char *fp, const char *nickname,
                         const char *platform, const char *contact,
                         const char **msg, int should_log);
 static void clear_cached_dir(cached_dir_t *d);
-static signed_descriptor_t *get_signed_descriptor_by_fp(const char *fp,
+static const signed_descriptor_t *get_signed_descriptor_by_fp(
+                                                        const char *fp,
                                                         int extrainfo,
                                                         time_t publish_cutoff);
 static int dirserv_add_extrainfo(extrainfo_t *ei, const char **msg);
@@ -303,7 +303,7 @@ dirserv_load_fingerprint_file(void)
  *
  * If the status is 'FP_REJECT' and <b>msg</b> is provided, set
  * *<b>msg</b> to an explanation of why. */
-static uint32_t
+uint32_t
 dirserv_router_get_status(const routerinfo_t *router, const char **msg)
 {
   char d[DIGEST_LEN];
@@ -325,7 +325,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg)
 /** Return true if there is no point in downloading the router described by
  * <b>rs</b> because this directory would reject it. */
 int
-dirserv_would_reject_router(routerstatus_t *rs)
+dirserv_would_reject_router(const routerstatus_t *rs)
 {
   uint32_t res;
 
@@ -360,7 +360,7 @@ dirserv_get_name_status(const char *id_digest, const char *nickname)
   return 0;
 }
 
-/** Helper: As dirserv_get_router_status, but takes the router fingerprint
+/** Helper: As dirserv_router_get_status, but takes the router fingerprint
  * (hex, no spaces), nickname, address (used for logging only), IP address, OR
  * port, platform (logging only) and contact info (logging only) as arguments.
  *
@@ -375,7 +375,7 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
                         const char **msg, int should_log)
 {
   int reject_unlisted = get_options()->AuthDirRejectUnlisted;
-  uint32_t result = 0;
+  uint32_t result;
   router_status_t *status_by_digest;
 
   if (!fingerprint_list)
@@ -533,7 +533,7 @@ dirserv_router_has_valid_address(routerinfo_t *ri)
  */
 int
 authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
-                               int complain)
+                               int complain, int *valid_out)
 {
   /* Okay.  Now check whether the fingerprint is recognized. */
   uint32_t status = dirserv_router_get_status(ri, msg);
@@ -574,15 +574,24 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
     *msg = "Rejected: Address is not an IP, or IP is a private address.";
     return -1;
   }
-  /* Okay, looks like we're willing to accept this one. */
-  ri->is_named = (status & FP_NAMED) ? 1 : 0;
-  ri->is_valid = (status & FP_INVALID) ? 0 : 1;
-  ri->is_bad_directory = (status & FP_BADDIR) ? 1 : 0;
-  ri->is_bad_exit = (status & FP_BADEXIT) ? 1 : 0;
+
+  *valid_out = ! (status & FP_INVALID);
 
   return 0;
 }
 
+/** Update the relevant flags of <b>node</b> based on our opinion as a
+ * directory authority in <b>authstatus</b>, as returned by
+ * dirserv_router_get_status or equivalent.  */
+void
+dirserv_set_node_flags_from_authoritative_status(node_t *node,
+                                                 uint32_t authstatus)
+{
+  node->is_valid = (authstatus & FP_INVALID) ? 0 : 1;
+  node->is_bad_directory = (authstatus & FP_BADDIR) ? 1 : 0;
+  node->is_bad_exit = (authstatus & FP_BADEXIT) ? 1 : 0;
+}
+
 /** True iff <b>a</b> is more severe than <b>b</b>. */
 static int
 WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b)
@@ -707,7 +716,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
    * from this server.  (We do this here and not in router_add_to_routerlist
    * because we want to be able to accept the newest router descriptor that
    * another authority has, so we all converge on the same one.) */
-  ri_old = router_get_by_digest(ri->cache_info.identity_digest);
+  ri_old = router_get_mutable_by_digest(ri->cache_info.identity_digest);
   if (ri_old && ri_old->cache_info.published_on < ri->cache_info.published_on
       && router_differences_are_cosmetic(ri_old, ri)
       && !router_is_me(ri)) {
@@ -751,8 +760,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
     routerlist_descriptors_added(changed, 0);
     smartlist_free(changed);
     if (!*msg) {
-      *msg =  ri->is_valid ? "Descriptor for valid server accepted" :
-        "Descriptor for invalid server accepted";
+      *msg =  "Descriptor accepted";
     }
     log_info(LD_DIRSERV,
              "Added descriptor from '%s' (source: %s): %s.",
@@ -767,7 +775,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
 static was_router_added_t
 dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
 {
-  routerinfo_t *ri;
+  const routerinfo_t *ri;
   int r;
   tor_assert(msg);
   *msg = NULL;
@@ -807,50 +815,60 @@ dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
 static void
 directory_remove_invalid(void)
 {
-  int i;
   int changed = 0;
   routerlist_t *rl = router_get_routerlist();
+  smartlist_t *nodes = smartlist_create();
+  smartlist_add_all(nodes, nodelist_get_list());
 
-  routerlist_assert_ok(rl);
-
-  for (i = 0; i < smartlist_len(rl->routers); ++i) {
+  SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) {
     const char *msg;
-    routerinfo_t *ent = smartlist_get(rl->routers, i);
-    uint32_t r = dirserv_router_get_status(ent, &msg);
+    routerinfo_t *ent = node->ri;
+    uint32_t r;
+    if (!ent)
+      continue;
+    r = dirserv_router_get_status(ent, &msg);
     if (r & FP_REJECT) {
       log_info(LD_DIRSERV, "Router '%s' is now rejected: %s",
                ent->nickname, msg?msg:"");
       routerlist_remove(rl, ent, 0, time(NULL));
-      i--;
       changed = 1;
       continue;
     }
-    if (bool_neq((r & FP_NAMED), ent->is_named)) {
+#if 0
+    if (bool_neq((r & FP_NAMED), ent->auth_says_is_named)) {
       log_info(LD_DIRSERV,
                "Router '%s' is now %snamed.", ent->nickname,
                (r&FP_NAMED)?"":"un");
       ent->is_named = (r&FP_NAMED)?1:0;
       changed = 1;
     }
-    if (bool_neq((r & FP_INVALID), !ent->is_valid)) {
+    if (bool_neq((r & FP_UNNAMED), ent->auth_says_is_unnamed)) {
+      log_info(LD_DIRSERV,
+               "Router '%s' is now %snamed. (FP_UNNAMED)", ent->nickname,
+               (r&FP_NAMED)?"":"un");
+      ent->is_named = (r&FP_NUNAMED)?0:1;
+      changed = 1;
+    }
+#endif
+    if (bool_neq((r & FP_INVALID), !node->is_valid)) {
       log_info(LD_DIRSERV, "Router '%s' is now %svalid.", ent->nickname,
                (r&FP_INVALID) ? "in" : "");
-      ent->is_valid = (r&FP_INVALID)?0:1;
+      node->is_valid = (r&FP_INVALID)?0:1;
       changed = 1;
     }
-    if (bool_neq((r & FP_BADDIR), ent->is_bad_directory)) {
+    if (bool_neq((r & FP_BADDIR), node->is_bad_directory)) {
       log_info(LD_DIRSERV, "Router '%s' is now a %s directory", ent->nickname,
                (r & FP_BADDIR) ? "bad" : "good");
-      ent->is_bad_directory = (r&FP_BADDIR) ? 1: 0;
+      node->is_bad_directory = (r&FP_BADDIR) ? 1: 0;
       changed = 1;
     }
-    if (bool_neq((r & FP_BADEXIT), ent->is_bad_exit)) {
+    if (bool_neq((r & FP_BADEXIT), node->is_bad_exit)) {
       log_info(LD_DIRSERV, "Router '%s' is now a %s exit", ent->nickname,
                (r & FP_BADEXIT) ? "bad" : "good");
-      ent->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
+      node->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
       changed = 1;
     }
-  }
+  } SMARTLIST_FOREACH_END(node);
   if (changed)
     directory_set_dirty();
 
@@ -893,10 +911,11 @@ directory_set_dirty(void)
  * as running iff <b>is_live</b> is true.
  */
 static char *
-list_single_server_status(routerinfo_t *desc, int is_live)
+list_single_server_status(const routerinfo_t *desc, int is_live)
 {
   char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
   char *cp;
+  const node_t *node;
 
   tor_assert(desc);
 
@@ -904,7 +923,8 @@ list_single_server_status(routerinfo_t *desc, int is_live)
   if (!is_live) {
     *cp++ = '!';
   }
-  if (desc->is_valid) {
+  node = node_get_by_id(desc->cache_info.identity_digest);
+  if (node && node->is_valid) {
     strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
     cp += strlen(cp);
     *cp++ = '=';
@@ -943,6 +963,8 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
     unreachable.
    */
   int answer;
+  node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest);
+  tor_assert(node);
 
   if (router_is_me(router)) {
     /* We always know if we are down ourselves. */
@@ -967,7 +989,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
     rep_hist_note_router_unreachable(router->cache_info.identity_digest, now);
   }
 
-  router->is_running = answer;
+  node->is_running = answer;
 }
 
 /** Based on the routerinfo_ts in <b>routers</b>, allocate the
@@ -995,6 +1017,8 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
   rs_entries = smartlist_create();
 
   SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
+    const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+    tor_assert(node);
     if (authdir) {
       /* Update router status in routerinfo_t. */
       dirserv_set_router_is_running(ri, now);
@@ -1002,12 +1026,13 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
     if (for_controller) {
       char name_buf[MAX_VERBOSE_NICKNAME_LEN+2];
       char *cp = name_buf;
-      if (!ri->is_running)
+      if (!node->is_running)
         *cp++ = '!';
       router_get_verbose_nickname(cp, ri);
       smartlist_add(rs_entries, tor_strdup(name_buf));
     } else if (ri->cache_info.published_on >= cutoff) {
-      smartlist_add(rs_entries, list_single_server_status(ri, ri->is_running));
+      smartlist_add(rs_entries, list_single_server_status(ri,
+                                                          node->is_running));
     }
   } SMARTLIST_FOREACH_END(ri);
 
@@ -1045,12 +1070,12 @@ format_versions_list(config_line_t *ln)
  * not hibernating, and not too old. Else return 0.
  */
 static int
-router_is_active(routerinfo_t *ri, time_t now)
+router_is_active(const routerinfo_t *ri, const node_t *node, time_t now)
 {
   time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
   if (ri->cache_info.published_on < cutoff)
     return 0;
-  if (!ri->is_running || !ri->is_valid || ri->is_hibernating)
+  if (!node->is_running || !node->is_valid || ri->is_hibernating)
     return 0;
   return 1;
 }
@@ -1151,7 +1176,7 @@ dirserv_dump_directory_to_string(char **dir_out,
 int
 directory_fetches_from_authorities(or_options_t *options)
 {
-  routerinfo_t *me;
+  const routerinfo_t *me;
   uint32_t addr;
   int refuseunknown;
   if (options->FetchDirInfoEarly)
@@ -1716,7 +1741,7 @@ static uint64_t total_exit_bandwidth = 0;
 /** Helper: estimate the uptime of a router given its stated uptime and the
  * amount of time since it last stated its stated uptime. */
 static INLINE long
-real_uptime(routerinfo_t *router, time_t now)
+real_uptime(const routerinfo_t *router, time_t now)
 {
   if (now < router->cache_info.published_on)
     return router->uptime;
@@ -1767,7 +1792,8 @@ dirserv_thinks_router_is_unreliable(time_t now,
  * been set.
  */
 static int
-dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
+dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
+                                const node_t *node, time_t now)
 {
   long uptime = real_uptime(router, now);
 
@@ -1777,7 +1803,7 @@ dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
    * version is too old. */
   return (router->wants_to_be_hs_dir && router->dir_port &&
           uptime > get_options()->MinUptimeHidServDirectoryV2 &&
-          router->is_running);
+          node->is_running);
 }
 
 /** Look through the routerlist, the Mean Time Between Failure history, and
@@ -1825,19 +1851,22 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
   /* Weighted fractional uptime for each active router. */
   wfus = tor_malloc(sizeof(double)*smartlist_len(rl->routers));
 
+  nodelist_assert_ok();
+
   /* Now, fill in the arrays. */
-  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
-    if (router_is_active(ri, now)) {
+  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
+    routerinfo_t *ri = node->ri;
+    if (ri && router_is_active(ri, node, now)) {
       const char *id = ri->cache_info.identity_digest;
       uint32_t bw;
-      ri->is_exit = (!router_exit_policy_rejects_all(ri) &&
-                    exit_policy_is_general_exit(ri->exit_policy));
+      node->is_exit = (!router_exit_policy_rejects_all(ri) &&
+                       exit_policy_is_general_exit(ri->exit_policy));
       uptimes[n_active] = (uint32_t)real_uptime(ri, now);
       mtbfs[n_active] = rep_hist_get_stability(id, now);
       tks  [n_active] = rep_hist_get_weighted_time_known(id, now);
       bandwidths[n_active] = bw = router_get_advertised_bandwidth(ri);
       total_bandwidth += bw;
-      if (ri->is_exit && !ri->is_bad_exit) {
+      if (node->is_exit && !node->is_bad_exit) {
         total_exit_bandwidth += bw;
       } else {
         bandwidths_excluding_exits[n_active_nonexit] = bw;
@@ -1845,7 +1874,7 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
       }
       ++n_active;
     }
-  });
+  } SMARTLIST_FOREACH_END(node);
 
   /* Now, compute thresholds. */
   if (n_active) {
@@ -1871,15 +1900,17 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
   /* Now that we have a time-known that 7/8 routers are known longer than,
    * fill wfus with the wfu of every such "familiar" router. */
   n_familiar = 0;
-  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
-      if (router_is_active(ri, now)) {
+
+  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
+      routerinfo_t *ri = node->ri;
+      if (ri && router_is_active(ri, node, now)) {
         const char *id = ri->cache_info.identity_digest;
         long tk = rep_hist_get_weighted_time_known(id, now);
         if (tk < guard_tk)
           continue;
         wfus[n_familiar++] = rep_hist_get_weighted_fractional_uptime(id, now);
       }
-    });
+  } SMARTLIST_FOREACH_END(node);
   if (n_familiar)
     guard_wfu = median_double(wfus, n_familiar);
   if (guard_wfu > WFU_TO_GUARANTEE_GUARD)
@@ -1950,7 +1981,7 @@ version_from_platform(const char *platform)
  */
 int
 routerstatus_format_entry(char *buf, size_t buf_len,
-                          routerstatus_t *rs, const char *version,
+                          const routerstatus_t *rs, const char *version,
                           routerstatus_format_type_t format)
 {
   int r;
@@ -2004,7 +2035,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
                    rs->is_possible_guard?" Guard":"",
                    rs->is_hs_dir?" HSDir":"",
                    rs->is_named?" Named":"",
-                   rs->is_running?" Running":"",
+                   rs->is_flagged_running?" Running":"",
                    rs->is_stable?" Stable":"",
                    rs->is_unnamed?" Unnamed":"",
                    rs->is_v2_dir?" V2Dir":"",
@@ -2026,7 +2057,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
   }
 
   if (format != NS_V2) {
-    routerinfo_t* desc = router_get_by_digest(rs->identity_digest);
+    const routerinfo_t* desc = router_get_by_digest(rs->identity_digest);
     uint32_t bw;
 
     if (format != NS_CONTROL_PORT) {
@@ -2122,6 +2153,8 @@ _compare_routerinfo_by_ip_and_bw(const void **a, const void **b)
   routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b;
   int first_is_auth, second_is_auth;
   uint32_t bw_first, bw_second;
+  const node_t *node_first, *node_second;
+  int first_is_running, second_is_running;
 
   /* we return -1 if first should appear before second... that is,
    * if first is a better router. */
@@ -2144,9 +2177,14 @@ _compare_routerinfo_by_ip_and_bw(const void **a, const void **b)
   else if (!first_is_auth && second_is_auth)
     return 1;
 
-  else if (first->is_running && !second->is_running)
+  node_first = node_get_by_id(first->cache_info.identity_digest);
+  node_second = node_get_by_id(second->cache_info.identity_digest);
+  first_is_running = node_first && node_first->is_running;
+  second_is_running = node_second && node_second->is_running;
+
+  if (first_is_running && !second_is_running)
     return -1;
-  else if (!first->is_running && second->is_running)
+  else if (!first_is_running && second_is_running)
     return 1;
 
   bw_first = router_get_advertised_bandwidth(first);
@@ -2215,7 +2253,9 @@ get_possible_sybil_list(const smartlist_t *routers)
  */
 void
 set_routerstatus_from_routerinfo(routerstatus_t *rs,
-                                 routerinfo_t *ri, time_t now,
+                                 node_t *node,
+                                 routerinfo_t *ri,
+                                 time_t now,
                                  int naming, int listbadexits,
                                  int listbaddirs)
 {
@@ -2227,48 +2267,46 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
     router_digest_is_trusted_dir(ri->cache_info.identity_digest);
 
   /* Already set by compute_performance_thresholds. */
-  rs->is_exit = ri->is_exit;
-  rs->is_stable = ri->is_stable =
-    router_is_active(ri, now) &&
+  rs->is_exit = node->is_exit;
+  rs->is_stable = node->is_stable =
+    router_is_active(ri, node, now) &&
     !dirserv_thinks_router_is_unreliable(now, ri, 1, 0) &&
     !unstable_version;
-  rs->is_fast = ri->is_fast =
-    router_is_active(ri, now) &&
+  rs->is_fast = node->is_fast =
+    router_is_active(ri, node, now) &&
     !dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
-  rs->is_running = ri->is_running; /* computed above */
+  rs->is_flagged_running = node->is_running; /* computed above */
 
   if (naming) {
     uint32_t name_status = dirserv_get_name_status(
-                         ri->cache_info.identity_digest, ri->nickname);
+                                              node->identity, ri->nickname);
     rs->is_named = (naming && (name_status & FP_NAMED)) ? 1 : 0;
     rs->is_unnamed = (naming && (name_status & FP_UNNAMED)) ? 1 : 0;
   }
-  rs->is_valid = ri->is_valid;
+  rs->is_valid = node->is_valid;
 
-  if (rs->is_fast &&
+  if (node->is_fast &&
       (router_get_advertised_bandwidth(ri) >= BANDWIDTH_TO_GUARANTEE_GUARD ||
        router_get_advertised_bandwidth(ri) >=
                               MIN(guard_bandwidth_including_exits,
                                   guard_bandwidth_excluding_exits))) {
-    long tk = rep_hist_get_weighted_time_known(
-                                      ri->cache_info.identity_digest, now);
-    double wfu = rep_hist_get_weighted_fractional_uptime(
-                                      ri->cache_info.identity_digest, now);
+    long tk = rep_hist_get_weighted_time_known(node->identity, now);
+    double wfu = rep_hist_get_weighted_fractional_uptime(node->identity, now);
     rs->is_possible_guard = (wfu >= guard_wfu && tk >= guard_tk) ? 1 : 0;
   } else {
     rs->is_possible_guard = 0;
   }
-  rs->is_bad_directory = listbaddirs && ri->is_bad_directory;
-  rs->is_bad_exit = listbadexits && ri->is_bad_exit;
-  ri->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, now);
-  rs->is_hs_dir = ri->is_hs_dir;
+  rs->is_bad_directory = listbaddirs && node->is_bad_directory;
+  rs->is_bad_exit = listbadexits && node->is_bad_exit;
+  node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now);
+  rs->is_hs_dir = node->is_hs_dir;
   rs->is_v2_dir = ri->dir_port != 0;
 
   if (!strcasecmp(ri->nickname, UNNAMED_ROUTER_NICKNAME))
     rs->is_named = rs->is_unnamed = 0;
 
   rs->published_on = ri->cache_info.published_on;
-  memcpy(rs->identity_digest, ri->cache_info.identity_digest, DIGEST_LEN);
+  memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
   memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
          DIGEST_LEN);
   rs->addr = ri->addr;
@@ -2285,7 +2323,7 @@ static void
 clear_status_flags_on_sybil(routerstatus_t *rs)
 {
   rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
-    rs->is_running = rs->is_named = rs->is_valid = rs->is_v2_dir =
+    rs->is_flagged_running = rs->is_named = rs->is_valid = rs->is_v2_dir =
     rs->is_hs_dir = rs->is_possible_guard = rs->is_bad_exit =
     rs->is_bad_directory = 0;
   /* FFFF we might want some mechanism to check later on if we
@@ -2293,18 +2331,6 @@ clear_status_flags_on_sybil(routerstatus_t *rs)
    * forget to add it to this clause. */
 }
 
-/** Clear all the status flags in routerinfo <b>router</b>. We put this
- * function here because it's eerily similar to
- * clear_status_flags_on_sybil() above. One day we should merge them. */
-void
-router_clear_status_flags(routerinfo_t *router)
-{
-  router->is_valid = router->is_running = router->is_hs_dir =
-    router->is_fast = router->is_stable =
-    router->is_possible_guard = router->is_exit =
-    router->is_bad_exit = router->is_bad_directory = 0;
-}
-
 /**
  * Helper function to parse out a line in the measured bandwidth file
  * into a measured_bw_line_t output structure. Returns -1 on failure
@@ -2552,17 +2578,20 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
       routerstatus_t *rs;
       vote_routerstatus_t *vrs;
       microdesc_t *md;
+      node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+      if (!node)
+        continue;
 
       vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
       rs = &vrs->status;
-      set_routerstatus_from_routerinfo(rs, ri, now,
+      set_routerstatus_from_routerinfo(rs, node, ri, now,
                                        naming, listbadexits, listbaddirs);
 
       if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
         clear_status_flags_on_sybil(rs);
 
       if (!vote_on_reachability)
-        rs->is_running = 0;
+        rs->is_flagged_running = 0;
 
       vrs->version = version_from_platform(ri->platform);
       md = dirvote_create_microdescriptor(ri);
@@ -2795,8 +2824,12 @@ generate_v2_networkstatus_opinion(void)
     if (ri->cache_info.published_on >= cutoff) {
       routerstatus_t rs;
       char *version = version_from_platform(ri->platform);
-
-      set_routerstatus_from_routerinfo(&rs, ri, now,
+      node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+      if (!node) {
+        tor_free(version);
+        continue;
+      }
+      set_routerstatus_from_routerinfo(&rs, node, ri, now,
                                        naming, listbadexits, listbaddirs);
 
       if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
@@ -2879,7 +2912,7 @@ dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result,
 
   if (!strcmp(key,"authority")) {
     if (authdir_mode_v2(get_options())) {
-      routerinfo_t *me = router_get_my_routerinfo();
+      const routerinfo_t *me = router_get_my_routerinfo();
       if (me)
         smartlist_add(result,
                       tor_memdup(me->cache_info.identity_digest, DIGEST_LEN));
@@ -2965,7 +2998,7 @@ dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
                       smartlist_add(fps_out,
                       tor_memdup(r->cache_info.identity_digest, DIGEST_LEN)));
   } else if (!strcmp(key, "authority")) {
-    routerinfo_t *ri = router_get_my_routerinfo();
+    const routerinfo_t *ri = router_get_my_routerinfo();
     if (ri)
       smartlist_add(fps_out,
                     tor_memdup(ri->cache_info.identity_digest, DIGEST_LEN));
@@ -2985,8 +3018,8 @@ dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
 
   if (for_unencrypted_conn) {
     /* Remove anything that insists it not be sent unencrypted. */
-    SMARTLIST_FOREACH(fps_out, char *, cp, {
-        signed_descriptor_t *sd;
+    SMARTLIST_FOREACH_BEGIN(fps_out, char *, cp) {
+        const signed_descriptor_t *sd;
         if (by_id)
           sd = get_signed_descriptor_by_fp(cp,is_extrainfo,0);
         else if (is_extrainfo)
@@ -2997,7 +3030,7 @@ dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
           tor_free(cp);
           SMARTLIST_DEL_CURRENT(fps_out, cp);
         }
-      });
+    } SMARTLIST_FOREACH_END(cp);
   }
 
   if (!smartlist_len(fps_out)) {
@@ -3036,9 +3069,9 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
     SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
                       smartlist_add(descs_out, &(r->cache_info)));
   } else if (!strcmp(key, "/tor/server/authority")) {
-    routerinfo_t *ri = router_get_my_routerinfo();
+    const routerinfo_t *ri = router_get_my_routerinfo();
     if (ri)
-      smartlist_add(descs_out, &(ri->cache_info));
+      smartlist_add(descs_out, (void*) &(ri->cache_info));
   } else if (!strcmpstart(key, "/tor/server/d/")) {
     smartlist_t *digests = smartlist_create();
     key += strlen("/tor/server/d/");
@@ -3062,17 +3095,17 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
        {
          if (router_digest_is_me(d)) {
            /* make sure desc_routerinfo exists */
-           routerinfo_t *ri = router_get_my_routerinfo();
+           const routerinfo_t *ri = router_get_my_routerinfo();
            if (ri)
-             smartlist_add(descs_out, &(ri->cache_info));
+             smartlist_add(descs_out, (void*) &(ri->cache_info));
          } else {
-           routerinfo_t *ri = router_get_by_digest(d);
+           const routerinfo_t *ri = router_get_by_digest(d);
            /* Don't actually serve a descriptor that everyone will think is
             * expired.  This is an (ugly) workaround to keep buggy 0.1.1.10
             * Tors from downloading descriptors that they will throw away.
             */
            if (ri && ri->cache_info.published_on > cutoff)
-             smartlist_add(descs_out, &(ri->cache_info));
+             smartlist_add(descs_out, (void*) &(ri->cache_info));
          }
        });
     SMARTLIST_FOREACH(digests, char *, d, tor_free(d));
@@ -3121,6 +3154,7 @@ dirserv_orconn_tls_done(const char *address,
       }
     }
   });
+
   /* FFFF Maybe we should reinstate the code that dumps routers with the same
    * addr/port but with nonmatching keys, but instead of dumping, we should
    * skip testing. */
@@ -3130,7 +3164,8 @@ dirserv_orconn_tls_done(const char *address,
  * an upload or a download.  Used to decide whether to relaunch reachability
  * testing for the server. */
 int
-dirserv_should_launch_reachability_test(routerinfo_t *ri, routerinfo_t *ri_old)
+dirserv_should_launch_reachability_test(const routerinfo_t *ri,
+                                        const routerinfo_t *ri_old)
 {
   if (!authdir_mode_handles_descs(get_options(), ri->purpose))
     return 0;
@@ -3254,7 +3289,7 @@ dirserv_remove_old_statuses(smartlist_t *fps, time_t cutoff)
  * its extra-info document if <b>extrainfo</b> is true. Return
  * NULL if not found or if the descriptor is older than
  * <b>publish_cutoff</b>. */
-static signed_descriptor_t *
+static const signed_descriptor_t *
 get_signed_descriptor_by_fp(const char *fp, int extrainfo,
                             time_t publish_cutoff)
 {
@@ -3264,7 +3299,7 @@ get_signed_descriptor_by_fp(const char *fp, int extrainfo,
     else
       return &(router_get_my_routerinfo()->cache_info);
   } else {
-    routerinfo_t *ri = router_get_by_digest(fp);
+    const routerinfo_t *ri = router_get_by_digest(fp);
     if (ri &&
         ri->cache_info.published_on > publish_cutoff) {
       if (extrainfo)
@@ -3332,7 +3367,7 @@ dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs,
   tor_assert(fps);
   if (is_serverdescs) {
     int n = smartlist_len(fps);
-    routerinfo_t *me = router_get_my_routerinfo();
+    const routerinfo_t *me = router_get_my_routerinfo();
     result = (me?me->cache_info.signed_descriptor_len:2048) * n;
     if (compressed)
       result /= 2; /* observed compressibility is between 35 and 55%. */
@@ -3399,7 +3434,7 @@ connection_dirserv_add_servers_to_outbuf(dir_connection_t *conn)
          connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) {
     const char *body;
     char *fp = smartlist_pop_last(conn->fingerprint_stack);
-    signed_descriptor_t *sd = NULL;
+    const signed_descriptor_t *sd = NULL;
     if (by_fp) {
       sd = get_signed_descriptor_by_fp(fp, extra, publish_cutoff);
     } else {

+ 11 - 7
src/or/dirserv.h

@@ -40,8 +40,6 @@
    MAX_V_LINE_LEN                                                       \
    )
 
-#define UNNAMED_ROUTER_NICKNAME "Unnamed"
-
 int connection_dirserv_flushed_some(dir_connection_t *conn);
 
 int dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk);
@@ -99,13 +97,19 @@ void dirserv_orconn_tls_done(const char *address,
                              uint16_t or_port,
                              const char *digest_rcvd,
                              int as_advertised);
-int dirserv_should_launch_reachability_test(routerinfo_t *ri,
-                                            routerinfo_t *ri_old);
+int dirserv_should_launch_reachability_test(const routerinfo_t *ri,
+                                            const routerinfo_t *ri_old);
 void dirserv_single_reachability_test(time_t now, routerinfo_t *router);
 void dirserv_test_reachability(time_t now);
 int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
-                                   int complain);
-int dirserv_would_reject_router(routerstatus_t *rs);
+                                   int complain,
+                                   int *valid_out);
+uint32_t dirserv_router_get_status(const routerinfo_t *router,
+                                   const char **msg);
+void dirserv_set_node_flags_from_authoritative_status(node_t *node,
+                                                      uint32_t authstatus);
+
+int dirserv_would_reject_router(const routerstatus_t *rs);
 int dirserv_remove_old_statuses(smartlist_t *fps, time_t cutoff);
 int dirserv_have_any_serverdesc(smartlist_t *fps, int spool_src);
 int dirserv_have_any_microdesc(const smartlist_t *fps);
@@ -114,7 +118,7 @@ size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs,
 size_t dirserv_estimate_microdesc_size(const smartlist_t *fps, int compressed);
 
 int routerstatus_format_entry(char *buf, size_t buf_len,
-                              routerstatus_t *rs, const char *platform,
+                              const routerstatus_t *rs, const char *platform,
                               routerstatus_format_type_t format);
 void dirserv_free_all(void);
 void cached_dir_decref(cached_dir_t *d);

+ 1 - 0
src/or/dirvote.h

@@ -63,6 +63,7 @@ const char *dirvote_get_pending_detached_signatures(void);
 #define DGV_INCLUDE_PREVIOUS 4
 const cached_dir_t *dirvote_get_vote(const char *fp, int flags);
 void set_routerstatus_from_routerinfo(routerstatus_t *rs,
+                                      node_t *node,
                                       routerinfo_t *ri, time_t now,
                                       int naming, int listbadexits,
                                       int listbaddirs);

+ 1 - 1
src/or/dns.c

@@ -668,7 +668,7 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
   cached_resolve_t *resolve;
   cached_resolve_t search;
   pending_connection_t *pending_connection;
-  routerinfo_t *me;
+  const routerinfo_t *me;
   tor_addr_t addr;
   time_t now = time(NULL);
   uint8_t is_reverse = 0;

+ 13 - 2
src/or/main.c

@@ -33,6 +33,7 @@
 #include "main.h"
 #include "microdesc.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "ntmain.h"
 #include "onion.h"
 #include "policies.h"
@@ -1043,6 +1044,15 @@ run_scheduled_events(time_t now)
    */
   consider_hibernation(now);
 
+  /* XXXX NM REMOVE THIS. XXXX NM XXXX NM XXXX NM*/
+  {
+    static time_t nl_check_time = 0;
+    if (nl_check_time <= now) {
+      nodelist_assert_ok();
+      nl_check_time = now + 30;
+    }
+  }
+
   /* 0b. If we've deferred a signewnym, make sure it gets handled
    * eventually. */
   if (signewnym_is_pending &&
@@ -1267,7 +1277,7 @@ run_scheduled_events(time_t now)
         /* If we haven't checked for 12 hours and our bandwidth estimate is
          * low, do another bandwidth test. This is especially important for
          * bridges, since they might go long periods without much use. */
-        routerinfo_t *me = router_get_my_routerinfo();
+        const routerinfo_t *me = router_get_my_routerinfo();
         if (time_to_recheck_bandwidth && me &&
             me->bandwidthcapacity < me->bandwidthrate &&
             me->bandwidthcapacity < 51200) {
@@ -1468,7 +1478,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
       (stats_n_seconds_working+seconds_elapsed) /
         TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
     /* every 20 minutes, check and complain if necessary */
-    routerinfo_t *me = router_get_my_routerinfo();
+    const routerinfo_t *me = router_get_my_routerinfo();
     if (me && !check_whether_orport_reachable()) {
       log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
                "its ORPort is reachable. Please check your firewalls, ports, "
@@ -2209,6 +2219,7 @@ tor_free_all(int postfork)
   connection_free_all();
   buf_shrink_freelists(1);
   memarea_clear_freelist();
+  nodelist_free_all();
   microdesc_free_all();
   if (!postfork) {
     config_free_all();

+ 12 - 3
src/or/microdesc.c

@@ -4,11 +4,13 @@
 #include "or.h"
 #include "config.h"
 #include "directory.h"
+#include "dirserv.h"
 #include "microdesc.h"
-#include "routerparse.h"
 #include "networkstatus.h"
+#include "nodelist.h"
+#include "policies.h"
 #include "routerlist.h"
-#include "dirserv.h"
+#include "routerparse.h"
 
 /** A data structure to hold a bunch of cached microdescriptors.  There are
  * two active files in the cache: a "cache file" that we mmap, and a "journal
@@ -249,6 +251,12 @@ microdescs_add_list_to_cache(microdesc_cache_t *cache,
       microdesc_cache_rebuild(cache);
   }
 
+  {
+    networkstatus_t *ns = networkstatus_get_latest_consensus();
+    if (ns && ns->flavor == FLAV_MICRODESC)
+      SMARTLIST_FOREACH(added, microdesc_t *, md, nodelist_add_microdesc(md));
+  }
+
   return added;
 }
 
@@ -474,7 +482,7 @@ microdesc_free(microdesc_t *md)
     SMARTLIST_FOREACH(md->family, char *, cp, tor_free(cp));
     smartlist_free(md->family);
   }
-  tor_free(md->exitsummary);
+  short_policy_free(md->exit_policy);
 
   tor_free(md);
 }
@@ -612,3 +620,4 @@ update_microdescs_from_networkstatus(time_t now)
       md->last_listed = ns->valid_after;
   } SMARTLIST_FOREACH_END(rs);
 }
+

+ 80 - 141
src/or/networkstatus.c

@@ -22,6 +22,7 @@
 #include "main.h"
 #include "microdesc.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "relay.h"
 #include "router.h"
 #include "routerlist.h"
@@ -106,9 +107,8 @@ void
 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;
@@ -939,10 +939,9 @@ compare_digest_to_routerstatus_entry(const void *_key, const void **_member)
   return memcmp(key, rs->identity_digest, DIGEST_LEN);
 }
 
-/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
- * NULL if none was found. */
+/** As networkstatus_v2_find_entry, but do not return a const pointer */
 routerstatus_t *
-networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
+networkstatus_v2_find_mutable_entry(networkstatus_v2_t *ns, const char *digest)
 {
   return smartlist_bsearch(ns->entries, digest,
                            compare_digest_to_routerstatus_entry);
@@ -950,13 +949,28 @@ networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
 
 /** 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);
 }
 
+/** 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 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
@@ -979,11 +993,11 @@ 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 in <b>consensus</b> 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(networkstatus_t *consensus,
+router_get_mutable_consensus_status_by_descriptor_digest(
+                                                 networkstatus_t *consensus,
                                                  const char *digest)
 {
   if (!consensus)
@@ -1001,6 +1015,17 @@ router_get_consensus_status_by_descriptor_digest(networkstatus_t *consensus,
   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
  * status, or NULL if the digest is unrecognized. */
 download_status_t *
@@ -1009,8 +1034,8 @@ router_get_dl_status_by_descriptor_digest(const char *d)
   routerstatus_t *rs;
   if (!current_ns_consensus)
     return NULL;
-  if ((rs = router_get_consensus_status_by_descriptor_digest(
-                                                 current_ns_consensus, d)))
+  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);
@@ -1018,10 +1043,9 @@ router_get_dl_status_by_descriptor_digest(const char *d)
   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;
@@ -1029,100 +1053,27 @@ router_get_consensus_status_by_id(const char *digest)
                            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
@@ -1524,7 +1475,7 @@ routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b)
          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 ||
@@ -1565,13 +1516,14 @@ notify_control_networkstatus_changed(const networkstatus_t *old_c,
   }
   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);
@@ -1595,7 +1547,6 @@ networkstatus_copy_old_consensus_info(networkstatus_t *new_c,
                                 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,
@@ -1833,6 +1784,8 @@ networkstatus_set_current_consensus(const char *consensus,
     /* 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);
@@ -1975,7 +1928,7 @@ download_status_map_update_from_v2_networkstatus(void)
 
   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))
@@ -2003,7 +1956,8 @@ routerstatus_list_update_named_server_map(void)
   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,
@@ -2025,7 +1979,6 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
   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;
@@ -2039,25 +1992,19 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
                          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)) {
@@ -2065,28 +2012,17 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
         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) {
@@ -2098,7 +2034,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
   /* 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),
@@ -2131,7 +2067,7 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
   }
   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)
@@ -2144,7 +2080,7 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
  * 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);
@@ -2177,6 +2113,9 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
 
   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)
@@ -2184,7 +2123,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
     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));
   });
 
@@ -2300,7 +2239,7 @@ getinfo_helper_networkstatus(control_connection_t *conn,
                              const char *question, char **answer,
                              const char **errmsg)
 {
-  routerstatus_t *status;
+  const routerstatus_t *status;
   (void) conn;
 
   if (!current_consensus) {
@@ -2311,7 +2250,7 @@ getinfo_helper_networkstatus(control_connection_t *conn,
   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));
       });

+ 17 - 7
src/or/networkstatus.h

@@ -38,20 +38,30 @@ int router_set_networkstatus_v2(const char *s, time_t arrived_at,
 void networkstatus_v2_list_clean(time_t now);
 int compare_digest_to_routerstatus_entry(const void *_key,
                                          const void **_member);
-routerstatus_t *networkstatus_v2_find_entry(networkstatus_v2_t *ns,
+const routerstatus_t *networkstatus_v2_find_entry(networkstatus_v2_t *ns,
                                          const char *digest);
-routerstatus_t *networkstatus_vote_find_entry(networkstatus_t *ns,
+const routerstatus_t *networkstatus_vote_find_entry(networkstatus_t *ns,
+                                              const char *digest);
+routerstatus_t *networkstatus_v2_find_mutable_entry(networkstatus_v2_t *ns,
+                                        const char *digest);
+routerstatus_t *networkstatus_vote_find_mutable_entry(networkstatus_t *ns,
                                               const char *digest);
 int networkstatus_vote_find_entry_idx(networkstatus_t *ns,
                                       const char *digest, int *found_out);
 const smartlist_t *networkstatus_get_v2_list(void);
 download_status_t *router_get_dl_status_by_descriptor_digest(const char *d);
-routerstatus_t *router_get_consensus_status_by_id(const char *digest);
-routerstatus_t *router_get_consensus_status_by_descriptor_digest(
+const routerstatus_t *router_get_consensus_status_by_id(const char *digest);
+routerstatus_t *router_get_mutable_consensus_status_by_id(
+                                   const char *digest);
+const routerstatus_t *router_get_consensus_status_by_descriptor_digest(
+                                   networkstatus_t *consensus,
+                                   const char *digest);
+routerstatus_t *router_get_mutable_consensus_status_by_descriptor_digest(
                                    networkstatus_t *consensus,
                                    const char *digest);
-routerstatus_t *router_get_consensus_status_by_nickname(const char *nickname,
-                                                       int warn_if_unnamed);
+const routerstatus_t *router_get_consensus_status_by_nickname(
+                                   const char *nickname,
+                                   int warn_if_unnamed);
 const char *networkstatus_get_router_digest_by_nickname(const char *nickname);
 int networkstatus_nickname_is_unnamed(const char *nickname);
 void networkstatus_consensus_download_failed(int status_code,
@@ -83,7 +93,7 @@ void routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
 void signed_descs_update_status_from_consensus_networkstatus(
                                                          smartlist_t *descs);
 
-char *networkstatus_getinfo_helper_single(routerstatus_t *rs);
+char *networkstatus_getinfo_helper_single(const routerstatus_t *rs);
 char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now);
 void networkstatus_dump_bridge_status_to_file(time_t now);
 int32_t get_net_param_from_list(smartlist_t *net_params, const char *name,

+ 730 - 0
src/or/nodelist.c

@@ -0,0 +1,730 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2010, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "config.h"
+#include "dirserv.h"
+#include "microdesc.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "policies.h"
+#include "router.h"
+#include "routerlist.h"
+
+#include <string.h>
+
+static void nodelist_drop_node(node_t *node, int remove_from_ht);
+static void node_free(node_t *node);
+
+/** A nodelist_t holds a node_t object for every router we're "willing to use
+ * for something".  Specifically, it should hold a node_t for every node that
+ * is currently in the routerlist, or currently in the consensus we're using.
+ */
+typedef struct nodelist_t {
+  /* A list of all the nodes. */
+  smartlist_t *nodes;
+  /* Hash table to map from node ID digest to node. */
+  HT_HEAD(nodelist_map, node_t) nodes_by_id;
+
+} nodelist_t;
+
+static INLINE unsigned int
+node_id_hash(const node_t *node)
+{
+#if SIZEOF_INT == 4
+  const uint32_t *p = (const uint32_t*)node->identity;
+  return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4];
+#elif SIZEOF_INT == 8
+  const uint64_t *p = (const uint32_t*)node->identity;
+  const uint32_t *p32 = (const uint32_t*)node->identity;
+  return p[0] ^ p[1] ^ p32[4];
+#endif
+}
+
+static INLINE unsigned int
+node_id_eq(const node_t *node1, const node_t *node2)
+{
+  return 0 == memcmp(node1->identity, node2->identity, DIGEST_LEN);
+}
+
+HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq);
+HT_GENERATE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq,
+            0.6, malloc, realloc, free);
+
+/** The global nodelist. */
+static nodelist_t *the_nodelist=NULL;
+
+/** Create an empty nodelist if we haven't done so already. */
+static void
+init_nodelist(void)
+{
+  if (PREDICT_UNLIKELY(the_nodelist == NULL)) {
+    the_nodelist = tor_malloc_zero(sizeof(nodelist_t));
+    HT_INIT(nodelist_map, &the_nodelist->nodes_by_id);
+    the_nodelist->nodes = smartlist_create();
+  }
+}
+
+/** As node_get_by_id, but returns a non-const pointer */
+node_t *
+node_get_mutable_by_id(const char *identity_digest)
+{
+  node_t search, *node;
+  if (PREDICT_UNLIKELY(the_nodelist == NULL))
+    return NULL;
+
+  memcpy(&search.identity, identity_digest, DIGEST_LEN);
+  node = HT_FIND(nodelist_map, &the_nodelist->nodes_by_id, &search);
+  return node;
+}
+
+/** Return the node_t whose identity is <b>identity_digest</b>, or NULL
+ * if no such node exists. */
+const node_t *
+node_get_by_id(const char *identity_digest)
+{
+  return node_get_mutable_by_id(identity_digest);
+}
+
+/** Internal: return the node_t whose identity_digest is
+ * <b>identity_digest</b>.  If none exists, create a new one, add it to the
+ * nodelist, and return it.
+ *
+ * Requires that the nodelist be initialized.
+ */
+static node_t *
+node_get_or_create(const char *identity_digest)
+{
+  node_t *node;
+
+  if ((node = node_get_mutable_by_id(identity_digest)))
+    return node;
+
+  node = tor_malloc_zero(sizeof(node_t));
+  memcpy(node->identity, identity_digest, DIGEST_LEN);
+  HT_INSERT(nodelist_map, &the_nodelist->nodes_by_id, node);
+
+  smartlist_add(the_nodelist->nodes, node);
+  node->nodelist_idx = smartlist_len(the_nodelist->nodes) - 1;
+
+  node->country = -1;
+
+  return node;
+}
+
+/** Add <b>ri</b> to the nodelist. */
+node_t *
+nodelist_add_routerinfo(routerinfo_t *ri)
+{
+  node_t *node;
+  init_nodelist();
+  node = node_get_or_create(ri->cache_info.identity_digest);
+  node->ri = ri;
+
+  if (node->country == -1)
+    node_set_country(node);
+
+  if (authdir_mode(get_options())) {
+    const char *discard=NULL;
+    uint32_t status = dirserv_router_get_status(ri, &discard);
+    dirserv_set_node_flags_from_authoritative_status(node, status);
+  }
+
+  return node;
+}
+
+/** Set the appropriate node_t to use <b>md</b> as its microdescriptor.
+ *
+ * Called when a new microdesc has arrived and the usable consensus flavor
+ * is "microdesc".
+ **/
+node_t *
+nodelist_add_microdesc(microdesc_t *md)
+{
+  networkstatus_t *ns =
+    networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC);
+  const routerstatus_t *rs;
+  node_t *node;
+  if (ns == NULL)
+    return NULL;
+  init_nodelist();
+
+  /* Microdescriptors don't carry an identity digest, so we need to figure
+   * it out by looking up the routerstatus. */
+  rs = router_get_consensus_status_by_descriptor_digest(ns, md->digest);
+  if (rs == NULL)
+    return NULL;
+  node = node_get_mutable_by_id(rs->identity_digest);
+  if (node)
+    node->md = md;
+  return node;
+}
+
+/** Tell the nodelist that the current usable consensus to <b>ns</b>.
+ * This makes the nodelist change all of the routerstatus entries for
+ * the nodes, drop nodes that no longer have enough info to get used,
+ * and grab microdescriptors into nodes as appropriate.
+ */
+void
+nodelist_set_consensus(networkstatus_t *ns)
+{
+  or_options_t *options = get_options();
+  int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
+  init_nodelist();
+
+  SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node,
+                    node->rs = NULL);
+
+  SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
+    node_t *node = node_get_or_create(rs->identity_digest);
+    node->rs = rs;
+    if (ns->flavor == FLAV_MICRODESC) {
+      if (node->md == NULL ||
+          0!=memcmp(node->md->digest,rs->descriptor_digest,DIGEST256_LEN)) {
+        node->md = microdesc_cache_lookup_by_digest256(NULL,
+                                                       rs->descriptor_digest);
+      }
+    }
+
+    node_set_country(node);
+
+    /* If we're not an authdir, believe others. */
+    if (!authdir) {
+      node->is_valid = rs->is_valid;
+      node->is_running = rs->is_flagged_running;
+      node->is_fast = rs->is_fast;
+      node->is_stable = rs->is_stable;
+      node->is_possible_guard = rs->is_possible_guard;
+      node->is_exit = rs->is_exit;
+      node->is_bad_directory = rs->is_bad_directory;
+      node->is_bad_exit = rs->is_bad_exit;
+      node->is_hs_dir = rs->is_hs_dir;
+    }
+
+  } SMARTLIST_FOREACH_END(rs);
+
+  nodelist_purge();
+
+  if (! authdir) {
+    SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+      /* We have no routerstatus for this router. Clear flags so we can skip
+       * it, maybe.*/
+      if (!node->rs) {
+        tor_assert(node->ri); /* if it had only an md, or nothing, purge
+                               * would have removed it. */
+        if (node->ri->purpose == ROUTER_PURPOSE_GENERAL) {
+          /* Clear all flags. */
+          node->is_valid = node->is_running = node->is_hs_dir =
+            node->is_fast = node->is_stable =
+            node->is_possible_guard = node->is_exit =
+            node->is_bad_exit = node->is_bad_directory = 0;
+        }
+      }
+    } SMARTLIST_FOREACH_END(node);
+  }
+}
+
+/** Helper: return true iff a node has a usable amount of information*/
+static INLINE int
+node_is_usable(const node_t *node)
+{
+  return (node->rs) || (node->ri);
+}
+
+/** Tell the nodelist that <b>md</b> is no longer a microdescriptor for the
+ * node with <b>identity_digest</b>. */
+void
+nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md)
+{
+  node_t *node = node_get_mutable_by_id(identity_digest);
+  if (node && node->md == md)
+    node->md = NULL;
+}
+
+/** Tell the nodelist that <b>ri</b> is no longer in the routerlist. */
+void
+nodelist_remove_routerinfo(routerinfo_t *ri)
+{
+  node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+  if (node && node->ri == ri) {
+    node->ri = NULL;
+    if (! node_is_usable(node)) {
+      nodelist_drop_node(node, 1);
+      node_free(node);
+    }
+  }
+}
+
+/** Remove <b>node</b> from the nodelist.  (Asserts that it was there to begin
+ * with.) */
+static void
+nodelist_drop_node(node_t *node, int remove_from_ht)
+{
+  node_t *tmp;
+  int idx;
+  if (remove_from_ht) {
+    tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node);
+    tor_assert(tmp == node);
+  }
+
+  idx = node->nodelist_idx;
+  tor_assert(idx >= 0);
+
+  tor_assert(node == smartlist_get(the_nodelist->nodes, idx));
+  smartlist_del(the_nodelist->nodes, idx);
+  if (idx < smartlist_len(the_nodelist->nodes)) {
+    tmp = smartlist_get(the_nodelist->nodes, idx);
+    tmp->nodelist_idx = idx;
+  }
+  node->nodelist_idx = -1;
+}
+
+/** Release storage held by <b>node</b>  */
+static void
+node_free(node_t *node)
+{
+  if (!node)
+    return;
+  tor_assert(node->nodelist_idx == -1);
+  tor_free(node);
+}
+
+/** Remove all entries from the nodelist that don't have enough info to be
+ * usable for anything. */
+void
+nodelist_purge(void)
+{
+  node_t **iter;
+  if (PREDICT_UNLIKELY(the_nodelist == NULL))
+    return;
+
+  /* Remove the non-usable nodes. */
+  for (iter = HT_START(nodelist_map, &the_nodelist->nodes_by_id); iter; ) {
+    node_t *node = *iter;
+
+    if (node_is_usable(node)) {
+      iter = HT_NEXT(nodelist_map, &the_nodelist->nodes_by_id, iter);
+    } else {
+      iter = HT_NEXT_RMV(nodelist_map, &the_nodelist->nodes_by_id, iter);
+      nodelist_drop_node(node, 0);
+      node_free(node);
+    }
+  }
+  nodelist_assert_ok();
+}
+
+/** Release all storage held by the nodelist. */
+void
+nodelist_free_all(void)
+{
+  if (PREDICT_UNLIKELY(the_nodelist == NULL))
+    return;
+
+  HT_CLEAR(nodelist_map, &the_nodelist->nodes_by_id);
+  SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+    node->nodelist_idx = -1;
+    node_free(node);
+  } SMARTLIST_FOREACH_END(node);
+
+  smartlist_free(the_nodelist->nodes);
+
+  tor_free(the_nodelist);
+}
+
+/** Check that the nodelist is internally consistent, and consistent with
+ * the directory info it's derived from.
+ */
+void
+nodelist_assert_ok(void)
+{
+  routerlist_t *rl = router_get_routerlist();
+  networkstatus_t *ns = networkstatus_get_latest_consensus();
+  digestmap_t *dm = digestmap_new();
+
+  if (!the_nodelist)
+    return;
+
+  /* every routerinfo in rl->routers should be in the nodelist. */
+  if (rl) {
+    SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) {
+      const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+      tor_assert(node && node->ri == ri);
+      tor_assert(0 == memcmp(ri->cache_info.identity_digest,
+                             node->identity, DIGEST_LEN));
+      tor_assert(! digestmap_get(dm, node->identity));
+      digestmap_set(dm, node->identity, (void*)node);
+    } SMARTLIST_FOREACH_END(ri);
+  }
+
+  /* every routerstatus in ns should be in the nodelist */
+  if (ns) {
+    SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
+      const node_t *node = node_get_by_id(rs->identity_digest);
+      tor_assert(node && node->rs == rs);
+      tor_assert(0 == memcmp(rs->identity_digest, node->identity, DIGEST_LEN));
+      digestmap_set(dm, node->identity, (void*)node);
+      if (ns->flavor == FLAV_MICRODESC) {
+        /* If it's a microdesc consensus, every entry that has a
+         * microdescriptor should be in the nodelist.
+         */
+        microdesc_t *md =
+          microdesc_cache_lookup_by_digest256(NULL, rs->descriptor_digest);
+        tor_assert(md == node->md);
+      }
+    } SMARTLIST_FOREACH_END(rs);
+  }
+
+  /* The nodelist should have no other entries, and its entries should be
+   * well-formed. */
+  SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+    tor_assert(digestmap_get(dm, node->identity) != NULL);
+    tor_assert(node_sl_idx == node->nodelist_idx);
+  } SMARTLIST_FOREACH_END(node);
+
+  tor_assert((long)smartlist_len(the_nodelist->nodes) ==
+             (long)HT_SIZE(&the_nodelist->nodes_by_id));
+
+  digestmap_free(dm, NULL);
+}
+
+/** Return a list of a node_t * for every node we know about.  The caller
+ * MUST NOT modify the list. (You can set and clear flags in the nodes if
+ * you must, but you must not add or remove nodes.) */
+smartlist_t *
+nodelist_get_list(void)
+{
+  init_nodelist();
+  return the_nodelist->nodes;
+}
+
+/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
+ * the corresponding node_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. */
+const node_t *
+node_get_by_nickname(const char *nickname, int warn_if_unnamed)
+{
+  char digest_buf[DIGEST_LEN];
+  char nn_buf[MAX_NICKNAME_LEN+1];
+  char nn_char='\0';
+
+  if (!the_nodelist)
+    return NULL;
+
+  /* ???? NM Naming authorities had an additional weird behavior here where
+     they would treat their own namings as slightly authoritative in a
+     strange and inconsistent way.  I think that this way is better, but we
+     could get the old behavior back if we wanted to by adding a function
+     to look in the fp_by_name table in fingerprint_list, and using this
+     function to override the name-to-digest lookup below if we are a
+     naming server.  -NM
+   */
+
+  /* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */
+  if (hex_digest_nickname_decode(nickname, digest_buf, &nn_char, nn_buf)==0) {
+    const node_t *node = node_get_by_id(digest_buf);
+    if (!node)
+      return NULL;
+    if (nn_char) {
+      const char *real_name = node_get_nickname(node);
+      if (!real_name || strcasecmp(real_name, nn_buf))
+        return NULL;
+      if (nn_char == '=') {
+        const char *named_id =
+          networkstatus_get_router_digest_by_nickname(nn_buf);
+        if (!named_id || memcmp(named_id, digest_buf, DIGEST_LEN))
+          return NULL;
+      }
+    }
+    return node;
+  }
+
+  if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME))
+    return NULL;
+
+  /* Okay, so if we get here, the nickname is just a nickname.  Is there
+   * a binding for it in the consensus? */
+  {
+    const char *named_id =
+      networkstatus_get_router_digest_by_nickname(nickname);
+    if (named_id)
+      return node_get_by_id(named_id);
+  }
+
+  /* Is it marked as owned-by-someone-else? */
+  if (networkstatus_nickname_is_unnamed(nickname)) {
+    log_info(LD_GENERAL, "The name %s is listed as Unnamed: there is some "
+             "router that holds it, but not one listed in the current "
+             "consensus.", escaped(nickname));
+    return NULL;
+  }
+
+  /* Okay, so the name is not canonical for anybody. */
+  {
+    smartlist_t *matches = smartlist_create();
+    const node_t *choice = NULL;
+
+    SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+      if (!strcasecmp(node_get_nickname(node), nickname))
+        smartlist_add(matches, node);
+    } SMARTLIST_FOREACH_END(node);
+
+    if (smartlist_len(matches)>1 && warn_if_unnamed) {
+      int any_unwarned = 0;
+      SMARTLIST_FOREACH_BEGIN(matches, node_t *, node) {
+        if (!node->name_lookup_warned) {
+          node->name_lookup_warned = 1;
+          any_unwarned = 1;
+        }
+      } SMARTLIST_FOREACH_END(node);
+
+      if (any_unwarned) {
+        log_warn(LD_CONFIG, "There are multiple matches for the name %s, "
+                 "but none is listed as Named in the directory consensus. "
+                 "Choosing one arbitrarily.", nickname);
+      }
+    } else if (smartlist_len(matches)>1 && warn_if_unnamed) {
+      char fp[HEX_DIGEST_LEN+1];
+      node_t *node = smartlist_get(matches, 0);
+      if (node->name_lookup_warned) {
+        base16_encode(fp, sizeof(fp), node->identity, DIGEST_LEN);
+        log_warn(LD_CONFIG,
+                 "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);
+        node->name_lookup_warned = 1;
+      }
+    }
+
+    if (smartlist_len(matches))
+      choice = smartlist_get(matches, 0);
+
+    smartlist_free(matches);
+    return choice;
+  }
+}
+
+/** Return the nickname of <b>node</b>, or NULL if we can't find one. */
+const char *
+node_get_nickname(const node_t *node)
+{
+  tor_assert(node);
+  if (node->rs)
+    return node->rs->nickname;
+  else if (node->ri)
+    return node->ri->nickname;
+  else
+    return NULL;
+}
+
+/** Return true iff the nickname of <b>node</b> is canonical, based on the
+ * latest consensus. */
+int
+node_is_named(const node_t *node)
+{
+  const char *named_id;
+  const char *nickname = node_get_nickname(node);
+  if (!nickname)
+    return 0;
+  named_id = networkstatus_get_router_digest_by_nickname(nickname);
+  if (!named_id)
+    return 0;
+  return !memcmp(named_id, node->identity, DIGEST_LEN);
+}
+
+/** Return true iff <b>node</b> appears to be a directory authority or
+ * directory cache */
+int
+node_is_dir(const node_t *node)
+{
+  if (node->rs)
+    return node->rs->dir_port != 0;
+  else if (node->ri)
+    return node->ri->dir_port != 0;
+  else
+    return 0;
+}
+
+
+/** Return true iff <b>node</b> has either kind of usable descriptor -- that
+ * is, a routerdecriptor or a microdescriptor. */
+int
+node_has_descriptor(const node_t *node)
+{
+  return (node->ri ||
+          (node->rs && node->md));
+}
+
+/** Return the router_purpose of <b>node</b>. */
+int
+node_get_purpose(const node_t *node)
+{
+  if (node->ri)
+    return node->ri->purpose;
+  else
+    return ROUTER_PURPOSE_GENERAL;
+}
+
+/** Compute the verbose ("extended") nickname of <b>node</b> and store it
+ * into the MAX_VERBOSE_NICKNAME_LEN+1 character buffer at
+ * <b>verbose_nickname_out</b> */
+void
+node_get_verbose_nickname(const node_t *node,
+                          char *verbose_name_out)
+{
+  const char *nickname = node_get_nickname(node);
+  int is_named = node_is_named(node);
+  verbose_name_out[0] = '$';
+  base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, node->identity,
+                DIGEST_LEN);
+  if (!nickname)
+    return;
+  verbose_name_out[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
+  strlcpy(verbose_name_out+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1);
+}
+
+/** Return true iff it seems that <b>node</b> allows circuits to exit
+ * through it directlry from the client. */
+int
+node_allows_single_hop_exits(const node_t *node)
+{
+  if (node && node->ri)
+    return node->ri->allow_single_hop_exits;
+  else
+    return 0;
+}
+
+/** Return true iff it seems that <b>node</b> has an exit policy that doesn't
+ * actually permit anything to exit, or we don't know its exit policy */
+int
+node_exit_policy_rejects_all(const node_t *node)
+{
+  if (node->ri)
+    return node->ri->policy_is_reject_star;
+  else if (node->md)
+    return node->md->exit_policy == NULL ||
+      short_policy_is_reject_star(node->md->exit_policy);
+  else
+    return 1;
+}
+
+/** Copy the address for <b>node</b> into *<b>addr_out</b>. */
+int
+node_get_addr(const node_t *node, tor_addr_t *addr_out)
+{
+  if (node->ri) {
+    tor_addr_from_ipv4h(addr_out, node->ri->addr);
+    return 0;
+  } else if (node->rs) {
+    tor_addr_from_ipv4h(addr_out, node->rs->addr);
+    return 0;
+  }
+  return -1;
+}
+
+/** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't
+ * seem to have one.  */
+uint32_t
+node_get_addr_ipv4h(const node_t *node)
+{
+  if (node->ri) {
+    return node->ri->addr;
+  } else if (node->rs) {
+    return node->rs->addr;
+  }
+  return 0;
+}
+
+/** Copy a string representation of the IP address for <b>node</b> into the
+ * <b>len</b>-byte buffer at <b>buf</b>.
+ */
+void
+node_get_address_string(const node_t *node, char *buf, size_t len)
+{
+  if (node->ri) {
+    strlcpy(buf, node->ri->address, len);
+  } else if (node->rs) {
+    tor_addr_t addr;
+    tor_addr_from_ipv4h(&addr, node->rs->addr);
+    tor_addr_to_str(buf, &addr, len, 0);
+  } else {
+    buf[0] = '\0';
+  }
+}
+
+/** Return <b>node</b>'s declared uptime, or -1 if it doesn't seem to have
+ * one. */
+long
+node_get_declared_uptime(const node_t *node)
+{
+  if (node->ri)
+    return node->ri->uptime;
+  else
+    return -1;
+}
+
+/** Return <b>node</b>'s declared or_port */
+uint16_t
+node_get_orport(const node_t *node)
+{
+  if (node->ri)
+    return node->ri->or_port;
+  else if (node->rs)
+    return node->rs->or_port;
+  else
+    return 0;
+}
+
+/** Return <b>node</b>'s platform string, or NULL if we don't know it. */
+const char *
+node_get_platform(const node_t *node)
+{
+  /* If we wanted, we could record the version in the routerstatus_t, since
+   * the consensus lists it.  We don't, though, so this function just won't
+   * work with microdescriptors. */
+  if (node->ri)
+    return node->ri->platform;
+  else
+    return NULL;
+}
+
+/** Return <b>node</b>'s time of publication, or 0 if we don't have one. */
+time_t
+node_get_published_on(const node_t *node)
+{
+  if (node->ri)
+    return node->ri->cache_info.published_on;
+  else
+    return 0;
+}
+
+/** Return true iff <b>node</b> is one representing this router. */
+int
+node_is_me(const node_t *node)
+{
+  return router_digest_is_me(node->identity);
+}
+
+/** Return <b>node</b> declared family (as a list of names), or NULL if
+ * the node didn't declare a family. */
+const smartlist_t *
+node_get_declared_family(const node_t *node)
+{
+  if (node->ri && node->ri->declared_family)
+    return node->ri->declared_family;
+  else if (node->md && node->md->family)
+    return node->md->family;
+  else
+    return NULL;
+}
+
+/* KILLTHIS XXXX NM -- it's a dummy to keep UNIMPLEMENTED_NODELIST()
+ * working */
+int unimplemented_nodelist_truth = 1;
+

+ 66 - 0
src/or/nodelist.h

@@ -0,0 +1,66 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2010, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file microdesc.h
+ * \brief Header file for microdesc.c.
+ **/
+
+#ifndef _TOR_NODELIST_H
+#define _TOR_NODELIST_H
+
+node_t *node_get_mutable_by_id(const char *identity_digest);
+const node_t *node_get_by_id(const char *identity_digest);
+node_t *nodelist_add_routerinfo(routerinfo_t *ri);
+node_t *nodelist_add_microdesc(microdesc_t *md);
+void nodelist_set_consensus(networkstatus_t *ns);
+
+void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md);
+void nodelist_remove_routerinfo(routerinfo_t *ri);
+void nodelist_purge(void);
+
+void nodelist_free_all(void);
+void nodelist_assert_ok(void);
+
+const node_t *node_get_by_nickname(const char *nickname, int warn_if_unnamed);
+void node_get_verbose_nickname(const node_t *node,
+                               char *verbose_name_out);
+int node_is_named(const node_t *node);
+int node_is_dir(const node_t *node);
+int node_has_descriptor(const node_t *node);
+int node_get_purpose(const node_t *node);
+#define node_is_bridge(node) \
+  (node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE)
+int node_is_me(const node_t *node);
+int node_exit_policy_rejects_all(const node_t *node);
+int node_get_addr(const node_t *node, tor_addr_t *addr_out);
+uint32_t node_get_addr_ipv4h(const node_t *node);
+int node_allows_single_hop_exits(const node_t *node);
+uint16_t node_get_orport(const node_t *node);
+const char *node_get_nickname(const node_t *node);
+const char *node_get_platform(const node_t *node);
+void node_get_address_string(const node_t *node, char *cp, size_t len);
+long node_get_declared_uptime(const node_t *node);
+time_t node_get_published_on(const node_t *node);
+const smartlist_t *node_get_declared_family(const node_t *node);
+
+smartlist_t *nodelist_get_list(void);
+
+/* XXXX These need to move out of routerlist.c */
+void nodelist_refresh_countries(void);
+void node_set_country(node_t *node);
+void nodelist_add_node_family(smartlist_t *nodes, const node_t *node);
+int nodes_in_same_family(const node_t *node1, const node_t *node2);
+
+/* This means: implement this code or function or thing, nick! */
+#define UNIMPLEMENTED_NODELIST()                        \
+  while (unimplemented_nodelist_truth) {                \
+    tor_assert(0);                                      \
+  }
+extern int unimplemented_nodelist_truth;
+
+#endif
+

+ 112 - 34
src/or/or.h

@@ -809,6 +809,9 @@ typedef enum {
  * Tor 0.1.2.x is obsolete, we can remove this. */
 #define DEFAULT_CLIENT_NICKNAME "client"
 
+/** Name chosen by routers that don't configure nicknames */
+#define UNNAMED_ROUTER_NICKNAME "Unnamed"
+
 /** Number of bytes in a SOCKS4 header. */
 #define SOCKS4_NETWORK_LEN 8
 
@@ -1510,52 +1513,44 @@ typedef struct {
   unsigned int allow_single_hop_exits:1;  /**< Whether the router says
                                            * it allows single hop exits. */
 
-  /* local info */
-  unsigned int is_running:1; /**< As far as we know, is this OR currently
-                              * running? */
-  unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
-                               *  (For Authdir: Have we validated this OR?)
-                               */
-  unsigned int is_named:1; /**< Do we believe the nickname that this OR gives
-                            * us? */
-  unsigned int is_fast:1; /** Do we think this is a fast OR? */
-  unsigned int is_stable:1; /** Do we think this is a stable OR? */
-  unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
-  unsigned int is_exit:1; /**< Do we think this is an OK exit? */
-  unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
-                               * or otherwise nasty? */
-  unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
-                                    * underpowered, or otherwise useless? */
   unsigned int wants_to_be_hs_dir:1; /**< True iff this router claims to be
                                       * a hidden service directory. */
-  unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
-                             * directory according to the authorities. */
   unsigned int policy_is_reject_star:1; /**< True iff the exit policy for this
                                          * router rejects everything. */
   /** True if, after we have added this router, we should re-launch
    * tests for it. */
   unsigned int needs_retest_if_added:1;
 
-/** Tor can use this router for general positions in circuits. */
+/** Tor can use this router for general positions in circuits; we got it
+ * from a directory server as usual, or we're an authority and a server
+ * uploaded it. */
 #define ROUTER_PURPOSE_GENERAL 0
-/** Tor should avoid using this router for circuit-building. */
+/** Tor should avoid using this router for circuit-building: we got it
+ * from a crontroller.  If the controller wants to use it, it'll have to
+ * ask for it by identity. */
 #define ROUTER_PURPOSE_CONTROLLER 1
-/** Tor should use this router only for bridge positions in circuits. */
+/** Tor should use this router only for bridge positions in circuits: we got
+ * it via a directory request from the bridge itself, or a bridge
+ * authority. x*/
 #define ROUTER_PURPOSE_BRIDGE 2
 /** Tor should not use this router; it was marked in cached-descriptors with
  * a purpose we didn't recognize. */
 #define ROUTER_PURPOSE_UNKNOWN 255
 
-  uint8_t purpose; /** What positions in a circuit is this router good for? */
+  /* In what way did we find out about this router?  One of ROUTER_PURPOSE_*.
+   * Routers of different purposes are kept segregated and used for different
+   * things; see notes on ROUTER_PURPOSE_* macros above.
+   */
+  uint8_t purpose;
 
   /* The below items are used only by authdirservers for
    * reachability testing. */
+
   /** When was the last time we could reach this OR? */
   time_t last_reachable;
   /** When did we start testing reachability for this OR? */
   time_t testing_since;
-  /** According to the geoip db what country is this router in? */
-  country_t country;
+
 } routerinfo_t;
 
 /** Information needed to keep and cache a signed extra-info document. */
@@ -1591,7 +1586,11 @@ typedef struct routerstatus_t {
   unsigned int is_exit:1; /**< True iff this router is a good exit. */
   unsigned int is_stable:1; /**< True iff this router stays up a long time. */
   unsigned int is_fast:1; /**< True iff this router has good bandwidth. */
-  unsigned int is_running:1; /**< True iff this router is up. */
+  /** True iff this router is called 'running' in the consensus. We give it
+   * this funny name so that we don't accidentally use this bit as a view of
+   * whether we think the router is *currently* running.  If that's what you
+   * want to know, look at is_running in node_t. */
+  unsigned int is_flagged_running:1;
   unsigned int is_named:1; /**< True iff "nickname" belongs to this router. */
   unsigned int is_unnamed:1; /**< True iff "nickname" belongs to another
                               * router. */
@@ -1643,15 +1642,31 @@ typedef struct routerstatus_t {
    * from this authority.)  Applies in v2 networkstatus document only.
    */
   unsigned int need_to_mirror:1;
-  unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
-                                      * to this (unnamed) router by nickname?
-                                      */
   time_t last_dir_503_at; /**< When did this router last tell us that it
                            * was too busy to serve directory info? */
   download_status_t dl_status;
 
 } routerstatus_t;
 
+/** A single entry in a parsed policy summary, describing a range of ports. */
+typedef struct short_policy_entry_t {
+  uint16_t min_port, max_port;
+} short_policy_entry_t;
+
+/** A short_poliy_t is the parsed version of a policy summary. */
+typedef struct short_policy_t {
+  /** True if the members of 'entries' are port ranges to accept; false if
+   * they are port ranges to reject */
+  unsigned int is_accept : 1;
+  /** The actual number of values in 'entries'. */
+  unsigned int n_entries : 31;
+  /** An array of (probably more than 1!) short_policy_entry_t values,
+   * each descriping a range of ports that this policy accepts or rejects
+   * (depending on the value of is_accept).
+   */
+  short_policy_entry_t entries[1];
+} short_policy_t;
+
 /** A microdescriptor is the smallest amount of information needed to build a
  * circuit through a router.  They are generated by the directory authorities,
  * using information from the uploaded routerinfo documents.  They are not
@@ -1693,11 +1708,72 @@ typedef struct microdesc_t {
   crypto_pk_env_t *onion_pkey;
   /** As routerinfo_t.family */
   smartlist_t *family;
-  /** Encoded exit policy summary */
-  char *exitsummary; /**< exit policy summary -
-                      * XXX this probably should not stay a string. */
+  /** Exit policy summary */
+  short_policy_t *exit_policy;
 } microdesc_t;
 
+/** A node_t represents a Tor router.
+ *
+ * Specifically, a node_t is a Tor router as we are using it: a router that
+ * we are considering for circuits, connections, and so on.  A node_t is a
+ * thin wrapper around the routerstatus, routerinfo, and microdesc for a
+ * single wrapper, and provides a consistent interface for all of them.
+ *
+ * Also, a node_t has mutable state.  While a routerinfo, a routerstatus,
+ * and a microdesc have[*] only the information read from a router
+ * descriptor, a consensus entry, and a microdescriptor (respectively)...
+ * a node_t has flags based on *our own current opinion* of the node.
+ *
+ * [*] Actually, there is some leftover information in each that is mutable.
+ *  We should try to excise that.
+ */
+typedef struct node_t {
+  /* Indexing information */
+
+  /** Used to look up the node_t by its identity digest. */
+  HT_ENTRY(node_t) ht_ent;
+  /** Position of the node within the list of nodes */
+  int nodelist_idx;
+
+  /** The identity digest of this node_t.  No more than one node_t per
+   * identity may exist at a time. */
+  char identity[DIGEST_LEN];
+
+  microdesc_t *md;
+  routerinfo_t *ri;
+  routerstatus_t *rs;
+
+  /* local info: copied from routerstatus, then possibly frobbed based
+   * on experience.  Authorities set this stuff directly. */
+
+  unsigned int is_running:1; /**< As far as we know, is this OR currently
+                              * running? */
+  unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
+                               *  (For Authdir: Have we validated this OR?)
+                               */
+  unsigned int is_fast:1; /** Do we think this is a fast OR? */
+  unsigned int is_stable:1; /** Do we think this is a stable OR? */
+  unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
+  unsigned int is_exit:1; /**< Do we think this is an OK exit? */
+  unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
+                               * or otherwise nasty? */
+  unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
+                                    * underpowered, or otherwise useless? */
+  unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
+                             * directory according to the authorities. */
+
+  /* Local info: warning state. */
+
+  unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
+                                      * to this (unnamed) router by nickname?
+                                      */
+
+  /* Local info: derived. */
+
+  /** According to the geoip db what country is this router in? */
+  country_t country;
+} node_t;
+
 /** How many times will we try to download a router's descriptor before giving
  * up? */
 #define MAX_ROUTERDESC_DOWNLOAD_FAILURES 8
@@ -2667,7 +2743,8 @@ typedef struct {
 
   char *MyFamily; /**< Declared family for this OR. */
   config_line_t *NodeFamilies; /**< List of config lines for
-                                       * node families */
+                                * node families */
+  smartlist_t *NodeFamilySets; /**< List of parsed NodeFamilies values. */
   config_line_t *AuthDirBadDir; /**< Address policy for descriptors to
                                  * mark as bad dir mirrors. */
   config_line_t *AuthDirBadExit; /**< Address policy for descriptors to
@@ -3385,7 +3462,7 @@ typedef enum {
   ADDR_POLICY_PROBABLY_ACCEPTED=1,
   /** Part of the address was unknown, but as far as we can tell, it was
    * rejected. */
-  ADDR_POLICY_PROBABLY_REJECTED=2
+  ADDR_POLICY_PROBABLY_REJECTED=2,
 } addr_policy_result_t;
 
 /********************************* rephist.c ***************************/
@@ -3535,7 +3612,8 @@ typedef enum {
   CRN_NEED_GUARD = 1<<2,
   CRN_ALLOW_INVALID = 1<<3,
   /* XXXX not used, apparently. */
-  CRN_WEIGHT_AS_EXIT = 1<<5
+  CRN_WEIGHT_AS_EXIT = 1<<5,
+  CRN_NEED_DESC = 1<<6
 } router_crn_flags_t;
 
 /** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */

+ 213 - 1
src/or/policies.c

@@ -11,6 +11,7 @@
 #include "or.h"
 #include "config.h"
 #include "dirserv.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "routerparse.h"
 #include "ht.h"
@@ -261,7 +262,7 @@ fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port)
 /** Return true iff we think our firewall will let us make an OR connection to
  * <b>ri</b>. */
 int
-fascist_firewall_allows_or(routerinfo_t *ri)
+fascist_firewall_allows_or(const routerinfo_t *ri)
 {
   /* XXXX proposal 118 */
   tor_addr_t addr;
@@ -269,6 +270,22 @@ fascist_firewall_allows_or(routerinfo_t *ri)
   return fascist_firewall_allows_address_or(&addr, ri->or_port);
 }
 
+/** Return true iff we think our firewall will let us make an OR connection to
+ * <b>node</b>. */
+int
+fascist_firewall_allows_node(const node_t *node)
+{
+  if (node->ri) {
+    return fascist_firewall_allows_or(node->ri);
+  } else if (node->rs) {
+    tor_addr_t addr;
+    tor_addr_from_ipv4h(&addr, node->rs->addr);
+    return fascist_firewall_allows_address_or(&addr, node->rs->or_port);
+  } else {
+    return 1;
+  }
+}
+
 /** Return true iff we think our firewall will let us make a directory
  * connection to addr:port. */
 int
@@ -858,6 +875,7 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
   return 0;
 }
 
+#if 0
 /** Replace the exit policy of <b>r</b> with reject *:*. */
 void
 policies_set_router_exitpolicy_to_reject_all(routerinfo_t *r)
@@ -868,6 +886,15 @@ policies_set_router_exitpolicy_to_reject_all(routerinfo_t *r)
   item = router_parse_addr_policy_item_from_string("reject *:*", -1);
   smartlist_add(r->exit_policy, item);
 }
+#endif
+
+/** Replace the exit policy of <b>node</b> with reject *:* */
+void
+policies_set_node_exitpolicy_to_reject_all(node_t *node)
+{
+  (void)node;
+  UNIMPLEMENTED_NODELIST();
+}
 
 /** Return 1 if there is at least one /8 subnet in <b>policy</b> that
  * allows exiting to <b>port</b>.  Otherwise, return 0. */
@@ -1288,6 +1315,191 @@ policy_summarize(smartlist_t *policy)
   return result;
 }
 
+/** Convert a summarized policy string into a short_policy_t.  Return NULL
+ * if the string is not well-formed. */
+short_policy_t *
+parse_short_policy(const char *summary)
+{
+  const char *orig_summary = summary;
+  short_policy_t *result;
+  int is_accept;
+  int n_entries;
+  short_policy_entry_t entries[MAX_EXITPOLICY_SUMMARY_LEN]; /* overkill */
+  const char *next;
+
+  if (!strcmpstart(summary, "accept ")) {
+    is_accept = 1;
+    summary += strlen("accept ");
+  } else if (!strcmpstart(summary, "reject ")) {
+    is_accept = 0;
+    summary += strlen("reject ");
+  } else {
+    log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Unrecognized policy summary keyword");
+    return NULL;
+  }
+
+  n_entries = 0;
+  for ( ; *summary; summary = next) {
+    const char *comma = strchr(summary, ',');
+    unsigned low, high;
+    char dummy;
+    char ent_buf[32];
+
+    next = comma ? comma+1 : strchr(summary, '\0');
+
+    if (n_entries == MAX_EXITPOLICY_SUMMARY_LEN) {
+      log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Impossibly long policy summary %s",
+             escaped(orig_summary));
+      return NULL;
+    }
+
+    if (! TOR_ISDIGIT(*summary) || next-summary > (int)(sizeof(ent_buf)-1)) {
+      /* unrecognized entry format. skip it. */
+      continue;
+    }
+    if (next-summary < 2) {
+      /* empty; skip it. */
+      continue;
+    }
+
+    memcpy(ent_buf, summary, next-summary-1);
+    ent_buf[next-summary-1] = '\0';
+
+    if (tor_sscanf(ent_buf, "%u-%u%c", &low, &high, &dummy) == 2) {
+      if (low<1 || low>65535 || high<1 || high>65535) {
+        log_fn(LOG_PROTOCOL_WARN, LD_DIR,"Found bad entry in policy summary %s",
+              escaped(orig_summary));
+        return NULL;
+      }
+    } else if (tor_sscanf(ent_buf, "%u%c", &low, &dummy) == 1) {
+      if (low<1 || low>65535) {
+        log_fn(LOG_PROTOCOL_WARN, LD_DIR,"Found bad entry in policy summary %s",
+               escaped(orig_summary));
+        return NULL;
+      }
+      high = low;
+    } else {
+      log_fn(LOG_PROTOCOL_WARN, LD_DIR,"Found bad entry in policy summary %s",
+             escaped(orig_summary));
+      return NULL;
+    }
+
+    entries[n_entries].min_port = low;
+    entries[n_entries].max_port = high;
+    n_entries++;
+  }
+
+  if (n_entries == 0) {
+    log_fn(LOG_PROTOCOL_WARN, LD_DIR,
+           "Found no port-range entries in summary %s", escaped(orig_summary));
+    return NULL;
+  }
+
+  {
+    size_t size = sizeof(short_policy_t) +
+      sizeof(short_policy_entry_t)*(n_entries-1);
+    result = tor_malloc_zero(size);
+
+    tor_assert( (char*)&result->entries[n_entries-1] < ((char*)result)+size);
+  }
+
+  result->is_accept = is_accept;
+  result->n_entries = n_entries;
+  memcpy(result->entries, entries, sizeof(short_policy_entry_t)*n_entries);
+  return result;
+}
+
+/** Release all storage held in <b>policy</b>. */
+void
+short_policy_free(short_policy_t *policy)
+{
+  tor_free(policy);
+}
+
+/** See whether the <b>addr</b>:<b>port</b> address is likely to be accepted
+ * or rejected by the summarized policy <b>policy</b>.  Return values are as
+ * for compare_tor_addr_to_addr_policy.  Unlike the regular addr_policy
+ * functions, requires the <b>port</b> be specified. */
+addr_policy_result_t
+compare_tor_addr_to_short_policy(const tor_addr_t *addr, uint16_t port,
+                                 const short_policy_t *policy)
+{
+  int i;
+  int found_match = 0;
+  int accept;
+  (void)addr;
+
+  tor_assert(port != 0);
+
+  if (addr && (tor_addr_is_internal(addr, 0) ||
+               tor_addr_is_null(addr) ||
+               tor_addr_is_loopback(addr)))
+    return ADDR_POLICY_REJECTED;
+
+  for (i=0; i < policy->n_entries; ++i) {
+    const short_policy_entry_t *e = &policy->entries[i];
+    if (e->min_port <= port && port <= e->max_port) {
+      found_match = 1;
+      break;
+    }
+  }
+
+  if (found_match)
+    accept = policy->is_accept;
+  else
+    accept = ! policy->is_accept;
+
+  /* ???? are these right? */
+  if (accept)
+    return ADDR_POLICY_PROBABLY_ACCEPTED;
+  else
+    return ADDR_POLICY_REJECTED;
+}
+
+/** Return true iff <b>policy</b> seems reject all ports */
+int
+short_policy_is_reject_star(const short_policy_t *policy)
+{
+  /* This doesn't need to be as much on the lookout as policy_is_reject_star,
+   * since policy summaries are from the consensus or from consensus microdescs.
+   */
+  tor_assert(policy);
+  /* Check for an exact match of "reject 1-65535". */
+  return (policy->is_accept == 0 && policy->n_entries == 1 &&
+          policy->entries[0].min_port == 1 &&
+          policy->entries[0].max_port == 65535);
+}
+
+/** Decides whether addr:port is probably or definitely accepted or rejcted by
+ * <b>node</b>.  See compare_tor_addr_to_addr_policy for details on addr/port
+ * interpretation. */
+addr_policy_result_t
+compare_addr_to_node_policy(uint32_t addr, uint16_t port, const node_t *node)
+{
+  tor_addr_t a;
+  tor_addr_from_ipv4h(&a, addr);
+  return compare_tor_addr_to_node_policy(&a, port, node);
+}
+
+/** Decides whether addr:port is probably or definitely accepted or rejcted by
+ * <b>node</b>.  See compare_tor_addr_to_addr_policy for details on addr/port
+ * interpretation. */
+addr_policy_result_t
+compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port,
+                                const node_t *node)
+{
+  if (node->ri)
+    return compare_tor_addr_to_addr_policy(addr, port, node->ri->exit_policy);
+  else if (node->md && node->md) {
+    if (node->md->exit_policy == NULL)
+      return ADDR_POLICY_REJECTED;
+    else
+      return compare_tor_addr_to_short_policy(addr, port,
+                                              node->md->exit_policy);
+  } else
+    return ADDR_POLICY_PROBABLY_REJECTED;
+}
+
 /** Implementation for GETINFO control command: knows the answer for questions
  * about "exit-policy/..." */
 int

+ 16 - 2
src/or/policies.h

@@ -19,7 +19,8 @@
 
 int firewall_is_fascist_or(void);
 int fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port);
-int fascist_firewall_allows_or(routerinfo_t *ri);
+int fascist_firewall_allows_or(const routerinfo_t *ri);
+int fascist_firewall_allows_node(const node_t *node);
 int fascist_firewall_allows_address_dir(const tor_addr_t *addr, uint16_t port);
 int dir_policy_permits_address(const tor_addr_t *addr);
 int socks_policy_permits_address(const tor_addr_t *addr);
@@ -38,10 +39,16 @@ addr_policy_result_t compare_tor_addr_to_addr_policy(const tor_addr_t *addr,
                               uint16_t port, const smartlist_t *policy);
 addr_policy_result_t compare_addr_to_addr_policy(uint32_t addr,
                               uint16_t port, const smartlist_t *policy);
+
+addr_policy_result_t compare_addr_to_node_policy(uint32_t addr,
+                              uint16_t port, const node_t *node);
+addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
+                              uint16_t port, const node_t *node);
+
 int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
                                int rejectprivate, const char *local_address,
                                int add_default_policy);
-void policies_set_router_exitpolicy_to_reject_all(routerinfo_t *exitrouter);
+void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter);
 int exit_policy_is_general_exit(smartlist_t *policy);
 int policy_is_reject_star(const smartlist_t *policy);
 int getinfo_helper_policies(control_connection_t *conn,
@@ -56,5 +63,12 @@ void policies_free_all(void);
 
 char *policy_summarize(smartlist_t *policy);
 
+short_policy_t *parse_short_policy(const char *summary);
+void short_policy_free(short_policy_t *policy);
+int short_policy_is_reject_star(const short_policy_t *policy);
+addr_policy_result_t compare_tor_addr_to_short_policy(
+                          const tor_addr_t *addr, uint16_t port,
+                          const short_policy_t *policy);
+
 #endif
 

+ 8 - 6
src/or/relay.c

@@ -24,6 +24,7 @@
 #include "main.h"
 #include "mempool.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "reasons.h"
 #include "relay.h"
@@ -712,7 +713,7 @@ connection_ap_process_end_not_open(
     edge_connection_t *conn, crypt_path_t *layer_hint)
 {
   struct in_addr in;
-  routerinfo_t *exitrouter;
+  node_t *exitrouter;
   int reason = *(cell->payload+RELAY_HEADER_SIZE);
   int control_reason = reason | END_STREAM_REASON_FLAG_REMOTE;
   (void) layer_hint; /* unused */
@@ -720,11 +721,12 @@ connection_ap_process_end_not_open(
   if (rh->length > 0 && edge_reason_is_retriable(reason) &&
       !connection_edge_is_rendezvous_stream(conn)  /* avoid retry if rend */
       ) {
+    const char *chosen_exit_digest =
+      circ->build_state->chosen_exit->identity_digest;
     log_info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
              safe_str(conn->socks_request->address),
              stream_end_reason_to_string(reason));
-    exitrouter =
-      router_get_by_digest(circ->build_state->chosen_exit->identity_digest);
+    exitrouter = node_get_mutable_by_id(chosen_exit_digest);
     switch (reason) {
       case END_STREAM_REASON_EXITPOLICY:
         if (rh->length >= 5) {
@@ -759,8 +761,8 @@ connection_ap_process_end_not_open(
           log_info(LD_APP,
                  "Exitrouter '%s' seems to be more restrictive than its exit "
                  "policy. Not using this router as exit for now.",
-                 exitrouter->nickname);
-          policies_set_router_exitpolicy_to_reject_all(exitrouter);
+                 node_get_nickname(exitrouter));
+          policies_set_node_exitpolicy_to_reject_all(exitrouter);
         }
         /* rewrite it to an IP if we learned one. */
         if (addressmap_rewrite(conn->socks_request->address,
@@ -823,7 +825,7 @@ connection_ap_process_end_not_open(
       case END_STREAM_REASON_HIBERNATING:
       case END_STREAM_REASON_RESOURCELIMIT:
         if (exitrouter) {
-          policies_set_router_exitpolicy_to_reject_all(exitrouter);
+          policies_set_node_exitpolicy_to_reject_all(exitrouter);
         }
         if (conn->chosen_exit_optional) {
           /* stop wanting a specific exit */

+ 4 - 4
src/or/rendclient.c

@@ -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"
@@ -738,7 +739,6 @@ rend_client_get_random_intro(const rend_data_t *rend_query)
   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,
@@ -755,8 +755,8 @@ rend_client_get_random_intro(const rend_data_t *rend_query)
   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) {
-    router = router_get_by_nickname(intro->extend_info->nickname, 0);
-    if (!router) {
+    const node_t *node = node_get_by_nickname(intro->extend_info->nickname, 0);
+    if (!node) {
       log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
                intro->extend_info->nickname);
       rend_intro_point_free(intro);
@@ -764,7 +764,7 @@ rend_client_get_random_intro(const rend_data_t *rend_query)
       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);
 }

+ 21 - 20
src/or/rendservice.c

@@ -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"
@@ -1001,7 +1002,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
   } 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;
@@ -1028,8 +1029,8 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
     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? */
@@ -1037,7 +1038,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
       goto err;
     }
 
-    extend_info = extend_info_from_router(router);
+    extend_info = extend_info_from_node(node);
   }
 
   if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
@@ -1754,19 +1755,19 @@ void
 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);
@@ -1786,8 +1787,8 @@ rend_services_introduce(void)
        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) {
@@ -1806,8 +1807,8 @@ rend_services_introduce(void)
         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
@@ -1836,26 +1837,26 @@ rend_services_introduce(void)
 #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. */
@@ -1872,7 +1873,7 @@ rend_services_introduce(void)
       }
     }
   }
-  smartlist_free(intro_routers);
+  smartlist_free(intro_nodes);
 }
 
 /** Regenerate and upload rendezvous service descriptors for all

+ 21 - 16
src/or/rephist.c

@@ -14,6 +14,7 @@
 #include "circuitlist.h"
 #include "circuituse.h"
 #include "config.h"
+#include "nodelist.h"
 #include "rephist.h"
 #include "router.h"
 #include "routerlist.h"
@@ -579,7 +580,7 @@ rep_hist_dump_stats(time_t now, int severity)
   size_t len;
   int ret;
   unsigned long upt, downt;
-  routerinfo_t *r;
+  const node_t *node;
 
   rep_history_clean(now - get_options()->RephistTrackTime);
 
@@ -593,8 +594,8 @@ rep_hist_dump_stats(time_t now, int severity)
     digestmap_iter_get(orhist_it, &digest1, &or_history_p);
     or_history = (or_history_t*) or_history_p;
 
-    if ((r = router_get_by_digest(digest1)))
-      name1 = r->nickname;
+    if ((node = node_get_by_id(digest1)) && node_get_nickname(node))
+      name1 = node_get_nickname(node);
     else
       name1 = "(unknown)";
     base16_encode(hexdigest1, sizeof(hexdigest1), digest1, DIGEST_LEN);
@@ -624,8 +625,8 @@ rep_hist_dump_stats(time_t now, int severity)
            lhist_it = digestmap_iter_next(or_history->link_history_map,
                                           lhist_it)) {
         digestmap_iter_get(lhist_it, &digest2, &link_history_p);
-        if ((r = router_get_by_digest(digest2)))
-          name2 = r->nickname;
+        if ((node = node_get_by_id(digest2)) && node_get_nickname(node))
+          name2 = node_get_nickname(node);
         else
           name2 = "(unknown)";
 
@@ -871,28 +872,32 @@ rep_hist_get_router_stability_doc(time_t now)
   }
 
   DIGESTMAP_FOREACH(history_map, id, or_history_t *, hist) {
-    routerinfo_t *ri;
+    const node_t *node;
     char dbuf[BASE64_DIGEST_LEN+1];
     char header_buf[512];
     char *info;
     digest_to_base64(dbuf, id);
-    ri = router_get_by_digest(id);
-    if (ri) {
-      char *ip = tor_dup_ip(ri->addr);
+    node = node_get_by_id(id);
+    if (node) {
+      char ip[INET_NTOA_BUF_LEN+1];
       char tbuf[ISO_TIME_LEN+1];
-      format_iso_time(tbuf, ri->cache_info.published_on);
+      time_t published = node_get_published_on(node);
+      node_get_address_string(node,ip,sizeof(ip));
+      if (published > 0)
+        format_iso_time(tbuf, published);
+      else
+        strlcpy(tbuf, "???", sizeof(tbuf));
       tor_snprintf(header_buf, sizeof(header_buf),
                    "router %s %s %s\n"
                    "published %s\n"
                    "relevant-flags %s%s%s\n"
                    "declared-uptime %ld\n",
-                   dbuf, ri->nickname, ip,
+                   dbuf, node_get_nickname(node), ip,
                    tbuf,
-                   ri->is_running ? "Running " : "",
-                   ri->is_valid ? "Valid " : "",
-                   ri->is_hibernating ? "Hibernating " : "",
-                   ri->uptime);
-      tor_free(ip);
+                   node->is_running ? "Running " : "",
+                   node->is_valid ? "Valid " : "",
+                   node->ri && node->ri->is_hibernating ? "Hibernating " : "",
+                   node_get_declared_uptime(node));
     } else {
       tor_snprintf(header_buf, sizeof(header_buf),
                    "router %s {no descriptor}\n", dbuf);

+ 32 - 24
src/or/router.c

@@ -7,6 +7,7 @@
 #define ROUTER_PRIVATE
 
 #include "or.h"
+#include "circuitbuild.h"
 #include "circuitlist.h"
 #include "circuituse.h"
 #include "config.h"
@@ -19,6 +20,7 @@
 #include "hibernate.h"
 #include "main.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "relay.h"
 #include "rephist.h"
@@ -773,18 +775,21 @@ decide_to_advertise_dirport(or_options_t *options, uint16_t dir_port)
 void
 consider_testing_reachability(int test_or, int test_dir)
 {
-  routerinfo_t *me = router_get_my_routerinfo();
+  const routerinfo_t *me = router_get_my_routerinfo();
   int orport_reachable = check_whether_orport_reachable();
   tor_addr_t addr;
   if (!me)
     return;
 
   if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) {
+    extend_info_t *ei;
     log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
              !orport_reachable ? "reachability" : "bandwidth",
              me->address, me->or_port);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_TESTING, me,
-                             CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
+    ei = extend_info_from_router(me);
+    circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
+                            CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
+    extend_info_free(ei);
   }
 
   tor_addr_from_ipv4h(&addr, me->addr);
@@ -808,7 +813,7 @@ void
 router_orport_found_reachable(void)
 {
   if (!can_reach_or_port) {
-    routerinfo_t *me = router_get_my_routerinfo();
+    const routerinfo_t *me = router_get_my_routerinfo();
     log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from "
                "the outside. Excellent.%s",
                get_options()->_PublishServerDescriptor != NO_AUTHORITY ?
@@ -831,7 +836,7 @@ void
 router_dirport_found_reachable(void)
 {
   if (!can_reach_dir_port) {
-    routerinfo_t *me = router_get_my_routerinfo();
+    const routerinfo_t *me = router_get_my_routerinfo();
     log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable "
                "from the outside. Excellent.");
     can_reach_dir_port = 1;
@@ -1093,7 +1098,7 @@ static int desc_needs_upload = 0;
 void
 router_upload_dir_desc_to_dirservers(int force)
 {
-  routerinfo_t *ri;
+  const routerinfo_t *ri;
   extrainfo_t *ei;
   char *msg;
   size_t desc_len, extra_len = 0, total_len;
@@ -1186,7 +1191,7 @@ router_extrainfo_digest_is_me(const char *digest)
 
 /** A wrapper around router_digest_is_me(). */
 int
-router_is_me(routerinfo_t *router)
+router_is_me(const routerinfo_t *router)
 {
   return router_digest_is_me(router->cache_info.identity_digest);
 }
@@ -1205,7 +1210,7 @@ router_fingerprint_is_me(const char *fp)
 
 /** Return a routerinfo for this OR, rebuilding a fresh one if
  * necessary.  Return NULL on error, or if called on an OP. */
-routerinfo_t *
+const routerinfo_t *
 router_get_my_routerinfo(void)
 {
   if (!server_mode(get_options()))
@@ -1333,13 +1338,12 @@ router_rebuild_descriptor(int force)
   ri->policy_is_reject_star =
     policy_is_reject_star(ri->exit_policy);
 
-  if (desc_routerinfo) { /* inherit values */
-    ri->is_valid = desc_routerinfo->is_valid;
-    ri->is_running = desc_routerinfo->is_running;
-    ri->is_named = desc_routerinfo->is_named;
-  }
+#if 0
+  /* XXXX NM NM I belive this is safe to remove */
   if (authdir_mode(options))
     ri->is_valid = ri->is_named = 1; /* believe in yourself */
+#endif
+
   if (options->MyFamily) {
     smartlist_t *family;
     if (!warned_nonexistent_family)
@@ -1348,13 +1352,12 @@ router_rebuild_descriptor(int force)
     ri->declared_family = smartlist_create();
     smartlist_split_string(family, options->MyFamily, ",",
       SPLIT_SKIP_SPACE|SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
-    SMARTLIST_FOREACH(family, char *, name,
-     {
-       routerinfo_t *member;
+    SMARTLIST_FOREACH_BEGIN(family, char *, name) {
+       const node_t *member;
        if (!strcasecmp(name, options->Nickname))
-         member = ri;
+         goto skip; /* Don't list ourself, that's redundant */
        else
-         member = router_get_by_nickname(name, 1);
+         member = node_get_by_nickname(name, 1);
        if (!member) {
          int is_legal = is_legal_nickname_or_hexdigest(name);
          if (!smartlist_string_isin(warned_nonexistent_family, name) &&
@@ -1374,19 +1377,21 @@ router_rebuild_descriptor(int force)
            smartlist_add(ri->declared_family, name);
            name = NULL;
          }
-       } else if (router_is_me(member)) {
+       } else if (router_digest_is_me(member->identity)) {
          /* Don't list ourself in our own family; that's redundant */
+         /* XXX shouldn't be possible */
        } else {
          char *fp = tor_malloc(HEX_DIGEST_LEN+2);
          fp[0] = '$';
          base16_encode(fp+1,HEX_DIGEST_LEN+1,
-                       member->cache_info.identity_digest, DIGEST_LEN);
+                       member->identity, DIGEST_LEN);
          smartlist_add(ri->declared_family, fp);
          if (smartlist_string_isin(warned_nonexistent_family, name))
            smartlist_string_remove(warned_nonexistent_family, name);
        }
+    skip:
        tor_free(name);
-     });
+    } SMARTLIST_FOREACH_END(name);
 
     /* remove duplicates from the list */
     smartlist_sort_strings(ri->declared_family);
@@ -1444,8 +1449,6 @@ router_rebuild_descriptor(int force)
                          strlen(ri->cache_info.signed_descriptor_body),
                          ri->cache_info.signed_descriptor_digest);
 
-  routerinfo_set_country(ri);
-
   tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL));
 
   routerinfo_free(desc_routerinfo);
@@ -2099,10 +2102,15 @@ is_legal_hexdigest(const char *s)
 void
 router_get_verbose_nickname(char *buf, const routerinfo_t *router)
 {
+  const char *good_digest = networkstatus_get_router_digest_by_nickname(
+                                                         router->nickname);
+  int is_named = good_digest && !memcmp(good_digest,
+                                        router->cache_info.identity_digest,
+                                        DIGEST_LEN);
   buf[0] = '$';
   base16_encode(buf+1, HEX_DIGEST_LEN+1, router->cache_info.identity_digest,
                 DIGEST_LEN);
-  buf[1+HEX_DIGEST_LEN] = router->is_named ? '=' : '~';
+  buf[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
   strlcpy(buf+1+HEX_DIGEST_LEN+1, router->nickname, MAX_NICKNAME_LEN+1);
 }
 

+ 2 - 2
src/or/router.h

@@ -62,12 +62,12 @@ void router_new_address_suggestion(const char *suggestion,
                                    const dir_connection_t *d_conn);
 int router_compare_to_my_exit_policy(edge_connection_t *conn);
 int router_my_exit_policy_is_reject_star(void);
-routerinfo_t *router_get_my_routerinfo(void);
+const routerinfo_t *router_get_my_routerinfo(void);
 extrainfo_t *router_get_my_extrainfo(void);
 const char *router_get_my_descriptor(void);
 int router_digest_is_me(const char *digest);
 int router_extrainfo_digest_is_me(const char *digest);
-int router_is_me(routerinfo_t *router);
+int router_is_me(const routerinfo_t *router);
 int router_fingerprint_is_me(const char *fp);
 int router_pick_published_address(or_options_t *options, uint32_t *addr);
 int router_rebuild_descriptor(int force);

文件差異過大導致無法顯示
+ 351 - 287
src/or/routerlist.c


+ 48 - 40
src/or/routerlist.h

@@ -27,37 +27,34 @@ int router_reload_router_list(void);
 int authority_cert_dl_looks_uncertain(const char *id_digest);
 smartlist_t *router_get_trusted_dir_servers(void);
 
-routerstatus_t *router_pick_directory_server(authority_type_t type, int flags);
+const routerstatus_t *router_pick_directory_server(authority_type_t type,
+                                                   int flags);
 trusted_dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
 trusted_dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
-routerstatus_t *router_pick_trusteddirserver(authority_type_t type, int flags);
+const routerstatus_t *router_pick_trusteddirserver(authority_type_t type,
+                                                   int flags);
 int router_get_my_share_of_directory_requests(double *v2_share_out,
                                               double *v3_share_out);
 void router_reset_status_download_failures(void);
-void routerlist_add_family(smartlist_t *sl, routerinfo_t *router);
-int routers_in_same_family(routerinfo_t *r1, routerinfo_t *r2);
+void routerlist_add_family(smartlist_t *sl, const routerinfo_t *router);
 int routers_have_same_or_addr(const routerinfo_t *r1, const routerinfo_t *r2);
-void add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
-                                    int must_be_running);
-int router_nickname_is_in_list(routerinfo_t *router, const char *list);
-routerinfo_t *routerlist_find_my_routerinfo(void);
-routerinfo_t *router_find_exact_exit_enclave(const char *address,
+int router_nickname_is_in_list(const routerinfo_t *router, const char *list);
+const routerinfo_t *routerlist_find_my_routerinfo(void);
+const node_t *router_find_exact_exit_enclave(const char *address,
                                              uint16_t port);
-int router_is_unreliable(routerinfo_t *router, int need_uptime,
+int node_is_unreliable(const node_t *router, int need_uptime,
                          int need_capacity, int need_guard);
-uint32_t router_get_advertised_bandwidth(routerinfo_t *router);
-uint32_t router_get_advertised_bandwidth_capped(routerinfo_t *router);
+uint32_t router_get_advertised_bandwidth(const routerinfo_t *router);
+uint32_t router_get_advertised_bandwidth_capped(const routerinfo_t *router);
 
-routerinfo_t *routerlist_sl_choose_by_bandwidth(smartlist_t *sl,
-                                                bandwidth_weight_rule_t rule);
-routerstatus_t *routerstatus_sl_choose_by_bandwidth(smartlist_t *sl,
-                                                bandwidth_weight_rule_t rule);
+const node_t *node_sl_choose_by_bandwidth(smartlist_t *sl,
+                                          bandwidth_weight_rule_t rule);
 
-routerinfo_t *router_choose_random_node(smartlist_t *excludedsmartlist,
+const node_t *router_choose_random_node(smartlist_t *excludedsmartlist,
                                         struct routerset_t *excludedset,
                                         router_crn_flags_t flags);
 
-routerinfo_t *router_get_by_nickname(const char *nickname,
+const routerinfo_t *router_get_by_nickname(const char *nickname,
                                      int warn_if_unnamed);
 int router_digest_version_as_new_as(const char *digest, const char *cutoff);
 int router_digest_is_trusted_dir_type(const char *digest,
@@ -67,13 +64,14 @@ int router_digest_is_trusted_dir_type(const char *digest,
 
 int router_addr_is_trusted_dir(uint32_t addr);
 int hexdigest_to_digest(const char *hexdigest, char *digest);
-routerinfo_t *router_get_by_hexdigest(const char *hexdigest);
-routerinfo_t *router_get_by_digest(const char *digest);
+const routerinfo_t *router_get_by_hexdigest(const char *hexdigest);
+const routerinfo_t *router_get_by_digest(const char *digest);
+routerinfo_t *router_get_mutable_by_digest(const char *digest);
 signed_descriptor_t *router_get_by_descriptor_digest(const char *digest);
 signed_descriptor_t *router_get_by_extrainfo_digest(const char *digest);
 signed_descriptor_t *extrainfo_get_by_descriptor_digest(const char *digest);
-const char *signed_descriptor_get_body(signed_descriptor_t *desc);
-const char *signed_descriptor_get_annotations(signed_descriptor_t *desc);
+const char *signed_descriptor_get_body(const signed_descriptor_t *desc);
+const char *signed_descriptor_get_annotations(const signed_descriptor_t *desc);
 routerlist_t *router_get_routerlist(void);
 void routerinfo_free(routerinfo_t *router);
 void extrainfo_free(extrainfo_t *extrainfo);
@@ -132,9 +130,10 @@ void router_load_extrainfo_from_string(const char *s, const char *eos,
                                        int descriptor_digests);
 
 void routerlist_retry_directory_downloads(time_t now);
-int router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port,
-                                          int need_uptime);
-int router_exit_policy_rejects_all(routerinfo_t *router);
+int router_exit_policy_all_nodes_reject(uint32_t addr, uint16_t port,
+                                        int need_uptime);
+
+int router_exit_policy_rejects_all(const routerinfo_t *router);
 trusted_dir_server_t *add_trusted_dir_server(const char *nickname,
                            const char *address,
                            uint16_t dir_port, uint16_t or_port,
@@ -152,39 +151,43 @@ void router_dir_info_changed(void);
 const char *get_dir_info_status_string(void);
 int count_loading_descriptors_progress(void);
 void router_reset_descriptor_download_failures(void);
-int router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2);
-int routerinfo_incompatible_with_extrainfo(routerinfo_t *ri, extrainfo_t *ei,
+int router_differences_are_cosmetic(const routerinfo_t *r1,
+                                    const routerinfo_t *r2);
+int routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
+                                           extrainfo_t *ei,
                                            signed_descriptor_t *sd,
                                            const char **msg);
 
-void routerlist_assert_ok(routerlist_t *rl);
-const char *esc_router_info(routerinfo_t *router);
+void routerlist_assert_ok(const routerlist_t *rl);
+const char *esc_router_info(const routerinfo_t *router);
 void routers_sort_by_identity(smartlist_t *routers);
 
 routerset_t *routerset_new(void);
+void routerset_refresh_countries(routerset_t *rs);
 int routerset_parse(routerset_t *target, const char *s,
                     const char *description);
 void routerset_union(routerset_t *target, const routerset_t *source);
 int routerset_is_list(const routerset_t *set);
 int routerset_needs_geoip(const routerset_t *set);
-int routerset_contains_router(const routerset_t *set, routerinfo_t *ri);
+int routerset_contains_router(const routerset_t *set, const routerinfo_t *ri,
+                              country_t country);
 int routerset_contains_routerstatus(const routerset_t *set,
-                                    routerstatus_t *rs);
+                                    const routerstatus_t *rs,
+                                    country_t country);
 int routerset_contains_extendinfo(const routerset_t *set,
                                   const extend_info_t *ei);
-void routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset,
-                               int running_only);
-void routersets_get_disjunction(smartlist_t *target, const smartlist_t *source,
+int routerset_contains_node(const routerset_t *set, const node_t *node);
+void routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
+                             int running_only);
+void routersets_get_node_disjunction(smartlist_t *target,
+                                const smartlist_t *source,
                                 const routerset_t *include,
                                 const routerset_t *exclude, int running_only);
-void routerset_subtract_routers(smartlist_t *out,
-                                const routerset_t *routerset);
+void routerset_subtract_nodes(smartlist_t *out,
+                              const routerset_t *routerset);
 char *routerset_to_string(const routerset_t *routerset);
-void routerset_refresh_countries(routerset_t *target);
 int routerset_equal(const routerset_t *old, const routerset_t *new);
 void routerset_free(routerset_t *routerset);
-void routerinfo_set_country(routerinfo_t *ri);
-void routerlist_refresh_countries(void);
 void refresh_all_country_info(void);
 
 int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
@@ -195,8 +198,13 @@ int hid_serv_responsible_for_desc_id(const char *id);
 void list_pending_microdesc_downloads(digestmap_t *result);
 void launch_descriptor_downloads(int purpose,
                                  smartlist_t *downloadable,
-                                 routerstatus_t *source,
+                                 const routerstatus_t *source,
                                  time_t now);
 
+int hex_digest_nickname_decode(const char *hexdigest,
+                               char *digest_out,
+                               char *nickname_qualifier_out,
+                               char *nickname_out);
+
 #endif
 

+ 2 - 5
src/or/routerparse.c

@@ -1344,7 +1344,6 @@ router_parse_entry_from_string(const char *s, const char *end,
   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;
@@ -1543,8 +1542,6 @@ router_parse_entry_from_string(const char *s, const char *end,
                             "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;
@@ -2030,7 +2027,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
       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"))
@@ -4305,7 +4302,7 @@ microdescs_parse_from_string(const char *s, const char *eos,
     }
 
     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);

+ 5 - 0
src/test/test.c

@@ -576,6 +576,7 @@ test_policy_summary_helper(const char *policy_str,
   smartlist_t *policy = smartlist_create();
   char *summary = NULL;
   int r;
+  short_policy_t *short_policy = NULL;
 
   line.key = (char*)"foo";
   line.value = (char *)policy_str;
@@ -588,10 +589,14 @@ test_policy_summary_helper(const char *policy_str,
   test_assert(summary != NULL);
   test_streq(summary, expected_summary);
 
+  short_policy = parse_short_policy(summary);
+  tt_assert(short_policy);
+
  done:
   tor_free(summary);
   if (policy)
     addr_policy_list_free(policy);
+  short_policy_free(short_policy);
 }
 
 /** Run unit tests for generating summary lines of exit policies */

+ 7 - 5
src/test/test_dir.c

@@ -801,7 +801,7 @@ test_dir_v3_networkstatus(void)
   rs->or_port = 443;
   rs->dir_port = 8000;
   /* all flags but running cleared */
-  rs->is_running = 1;
+  rs->is_flagged_running = 1;
   smartlist_add(vote->routerstatus_list, vrs);
   test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
 
@@ -816,7 +816,7 @@ test_dir_v3_networkstatus(void)
   rs->addr = 0x99009901;
   rs->or_port = 443;
   rs->dir_port = 0;
-  rs->is_exit = rs->is_stable = rs->is_fast = rs->is_running =
+  rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running =
     rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
   smartlist_add(vote->routerstatus_list, vrs);
   test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
@@ -833,7 +833,8 @@ test_dir_v3_networkstatus(void)
   rs->or_port = 400;
   rs->dir_port = 9999;
   rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
-    rs->is_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
+    rs->is_flagged_running = rs->is_valid = rs->is_v2_dir =
+    rs->is_possible_guard = 1;
   smartlist_add(vote->routerstatus_list, vrs);
   test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
 
@@ -1073,7 +1074,8 @@ test_dir_v3_networkstatus(void)
   test_assert(!rs->is_fast);
   test_assert(!rs->is_possible_guard);
   test_assert(!rs->is_stable);
-  test_assert(rs->is_running); /* If it wasn't running it wouldn't be here */
+  /* (If it wasn't running it wouldn't be here) */
+  test_assert(rs->is_flagged_running);
   test_assert(!rs->is_v2_dir);
   test_assert(!rs->is_valid);
   test_assert(!rs->is_named);
@@ -1095,7 +1097,7 @@ test_dir_v3_networkstatus(void)
   test_assert(rs->is_fast);
   test_assert(rs->is_possible_guard);
   test_assert(rs->is_stable);
-  test_assert(rs->is_running);
+  test_assert(rs->is_flagged_running);
   test_assert(rs->is_v2_dir);
   test_assert(rs->is_valid);
   test_assert(!rs->is_named);

部分文件因文件數量過多而無法顯示