[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] Numerous changes to move towards client-side v2 directories.



Update of /home/or/cvsroot/tor/src/or
In directory moria:/tmp/cvs-serv15158/src/or

Modified Files:
	connection.c directory.c dirserv.c or.h router.c routerlist.c 
	routerparse.c 
Log Message:
Numerous changes to move towards client-side v2 directories.

connection.c:
- Add some more connection accessor functions to make directory
  download redundancy checking work.

directory.c, or.h, router.c, routerlist.c:
- Start on logic to note when networkstatus downloads fail.

dirserv.c, routerlist.c, routerparse.c:
- Start maintaining an is_named field in routerstatus_t.  Don't
  actually look at it yet.

dirserv.c, routerlist.c:
- Remove expired networkstatus objects.

or.h:
- Make some booleans into bitfields
- Add prototypes

routerlist.c:
- Sort networkstatus list by publication time
- Function to remove old (older than 10 days) networkstatus objects.
- Function to set a list of routerinfo_ts' status info from the
  current set of networkstatus objects.
- Function to tell which routerinfos we need to download based no the
  current set of networkstatus objects.
- Do not launch a networkstatus download if a redundant one is in progress.

routerparse.c:
- Keep router entries in networkstatus sorted by digest.



Index: connection.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/connection.c,v
retrieving revision 1.396
retrieving revision 1.397
diff -u -d -r1.396 -r1.397
--- connection.c	8 Sep 2005 21:21:54 -0000	1.396
+++ connection.c	12 Sep 2005 06:56:42 -0000	1.397
@@ -1479,6 +1479,29 @@
   return best;
 }
 
+/** Return a connection with give type, address, port, and purpose or NULL if
+ * no such connection exists. */
+connection_t *
+connection_get_by_type_addr_port_purpose(int type, uint32_t addr, uint16_t port,
+                                         int purpose)
+{
+  int i, n;
+  connection_t *conn;
+  connection_t **carray;
+
+  get_connection_array(&carray,&n);
+  for (i=0;i<n;i++) {
+    conn = carray[i];
+    if (conn->type == type &&
+        conn->addr == addr &&
+        conn->port == port &&
+        conn->purpose == purpose &&
+        !conn->marked_for_close)
+      return conn;
+  }
+  return NULL;
+}
+
 connection_t *
 connection_get_by_identity_digest(const char *digest, int type)
 {
@@ -1603,6 +1626,26 @@
   return NULL;
 }
 
+/** Return an open, non-marked connection of a given type and purpose, or NULL
+ * if no such connection exists. */
+connection_t *
+connection_get_by_type_purpose(int type, int purpose)
+{
+  int i, n;
+  connection_t *conn;
+  connection_t **carray;
+
+  get_connection_array(&carray,&n);
+  for (i=0;i<n;i++) {
+    conn = carray[i];
+    if (conn->type == type &&
+        !conn->marked_for_close &&
+        (purpose == conn->purpose))
+      return conn;
+  }
+  return NULL;
+}
+
 /** Return 1 if <b>conn</b> is a listener conn, else return 0. */
 int
 connection_is_listener(connection_t *conn)

Index: directory.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/directory.c,v
retrieving revision 1.271
retrieving revision 1.272
diff -u -d -r1.271 -r1.272
--- directory.c	12 Sep 2005 06:08:48 -0000	1.271
+++ directory.c	12 Sep 2005 06:56:42 -0000	1.272
@@ -51,6 +51,8 @@
 static int purpose_is_private(uint8_t purpose);
 static char *http_get_header(const char *headers, const char *which);
 static char *http_get_origin(const char *headers, connection_t *conn);
+static void connection_dir_download_networkstatus_failed(connection_t *conn);
+static void dir_networkstatus_download_failed(smartlist_t *failed);
 
 /********* START VARIABLES **********/
 
@@ -274,9 +276,36 @@
            conn->address);
     directory_get_from_dirserver(conn->purpose, NULL,
                                  0 /* don't retry_if_no_servers */);
+  } else if (conn->purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS) {
+    log_fn(LOG_INFO, "Giving up on directory server at '%s'; retrying",
+           conn->address);
+    connection_dir_download_networkstatus_failed(conn);
   }
 }
 
