diff --git a/doc/tor-spec.txt b/doc/tor-spec.txt
index 1718eab8e9..19e94af847 100644
--- a/doc/tor-spec.txt
+++ b/doc/tor-spec.txt
@@ -706,7 +706,7 @@ line, they must appear in the "ports" lines.
A Directory begins with a "signed-directory" item, followed by one each of
the following, in any order: "recommended-software", "published",
-"running-routers". It may include any number of "opt" items. After these
+"router-status". It may include any number of "opt" items. After these
items, a directory includes any number of router descriptors, and a single
"directory-signature" item.
@@ -723,7 +723,7 @@ items, a directory includes any number of router descriptors, and a single
A list of which versions of which implementations are currently
believed to be secure and compatible with the network.
- "running-routers" comma-separated-list
+ "running-routers" space-separated-list
A description of which routers are currently believed to be up or
down. Every entry consists of an optional "!", followed by either an
@@ -733,8 +733,30 @@ items, a directory includes any number of router descriptors, and a single
If a router's nickname is given, exactly one router of that nickname
will appear in the directory, and that router is "approved" by the
directory server. If a hashed identity key is given, that OR is not
- "approved".
+ "approved". [XXXX The 'running-routers' line is only provided for
+ backward compatibility. New code should parse 'router-status'
+ instead.]
+ "router-status" space-separated-list
+
+ A description of which routers are currently believed to be up or
+ down, and which are verified or unverified. Contains one entry for
+ every router that the directory server knows. Each entry is of the
+ format:
+
+ !name=$digest [Verified router, currently not live.]
+ name=$digest [Verified router, currently live.]
+ !$digest [Unverified router, currently not live.]
+ or $digest [Unverified router, currently live.]
+
+ (where 'name' is the router's nickname and 'digest' is a hexadecimal
+ encoding of the hash of the routers' identity key).
+
+ When parsing this line, clients should only mark a router as
+ 'verified' if its nickname AND digest match the one provided.
+ [XXXX 'router-status' was added in 0.0.9pre5; older directory code
+ uses 'running-routers' instead.]
+
"directory-signature" nickname-of-dirserver NL Signature
Note: The router descriptor for the directory server MUST appear first.
@@ -763,7 +785,7 @@ entries.
(see 7.2 above)
- "running-routers" list
+ "router-status" list
(see 7.2 above)
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index f414350bde..74409fe528 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -20,7 +20,8 @@ extern or_options_t options; /**< command-line and config-file options */
static int the_directory_is_dirty = 1;
static int runningrouters_is_dirty = 1;
-static int list_running_servers(char **nicknames_out);
+static int list_server_status(char **running_routers_out,
+ char **router_status_out);
static void directory_remove_unrecognized(void);
static int dirserv_regenerate_directory(void);
@@ -456,71 +457,80 @@ dirserv_load_from_directory_string(const char *dir)
return 0;
}
-/** Set *nicknames_out to a comma-separated list of all the ORs that we
- * believe are currently running (because we have open connections to
- * them). Return 0 on success; -1 on error.
+/**
+ * Allocate and return a description of the status of the server desc,
+ * for use in a running-routers line (if rr_format is true), or in a
+ * router-status line (if rr_format is false. The server is listed
+ * as running iff is_live is true.
+ */
+static char *
+list_single_server_status(descriptor_entry_t *desc, int is_live,
+ int rr_format)
+{
+ char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
+ char *cp;
+
+ tor_assert(desc);
+ tor_assert(desc->router);
+
+ cp = buf;
+ if (!is_live) {
+ *cp++ = '!';
+ }
+ if (desc->verified) {
+ strcpy(cp, desc->nickname);
+ cp += strlen(cp);
+ if (!rr_format)
+ *cp++ = '=';
+ }
+ if (!desc->verified || !rr_format) {
+ *cp++ = '$';
+ base16_encode(cp, HEX_DIGEST_LEN+1, desc->router->identity_digest,
+ DIGEST_LEN);
+ }
+ return tor_strdup(buf);
+}
+
+/** Allocate the contents of a running-routers line and a router-status line,
+ * and store them in *running_routers_out and *router_status_out
+ * respectively. Return 0 on success, -1 on failure.
*/
static int
-list_running_servers(char **nicknames_out)
+list_server_status(char **running_routers_out, char **router_status_out)
{
- connection_t **connection_array;
- int n_conns;
- connection_t *conn;
- char *cp;
- int i;
- size_t length;
- smartlist_t *nicknames_up, *nicknames_down;
- char *name;
- const char *s;
+ /* List of entries in running-routers style: An optional !, then either
+ * a nickname or a dollar-prefixed hexdigest. */
+ smartlist_t *rr_entries;
+ /* List of entries in a router-status style: An optional !, then an optional
+ * equals-suffixed nickname, then a dollar-prefixed hexdigest. */
+ smartlist_t *rs_entries;
- *nicknames_out = NULL;
- nicknames_up = smartlist_create();
- nicknames_down = smartlist_create();
- smartlist_add(nicknames_up, tor_strdup(options.Nickname));
+ tor_assert(running_routers_out || router_status_out);
- get_connection_array(&connection_array, &n_conns);
- for (i = 0; itype != CONN_TYPE_OR || !conn->nickname)
- continue; /* only list connections to ORs with certificates. */
- s = dirserv_get_nickname_by_digest(conn->identity_digest);
- if (s) {
- name = tor_strdup(s);
- } else {
- name = tor_malloc(HEX_DIGEST_LEN+2);
- *name = '$';
- base16_encode(name+1, HEX_DIGEST_LEN+1, conn->identity_digest, DIGEST_LEN);
- }
+ rr_entries = smartlist_create();
+ rs_entries = smartlist_create();
+
+ SMARTLIST_FOREACH(descriptor_list, descriptor_entry_t *, d,
+ {
+ int is_live;
+ tor_assert(d->router);
+ connection_t *conn = connection_get_by_identity_digest(
+ d->router->identity_digest, CONN_TYPE_OR);
+ is_live = (conn && conn->state == OR_CONN_STATE_OPEN);
+ smartlist_add(rr_entries, list_single_server_status(d, is_live, 1));
+ smartlist_add(rs_entries, list_single_server_status(d, is_live, 0));
+ });
+
+ if (running_routers_out)
+ *running_routers_out = smartlist_join_strings(rr_entries, " ", 0);
+ if (router_status_out)
+ *router_status_out = smartlist_join_strings(rs_entries, " ", 0);
+
+ SMARTLIST_FOREACH(rr_entries, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(rs_entries, char *, cp, tor_free(cp));
+ smartlist_free(rr_entries);
+ smartlist_free(rs_entries);
- if(conn->state == OR_CONN_STATE_OPEN)
- smartlist_add(nicknames_up, name);
- else
- smartlist_add(nicknames_down, name);
- }
- length = smartlist_len(nicknames_up) +
- 2*smartlist_len(nicknames_down) + 1;
- /* spaces + EOS + !'s + 1. */
- SMARTLIST_FOREACH(nicknames_up, char *, c, length += strlen(c));
- SMARTLIST_FOREACH(nicknames_down, char *, c, length += strlen(c));
- *nicknames_out = tor_malloc_zero(length);
- cp = *nicknames_out;
- for (i = 0; ipublished_on >= rr->published_on)
@@ -1027,18 +1027,15 @@ void routerlist_update_from_runningrouters(routerlist_t *list,
if (list->running_routers_updated_on >= rr->published_on)
return;
- if(me) { /* learn if the dirservers think I'm verified */
- router_update_status_from_smartlist(me,
- rr->published_on,
- rr->running_routers);
- }
- n_routers = smartlist_len(list->routers);
- for (i=0; irouters, i);
- router_update_status_from_smartlist(router,
- rr->published_on,
- rr->running_routers);
- }
+ all_routers = smartlist_create();
+ if(me) /* learn if the dirservers think I'm verified */
+ smartlist_add(all_routers, me);
+
+ smartlist_add_all(all_routers,list->routers);
+ SMARTLIST_FOREACH(rr->running_routers, const char *, cp,
+ routers_update_status_from_entry(all_routers, rr->published_on,
+ cp, rr->is_running_routers_format));
+ smartlist_free(all_routers);
list->running_routers_updated_on = rr->published_on;
}
@@ -1046,6 +1043,14 @@ void routerlist_update_from_runningrouters(routerlist_t *list,
* based in its status in the list of strings stored in running_list.
* All entries in running_list follow one of these formats:
* - nickname -- router is running and verified.
+ * (running-routers format)
+ *
- !nickname -- router is not-running and verified.
+ * (running-routers format)
+ *
- nickname=$hexdigest -- router is running and
+ * verified. (router-status format)
+ * (router-status format)
+ *
- !nickname=$hexdigest -- router is running and
+ * verified. (router-status format)
*
- !nickname -- router is not-running and verified.
*
- $hexdigest -- router is running and unverified.
*
- !$hexdigest -- router is not-running and unverified.
@@ -1053,57 +1058,115 @@ void routerlist_update_from_runningrouters(routerlist_t *list,
*
* Return 1 if we found router in running_list, else return 0.
*/
-int router_update_status_from_smartlist(routerinfo_t *router,
- time_t list_time,
- smartlist_t *running_list)
+int routers_update_status_from_entry(smartlist_t *routers,
+ time_t list_time,
+ const char *s,
+ int rr_format)
{
- int n_names, i, running, approved;
- const char *name;
-#if 1
- char *cp;
- size_t n;
- n = 0;
- for (i=0; inickname, cp);
- tor_free(cp);
-#endif
+ int is_running = 1;
+ int is_verified = 0;
+ int hex_digest_set = 0;
+ char nickname[MAX_NICKNAME_LEN+1];
+ char hexdigest[HEX_DIGEST_LEN+1];
+ char digest[DIGEST_LEN];
+ const char *cp, *end;
- running = approved = 0;
- n_names = smartlist_len(running_list);
- for (i=0; istatus_set_at < list_time) {
- router->status_set_at = list_time;
- router->is_running = 1;
- }
- router->is_verified = (name[0] != '$');
- return 1;
- }
- } else { /* *name == '!' */
- name++;
- if (router_nickname_matches(router, name)) {
- if (router->status_set_at < list_time) {
- router->status_set_at = list_time;
- router->is_running = 0;
- }
- router->is_verified = (name[0] != '$');
- return 1;
- }
+ /* First, parse the entry. */
+ cp = s;
+ if (*cp == '!') {
+ is_running = 0;
+ ++cp;
+ }
+
+ if (*cp != '$') {
+ /* It starts with a non-dollar character; that's a nickname. The nickname
+ * entry will either extend to a NUL (old running-routers format) or to an
+ * equals sign (new router-status format). */
+ is_verified = 1;
+ end = strchr(cp, '=');
+ if (!end)
+ end = strchr(cp,'\0');
+ tor_assert(end);
+ /* 'end' now points on character beyond the end of the nickname */
+ if (end == cp || end-cp > MAX_NICKNAME_LEN) {
+ log_fn(LOG_WARN, "Bad nickname length (%d) in router status entry (%s)",
+ end-cp, s);
+ return -1;
+ }
+ memcpy(nickname, cp, end-cp);
+ nickname[end-cp]='\0';
+ if (!is_legal_nickname(nickname)) {
+ log_fn(LOG_WARN, "Bad nickname (%s) in router status entry (%s)",
+ nickname, s);
+ return -1;
+ }
+ cp = end;
+ if (*cp == '=')
+ ++cp;
+ }
+ /* 'end' now points to the start of a hex digest, or EOS. */
+
+ /* Parse the hexdigest portion of the status. */
+ if (*cp == '$') {
+ hex_digest_set = 1;
+ ++cp;
+ if (strlen(cp) != HEX_DIGEST_LEN) {
+ log_fn(LOG_WARN, "Bad length (%d) on digest in router status entry (%s)",
+ strlen(cp), s);
+ return -1;
+ }
+ strcpy(hexdigest, cp);
+ if (base16_decode(digest, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0) {
+ log_fn(LOG_WARN, "Invalid digest in router status entry (%s)", s);
+ return -1;
}
}
+
+ /* Make sure that the entry was in the right format. */
+ if (rr_format) {
+ if (is_verified == hex_digest_set) {
+ log_fn(LOG_WARN, "Invalid syntax for running-routers member (%s)", s);
+ return -1;
+ }
+ } else {
+ if (!hex_digest_set) {
+ log_fn(LOG_WARN, "Invalid syntax for router-status member (%s)", s);
+ return -1;
+ }
+ }
+
+ /* Okay, we're done parsing. For all routers that match, update their status.
+ */
+ SMARTLIST_FOREACH(routers, routerinfo_t *, r,
+ {
+ int nickname_matches = is_verified && !strcasecmp(r->nickname, nickname);
+ int digest_matches = !memcmp(digest, r->identity_digest, DIGEST_LEN);
+ if (nickname_matches && (digest_matches||rr_format))
+ r->is_verified = 1;
+ else if (digest_matches)
+ r->is_verified = 0;
+ if (digest_matches || (nickname_matches&&rr_format))
+ if (r->status_set_at < list_time) {
+ r->is_running = is_running;
+ r->status_set_at = time(NULL);
+ }
+ });
+
+ return 0;
+}
+
+int router_update_status_from_smartlist(routerinfo_t *router,
+ time_t list_time,
+ smartlist_t *running_list,
+ int rr_format)
+{
+ smartlist_t *rl;
+ rl = smartlist_create();
+ smartlist_add(rl,router);
+ SMARTLIST_FOREACH(running_list, const char *, cp,
+ routers_update_status_from_entry(rl,list_time,cp,rr_format));
+
+ smartlist_free(rl);
return 0;
}
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 476c8af989..6d3ad66506 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -32,6 +32,7 @@ typedef enum {
K_ROUTER_SIGNATURE,
K_PUBLISHED,
K_RUNNING_ROUTERS,
+ K_ROUTER_STATUS,
K_PLATFORM,
K_OPT,
K_BANDWIDTH,
@@ -106,6 +107,7 @@ static struct {
{ "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY,RTR_ONLY },
{ "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ,RTR_ONLY },
{ "running-routers", K_RUNNING_ROUTERS, ARGS, NO_OBJ, DIR_ONLY },
+ { "router-status", K_ROUTER_STATUS, ARGS, NO_OBJ, DIR_ONLY },
{ "ports", K_PORTS, ARGS, NO_OBJ, RTR_ONLY },
{ "bandwidth", K_BANDWIDTH, ARGS, NO_OBJ, RTR_ONLY },
{ "platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ, RTR_ONLY },
@@ -393,9 +395,13 @@ router_parse_routerlist_from_directory(const char *str,
}
versions = tok->n_args ? tor_strdup(tok->args[0]) : tor_strdup("");
- if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) {
- log_fn(LOG_WARN, "Missing running-routers line from directory.");
- goto err;
+ /* Prefer router-status, then running-routers. */
+ if (!(tok = find_first_by_keyword(tokens, K_ROUTER_STATUS))) {
+ if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) {
+ log_fn(LOG_WARN,
+ "Missing running-routers/router-status line from directory.");
+ goto err;
+ }
}
good_nickname_list = smartlist_create();
@@ -408,11 +414,28 @@ router_parse_routerlist_from_directory(const char *str,
* router. */
str = end;
if (router_parse_list_from_string(&str, &new_dir,
- good_nickname_list, published_on)) {
+ good_nickname_list,
+ tok->tp==K_RUNNING_ROUTERS,
+ published_on)) {
log_fn(LOG_WARN, "Error reading routers from directory");
goto err;
}
+ /* Determine if my routerinfo is considered verified. */
+ {
+ static int have_warned_about_unverified_status = 0;
+ routerinfo_t *me = router_get_my_routerinfo();
+ if(me) {
+ if(router_update_status_from_smartlist(me, published_on,
+ good_nickname_list,
+ tok->tp==K_RUNNING_ROUTERS)==1 &&
+ me->is_verified == 0 && !have_warned_about_unverified_status) {
+ log_fn(LOG_WARN,"Dirserver %s lists your server as unverified. Please consider sending your identity fingerprint to the tor-ops.", dirnickname);
+ have_warned_about_unverified_status = 1;
+ }
+ }
+ }
+
new_dir->software_versions = versions; versions = NULL;
new_dir->published_on = published_on;
@@ -420,20 +443,6 @@ router_parse_routerlist_from_directory(const char *str,
smartlist_free(tokens);
tokens = NULL;
- /* Determine if my routerinfo is considered verified. */
- {
- static int have_warned_about_unverified_status = 0;
- routerinfo_t *me = router_get_my_routerinfo();
- if(me) {
- if(router_update_status_from_smartlist(me, published_on,
- good_nickname_list)==1 &&
- me->is_verified == 0 && !have_warned_about_unverified_status) {
- log_fn(LOG_WARN,"Dirserver %s lists your server as unverified. Please consider sending your identity fingerprint to the tor-ops.", dirnickname);
- have_warned_about_unverified_status = 1;
- }
- }
- }
-
if (*dest)
routerlist_free(*dest);
*dest = new_dir;
@@ -497,14 +506,18 @@ router_parse_runningrouters(const char *str)
goto err;
}
- if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) {
- log_fn(LOG_WARN, "Missing running-routers line from directory.");
- goto err;
+ if (!(tok = find_first_by_keyword(tokens, K_ROUTER_STATUS))) {
+ if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) {
+ log_fn(LOG_WARN,
+ "Missing running-routers/router-status line from directory.");
+ goto err;
+ }
}
new_list = tor_malloc_zero(sizeof(running_routers_t));
new_list->published_on = published_on;
new_list->running_routers = smartlist_create();
+ new_list->is_running_routers_format = (tok->tp == K_RUNNING_ROUTERS);
for (i=0;in_args;++i) {
smartlist_add(new_list->running_routers, tok->args[i]);
}
@@ -661,7 +674,7 @@ static int check_directory_signature(const char *digest,
int
router_parse_list_from_string(const char **s, routerlist_t **dest,
smartlist_t *good_nickname_list,
- time_t published_on)
+ int rr_format, time_t published_on)
{
routerinfo_t *router;
smartlist_t *routers;
@@ -692,10 +705,7 @@ router_parse_list_from_string(const char **s, routerlist_t **dest,
continue;
}
- if (good_nickname_list) {
- router_update_status_from_smartlist(router, published_on,
- good_nickname_list);
- } else {
+ if (!good_nickname_list) {
router->is_running = 1; /* start out assuming all dirservers are up */
router->is_verified = 1;
router->status_set_at = time(NULL);
@@ -704,6 +714,12 @@ router_parse_list_from_string(const char **s, routerlist_t **dest,
log_fn(LOG_DEBUG,"just added router #%d.",smartlist_len(routers));
}
+ if (good_nickname_list) {
+ SMARTLIST_FOREACH(good_nickname_list, const char *, cp,
+ routers_update_status_from_entry(routers, published_on,
+ cp, rr_format));
+ }
+
if (*dest)
routerlist_free(*dest);
*dest = tor_malloc_zero(sizeof(routerlist_t));