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
This commit is contained in:
Nick Mathewson 2004-10-27 00:48:51 +00:00
parent 26f3cb8652
commit b90b2bb848
5 changed files with 283 additions and 160 deletions

View File

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

View File

@ -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 *<b>nicknames_out</b> 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 <b>desc</b>,
* for use in a running-routers line (if <b>rr_format</b> is true), or in a
* router-status line (if <b>rr_format</b> is false. The server is listed
* as running iff <b>is_live</b> 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 *<b>running_routers_out</b> and *<b>router_status_out</b>
* 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; i<n_conns; ++i) {
conn = connection_array[i];
if (conn->type != 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; i<smartlist_len(nicknames_up); ++i) {
if (i)
strcat(cp, " ");
strcat(cp, (char*)smartlist_get(nicknames_up,i)); /* can't overflow */
while (*cp)
++cp;
}
for (i = 0; i<smartlist_len(nicknames_down); ++i) {
strcat(cp, " !");
strcat(cp, (char*)smartlist_get(nicknames_down,i)); /* can't overflow */
while (*cp)
++cp;
}
SMARTLIST_FOREACH(nicknames_up, char *, victim, tor_free(victim));
SMARTLIST_FOREACH(nicknames_down, char *, victim, tor_free(victim));
smartlist_free(nicknames_up);
smartlist_free(nicknames_down);
return 0;
}
@ -557,7 +567,8 @@ int
dirserv_dump_directory_to_string(char *s, size_t maxlen,
crypto_pk_env_t *private_key)
{
char *cp, *eos;
char *eos, *cp;
char *running_routers, *router_status;
char *identity_pkey; /* Identity key, DER64-encoded. */
char *recommended_versions;
char digest[20];
@ -570,7 +581,7 @@ dirserv_dump_directory_to_string(char *s, size_t maxlen,
if (!descriptor_list)
descriptor_list = smartlist_create();
if (list_running_servers(&cp))
if (list_server_status(&running_routers, &router_status))
return -1;
/* ASN.1-encode the public key. This is a temporary measure; once
@ -612,10 +623,13 @@ dirserv_dump_directory_to_string(char *s, size_t maxlen,
"published %s\n"
"recommended-software %s\n"
"running-routers %s\n"
"opt router-status %s\n"
"opt dir-signing-key %s\n\n",
published, recommended_versions, cp, identity_pkey);
published, recommended_versions, running_routers, router_status,
identity_pkey);
tor_free(cp);
tor_free(running_routers);
tor_free(router_status);
tor_free(identity_pkey);
i = strlen(s);
cp = s+i;
@ -788,6 +802,7 @@ static size_t runningrouters_len=0;
static int generate_runningrouters(crypto_pk_env_t *private_key)
{
char *s, *cp;
char *router_status;
char digest[DIGEST_LEN];
char signature[PK_BYTES];
int i;
@ -798,7 +813,7 @@ static int generate_runningrouters(crypto_pk_env_t *private_key)
len = 1024+(MAX_HEX_NICKNAME_LEN+2)*smartlist_len(descriptor_list);
s = tor_malloc_zero(len);
if (list_running_servers(&cp))
if (list_server_status(NULL, &router_status))
return -1;
/* ASN.1-encode the public key. This is a temporary measure; once
* everyone is running 0.0.9pre3 or later, we can shift to using a
@ -822,12 +837,12 @@ static int generate_runningrouters(crypto_pk_env_t *private_key)
format_iso_time(published, published_on);
sprintf(s, "network-status\n"
"published %s\n"
"running-routers %s\n"
"router-status %s\n"
"opt dir-signing-key %s\n"
"directory-signature %s\n"
"-----BEGIN SIGNATURE-----\n",
published, cp, identity_pkey, options.Nickname);
tor_free(cp);
published, router_status, identity_pkey, options.Nickname);
tor_free(router_status);
tor_free(identity_pkey);
if (router_get_runningrouters_hash(s,digest)) {
log_fn(LOG_WARN,"couldn't compute digest");

View File

@ -621,6 +621,7 @@ typedef struct running_routers_t {
time_t published_on; /**< When was the list marked as published? */
/** Which ORs are on the list? Entries may be prefixed with ! and $. */
smartlist_t *running_routers;
int is_running_routers_format; /**< Are we using the old entry format? */
} running_routers_t;
/** Holds accounting information for a single step in the layered encryption
@ -1469,9 +1470,14 @@ 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);
int routers_update_status_from_entry(smartlist_t *routers,
time_t list_time,
const char *s,
int rr_format);
int router_update_status_from_smartlist(routerinfo_t *r,
time_t list_time,
smartlist_t *running_list);
smartlist_t *running_list,
int rr_format);
void add_trusted_dir_server(const char *addr, uint16_t port,const char *digest);
void clear_trusted_dir_servers(void);
@ -1493,6 +1499,7 @@ int router_get_runningrouters_hash(const char *s, char *digest);
int router_parse_list_from_string(const char **s,
routerlist_t **dest,
smartlist_t *good_nickname_list,
int rr_format,
time_t published);
int router_parse_routerlist_from_directory(const char *s,
routerlist_t **dest,

View File

@ -1018,8 +1018,8 @@ void running_routers_free(running_routers_t *rr)
void routerlist_update_from_runningrouters(routerlist_t *list,
running_routers_t *rr)
{
int n_routers, i;
routerinfo_t *router, *me = router_get_my_routerinfo();
routerinfo_t *me = router_get_my_routerinfo();
smartlist_t *all_routers;
if (!list)
return;
if (list->published_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; i<n_routers; ++i) {
router = smartlist_get(list->routers, 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 <b>running_list</b>.
* All entries in <b>running_list</b> follow one of these formats:
* <ol><li> <b>nickname</b> -- router is running and verified.
* (running-routers format)
* <li> !<b>nickname</b> -- router is not-running and verified.
* (running-routers format)
* <li> <b>nickname</b>=$<b>hexdigest</b> -- router is running and
* verified. (router-status format)
* (router-status format)
* <li> !<b>nickname</b>=$<b>hexdigest</b> -- router is running and
* verified. (router-status format)
* <li> !<b>nickname</b> -- router is not-running and verified.
* <li> $<b>hexdigest</b> -- router is running and unverified.
* <li> !$<b>hexdigest</b> -- 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; i<smartlist_len(running_list); ++i) {
name = smartlist_get(running_list, i);
n += strlen(name) + 1;
}
cp = tor_malloc(n+2);
cp[0] = '\0';
for (i=0; i<smartlist_len(running_list); ++i) {
name = smartlist_get(running_list, i);
strlcat(cp, name, n);
strlcat(cp, " ", n);
}
log_fn(LOG_DEBUG, "Updating status of %s from list \"%s\"",
router->nickname, 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; i<n_names; ++i) {
name = smartlist_get(running_list, i);
if (*name != '!') {
if (router_nickname_matches(router, name)) {
if (router->status_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;
}

View File

@ -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;i<tok->n_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));