+/** DOCDOC */
+static void
+connection_dir_download_networkstatus_failed(connection_t *conn)
+{
+    if (!strcmpstart(conn->requested_resource, "all")) {
+      directory_get_from_dirserver(conn->purpose, "all.z",
+                                   0 /* don't retry_if_no_servers */);
+    } else if (!strcmpstart(conn->requested_resource, "fp/")) {
+      smartlist_t *failed = smartlist_create();
+      smartlist_split_string(failed, conn->requested_resource+3, "+", 0, 0);
+      if (smartlist_len(failed)) {
+        char *last = smartlist_get(failed,smartlist_len(failed)-1);
+        size_t last_len = strlen(last);
+        if (!strcmp(last+last_len-2, ".z"))
+          last[last_len-2] = '\0';
+
+        dir_networkstatus_download_failed(failed);
+        SMARTLIST_FOREACH(failed, char *, cp, tor_free(cp));
+      }
+      smartlist_free(failed);
+    }
+}
+
 /** Helper for directory_initiate_command_(router|trusted_dir): send the
  * command to a server whose address is <b>address</b>, whose IP is
  * <b>addr</b>, whose directory port is <b>dir_port</b>, whose tor version is
@@ -896,6 +925,7 @@
              status_code, reason, conn->address, conn->port,
              conn->requested_resource);
       tor_free(body); tor_free(headers); tor_free(reason);
+      connection_dir_download_networkstatus_failed(conn);
       return -1;
     }
     if (conn->requested_resource &&
@@ -922,6 +952,9 @@
         break;
     }
     if (which) {
+      if (smartlist_len(which)) {
+        dir_networkstatus_download_failed(which);
+      }
       SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
       smartlist_free(which);
     }
@@ -1451,3 +1484,18 @@
   return 0;
 }
 
+/** DOCDOC */
+static void
+dir_networkstatus_download_failed(smartlist_t *failed)
+{
+  SMARTLIST_FOREACH(failed, const char *, fp,
+  {
+    char digest[DIGEST_LEN];
+    trusted_dir_server_t *dir;
+    base16_decode(digest, DIGEST_LEN, fp, strlen(fp));
+    dir = router_get_trusteddirserver_by_digest(digest);
+
+    ++dir->n_networkstatus_failures;
+  });
+}
+

Index: dirserv.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/dirserv.c,v
retrieving revision 1.220
retrieving revision 1.221
diff -u -d -r1.220 -r1.221
--- dirserv.c	9 Sep 2005 21:03:57 -0000	1.220
+++ dirserv.c	12 Sep 2005 06:56:42 -0000	1.221
@@ -388,11 +388,11 @@
       changed = 1;
     } else if (r>0 && !ent->is_verified) {
       log(LOG_INFO, "Router '%s' is now approved.", ent->nickname);
-      ent->is_verified = 1;
+      ent->is_verified = ent->is_named = 1;
       changed = 1;
     } else if (r==0 && ent->is_verified) {
       log(LOG_INFO, "Router '%s' is no longer approved.", ent->nickname);
-      ent->is_verified = 0;
+      ent->is_verified = ent->is_named = 0;
       changed = 1;
     }
   }
@@ -752,6 +752,8 @@
 /** We've just received a v2 network-status for an authoritative directory
  * with fingerprint <b>fp</b> (hex digest, no spaces), published at
  * <b>published</b>.  Store it so we can serve it to others.
+ *
+ * DOCDOC directory==NULL, published==0
  */
 void
 dirserv_set_cached_networkstatus_v2(const char *directory, const char *fp,
@@ -764,12 +766,19 @@
   tor_assert(strlen(fp) == HEX_DIGEST_LEN);
 
   if (!(d = strmap_get(cached_v2_networkstatus, fp))) {
+    if (!directory)
+      return;
     d = tor_malloc_zero(sizeof(cached_dir_t));
     strmap_set(cached_v2_networkstatus, fp, d);
   }
 
   tor_assert(d);
-  set_cached_dir(d, tor_strdup(directory), published);
+  if (directory) {
+    set_cached_dir(d, tor_strdup(directory), published);
+  } else {
+    free_cached_dir(d);
+    strmap_remove(cached_v2_networkstatus, fp);
+  }
 }
 
 static cached_dir_t *

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/or.h,v
retrieving revision 1.674
retrieving revision 1.675
diff -u -d -r1.674 -r1.675
--- or.h	8 Sep 2005 20:36:40 -0000	1.674
+++ or.h	12 Sep 2005 06:56:42 -0000	1.675
@@ -755,17 +755,22 @@
   addr_policy_t *exit_policy; /**< What streams will this OR permit
                                       * to exit? */
   long uptime; /**< How many seconds the router claims to have been up */
