Make structs for (v2) network-status and its per-router components. Add functions to parse them. Re-do the parsing logic a litt.e. Change signatures in or.h to support new DNS TTL logic.

svn:r4898
This commit is contained in:
Nick Mathewson 2005-09-02 20:37:31 +00:00
parent 9bc3d34682
commit 815c092b22
3 changed files with 450 additions and 52 deletions

View File

@ -766,6 +766,42 @@ typedef struct running_routers_t {
smartlist_t *running_routers; smartlist_t *running_routers;
} running_routers_t; } running_routers_t;
/** Contents of a network status object */
typedef struct routerstatus_t {
time_t published_on;
char nickname[MAX_NICKNAME_LEN+1];
char identity_digest[DIGEST_LEN];
char descriptor_digest[DIGEST_LEN];
uint32_t addr;
uint16_t or_port;
uint16_t dir_port;
unsigned int is_exit:1;
unsigned int is_stable:1;
unsigned int is_fast:1;
unsigned int is_running:1;
unsigned int is_named:1;
unsigned int is_valid:1;
} routerstatus_t;
/** Contents of a network status object */
typedef struct networkstatus_t {
time_t published_on;
char *source_address;
uint32_t source_addr;
uint16_t source_dirport;
char fingerprint[DIGEST_LEN];
char *contact;
crypto_pk_env_t *signing_key;
char *client_versions;
char *server_versions;
int binds_names:1;
smartlist_t *entries;
} networkstatus_t;
/** Contents of a directory of onion routers. */ /** Contents of a directory of onion routers. */
typedef struct { typedef struct {
/** List of routerinfo_t */ /** List of routerinfo_t */
@ -1483,7 +1519,8 @@ void connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
void connection_ap_handshake_socks_resolved(connection_t *conn, void connection_ap_handshake_socks_resolved(connection_t *conn,
int answer_type, int answer_type,
size_t answer_len, size_t answer_len,
const char *answer); const char *answer,
int ttl);
int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
int connection_exit_begin_resolve(cell_t *cell, circuit_t *circ); int connection_exit_begin_resolve(cell_t *cell, circuit_t *circ);
@ -1504,7 +1541,7 @@ int addressmap_already_mapped(const char *address);
void addressmap_register(const char *address, char *new_address, time_t expires); void addressmap_register(const char *address, char *new_address, time_t expires);
int client_dns_incr_failures(const char *address); int client_dns_incr_failures(const char *address);
void client_dns_clear_failures(const char *address); void client_dns_clear_failures(const char *address);
void client_dns_set_addressmap(const char *address, uint32_t val, const char *exitname); void client_dns_set_addressmap(const char *address, uint32_t val, const char *exitname, int ttl);
const char *addressmap_register_virtual_address(int type, char *new_address); const char *addressmap_register_virtual_address(int type, char *new_address);
void addressmap_get_mappings(smartlist_t *sl, time_t min_expires, time_t max_expires); void addressmap_get_mappings(smartlist_t *sl, time_t min_expires, time_t max_expires);
@ -1963,9 +2000,8 @@ trusted_dir_server_t *router_pick_trusteddirserver(int requireother,
int fascistfirewall, int fascistfirewall,
int retry_if_no_servers); int retry_if_no_servers);
int all_trusted_directory_servers_down(void); int all_trusted_directory_servers_down(void);
struct smartlist_t; void routerlist_add_family(smartlist_t *sl, routerinfo_t *router);
void routerlist_add_family(struct smartlist_t *sl, routerinfo_t *router); void add_nickname_list_to_smartlist(smartlist_t *sl, const char *list, int warn_if_down);
void add_nickname_list_to_smartlist(struct smartlist_t *sl, const char *list, int warn_if_down);
routerinfo_t *routerlist_find_my_routerinfo(void); routerinfo_t *routerlist_find_my_routerinfo(void);
int exit_policy_implicitly_allows_local_networks(addr_policy_t *policy, int exit_policy_implicitly_allows_local_networks(addr_policy_t *policy,
int warn); int warn);
@ -1981,7 +2017,7 @@ int router_is_unreliable(routerinfo_t *router, int need_uptime, int need_capacit
routerinfo_t *routerlist_sl_choose_by_bandwidth(smartlist_t *sl); routerinfo_t *routerlist_sl_choose_by_bandwidth(smartlist_t *sl);
routerinfo_t *router_choose_random_node(const char *preferred, routerinfo_t *router_choose_random_node(const char *preferred,
const char *excluded, const char *excluded,
struct smartlist_t *excludedsmartlist, smartlist_t *excludedsmartlist,
int need_uptime, int need_bandwidth, int need_uptime, int need_bandwidth,
int allow_unverified, int strict); int allow_unverified, int strict);
routerinfo_t *router_get_by_nickname(const char *nickname); routerinfo_t *router_get_by_nickname(const char *nickname);
@ -1992,6 +2028,8 @@ void router_get_routerlist(routerlist_t **prouterlist);
time_t routerlist_get_published_time(void); time_t routerlist_get_published_time(void);
void routerlist_free(routerlist_t *routerlist); void routerlist_free(routerlist_t *routerlist);
void routerinfo_free(routerinfo_t *router); void routerinfo_free(routerinfo_t *router);
void routerstatus_free(routerstatus_t *routerstatus);
void networkstatus_free(networkstatus_t *networkstatus);
void routerlist_free_current(void); void routerlist_free_current(void);
void free_trusted_dir_servers(void); void free_trusted_dir_servers(void);
routerinfo_t *routerinfo_copy(const routerinfo_t *router); routerinfo_t *routerinfo_copy(const routerinfo_t *router);
@ -2069,5 +2107,7 @@ int tor_version_as_new_as(const char *platform, const char *cutoff);
int tor_version_compare(tor_version_t *a, tor_version_t *b); int tor_version_compare(tor_version_t *a, tor_version_t *b);
void assert_addr_policy_ok(addr_policy_t *t); void assert_addr_policy_ok(addr_policy_t *t);
networkstatus_t *networkstatus_parse_from_string(const char *s);
#endif #endif

View File

@ -818,6 +818,29 @@ routerlist_free_current(void)
} }
} }
/** Free all storage held by the routerstatus object <b>rs</b>. */
void
routerstatus_free(routerstatus_t *rs)
{
tor_free(rs);
}
/** Free all storage held by the networkstatus object <b>ns</b>. */
void
networkstatus_free(networkstatus_t *ns)
{
tor_free(ns->source_address);
tor_free(ns->contact);
crypto_free_pk_env(ns->signing_key);
tor_free(ns->client_versions);
tor_free(ns->server_versions);
if (ns->entries) {
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs, routerstatus_free(rs));
smartlist_free(ns->entries);
}
tor_free(ns);
}
/** Free all entries in the list of trusted directory servers. */ /** Free all entries in the list of trusted directory servers. */
void void
free_trusted_dir_servers(void) free_trusted_dir_servers(void)

