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
This commit is contained in:
Nick Mathewson 2004-06-25 00:29:31 +00:00
parent e9b882554e
commit 76f769deb9
9 changed files with 340 additions and 58 deletions

View file

@ -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

View file

@ -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) {

View file

@ -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;
}
/*

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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,40 +385,9 @@ 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 (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]);
if (r && r->is_trusted_dir) {
pkey = r->identity_pkey;
} else if (!r && pkey) {
/* pkey provided for debugging purposes. */
} else if (!r) {
log_fn(LOG_WARN, "Directory was signed by unrecognized server %s",
tok->args[0]);
goto err;
} else if (r && !r->is_trusted_dir) {
log_fn(LOG_WARN, "Directory was signed by non-trusted server %s",
tok->args[0]);
goto err;
}
}
if (strcmp(tok->object_type, "SIGNATURE") || tok->object_size != 128) {
log_fn(LOG_WARN, "Bad object type or length on directory signature");
if (check_directory_signature(digest, smartlist_get(tokens,0), pkey)<0) {
goto err;
}
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;
}
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;
}
}
if (*dest)
routerlist_free(*dest);
@ -430,6 +411,124 @@ router_parse_routerlist_from_directory(const char *str,
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]);
if (r && r->is_trusted_dir) {
pkey = r->identity_pkey;
} else if (!r && pkey) {
/* pkey provided for debugging purposes. */
} else if (!r) {
log_fn(LOG_WARN, "Directory was signed by unrecognized server %s",
tok->args[0]);
return -1;
} else if (r && !r->is_trusted_dir) {
log_fn(LOG_WARN, "Directory was signed by non-trusted server %s",
tok->args[0]);
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");
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.");
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.");
return -1;
}
} else {
/* XXXX008 freak out, unless testing. */
}
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