From b90b2bb848cbbfbc7babda6498e014b687704c29 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 27 Oct 2004 00:48:51 +0000 Subject: [PATCH] Add and document router-status line using new unified liveness/verifiedness format; continue to generate running-routers; continue to parse running-routers when no router-status line is found svn:r2592 --- doc/tor-spec.txt | 30 ++++++- src/or/dirserv.c | 151 +++++++++++++++++++---------------- src/or/or.h | 9 ++- src/or/routerlist.c | 185 +++++++++++++++++++++++++++++-------------- src/or/routerparse.c | 68 ++++++++++------ 5 files changed, 283 insertions(+), 160 deletions(-) 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: *
  1. nickname -- router is running and verified. + * (running-routers format) + *
  2. !nickname -- router is not-running and verified. + * (running-routers format) + *
  3. nickname=$hexdigest -- router is running and + * verified. (router-status format) + * (router-status format) + *
  4. !nickname=$hexdigest -- router is running and + * verified. (router-status format) *
  5. !nickname -- router is not-running and verified. *
  6. $hexdigest -- router is running and unverified. *
  7. !$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));