From 7bb202fd1944d49f294420bbf7b874a5ccbf0f34 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 19 Oct 2007 18:56:24 +0000 Subject: [PATCH] r15965@catbus: nickm | 2007-10-19 13:32:11 -0400 Client-side implementation for proposal 122. svn:r12051 --- ChangeLog | 3 ++ doc/TODO | 4 +-- doc/spec/proposals/122-unnamed-flag.txt | 2 +- src/or/networkstatus.c | 43 ++++++++++++++++++++++--- src/or/or.h | 8 +++++ src/or/routerlist.c | 2 ++ src/or/routerparse.c | 41 ++++++++++++++++++++--- 7 files changed, 91 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 36b52c649e..472396bbc7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,9 @@ Changes in version 0.2.0.9-alpha - 2007-10-?? that it shouldn't be considered to exist at all anymore. Now we clear all the flags for routers that fall out of the networkstatus consensus. Fixes bug 529. + - If the consensus list a router as "Unnamed", the name is assigned + to a different router: do not identify the router by that name. + (Partially implements proposal 122.) o Minor features (v3 directory protocol): - Allow tor-gencert to generate a new certificate without replacing the diff --git a/doc/TODO b/doc/TODO index 4102edec46..54ce19ea16 100644 --- a/doc/TODO +++ b/doc/TODO @@ -93,11 +93,11 @@ Things we'd like to do in 0.2.0.x: - 105: Version negotiation for the Tor protocol . 111: Prioritize local traffic over relayed. - Merge into tor-spec.txt. - - 122: Network status entries need an Unnamed flag + . 122: Network status entries need an Unnamed flag - Merge into dir-spec.txt - Implement voting side - Implement consensus side - - Implement client side + o Implement client side - Refactoring: . Make cells get buffered on circuit, not on the or_conn. diff --git a/doc/spec/proposals/122-unnamed-flag.txt b/doc/spec/proposals/122-unnamed-flag.txt index 8b169b6535..63e35e712a 100644 --- a/doc/spec/proposals/122-unnamed-flag.txt +++ b/doc/spec/proposals/122-unnamed-flag.txt @@ -4,7 +4,7 @@ Version: $Revision$ Last-Modified: $Date$ Author: Roger Dingledine Created: 04-Oct-2007 -Status: Open +Status: Accepted 1. Overview: diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 154d826769..aa70c2703b 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -25,8 +25,11 @@ static smartlist_t *networkstatus_v2_list = NULL; * time we called download_status_map_update_from_v2_networkstatus() */ static int networkstatus_v2_list_has_changed = 0; -/** Map from lowercase nickname to digest of named server, if any. */ +/** Map from lowercase nickname to identity digest of named server, if any. */ static strmap_t *named_server_map = NULL; +/** Map from lowercase nickname to (void*)1 for all names that are listed + * as unnamed for some server in the consensus. */ +static strmap_t *unnamed_server_map = NULL; /** Most recently received and validated v3 consensus network status. */ static networkstatus_vote_t *current_consensus = NULL; @@ -579,6 +582,7 @@ router_get_consensus_status_by_nickname(const char *nickname, routerstatus_t *best=NULL; smartlist_t *matches=NULL; const char *named_id=NULL; + int any_unnamed=0; if (!current_consensus || !nickname) return NULL; @@ -597,6 +601,10 @@ router_get_consensus_status_by_nickname(const char *nickname, if (named_id) return networkstatus_vote_find_entry(current_consensus, named_id); + if (unnamed_server_map && + strmap_get_lc(named_server_map, nickname)) + return NULL; /* XXXX020 should we warn? */ + /*XXXX020 is this behavior really what we want? */ matches = smartlist_create(); SMARTLIST_FOREACH(current_consensus->routerstatus_list, @@ -604,16 +612,22 @@ router_get_consensus_status_by_nickname(const char *nickname, { if (!strcasecmp(lrs->nickname, nickname)) { if (lrs->is_named) { + /* XXXX020 this should never happen. */ smartlist_free(matches); return lrs; } else { + if (lrs->is_unnamed) + smartlist_free(matches); /* nor should this. */ smartlist_add(matches, lrs); best = lrs; } } }); - if (smartlist_len(matches)>1 && warn_if_unnamed) { + if (any_unnamed) { + /* XXXX020 should we warn? */ + return NULL; + } else if (smartlist_len(matches)>1 && warn_if_unnamed) { int any_unwarned=0; SMARTLIST_FOREACH(matches, routerstatus_t *, lrs, { @@ -654,6 +668,13 @@ networkstatus_get_router_digest_by_nickname(const char *nickname) return strmap_get_lc(named_server_map, nickname); } +/** DOCDOC */ +int +networkstatus_nickname_is_unnamed(const char *nickname) +{ + return strmap_get_lc(named_server_map, nickname) != NULL; +} + /** How frequently do directory authorities re-download fresh networkstatus * documents? */ #define AUTHORITY_NS_CACHE_INTERVAL (5*60) @@ -1061,6 +1082,11 @@ routers_update_all_from_networkstatus(time_t now) log_info(LD_GENERAL, "The latest consensus does not list us." "Are you misconfigured?"); have_warned_about_invalid_status = 1; + } else if (rs->is_unnamed) { + /* XXXX020 this isn't a useful warning. */ + log_info(LD_GENERAL, "The directory have assigned the nickname " + "you're using to a different identity."); + have_warned_about_invalid_status = 1; } else if (!rs->is_named) { /*XXXX020 this isn't a correct warning. */ log_info(LD_GENERAL, "The directory authorities do not recognize " @@ -1150,11 +1176,17 @@ routerstatus_list_update_named_server_map(void) if (named_server_map) strmap_free(named_server_map, _tor_free); named_server_map = strmap_new(); + if (unnamed_server_map) + strmap_free(unnamed_server_map, NULL); + named_server_map = strmap_new(); SMARTLIST_FOREACH(current_consensus->routerstatus_list, routerstatus_t *, rs, { if (rs->is_named) { - strmap_set(named_server_map, rs->nickname, - tor_memdup(rs->identity_digest, DIGEST_LEN)); + strmap_set_lc(named_server_map, rs->nickname, + tor_memdup(rs->identity_digest, DIGEST_LEN)); + } + if (rs->is_unnamed) { + strmap_set_lc(unnamed_server_map, rs->nickname, (void*)1); } }); } @@ -1348,5 +1380,8 @@ networkstatus_free_all(void) if (named_server_map) { strmap_free(named_server_map, _tor_free); } + if (unnamed_server_map) { + strmap_free(unnamed_server_map, NULL); + } } diff --git a/src/or/or.h b/src/or/or.h index bbf18bdb5b..2c9f436f80 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1221,6 +1221,8 @@ typedef struct routerstatus_t { unsigned int is_fast:1; /**< True iff this router has good bandwidth. */ unsigned int is_running:1; /**< True iff this router is up. */ unsigned int is_named:1; /**< True iff "nickname" belongs to this router. */ + unsigned int is_unnamed:1; /**< True iff "nickname" belongs to another + * router. */ unsigned int is_valid:1; /**< True iff this router is validated. */ unsigned int is_v2_dir:1; /**< True iff this router can serve directory * information with v2 of the directory @@ -1349,6 +1351,11 @@ typedef struct networkstatus_vote_t { time_t valid_until; /**< Time after which this vote or consensus should not * be used. */ + /** Consensus only: what method was used to produce this consensus? */ + int consensus_method; + /** Vote only: what methods is this voter willing to use? */ + smartlist_t *supported_methods; + /** How long does this vote/consensus claim that authorities take to * distribute their votes to one another? */ int vote_seconds; @@ -3091,6 +3098,7 @@ routerstatus_t *router_get_consensus_status_by_descriptor_digest( 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); int should_delay_dir_fetches(or_options_t *options); void update_networkstatus_downloads(time_t now); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index b2a12d28d6..6526d5f550 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -1701,6 +1701,8 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed) if ((named_digest = networkstatus_get_router_digest_by_nickname(nickname))) { return rimap_get(routerlist->identity_map, named_digest); } + if (networkstatus_nickname_is_unnamed(nickname)) + return NULL; /* If we reach this point, there's no canonical value for the nickname. */ diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 73200dcfe4..119345b1dd 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -1534,12 +1534,17 @@ find_start_of_next_routerstatus(const char *s) * router status. Return NULL and advance *s on error. * * If vote and vote_rs are provided, don't allocate a fresh - * routerstatus but use vote_rs instead + * routerstatus but use vote_rs instead. + * + * If consensus_method is nonzero, this routerstatus is part of a + * consensus, and we should parse it according to the method used to + * make that consensus. **/ static routerstatus_t * routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens, networkstatus_vote_t *vote, - vote_routerstatus_t *vote_rs) + vote_routerstatus_t *vote_rs, + int consensus_method) { const char *eos; routerstatus_t *rs = NULL; @@ -1645,6 +1650,11 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens, rs->is_bad_directory = 1; else if (!strcmp(tok->args[i], "Authority")) rs->is_authority = 1; + else if (!strcmp(tok->args[i], "Unnamed") && + consensus_method >= 2) { + /* Unnamed is computed right by consensus method 2 and later. */ + rs->is_unnamed = 1; + } } } if ((tok = find_first_by_keyword(tokens, K_V))) { @@ -1830,7 +1840,7 @@ networkstatus_v2_parse_from_string(const char *s) smartlist_clear(tokens); while (!strcmpstart(s, "r ")) { routerstatus_t *rs; - if ((rs = routerstatus_parse_entry_from_string(&s, tokens, NULL, NULL))) + if ((rs = routerstatus_parse_entry_from_string(&s, tokens, NULL, NULL, 0))) smartlist_add(ns->entries, rs); } smartlist_sort(ns->entries, _compare_routerstatus_entries); @@ -1936,6 +1946,26 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, tok = find_first_by_keyword(tokens, K_PUBLISHED); if (parse_iso_time(tok->args[0], &ns->published)) goto err; + + ns->supported_methods = smartlist_create(); + tok = find_first_by_keyword(tokens, K_CONSENSUS_METHODS); + if (tok) { + for (i=0; i < tok->n_args; ++i) + smartlist_add(ns->supported_methods, tok->args[i]); + tok->n_args = 0; /* Prevent double free. */ + } else { + smartlist_add(ns->supported_methods, tor_strdup("1")); + } + } else { + tok = find_first_by_keyword(tokens, K_CONSENSUS_METHOD); + if (tok) { + ns->consensus_method = (int)tor_parse_long(tok->args[0], 10, 1, INT_MAX, + &ok, NULL); + if (!ok) + goto err; + } else { + ns->consensus_method = 1; + } } tok = find_first_by_keyword(tokens, K_VALID_AFTER); @@ -2086,7 +2116,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, while (!strcmpstart(s, "r ")) { if (is_vote) { vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t)); - if (routerstatus_parse_entry_from_string(&s, tokens, ns, rs)) + if (routerstatus_parse_entry_from_string(&s, tokens, ns, rs, 0)) smartlist_add(ns->routerstatus_list, rs); else { tor_free(rs->version); @@ -2094,7 +2124,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } } else { routerstatus_t *rs; - if ((rs =routerstatus_parse_entry_from_string(&s, tokens, NULL, NULL))) + if ((rs = routerstatus_parse_entry_from_string(&s, tokens, NULL, NULL, + ns->consensus_method))) smartlist_add(ns->routerstatus_list, rs); } }