View File

@ -46,6 +46,13 @@ typedef enum {
K_HIBERNATING, K_HIBERNATING,
K_READ_HISTORY, K_READ_HISTORY,
K_WRITE_HISTORY, K_WRITE_HISTORY,
K_NETWORK_STATUS_VERSION,
K_DIR_SOURCE,
K_DIR_OPTIONS,
K_CLIENT_VERSIONS,
K_SERVER_VERSIONS,
K_R,
K_S,
_UNRECOGNIZED, _UNRECOGNIZED,
_ERR, _ERR,
_EOF, _EOF,
@ -91,40 +98,50 @@ typedef enum {
/** Rules for where a keyword can appear. */ /** Rules for where a keyword can appear. */
typedef enum { typedef enum {
ANY = 0, /**< Appears in router descriptor or in directory sections. */ DIR = 1, /**< Appears only in directory. */
DIR_ONLY, /**< Appears only in directory. */ RTR = 2, /**< Appears only in router descriptor or runningrouters */
RTR_ONLY, /**< Appears only in router descriptor or runningrouters */ NETSTATUS = 4, /**< v2 or later ("versioned") network status. */
RTRSTATUS = 8,
ANY = 15, /**< Appears in router descriptor or in directory sections. */
} where_syntax; } where_syntax;
/** Table mapping keywords to token value and to argument rules. */ /** Table mapping keywords to token value and to argument rules. */
static struct { static struct {
const char *t; int v; arg_syntax s; obj_syntax os; where_syntax ws; const char *t; int v; arg_syntax s; obj_syntax os; int ws;
} token_table[] = { } token_table[] = {
{ "accept", K_ACCEPT, ARGS, NO_OBJ, RTR_ONLY }, { "accept", K_ACCEPT, ARGS, NO_OBJ, RTR },
{ "directory-signature", K_DIRECTORY_SIGNATURE, ARGS, NEED_OBJ,DIR_ONLY}, { "directory-signature", K_DIRECTORY_SIGNATURE, ARGS, NEED_OBJ,DIR},
{ "reject", K_REJECT, ARGS, NO_OBJ, RTR_ONLY }, { "r", K_R, ARGS, NO_OBJ, RTRSTATUS },
{ "router", K_ROUTER, ARGS, NO_OBJ, RTR_ONLY }, { "s", K_S, ARGS, NO_OBJ, RTRSTATUS },
{ "recommended-software",K_RECOMMENDED_SOFTWARE,ARGS, NO_OBJ, DIR_ONLY }, { "reject", K_REJECT, ARGS, NO_OBJ, RTR },
{ "signed-directory", K_SIGNED_DIRECTORY, NO_ARGS, NO_OBJ, DIR_ONLY }, { "router", K_ROUTER, ARGS, NO_OBJ, RTR },
{ "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY,RTR_ONLY }, { "recommended-software",K_RECOMMENDED_SOFTWARE,ARGS, NO_OBJ, DIR },
{ "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY,RTR_ONLY }, { "signed-directory", K_SIGNED_DIRECTORY, NO_ARGS, NO_OBJ, DIR },
{ "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ,RTR_ONLY }, { "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY,RTR },
{ "running-routers", K_RUNNING_ROUTERS, ARGS, NO_OBJ, DIR_ONLY }, { "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY,RTR },
{ "router-status", K_ROUTER_STATUS, ARGS, NO_OBJ, DIR_ONLY }, { "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ,RTR },
{ "ports", K_PORTS, ARGS, NO_OBJ, RTR_ONLY }, { "running-routers", K_RUNNING_ROUTERS, ARGS, NO_OBJ, DIR },
{ "bandwidth", K_BANDWIDTH, ARGS, NO_OBJ, RTR_ONLY }, { "router-status", K_ROUTER_STATUS, ARGS, NO_OBJ, DIR },
{ "platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ, RTR_ONLY }, { "ports", K_PORTS, ARGS, NO_OBJ, RTR },
{ "bandwidth", K_BANDWIDTH, ARGS, NO_OBJ, RTR },
{ "platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ, RTR },
{ "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ, ANY }, { "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ, ANY },
{ "opt", K_OPT, CONCAT_ARGS, OBJ_OK, ANY }, { "opt", K_OPT, CONCAT_ARGS, OBJ_OK, ANY },
{ "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ, ANY }, { "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ, ANY },
{ "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ, DIR_ONLY }, { "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ, DIR },
{ "uptime", K_UPTIME, ARGS, NO_OBJ, RTR_ONLY }, { "uptime", K_UPTIME, ARGS, NO_OBJ, RTR },
{ "dir-signing-key", K_DIR_SIGNING_KEY, ARGS, OBJ_OK, DIR_ONLY }, { "dir-signing-key", K_DIR_SIGNING_KEY, ARGS, OBJ_OK, DIR|NETSTATUS},
{ "family", K_FAMILY, ARGS, NO_OBJ, RTR_ONLY }, { "family", K_FAMILY, ARGS, NO_OBJ, RTR },
{ "fingerprint", K_FINGERPRINT, ARGS, NO_OBJ, ANY }, { "fingerprint", K_FINGERPRINT, ARGS, NO_OBJ, ANY },
{ "hibernating", K_HIBERNATING, ARGS, NO_OBJ, RTR_ONLY }, { "hibernating", K_HIBERNATING, ARGS, NO_OBJ, RTR },
{ "read-history", K_READ_HISTORY, ARGS, NO_OBJ, RTR_ONLY }, { "read-history", K_READ_HISTORY, ARGS, NO_OBJ, RTR },
{ "write-history", K_WRITE_HISTORY, ARGS, NO_OBJ, RTR_ONLY }, { "write-history", K_WRITE_HISTORY, ARGS, NO_OBJ, RTR },
{ "network-status-version", K_NETWORK_STATUS_VERSION,
ARGS, NO_OBJ, NETSTATUS },
{ "dir-source", K_DIR_SOURCE, ARGS, NO_OBJ, NETSTATUS },
{ "dir-options", K_DIR_OPTIONS, ARGS, NO_OBJ, NETSTATUS },
{ "client-versions", K_CLIENT_VERSIONS, ARGS, NO_OBJ, NETSTATUS },
{ "server-versions", K_SERVER_VERSIONS, ARGS, NO_OBJ, NETSTATUS },
{ NULL, -1, NO_ARGS, NO_OBJ, ANY } { NULL, -1, NO_ARGS, NO_OBJ, ANY }
}; };
@ -138,12 +155,13 @@ static smartlist_t *find_all_exitpolicy(smartlist_t *s);
static directory_token_t *find_first_by_keyword(smartlist_t *s, static directory_token_t *find_first_by_keyword(smartlist_t *s,
directory_keyword keyword); directory_keyword keyword);
static int tokenize_string(const char *start, const char *end, static int tokenize_string(const char *start, const char *end,
smartlist_t *out, int is_dir); smartlist_t *out, where_syntax where);
static directory_token_t *get_next_token(const char **s, where_syntax where); static directory_token_t *get_next_token(const char **s, where_syntax where);
static int check_directory_signature(const char *digest, static int check_directory_signature(const char *digest,
directory_token_t *tok, directory_token_t *tok,
crypto_pk_env_t *pkey, crypto_pk_env_t *pkey,
crypto_pk_env_t *declared_key); crypto_pk_env_t *declared_key,
int check_authority);
static crypto_pk_env_t *find_dir_signing_key(const char *str); static crypto_pk_env_t *find_dir_signing_key(const char *str);
/* static */ int is_obsolete_version(const char *myversion, /* static */ int is_obsolete_version(const char *myversion,
const char *versionlist); const char *versionlist);
@ -426,7 +444,7 @@ router_parse_routerlist_from_directory(const char *str,
} }
++cp; ++cp;
tokens = smartlist_create(); tokens = smartlist_create();
if (tokenize_string(cp,strchr(cp,'\0'),tokens,1)) { if (tokenize_string(cp,strchr(cp,'\0'),tokens,DIR)) {
log_fn(LOG_WARN, "Error tokenizing directory signature"); goto err; log_fn(LOG_WARN, "Error tokenizing directory signature"); goto err;
} }
if (smartlist_len(tokens) != 1) { if (smartlist_len(tokens) != 1) {
@ -437,7 +455,7 @@ router_parse_routerlist_from_directory(const char *str,
log_fn(LOG_WARN,"Expected a single directory signature"); goto err; log_fn(LOG_WARN,"Expected a single directory signature"); goto err;
} }
declared_key = find_dir_signing_key(str); declared_key = find_dir_signing_key(str);
if (check_directory_signature(digest, tok, pkey, declared_key)<0) if (check_directory_signature(digest, tok, pkey, declared_key, 1)<0)
goto err; goto err;
/* now we know tok->n_args == 1, so it's safe to access tok->args[0] */ /* now we know tok->n_args == 1, so it's safe to access tok->args[0] */
@ -465,7 +483,7 @@ router_parse_routerlist_from_directory(const char *str,
} }
tokens = smartlist_create(); tokens = smartlist_create();
if (tokenize_string(str,end,tokens,1)) { if (tokenize_string(str,end,tokens,DIR)) {
log_fn(LOG_WARN, "Error tokenizing directory"); goto err; log_fn(LOG_WARN, "Error tokenizing directory"); goto err;
} }
if (smartlist_len(tokens) < 1) { if (smartlist_len(tokens) < 1) {
@ -599,7 +617,7 @@ router_parse_runningrouters(const char *str, int write_to_cache)
goto err; goto err;
} }
tokens = smartlist_create(); tokens = smartlist_create();
if (tokenize_string(str,str+strlen(str),tokens,1)) { if (tokenize_string(str,str+strlen(str),tokens,DIR)) {
log_fn(LOG_WARN, "Error tokenizing directory"); goto err; log_fn(LOG_WARN, "Error tokenizing directory"); goto err;
} }
if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) { if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
@ -648,7 +666,7 @@ router_parse_runningrouters(const char *str, int write_to_cache)
goto err; goto err;
} }
declared_key = find_dir_signing_key(str); declared_key = find_dir_signing_key(str);
if (check_directory_signature(digest, tok, NULL, declared_key) < 0) if (check_directory_signature(digest, tok, NULL, declared_key, 1) < 0)
goto err; goto err;
goto done; goto done;
@ -684,7 +702,7 @@ find_dir_signing_key(const char *str)
return NULL; return NULL;
++cp; /* Now cp points to the start of the token. */ ++cp; /* Now cp points to the start of the token. */
tok = get_next_token(&cp, DIR_ONLY); tok = get_next_token(&cp, DIR);
if (!tok) { if (!tok) {
log_fn(LOG_WARN, "Unparseable dir-signing-key token"); log_fn(LOG_WARN, "Unparseable dir-signing-key token");
return NULL; return NULL;
@ -737,7 +755,7 @@ dir_signing_key_is_trusted(crypto_pk_env_t *key)
* *
* If <b>declared_key</b> is set, the directory has declared what key * If <b>declared_key</b> is set, the directory has declared what key
* was used to sign it, so we will use that key only if it is an * was used to sign it, so we will use that key only if it is an
* authoritative directory signing key. * authoritative directory signing key or if check_authority is 0.
* *
* Otherwise, if pkey is provided, try to use it. * Otherwise, if pkey is provided, try to use it.
* *
@ -748,7 +766,8 @@ static int
check_directory_signature(const char *digest, check_directory_signature(const char *digest,
directory_token_t *tok, directory_token_t *tok,
crypto_pk_env_t *pkey, crypto_pk_env_t *pkey,
crypto_pk_env_t *declared_key) crypto_pk_env_t *declared_key,
int check_authority)
{ {
char signed_digest[PK_BYTES]; char signed_digest[PK_BYTES];
crypto_pk_env_t *_pkey = NULL; crypto_pk_env_t *_pkey = NULL;
@ -759,7 +778,7 @@ check_directory_signature(const char *digest,
} }
if (declared_key) { if (declared_key) {
if (dir_signing_key_is_trusted(declared_key)) if (!check_authority || dir_signing_key_is_trusted(declared_key))
_pkey = declared_key; _pkey = declared_key;
} }
if (!_pkey && pkey) { if (!_pkey && pkey) {
@ -880,7 +899,7 @@ router_parse_entry_from_string(const char *s, const char *end)
return NULL; return NULL;
} }
tokens = smartlist_create(); tokens = smartlist_create();
if (tokenize_string(s,end,tokens,0)) { if (tokenize_string(s,end,tokens,RTR)) {
log_fn(LOG_WARN, "Error tokeninzing router descriptor."); log_fn(LOG_WARN, "Error tokeninzing router descriptor.");
goto err; goto err;
} }
@ -1090,6 +1109,319 @@ router_parse_entry_from_string(const char *s, const char *end)
return router; return router;
} }
/** Helper: given a string <b>s</b>, return the start of the next router-status
* object (starting with "r " at the start of a line). If none is found,
* return the start of the next directory signature. If none is found, return
* the end of the string. */
static INLINE const char *
find_start_of_next_routerstatus(const char *s)
{
const char *eos = strstr(s, "\nr ");
if (!eos)
eos = strstr(s, "\ndirectory-signature");
if (eos)
return eos+1;
else
return s + strlen(s);
}
/** Given a string at *<b>s</b>, containing a routerstatus object, and an
* empty smartlist at <b>tokens</b>, parse and return the first router status
* object in the string, and advance *<b>s</b> to just after the end of the
* router status. Return NULL and advance *<b>s</b> on error. */
static routerstatus_t *
routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens)
{
#define BASE64_DIGEST_LEN 27
const char *eos;
routerstatus_t *rs = NULL;
directory_token_t *tok;
char base64buf_in[BASE64_DIGEST_LEN+3];
char base64buf_out[128];
char timebuf[ISO_TIME_LEN+1];
struct in_addr in;
tor_assert(tokens);
eos = find_start_of_next_routerstatus(*s);
if (tokenize_string(*s, eos, tokens, RTRSTATUS)) {
log_fn(LOG_WARN, "Error tokenizing router status");
goto err;
}
if (smartlist_len(tokens) < 1) {
log_fn(LOG_WARN, "Impossibly short router status");
goto err;
}
if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
log_fn(LOG_WARN, "Unrecognized keyword \"%s\" in router status; skipping.",
tok->args[0]);
goto err;
}
if (!(tok = find_first_by_keyword(tokens, K_R))) {
log_fn(LOG_WARN, "Missing 'r' keywork in router status; skipping.");
goto err;
}
if (tok->n_args < 8) {
log_fn(LOG_WARN,
"Too few arguments to 'r' keywork in router status; skipping.");
}
rs = tor_malloc_zero(sizeof(routerstatus_t));
if (!is_legal_nickname(tok->args[0])) {
log_fn(LOG_WARN,
"Invalid nickname '%s' in router status; skipping.", tok->args[0]);
goto err;
}
strlcpy(rs->nickname, tok->args[0], sizeof(rs->nickname));
if (strlen(tok->args[1]) != BASE64_DIGEST_LEN) {
log_fn(LOG_WARN, "Digest '%s' is wrong length in router status; skipping.",
tok->args[1]);
goto err;
}
memcpy(base64buf_in, tok->args[1], BASE64_DIGEST_LEN);
memcpy(base64buf_in+BASE64_DIGEST_LEN, "=\n\0", 3);
if (base64_decode(base64buf_out, sizeof(base64buf_out),
base64buf_in, sizeof(base64buf_in)) != DIGEST_LEN) {
log_fn(LOG_WARN, "Error decoding digest '%s'", tok->args[1]);
goto err;
}
memcpy(rs->identity_digest, base64buf_out, DIGEST_LEN);
if (strlen(tok->args[2]) != BASE64_DIGEST_LEN) {
log_fn(LOG_WARN, "Digest '%s' is wrong length in router status; skipping.",
tok->args[2]);
goto err;
}
memcpy(base64buf_in, tok->args[2], BASE64_DIGEST_LEN);
memcpy(base64buf_in+BASE64_DIGEST_LEN, "=\n\0", 3);
if (base64_decode(base64buf_out, sizeof(base64buf_out),
base64buf_in, sizeof(base64buf_in)) != DIGEST_LEN) {
log_fn(LOG_WARN, "Error decoding digest '%s'", tok->args[2]);
goto err;
}
memcpy(rs->descriptor_digest, base64buf_out, DIGEST_LEN);
if (tor_snprintf(timebuf, sizeof(timebuf), "%s %s",
tok->args[3], tok->args[4]) < 0 ||
parse_iso_time(timebuf, &rs->published_on)<0) {
log_fn(LOG_WARN, "Error parsing time '%s %s'", tok->args[3], tok->args[4]);
goto err;
}
if (tor_inet_aton(tok->args[5], &in) != 0) {
log_fn(LOG_WARN, "Error parsing address '%s'", tok->args[5]);
goto err;
}
rs->addr = ntohl(in.s_addr);
rs->or_port =(uint16_t) tor_parse_long(tok->args[6],10,0,65535,NULL,NULL);
rs->dir_port = (uint16_t) tor_parse_long(tok->args[7],10,0,65535,NULL,NULL);
if ((tok = find_first_by_keyword(tokens, K_S))) {
int i;
for (i=0; i < tok->n_args; ++i) {
if (!strcmp(tok->args[i], "Exit"))
rs->is_exit = 1;
else if (!strcmp(tok->args[i], "Stable"))
rs->is_stable = 1;
else if (!strcmp(tok->args[i], "Fast"))
rs->is_fast = 1;
else if (!strcmp(tok->args[i], "Running"))
rs->is_running = 1;
else if (!strcmp(tok->args[i], "Named"))
rs->is_named = 1;
else if (!strcmp(tok->args[i], "Valid"))
rs->is_valid = 1;
}
}
goto done;
err:
if (rs)
routerstatus_free(rs);
rs = NULL;
done:
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
smartlist_clear(tokens);
*s = eos;
return rs;
}
/** Given a versioned (v2 or later) network-status object in <b>s</b>, try to
* parse it and return the result. Return NULL on failure. Check the
* signature of the network status, but do not (yet) check the signing key for
* authority.
*/
networkstatus_t *
networkstatus_parse_from_string(const char *s)
{
const char *eos;
smartlist_t *tokens = smartlist_create();
networkstatus_t *ns = NULL;
char ns_digest[DIGEST_LEN];
char tmp_digest[DIGEST_LEN];
struct in_addr in;
directory_token_t *tok;
if (router_get_networkstatus_v2_hash(s, ns_digest)) {
log_fn(LOG_WARN, "Unable to compute digest of network-status");
goto err;
}
eos = find_start_of_next_routerstatus(s);
if (tokenize_string(s, eos, tokens, NETSTATUS)) {
log_fn(LOG_WARN, "Error tokenizing network-status header.");
goto err;
}
if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
log_fn(LOG_WARN, "Unrecognized keyword '%s'; can't parse network-status",
tok->args[0]);
goto err;
}
ns = tor_malloc_zero(sizeof(networkstatus_t));
if (!(tok = find_first_by_keyword(tokens, K_NETWORK_STATUS_VERSION))) {
log_fn(LOG_WARN, "Couldn't find network-status-version keyword");
goto err;
}
/* XXXX do something with the version? */
if (!(tok = find_first_by_keyword(tokens, K_DIR_SOURCE))) {
log_fn(LOG_WARN, "Couldn't find dir-source keyword");
goto err;
}
if (tok->n_args < 3) {
log_fn(LOG_WARN, "Too few arguments to dir-source keyword");
goto err;
}
ns->source_address = tok->args[0]; tok->args[0] = NULL;
if (tor_inet_aton(tok->args[1], &in) != 0) {
log_fn(LOG_WARN, "Error parsing address '%s'", tok->args[1]);
goto err;
}
ns->source_addr = ntohl(in.s_addr);
ns->source_dirport =
(uint16_t) tor_parse_long(tok->args[2],10,0,65535,NULL,NULL);
if (ns->source_dirport == 0) {
log_fn(LOG_WARN, "Directory source without dirport; skipping.");
goto err;
}
if (!(tok = find_first_by_keyword(tokens, K_FINGERPRINT))) {
log_fn(LOG_WARN, "Couldn't find fingerprint keyword");
goto err;
}
if (tok->n_args < 1) {
log_fn(LOG_WARN, "Too few arguments to fingerprint");
goto err;
}
if (base16_decode(ns->fingerprint, DIGEST_LEN, tok->args[0],
strlen(tok->args[0]))) {
log_fn(LOG_WARN, "Couldn't decode fingerprint '%s'", tok->args[0]);
goto err;
}
if ((tok = find_first_by_keyword(tokens, K_CONTACT)) && tok->n_args) {
ns->contact = tok->args[0];
tok->args[0] = NULL;
}
if (!(tok = find_first_by_keyword(tokens, K_DIR_SIGNING_KEY)) || !tok->key) {
log_fn(LOG_WARN, "Missing dir-signing-key");
goto err;
}
ns->signing_key = tok->key;
tok->key = NULL;
if (crypto_pk_get_digest(ns->signing_key, tmp_digest)<0) {
log_fn(LOG_WARN, "Couldn't compute signing key digest");
goto err;
}
if (memcmp(tmp_digest, ns->fingerprint, DIGEST_LEN)) {
log_fn(LOG_WARN, "network-status fingerprint did not match dir-signing-key");
goto err;
}
if (!(tok = find_first_by_keyword(tokens, K_CLIENT_VERSIONS)) || tok->n_args<1){
log_fn(LOG_WARN, "Missing client-versions");
goto err;
}
ns->client_versions = tok->args[0];
/* XXXX NM When to check these ?? */
if (!(tok = find_first_by_keyword(tokens, K_CLIENT_VERSIONS)) || tok->n_args<1){
log_fn(LOG_WARN, "Missing client-versions");
goto err;
}
ns->client_versions = tok->args[0];
tok->args[0] = NULL;
if (!(tok = find_first_by_keyword(tokens, K_SERVER_VERSIONS)) || tok->n_args<1){
log_fn(LOG_WARN, "Missing server-versions");
goto err;
}
ns->server_versions = tok->args[0];
tok->args[0] = NULL;
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_iso_time(tok->args[0], &ns->published_on) < 0) {
goto err;
}
if ((tok = find_first_by_keyword(tokens, K_DIR_OPTIONS))) {
int i;
for (i=0; i < tok->n_args; ++i) {
if (!strcmp(tok->args[i], "Names"))
ns->binds_names = 1;
}
}
ns->entries = smartlist_create();
s = eos;
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
smartlist_clear(tokens);
while (!strcmpstart(s, "r ")) {
routerstatus_t *rs;
if ((rs = routerstatus_parse_entry_from_string(&s, tokens)))
smartlist_add(ns->entries, rs);
}
if (tokenize_string(s, NULL, tokens, NETSTATUS)) {
log_fn(LOG_WARN, "Error tokenizing network-status footer.");
goto err;
}
if (smartlist_len(tokens) < 1) {
log_fn(LOG_WARN, "Too few items in network-status footer.");
goto err;
}
tok = smartlist_get(tokens, smartlist_len(tokens)-1);
if (tok->tp != K_DIRECTORY_SIGNATURE) {
log_fn(LOG_WARN, "Expected network-status footer to end with a signature.");
goto err;
}
if (check_directory_signature(ns_digest, tok, NULL, ns->signing_key, 0))
goto err;
goto done;
err:
networkstatus_free(ns);
ns = NULL;
done:
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
smartlist_free(tokens);
return ns;
}
/** Parse the exit policy in the string <b>s</b> and return it. If /** Parse the exit policy in the string <b>s</b> and return it. If
* assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or * assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
* ADDR_POLICY_REJECT) for items that specify no action. * ADDR_POLICY_REJECT) for items that specify no action.
@ -1120,7 +1452,7 @@ router_parse_addr_policy_from_string(const char *s, int assume_action)
tor_free(tmp); tor_free(tmp);
cp = tmp = new_str; cp = tmp = new_str;
} }
tok = get_next_token(&cp, RTR_ONLY); tok = get_next_token(&cp, RTR);
if (tok->tp == _ERR) { if (tok->tp == _ERR) {
log_fn(LOG_WARN, "Error reading exit policy: %s", tok->error); log_fn(LOG_WARN, "Error reading exit policy: %s", tok->error);
goto err; goto err;
@ -1269,8 +1601,8 @@ token_free(directory_token_t *tok)
} }
/** Helper function: read the next token from *s, advance *s to the end /** Helper function: read the next token from *s, advance *s to the end
* of the token, and return the parsed token. If 'where' is DIR_ONLY * of the token, and return the parsed token. If 'where' is DIR
* or RTR_ONLY, reject all tokens of the wrong type. * or RTR, reject all tokens of the wrong type.
*/ */
static directory_token_t * static directory_token_t *
get_next_token(const char **s, where_syntax where) get_next_token(const char **s, where_syntax where)
@ -1317,11 +1649,15 @@ get_next_token(const char **s, where_syntax where)
tok->tp = token_table[i].v; tok->tp = token_table[i].v;
a_syn = token_table[i].s; a_syn = token_table[i].s;
o_syn = token_table[i].os; o_syn = token_table[i].os;
if (token_table[i].ws != ANY && token_table[i].ws != where) { if (!(token_table[i].ws & where)) {
if (where == DIR_ONLY) { if (where == DIR) {
RET_ERR("Found a router-only token in a directory section"); RET_ERR("Found an out-of-place token in a directory section");
} else if (where == RTR) {
RET_ERR("Found an out-of-place token in a router descriptor");
} else if (where == NETSTATUS) {
RET_ERR("Found an out-of-place token in a network-status header");
} else { } else {
RET_ERR("Found a directory-only token in a router descriptor"); RET_ERR("Found an out-of-place token in a router status body");
} }
} }
if (a_syn == ARGS) { if (a_syn == ARGS) {
@ -1461,11 +1797,10 @@ get_next_token(const char **s, where_syntax where)
*/ */
static int static int
tokenize_string(const char *start, const char *end, smartlist_t *out, tokenize_string(const char *start, const char *end, smartlist_t *out,
int is_dir) where_syntax where)
{ {
const char **s; const char **s;
directory_token_t *tok = NULL; directory_token_t *tok = NULL;
where_syntax where = is_dir ? DIR_ONLY : RTR_ONLY;
s = &start; s = &start;
while (*s < end && (!tok || tok->tp != _EOF)) { while (*s < end && (!tok || tok->tp != _EOF)) {
tok = get_next_token(s, where); tok = get_next_token(s, where);