-  uint8_t is_hibernating; /**< Whether the router claims to be hibernating */
   smartlist_t *declared_family; /**< Nicknames of router which this router
                                  * claims are its family. */
   char *contact_info; /**< Declared contact info for this router. */
+  unsigned int is_hibernating:1; /**< Whether the router claims to be
+                                  * hibernating */
 
   /* local info */
-  int is_running; /**< As far as we know, is this OR currently running? */
+  unsigned int is_running:1; /**< As far as we know, is this OR currently
+                              * running? */
+  unsigned int is_verified:1; /**< Has a trusted dirserver validated this OR?
+                               *  (For Authdir: Have we validated this OR?)
+                               */
+  /*XXXX Make this get used once we think we do naming right. NM */
+  unsigned int is_named:1; /* Do we believe the nickname that this OR gives us? */
+
   time_t status_set_at; /**< When did we last update is_running? */
-  int is_verified; /**< Has a trusted dirserver validated this OR?
-                    * (For Authdir: Have we validated this OR?)
-                    */
 
   /* The below items are used only by authdirservers for
    * reachability testing. */
@@ -1504,6 +1509,9 @@
 connection_t *connection_get_by_global_id(uint32_t id);
 
 connection_t *connection_get_by_type(int type);
+connection_t *connection_get_by_type_purpose(int type, int purpose);
+connection_t *connection_get_by_type_addr_port_purpose(int type, uint32_t addr,
+                                                     uint16_t port, int purpose);
 connection_t *connection_get_by_type_state(int type, int state);
 connection_t *connection_get_by_type_state_lastwritten(int type, int state);
 connection_t *connection_get_by_type_state_rendquery(int type, int state, const char *rendquery);
@@ -2015,8 +2023,9 @@
   uint32_t addr;
   uint16_t dir_port;
   char digest[DIGEST_LEN];
-  int is_running;
-  int supports_v1_protocol;
+  unsigned int is_running:1;
+  unsigned int supports_v1_protocol:1;
+  int n_networkstatus_failures;
 } trusted_dir_server_t;
 
 int router_reload_router_list(void);
@@ -2097,6 +2106,8 @@
 networkstatus_t *networkstatus_get_by_digest(const char *digest);
 void update_networkstatus_cache_downloads(time_t now);
 void update_networkstatus_client_downloads(time_t now);
+void routers_update_status_from_networkstatus(smartlist_t *routers);
+smartlist_t *router_list_superseded(void);
 
 /********************************* routerparse.c ************************/
 

Index: router.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/router.c,v
retrieving revision 1.205
retrieving revision 1.206
diff -u -d -r1.205 -r1.206
--- router.c	8 Sep 2005 20:18:15 -0000	1.205
+++ router.c	12 Sep 2005 06:56:42 -0000	1.206
@@ -764,10 +764,13 @@
   config_parse_addr_policy(get_options()->ExitPolicy, &ri->exit_policy, -1);
   options_append_default_exit_policy(&ri->exit_policy);
 
-  if (desc_routerinfo) /* inherit values */
+  if (desc_routerinfo) { /* inherit values */
     ri->is_verified = desc_routerinfo->is_verified;
+    ri->is_running = desc_routerinfo->is_running;
+    ri->is_named = desc_routerinfo->is_named;
+  }
   if (authdir_mode(options))
