diff options
author | Nick Mathewson <nickm@torproject.org> | 2004-06-25 00:29:31 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2004-06-25 00:29:31 +0000 |
commit | 76f769deb9dab9e68cb2dd49c6a5a852f2ca2cd7 (patch) | |
tree | 8b833310f12954d9ebf61d267c61f87c7481afbf | |
parent | e9b882554e44641011e3ee5be26e7cc200f3dd71 (diff) | |
download | tor-76f769deb9dab9e68cb2dd49c6a5a852f2ca2cd7.tar tor-76f769deb9dab9e68cb2dd49c6a5a852f2ca2cd7.tar.gz |
Remaining 008pre1 items done; deferred where more design is needed.
More docs and (way more!) testing needed.
Done:
- Authdirservers down directories from others.
- Generate and use running-routers lists
- Cache directories; store across reboots.
- Refactor directory parsing a bit; note potential trouble spots.
svn:r1985
-rw-r--r-- | doc/TODO | 19 | ||||
-rw-r--r-- | src/or/directory.c | 12 | ||||
-rw-r--r-- | src/or/dirserv.c | 80 | ||||
-rw-r--r-- | src/or/main.c | 7 | ||||
-rw-r--r-- | src/or/or.h | 14 | ||||
-rw-r--r-- | src/or/rephist.c | 39 | ||||
-rw-r--r-- | src/or/router.c | 6 | ||||
-rw-r--r-- | src/or/routerlist.c | 48 | ||||
-rw-r--r-- | src/or/routerparse.c | 163 |
9 files changed, 335 insertions, 53 deletions
@@ -19,27 +19,30 @@ For scalability: For dtor: NICK pre1: - . make all ORs serve the directory too. + o make all ORs serve the directory too. o "AuthoritativeDir 1" for dirservers o non-authorative servers with dirport publish opt dircacheport o make clients read that and use it. o make clients able to read a normal dirport from non-trusted OR too o make ORs parse-and-keep-and-serve the directory they pull down - - authoritativedirservers should pull down directories from + o authoritativedirservers should pull down directories from other authdirservers, to merge descriptors. - - Have clients and dirservers preserve reputation info over + D Have clients and dirservers preserve reputation info over reboots. - - allow dirservers to serve running-router list separately. + [Deferred until we know what reputation info we actually want to + maintain. Our current algorithm Couldn't Possibly Work.] + . allow dirservers to serve running-router list separately. o "get /running-routers" will fetch just this. - - actually make the clients use this sometimes. - - distinguish directory-is-dirty from runninglist-is-dirty + o actually make the clients use this sometimes. + o distinguish directory-is-dirty from runninglist-is-dirty - ORs keep this too, and serve it - - tor remembers descriptor-lists across reboots. + - Design: do we need running and non-running lists? + o tor remembers descriptor-lists across reboots. . Packages define datadir as /var/lib/tor/. If no datadir is defined, then choose, make, and secure ~/.tor as datadir. o Adjust tor o Change torrc.sample - - Change packages + D Change packages (not till 0.0.8 packages!) - Look in ~/.torrc if no */etc/torrc is found? o Contact info, pgp fingerprint, comments in router desc. o Add a ContactInfo line to torrc, which gets published in diff --git a/src/or/directory.c b/src/or/directory.c index 3f8b3a94c..61c98df4c 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -352,6 +352,8 @@ int connection_dir_process_inbuf(connection_t *conn) { } if(conn->purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) { + running_routers_t *rrs; + routerlist_t *rl; /* just update our list of running routers, if this list is new info */ log_fn(LOG_INFO,"Received running-routers list (size %d):\n%s", body_len, body); if(status_code != 200) { @@ -361,7 +363,15 @@ int connection_dir_process_inbuf(connection_t *conn) { connection_mark_for_close(conn); return -1; } - /* XXX008 hand 'body' to something that parses a running-routers list. */ + if (!(rrs = router_parse_runningrouters(body))) { + log_fn(LOG_WARN, "Can't parse runningrouters list"); + free(body); free(headers); + connection_mark_for_close(conn); + return -1; + } + router_get_routerlist(&rl); + routerlist_update_from_runningrouters(rl,rrs); + running_routers_free(rrs); } if(conn->purpose == DIR_PURPOSE_UPLOAD_DIR) { diff --git a/src/or/dirserv.c b/src/or/dirserv.c index e8e727892..6e09ee1ec 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -16,6 +16,7 @@ extern or_options_t options; /**< command-line and config-file options */ /** Do we need to regenerate the directory when someone asks for it? */ static int the_directory_is_dirty = 1; +static int runningrouters_is_dirty = 1; static int list_running_servers(char **nicknames_out); static void directory_remove_unrecognized(void); @@ -406,13 +407,14 @@ void directory_set_dirty() { the_directory_is_dirty = 1; + runningrouters_is_dirty = 1; } -/** Load all descriptors from an earlier directory stored in the string +/** Load all descriptors from a directory stored in the string * <b>dir</b>. */ int -dirserv_init_from_directory_string(const char *dir) +dirserv_load_from_directory_string(const char *dir) { const char *cp = dir; while(1) { @@ -525,7 +527,9 @@ dirserv_dump_directory_to_string(char *s, unsigned int maxlen, "signed-directory\n" "published %s\n" "recommended-software %s\n" - "running-routers %s\n\n", published, options.RecommendedVersions, cp); + "running-routers %s\n\n", + published, options.RecommendedVersions, cp); + free(cp); i = strlen(s); cp = s+i; @@ -563,7 +567,7 @@ dirserv_dump_directory_to_string(char *s, unsigned int maxlen, log_fn(LOG_WARN,"couldn't base64-encode signature"); return -1; } - + if (strlcat(s, "-----END SIGNATURE-----\n", maxlen) >= maxlen) goto truncated; @@ -583,6 +587,7 @@ static int cached_directory_len = -1; void dirserv_set_cached_directory(const char *directory, time_t when) { time_t now; + char filename[512]; if (!options.AuthoritativeDir) return; now = time(NULL); @@ -591,6 +596,11 @@ void dirserv_set_cached_directory(const char *directory, time_t when) tor_free(cached_directory); cached_directory = tor_strdup(directory); cached_directory_len = strlen(cached_directory); + cached_directory_published = when; + sprintf(filename,"%s/cached-directory", options.DataDirectory); + if(write_str_to_file(filename,cached_directory) < 0) { + log_fn(LOG_WARN, "Couldn't write cached directory to disk. Ignoring."); + } } } @@ -644,12 +654,70 @@ size_t dirserv_get_directory(const char **directory) return the_directory_len; } +static char *runningrouters_string=NULL; +static size_t runningrouters_len=0; + +/** Replace the current running-routers list with a newly generated one. */ +static int generate_runningrouters(crypto_pk_env_t *private_key) +{ + char *s, *cp; + char digest[DIGEST_LEN]; + char signature[PK_BYTES]; + int i, len; + char published[33]; + time_t published_on; + + len = 1024+MAX_NICKNAME_LEN*smartlist_len(descriptor_list); + s = tor_malloc_zero(len); + if (list_running_servers(&cp)) + return -1; + published_on = time(NULL); + strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on)); + sprintf(s, "network-status\n" + "published %s\n" + "running-routers %s\n" + "directory-signature %s\n" + "-----BEGIN SIGNATURE-----\n", + published, cp, options.Nickname); + free(cp); + if (router_get_runningrouters_hash(s,digest)) { + log_fn(LOG_WARN,"couldn't compute digest"); + return -1; + } + if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) { + log_fn(LOG_WARN,"couldn't sign digest"); + return -1; + } + + i = strlen(s); + cp = s+i; + if (base64_encode(cp, len-i, signature, 128) < 0) { + log_fn(LOG_WARN,"couldn't base64-encode signature"); + return -1; + } + if (strlcat(s, "-----END SIGNATURE-----\n", len) >= len) { + return -1; + } + + tor_free(runningrouters_string); + runningrouters_string = s; + runningrouters_len = strlen(s); + runningrouters_is_dirty = 0; + return 0; +} + /** Set *<b>rr</b> to the most recently generated encoded signed * running-routers list, generating a new one as necessary. */ size_t dirserv_get_runningrouters(const char **rr) { - /* XXX008 fill in this function */ - return 0; + if (runningrouters_is_dirty) { + if(generate_runningrouters(get_identity_key())) { + log_fn(LOG_ERR, "Couldn't generate running-routers list?"); + return -1; + } + } + *rr = runningrouters_string; + return runningrouters_len; } /* diff --git a/src/or/main.c b/src/or/main.c index 983e9a34d..99e83f37c 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -451,9 +451,8 @@ static void run_scheduled_events(time_t now) { router_rebuild_descriptor(); router_upload_dir_desc_to_dirservers(); } - if(!options.DirPort) { - /* NOTE directory servers do not currently fetch directories. - * Hope this doesn't bite us later. */ + if(!options.DirPort || !options.AuthoritativeDir) { + /* XXXX should directories do this next part too? */ routerlist_remove_old_routers(); /* purge obsolete entries */ directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 0); } else { @@ -461,6 +460,8 @@ static void run_scheduled_events(time_t now) { dirserv_remove_old_servers(); /* dirservers try to reconnect too, in case connections have failed */ router_retry_connections(); + /* fetch another directory, in case it knows something we don't */ + directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 0); } /* Force an upload of our descriptors every DirFetchPostPeriod seconds. */ rend_services_upload(1); diff --git a/src/or/or.h b/src/or/or.h index e6a014dc5..496109a31 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -595,10 +595,17 @@ typedef struct { * published? */ time_t published_on; + time_t running_routers_updated_on; /** Which router is claimed to have signed it? */ char *signing_router; } routerlist_t; +/* DOCDOC */ +typedef struct running_routers_t { + time_t published_on; + smartlist_t *running_routers; +} running_routers_t; + /** Holds accounting information for a single step in the layered encryption * performed by a circuit. Used only at the client edge of a circuit. */ struct crypt_path_t { @@ -1130,7 +1137,7 @@ int dirserv_parse_fingerprint_file(const char *fname); int dirserv_router_fingerprint_is_known(const routerinfo_t *router); void dirserv_free_fingerprint_list(); int dirserv_add_descriptor(const char **desc); -int dirserv_init_from_directory_string(const char *dir); +int dirserv_load_from_directory_string(const char *dir); void dirserv_free_descriptors(); void dirserv_remove_old_servers(void); int dirserv_dump_directory_to_string(char *s, unsigned int maxlen, @@ -1352,11 +1359,15 @@ int router_compare_addr_to_exit_policy(uint32_t addr, uint16_t port, #define ADDR_POLICY_UNKNOWN 1 int router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port); int router_exit_policy_rejects_all(routerinfo_t *router); +void running_routers_free(running_routers_t *rr); +void routerlist_update_from_runningrouters(routerlist_t *list, + running_routers_t *rr); /********************************* routerparse.c ************************/ int router_get_router_hash(const char *s, char *digest); int router_get_dir_hash(const char *s, char *digest); +int router_get_runningrouters_hash(const char *s, char *digest); int router_parse_list_from_string(const char **s, routerlist_t **dest, int n_good_nicknames, @@ -1364,6 +1375,7 @@ int router_parse_list_from_string(const char **s, int router_parse_routerlist_from_directory(const char *s, routerlist_t **dest, crypto_pk_env_t *pkey); +running_routers_t *router_parse_runningrouters(const char *str); routerinfo_t *router_parse_entry_from_string(const char *s, const char *end); int router_add_exit_policy_from_string(routerinfo_t *router, const char *s); struct exit_policy_t *router_parse_exit_policy_from_string(const char *s); diff --git a/src/or/rephist.c b/src/or/rephist.c index cd71eabc4..418fc8dd6 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -248,6 +248,45 @@ void rep_hist_dump_stats(time_t now, int severity) } } +#if 0 +void write_rep_history(const char *filename) +{ + FILE *f = NULL; + char *tmpfile; + int completed = 0; + or_history_t *or_history; + link_history_t *link_history; + strmap_iter_t *lhist_it; + strmap_iter_t *orhist_it; + void *or_history_p, *link_history_p; + const char *name1; + + tmpfile = tor_malloc(strlen(filename)+5); + strcpy(tmpfile, filename); + strcat(tmpfile, "_tmp"); + + f = fopen(tmpfile, "w"); + if (!f) goto done; + for (orhist_it = strmap_iter_init(history_map); !strmap_iter_done(orhist_it); + orhist_it = strmap_iter_next(history_map,orhist_it)) { + strmap_iter_get(orhist_it, &name1, &or_history_p); + or_history = (or_history_t*) or_history_p; + fprintf(f, "link %s connected:u%ld failed:%uld uptime:%uld", + name1, or_history->since1, + } + + + done: + if (f) + fclose(f); + if (completed) + replace_file(filename, tmpfile); + else + unlink(tmpfile); + tor_free(tmpfile); +} +#endif + /* Local Variables: mode:c diff --git a/src/or/router.c b/src/or/router.c index dd6dd452c..9d386bbad 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -284,12 +284,14 @@ int init_keys(void) { if(!cp) { log_fn(LOG_INFO,"Cached directory %s not present. Ok.",keydir); } else { - if(dirserv_init_from_directory_string(cp) < 0) { + if(options.AuthoritativeDir && dirserv_load_from_directory_string(cp) < 0){ log_fn(LOG_ERR, "Cached directory %s is corrupt", keydir); free(cp); return -1; } - free(cp); + /* set time to 1 so it will be replaced on first download. + */ + dirserv_set_cached_directory(cp, 1); } /* success */ return 0; diff --git a/src/or/routerlist.c b/src/or/routerlist.c index ed9a06420..8dd51a8ce 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -477,7 +477,11 @@ int router_load_routerlist_from_directory(const char *s, log_fn(LOG_WARN, "Error resolving routerlist"); return -1; } + /* Remember the directory, if we're nonauthoritative.*/ dirserv_set_cached_directory(s, routerlist->published_on); + /* Learn about the descriptors in the directory, if we're authoritative */ + if (options.AuthoritativeDir) + dirserv_load_from_directory_string(s); return 0; } @@ -621,6 +625,50 @@ int router_exit_policy_rejects_all(routerinfo_t *router) { == ADDR_POLICY_REJECTED; } +/* DODCDOC */ +void running_routers_free(running_routers_t *rr) +{ + tor_assert(rr); + if (rr->running_routers) { + SMARTLIST_FOREACH(rr->running_routers, char *, s, tor_free(s)); + smartlist_free(rr->running_routers); + } + tor_free(rr); +} + +/* DOCDOC*/ +void routerlist_update_from_runningrouters(routerlist_t *list, + running_routers_t *rr) +{ + int n_routers, n_names, i, j, running; + routerinfo_t *router; + const char *name; + if (!routerlist) + return; + if (routerlist->published_on >= rr->published_on) + return; + if (routerlist->running_routers_updated_on >= rr->published_on) + return; + + n_routers = smartlist_len(list->routers); + n_names = smartlist_len(rr->running_routers); + for (i=0; i<n_routers; ++i) { + running = 0; + router = smartlist_get(list->routers, i); + for (j=0; j<n_names; ++j) { + name = smartlist_get(rr->running_routers, j); + if (!strcmp(name, router->nickname)) { + running=1; + break; + } + } + router->is_running = 1; /* arma: is this correct? */ + } + routerlist->running_routers_updated_on = rr->published_on; + /* XXXX008 Should there also be a list of which are down, so that we + * don't mark merely unknown routers as down? */ +} + /* Local Variables: mode:c diff --git a/src/or/routerparse.c b/src/or/routerparse.c index efd8f3731..60daf0e33 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -41,6 +41,7 @@ typedef enum { K_PORTS, K_DIRCACHEPORT, K_CONTACT, + K_NETWORK_STATUS, _UNRECOGNIZED, _ERR, _EOF, @@ -88,7 +89,7 @@ typedef enum { typedef enum { ANY = 0, /**< Appears in router descriptor or in directory sections. */ DIR_ONLY, /**< Appears only in directory. */ - RTR_ONLY, /**< Appears only in router descriptor. */ + RTR_ONLY, /**< Appears only in router descriptor or runningrouters */ } where_syntax; /** Table mapping keywords to token value and to argument rules. */ @@ -113,6 +114,7 @@ static struct { { "opt", K_OPT, CONCAT_ARGS, OBJ_OK, ANY }, { "dircacheport", K_DIRCACHEPORT, ARGS, NO_OBJ, RTR_ONLY }, { "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ, ANY }, + { "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ, DIR_ONLY }, { NULL, -1 } }; @@ -128,6 +130,10 @@ static directory_token_t *find_first_by_keyword(smartlist_t *s, static int tokenize_string(const char *start, const char *end, smartlist_t *out, int is_dir); static directory_token_t *get_next_token(const char **s, where_syntax where); +static int check_directory_signature(const char *digest, + directory_token_t *tok, + crypto_pk_env_t *pkey); + /** Set <b>digest</b> to the SHA-1 digest of the hash of the directory in * <b>s</b>. Return 0 on success, nonzero on failure. @@ -147,6 +153,13 @@ int router_get_router_hash(const char *s, char *digest) "router ","router-signature"); } +/** DOCDOC */ +int router_get_runningrouters_hash(const char *s, char *digest) +{ + return router_get_hash_impl(s,digest, + "network-status ","directory-signature"); +} + /** Parse a date of the format "YYYY-MM-DD hh:mm:ss" and store the result into * *<b>t</b>. */ @@ -273,13 +286,12 @@ router_parse_routerlist_from_directory(const char *str, crypto_pk_env_t *pkey) { directory_token_t *tok; - char digest[20]; - char signed_digest[128]; + char digest[DIGEST_LEN]; routerlist_t *new_dir = NULL; char *versions = NULL; - time_t published_on; - char *good_nickname_lst[1024]; int n_good_nicknames = 0; + char *good_nickname_lst[1024]; /* XXXX008 correct this limit. */ + time_t published_on; int i, r; const char *end; smartlist_t *tokens = NULL; @@ -373,6 +385,108 @@ router_parse_routerlist_from_directory(const char *str, (tok->tp != K_DIRECTORY_SIGNATURE)) { log_fn(LOG_WARN,"Expected a single directory signature"); goto err; } + if (check_directory_signature(digest, smartlist_get(tokens,0), pkey)<0) { + goto err; + } + + if (*dest) + routerlist_free(*dest); + *dest = new_dir; + + r = 0; + goto done; + err: + r = -1; + if (new_dir) + routerlist_free(new_dir); + tor_free(versions); + for (i = 0; i < n_good_nicknames; ++i) { + tor_free(good_nickname_lst[i]); + } + done: + if (tokens) { + SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok)); + smartlist_free(tokens); + } + return r; +} + +running_routers_t * +router_parse_runningrouters(const char *str) +{ + char digest[DIGEST_LEN]; + running_routers_t *new_list = NULL; + directory_token_t *tok; + time_t published_on; + int i; + + smartlist_t *tokens = NULL; + + if (router_get_runningrouters_hash(str, digest)) { + log_fn(LOG_WARN, "Unable to compute digest of directory"); + goto err; + } + tokens = smartlist_create(); + if (tokenize_string(str,str+strlen(str),tokens,1)) { + log_fn(LOG_WARN, "Error tokenizing directory"); goto err; + } + if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) { + log_fn(LOG_WARN, "Unrecognized keyword in \"%s\"; can't parse directory.", + tok->args[0]); + goto err; + } + tok = smartlist_get(tokens,0); + if (tok->tp != K_NETWORK_STATUS) { + log_fn(LOG_WARN, "Network-status starts with wrong token"); + goto err; + } + + if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) { + log_fn(LOG_WARN, "Missing published time on directory."); + goto err; + } + tor_assert(tok->n_args == 1); + if (parse_time(tok->args[0], &published_on) < 0) { + goto err; + } + + if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) { + log_fn(LOG_WARN, "Missing running-routers 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(); + for (i=0;i<tok->n_args;++i) { + smartlist_add(new_list->running_routers, tok->args[i]); + } + + if (!(tok = find_first_by_keyword(tokens, K_DIRECTORY_SIGNATURE))) { + log_fn(LOG_WARN, "Missing signature on directory"); + goto err; + } + if (check_directory_signature(digest, tok, NULL)<0) { + goto err; + } + + goto done; + err: + running_routers_free(new_list); + new_list = NULL; + done: + if (tokens) { + SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok)); + smartlist_free(tokens); + } + return new_list; +} + +static int check_directory_signature(const char *digest, + directory_token_t *tok, + crypto_pk_env_t *pkey) +{ + char signed_digest[PK_BYTES]; if (tok->n_args == 1) { routerinfo_t *r = router_get_by_nickname(tok->args[0]); log_fn(LOG_DEBUG, "Got directory signed by %s", tok->args[0]); @@ -383,53 +497,38 @@ router_parse_routerlist_from_directory(const char *str, } else if (!r) { log_fn(LOG_WARN, "Directory was signed by unrecognized server %s", tok->args[0]); - goto err; + return -1; } else if (r && !r->is_trusted_dir) { log_fn(LOG_WARN, "Directory was signed by non-trusted server %s", tok->args[0]); - goto err; + return -1; } + } else if (tok->n_args > 1) { + log_fn(LOG_WARN, "Too many arguments to directory-signature"); + return -1; } if (strcmp(tok->object_type, "SIGNATURE") || tok->object_size != 128) { log_fn(LOG_WARN, "Bad object type or length on directory signature"); - goto err; + return -1; } if (pkey) { if (crypto_pk_public_checksig(pkey, tok->object_body, 128, signed_digest) != 20) { log_fn(LOG_WARN, "Error reading directory: invalid signature."); - goto err; + return -1; } log(LOG_DEBUG,"Signed directory hash starts %s", hex_str(signed_digest,4)); - if (memcmp(digest, signed_digest, 20)) { log_fn(LOG_WARN, "Error reading directory: signature does not match."); - goto err; + return -1; } + } else { + /* XXXX008 freak out, unless testing. */ } - - if (*dest) - routerlist_free(*dest); - *dest = new_dir; - - r = 0; - goto done; - err: - r = -1; - if (new_dir) - routerlist_free(new_dir); - tor_free(versions); - for (i = 0; i < n_good_nicknames; ++i) { - tor_free(good_nickname_lst[i]); - } - done: - if (tokens) { - SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok)); - smartlist_free(tokens); - } - return r; + return 0; } + /** Given a string *<b>s</b> containing a concatenated * sequence of router descriptors, parses them and stores the result * in *<b>dest</b>. If good_nickname_lst is provided, then routers whose |