-    ri->is_verified = 1; /* believe in yourself */
+    ri->is_verified = ri->is_named = 1; /* believe in yourself */
   if (options->MyFamily) {
     ri->declared_family = smartlist_create();
     smartlist_split_string(ri->declared_family, options->MyFamily, ",",

Index: routerlist.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/routerlist.c,v
retrieving revision 1.281
retrieving revision 1.282
diff -u -d -r1.281 -r1.282
--- routerlist.c	10 Sep 2005 20:40:16 -0000	1.281
+++ routerlist.c	12 Sep 2005 06:56:42 -0000	1.282
@@ -980,6 +980,7 @@
       return -2;
     }
     router->is_verified = authdir_verified;
+    router->is_named = router->is_verified; /*XXXX NM not right. */
     if (tor_version_as_new_as(router->platform,"0.1.0.2-rc"))
       router->is_verified = 1;
   }
@@ -996,6 +997,7 @@
         if (authdir) {
           /* Update the is_verified status based on our lookup. */
           old_router->is_verified = router->is_verified;
+          old_router->is_named = router->is_named;
         } else {
           /* Update the is_running status to whatever we were told. */
           old_router->is_running = router->is_running;
@@ -1198,10 +1200,37 @@
   return 0;
 }
 
+/**DOCDOC */
+static char *
+networkstatus_get_cache_filename(const networkstatus_t *ns)
+{
+  const char *datadir = get_options()->DataDirectory;
+  size_t len = strlen(datadir)+64;
+  char fp[HEX_DIGEST_LEN+1];
+  char *fn = tor_malloc(len+1);
+  base16_encode(fp, HEX_DIGEST_LEN+1, ns->identity_digest, DIGEST_LEN);
+  tor_snprintf(fn, len, "%s/cached-status/%s",datadir,fp);
+  return fn;
+
+}
+/** DOCDOC */
+static int
+_compare_networkstatus_published_on(const void **_a, const void **_b)
+{
+  const networkstatus_t *a = *_a, *b = *_b;
+  if (a->published_on < b->published_on)
+    return -1;
+  else if (a->published_on > b->published_on)
+    return 1;
+  else
+    return 0;
+}
+
 /** How far in the future do we allow a network-status to get? (seconds) */
 #define NETWORKSTATUS_ALLOW_SKEW (48*60*60)
 /** DOCDOC returns 0 on no problems, -1 on problems.
  * requested fingerprints must be upcased.
+ * removes and frees items from requested_fingerpritns
  */
 int
 router_set_networkstatus(const char *s, time_t arrived_at,
@@ -1210,15 +1239,16 @@
   networkstatus_t *ns;
   int i, found;
   time_t now;
-  char fp[HEX_DIGEST_LEN+1];
   int skewed = 0;
+  trusted_dir_server_t *trusted_dir;
+  char fp[HEX_DIGEST_LEN+1];
 
   ns = networkstatus_parse_from_string(s);
   if (!ns) {
     log_fn(LOG_WARN, "Couldn't parse network status.");
     return -1;
   }
-  if (!router_digest_is_trusted_dir(ns->identity_digest)) {
+  if (!(trusted_dir=router_get_trusteddirserver_by_digest(ns->identity_digest))) {
     log_fn(LOG_INFO, "Network status was signed, but not by an authoritative directory we recognize.");
     networkstatus_free(ns);
     return -1;
@@ -1238,21 +1268,27 @@
     networkstatus_list = smartlist_create();
 
   if (source == NS_FROM_DIR && router_digest_is_me(ns->identity_digest)) {
-    /* Drop our own networkstatus when we get it from somebody else. */
+    /* Don't replace our own networkstatus when we get it from somebody else. */
     networkstatus_free(ns);
     return 0;
   }
 
   base16_encode(fp, HEX_DIGEST_LEN+1, ns->identity_digest, DIGEST_LEN);
 
-  if (requested_fingerprints &&
-      !smartlist_string_isin(requested_fingerprints, fp)) {
-    char *requested = smartlist_join_strings(requested_fingerprints," ",0,NULL);
-    log_fn(LOG_WARN, "We received a network status with a fingerprint (%s) that we never requested. (%s) Dropping.", fp, requested);
-    tor_free(requested);
-    return 0;
+  if (requested_fingerprints) {
+    if (smartlist_string_isin(requested_fingerprints, fp)) {
+      smartlist_string_remove(requested_fingerprints, fp);
+    } else {
+      char *requested = smartlist_join_strings(requested_fingerprints," ",0,NULL);
+      log_fn(LOG_WARN, "We received a network status with a fingerprint (%s) that we never requested. (%s) Dropping.", fp, requested);
+      tor_free(requested);
+      return 0;
+    }
   }
 
+  if (source != NS_FROM_CACHE)
+    trusted_dir->n_networkstatus_failures = 0;
+
   found = 0;
   for (i=0; i < smartlist_len(networkstatus_list); ++i) {
     networkstatus_t *old_ns = smartlist_get(networkstatus_list, i);
@@ -1281,11 +1317,10 @@
   if (!found)
     smartlist_add(networkstatus_list, ns);
 
+  smartlist_sort(networkstatus_list, _compare_networkstatus_published_on);
+
   if (source != NS_FROM_CACHE && !skewed) {
-    const char *datadir = get_options()->DataDirectory;
-    size_t len = strlen(datadir)+64;
-    char *fn = tor_malloc(len+1);
-    tor_snprintf(fn, len, "%s/cached-status/%s",datadir,fp);
+    char *fn = networkstatus_get_cache_filename(ns);
     if (write_str_to_file(fn, s, 0)<0) {
       log_fn(LOG_NOTICE, "Couldn't write cached network status to \"%s\"", fn);
     }
@@ -1298,6 +1333,56 @@
   return 0;
 }
 
+#define MAX_NETWORKSTATUS_AGE (10*24*60*60)
+/** DOCDOC */
+void
+networkstatus_list_clean(time_t now)
+{
+  int i;
+  if (!networkstatus_list)
+    return;
+
+  for (i = 0; i < smartlist_len(networkstatus_list); ++i) {
+    networkstatus_t *ns = smartlist_get(networkstatus_list, i);
+    char *fname = NULL;;
+    if (ns->published_on + MAX_NETWORKSTATUS_AGE > now)
+      continue;
+    /* Okay, this one is too old.  Remove it from the list, and delete it
+     * from the cache. */
+    smartlist_del(networkstatus_list, i--);
+    fname = networkstatus_get_cache_filename(ns);
+    if (file_status(fname) == FN_FILE) {
+      log_fn(LOG_INFO, "Removing too-old networkstatus in %s", fname);
+      unlink(fname);
+    }
+    tor_free(fname);
+    if (get_options()->DirPort) {
+      char fp[HEX_DIGEST_LEN+1];
+      base16_encode(fp, sizeof(fp), ns->identity_digest, DIGEST_LEN);
+      dirserv_set_cached_networkstatus_v2(NULL, fp, 0);
+    }
+    networkstatus_free(ns);
+  }
+}
+
+/** Helper for bsearching a list of routerstatus_t pointers.*/
+static int
+_compare_digest_to_routerstatus_entry(const void *_key, const void **_member)
+{
+  const char *key = _key;
+  const routerstatus_t *rs = *_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. */
+routerstatus_t *
+networkstatus_find_entry(networkstatus_t *ns, const char *digest)
+{
+  return smartlist_bsearch(ns->entries, digest,
+                           _compare_digest_to_routerstatus_entry);
+}
+
 /* XXXX These should be configurable, perhaps? NM */
 #define AUTHORITY_NS_CACHE_INTERVAL 10*60
 #define NONAUTHORITY_NS_CACHE_INTERVAL 15*60
@@ -1324,6 +1409,10 @@
          char resource[HEX_DIGEST_LEN+6];
          if (router_digest_is_me(ds->digest))
            continue;
+         if (connection_get_by_type_addr_port_purpose(
+                CONN_TYPE_DIR, ds->addr, ds->dir_port,
+                DIR_PURPOSE_FETCH_NETWORKSTATUS))
+           continue;
          strlcpy(resource, "fp/", sizeof(resource));
          base16_encode(resource+3, sizeof(resource)-3, ds->digest, DIGEST_LEN);
          strlcat(resource, ".z", sizeof(resource));
@@ -1331,7 +1420,9 @@
        });
   } else {
     /* A non-authority cache launches one connection to a random authority. */
-    directory_get_from_dirserver(DIR_PURPOSE_FETCH_NETWORKSTATUS,"all.z",1);
+    if (!connection_get_by_type_purpose(CONN_TYPE_DIR,
+                                        DIR_PURPOSE_FETCH_NETWORKSTATUS))
+      directory_get_from_dirserver(DIR_PURPOSE_FETCH_NETWORKSTATUS,"all.z",1);
   }
 }
 
@@ -1353,10 +1444,15 @@
   char *resource, *cp;
   size_t resource_len;
 
+  if (connection_get_by_type_purpose(CONN_TYPE_DIR,
+                                     DIR_PURPOSE_FETCH_NETWORKSTATUS))
+    return;
+
   /* This is a little tricky.  We want to download enough network-status
-   * objects so that we have at least half of them under NETWORKSTATUS_MAX_VALIDITY
-   * publication time.  We want to download a new *one* if the most recent
-   * one's publication time is under NETWORKSTATUS_CLIENT_DL_INTERVAL.
+   * objects so that we have at least half of them under
+   * NETWORKSTATUS_MAX_VALIDITY publication time.  We want to download a new
+   * *one* if the most recent one's publication time is under
+   * NETWORKSTATUS_CLIENT_DL_INTERVAL.
    */
   if (!trusted_dir_servers || !smartlist_len(trusted_dir_servers))
     return;
@@ -1707,7 +1803,7 @@
 {
   int authdir = get_options()->AuthoritativeDir;
   int is_running = 1;
-  int is_verified = 0;
+  int is_verified = 0, is_named = 0;
   int hex_digest_set = 0;
   char nickname[MAX_NICKNAME_LEN+1];
   char hexdigest[HEX_DIGEST_LEN+1];
@@ -1726,6 +1822,7 @@
      * entry will either extend to a NUL (old running-routers format) or to an
      * equals sign (new router-status format). */
     is_verified = 1;
+    is_named = 1;
     end = strchr(cp, '=');
     if (!end)
       end = strchr(cp,'\0');
@@ -1781,9 +1878,9 @@
       /* If we're not an authoritative directory, update verified status.
        */
       if (nickname_matches && digest_matches)
-        r->is_verified = 1;
+        r->is_verified = r->is_named = 1;
       else if (digest_matches)
-        r->is_verified = 0;
+        r->is_verified = r->is_named = 0;
     }
     if (digest_matches)
       if (r->status_set_at < list_time) {
@@ -1878,3 +1975,111 @@
   return NULL;
 }
 
+#define DEFAULT_RUNNING_INTERVAL 60*60
+#define MIN_TO_INFLUENCE_RUNNING 3
+void
+routers_update_status_from_networkstatus(smartlist_t *routers)
+{
+  int n_trusted, n_statuses, n_valid, n_naming, n_named, n_running, n_listing,
+    n_recent;
+  int i;
+  time_t now = time(NULL);
+
+  if (authdir_mode(get_options())) {
+    /* An authoritative directory should never believer someone else about
+     * a server's status. */
+    return;
+  }
+
+  if (!networkstatus_list)
+    networkstatus_list = smartlist_create();
+  if (!trusted_dir_servers)
+    trusted_dir_servers = smartlist_create();
+
+  n_trusted = smartlist_len(trusted_dir_servers);
+  n_statuses = smartlist_len(networkstatus_list);
+
+  if (n_statuses < (n_trusted/2)+1) {
+    /* Not enough statuses to adjust status. */
+    return;
+  }
+
+  if (n_statuses < MIN_TO_INFLUENCE_RUNNING) {
+    n_recent = n_statuses;
+  } else {
+    n_recent = 0;
+    for (i=smartlist_len(networkstatus_list)-1; i >= 0; --i) {
+      networkstatus_t *ns = smartlist_get(networkstatus_list, i);
+      if (ns->published_on + DEFAULT_RUNNING_INTERVAL < now)
+        break;
+      ++n_recent;
+    }
+    if (n_recent < MIN_TO_INFLUENCE_RUNNING) {
+      n_recent = MIN_TO_INFLUENCE_RUNNING;
+    }
+  }
+
+  SMARTLIST_FOREACH(routers, routerinfo_t *, router,
+  {
+    n_listing = n_valid = n_naming = n_named = n_running = 0;
+
+    SMARTLIST_FOREACH(networkstatus_list, networkstatus_t *, ns,
+    {
+      routerstatus_t *rs = networkstatus_find_entry(ns, router->identity_digest);
+      if (ns->binds_names)
+        ++n_naming;
+      if (!rs)
+        continue;
+      ++n_listing;
+      if (rs->is_named && !strcasecmp(rs->nickname, router->nickname))
+        ++n_named;
+      if (rs->is_valid)
+        ++n_valid;
+      if (ns_sl_idx >= smartlist_len(networkstatus_list)-n_recent) {
+        if (rs->is_running)
+          ++n_running;
+      }
+    });
+
+    log_fn(LOG_DEBUG, "Router '%s' at %s:%d is listed by %d/%d directories, "
+           "named by %d/%d, validated by %d/%d, and %d/%d recent directories "
+           "think it's running.",
+           router->nickname, router->address, router->or_port,
+           n_listing, n_statuses, n_named, n_naming, n_valid, n_statuses,
+           n_running, n_recent);
+
+    router->is_named = (n_named > n_naming/2);
+    router->is_verified = (n_valid > n_statuses/2);
+    router->is_running = (n_running > n_recent/2);
+  });
+}
+
+/** Return new list of ID digests for superseded routers.
+ * A router is superseded if any network-status has a router with a different
+ * digest published more recently.
+ */
+smartlist_t *
+router_list_superseded(void)
+{
+  smartlist_t *superseded = smartlist_create();
+
+  if (!routerlist || !networkstatus_list)
+    return superseded;
+
+  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
+  {
+    SMARTLIST_FOREACH(networkstatus_list, networkstatus_t *, ns,
+    {
+      routerstatus_t *rs = networkstatus_find_entry(ns, ri->identity_digest);
+      if (memcmp(ri->signed_descriptor_digest,rs->descriptor_digest,DIGEST_LEN)
+          && rs->published_on > ri->published_on) {
+        char *d = tor_malloc(DIGEST_LEN);
+        memcpy(d, ri->identity_digest, DIGEST_LEN);
+        smartlist_add(superseded, d);
+        break;
+      }
+    });
+  });
+  return superseded;
+}
+

Index: routerparse.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/routerparse.c,v
retrieving revision 1.135
retrieving revision 1.136
diff -u -d -r1.135 -r1.136
--- routerparse.c	12 Sep 2005 06:14:41 -0000	1.135
+++ routerparse.c	12 Sep 2005 06:56:42 -0000	1.136
@@ -849,7 +849,7 @@
 
     if (!good_nickname_list) {
       router->is_running = 1; /* start out assuming all dirservers are up */
-      router->is_verified = 1;
+      router->is_verified = router->is_named = 1;
       router->status_set_at = time(NULL);
     }
     smartlist_add(routers, router);
@@ -1245,6 +1245,14 @@
   return rs;
 }
 
+/** Helper to sort a smartlist of pointers to routerstatus_t */
+static int
+_compare_routerstatus_entries(const void **_a, const void **_b)
+{
+  const routerstatus_t *a = *_a, *b = *_b;
+  return memcmp(a->identity_digest, b->identity_digest, DIGEST_LEN);
+}
+
 /** Given a versioned (v2 or later) network-status object in <b>s</b>, try to
  * parse it and return the result.  Return NULL on failure.  Check the
  * signature of the network status, but do not (yet) check the signing key for
@@ -1387,6 +1395,7 @@
     if ((rs = routerstatus_parse_entry_from_string(&s, tokens)))
       smartlist_add(ns->entries, rs);
   }
+  smartlist_sort(ns->entries, _compare_routerstatus_entries);
 
   if (tokenize_string(s, NULL, tokens, NETSTATUS)) {
     log_fn(LOG_WARN, "Error tokenizing network-status footer.");