summaryrefslogtreecommitdiff
path: root/src/or/routerparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/routerparse.c')
-rw-r--r--src/or/routerparse.c257
1 files changed, 237 insertions, 20 deletions
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index fa30d86d1c..9aac94d1e4 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -67,7 +67,11 @@ typedef enum {
K_DIR_KEY_CERTIFICATION,
K_VOTE_STATUS,
+ K_VALID_AFTER,
+ K_FRESH_UNTIL,
K_VALID_UNTIL,
+ K_VOTING_DELAY,
+
K_KNOWN_FLAGS,
K_VOTE_DIGEST,
K_CONSENSUS_DIGEST,
@@ -110,7 +114,7 @@ typedef enum {
} obj_syntax;
#define AT_START 1
-#define AT_END 1
+#define AT_END 2
/** Determines the parsing rules for a single token type. */
typedef struct token_rule_t {
@@ -152,9 +156,9 @@ typedef struct token_rule_t {
/** An item that must appear exactly once */
#define T1(s,t,a,o) { s, t, a, o, 1, 1, 0 }
/** An item that must appear exactly once, at the start of the document */
-#define T1_START(s,t,a,o) { s, t, a, o, 1, 1, 0, AT_START }
+#define T1_START(s,t,a,o) { s, t, a, o, 1, 1, AT_START }
/** An item that must appear exactly once, at the end of the document */
-#define T1_END(s,t,a,o) { s, t, a, o, 1, 1, 0, AT_END }
+#define T1_END(s,t,a,o) { s, t, a, o, 1, 1, AT_END }
/** An item that must appear one or more times */
#define T1N(s,t,a,o) { s, t, a, o, 1, INT_MAX, 0 }
/** An item that must appear no more than once */
@@ -229,7 +233,7 @@ static token_rule_t netstatus_token_table[] = {
T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
T1( "dir-signing-key", K_DIR_SIGNING_KEY, NO_ARGS, NEED_KEY_1024 ),
T1( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
- T1( "network-status-version", K_NETWORK_STATUS_VERSION,
+ T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
GE(1), NO_OBJ ),
T1( "dir-source", K_DIR_SOURCE, GE(3), NO_OBJ ),
T01("dir-options", K_DIR_OPTIONS, ARGS, NO_OBJ ),
@@ -283,16 +287,15 @@ static token_rule_t dir_key_certificate_table[] = {
END_OF_TABLE
};
-#if 0
-/* XXXX This stuff is commented out for now so we can avoid warnings about
- * unused variables. */
-
-static token_rule_t status_vote_table[] = {
+static token_rule_t networkstatus_vote_token_table[] = {
T1("network-status-version", K_NETWORK_STATUS_VERSION,
GE(1), NO_OBJ ),
T1("vote-status", K_VOTE_STATUS, GE(1), NO_OBJ ),
T1("published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
+ T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
+ T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
+ T1("voting-delay", K_VOTING_DELAY, GE(2), NO_OBJ ),
T1("known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ),
T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
@@ -309,12 +312,18 @@ static token_rule_t status_vote_table[] = {
END_OF_TABLE
};
+#if 0
+/* XXXX This stuff is commented out for now so we can avoid warnings about
+ * unused variables. */
+
static token_rule_t status_consensus_table[] = {
T1("network-status-version", K_NETWORK_STATUS_VERSION,
GE(1), NO_OBJ ),
T1("vote-status", K_VOTE_STATUS, GE(1), NO_OBJ ),
- T1("published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
+ T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
+ T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
+ T1("voting-delay", K_VOTING_DELAY, GE(2), NO_OBJ ),
CERTIFICATE_MEMBERS
@@ -335,13 +344,13 @@ static token_rule_t status_consensus_table[] = {
END_OF_TABLE
};
+#endif
-static token_rule_t vote_footer_token_table[] = {
- T01("consensus-digest", K_CONSENSUS_DIGEST, EQ(1), NO_OBJ ),
+static token_rule_t networkstatus_vote_footer_token_table[] = {
T( "directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
END_OF_TABLE
};
-#endif
+
#undef T
@@ -1450,7 +1459,7 @@ find_start_of_next_routerstatus(const char *s)
static routerstatus_t *
routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens,
networkstatus_vote_t *vote,
- uint64_t *flags_out)
+ vote_routerstatus_t *vote_rs)
{
const char *eos;
routerstatus_t *rs = NULL;
@@ -1458,7 +1467,7 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens,
char timebuf[ISO_TIME_LEN+1];
struct in_addr in;
tor_assert(tokens);
- tor_assert(bool_eq(flags_out, vote));
+ tor_assert(bool_eq(vote, vote_rs));
eos = find_start_of_next_routerstatus(*s);
@@ -1473,7 +1482,11 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens,
tok = find_first_by_keyword(tokens, K_R);
tor_assert(tok);
tor_assert(tok->n_args >= 8);
- rs = tor_malloc_zero(sizeof(routerstatus_t));
+ if (vote_rs) {
+ rs = &vote_rs->status;
+ } else {
+ rs = tor_malloc_zero(sizeof(routerstatus_t));
+ }
if (!is_legal_nickname(tok->args[0])) {
log_warn(LD_DIR,
@@ -1516,10 +1529,11 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens,
tok = find_first_by_keyword(tokens, K_S);
if (tok && vote) {
int i, j;
+ vote_rs->flags = 0;
for (i=0; i < tok->n_args; ++i) {
for (j=0; vote->known_flags[j]; ++j) {
if (!strcmp(tok->args[i], vote->known_flags[j])) {
- *flags_out |= (1<<j);
+ vote_rs->flags |= (1<<j);
break;
}
}
@@ -1561,6 +1575,10 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens,
rs->version_supports_begindir =
tor_version_as_new_as(tok->args[0], "0.2.0.0-alpha-dev (r10070)");
}
+ if (vote_rs) {
+ vote_rs->version = tok->args[0];
+ tok->args[0] = NULL; /* suppress free() */
+ }
}
if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
@@ -1568,7 +1586,7 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens,
goto done;
err:
- if (rs)
+ if (rs && !vote_rs)
routerstatus_free(rs);
rs = NULL;
done:
@@ -1597,7 +1615,7 @@ _free_duplicate_routerstatus_entry(void *e)
routerstatus_free(e);
}
-/** Given a versioned (v2 or later) network-status object in <b>s</b>, try to
+/** Given a v2 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.
@@ -1629,7 +1647,12 @@ networkstatus_parse_from_string(const char *s)
memcpy(ns->networkstatus_digest, ns_digest, DIGEST_LEN);
tok = find_first_by_keyword(tokens, K_NETWORK_STATUS_VERSION);
- tor_assert(tok);
+ tor_assert(tok && tok->n_args >= 1);
+ if (strcmp(tok->args[0], "2")) {
+ log_warn(LD_BUG, "Got a non-v2 networkstatus. Version was "
+ "%s", escaped(tok->args[0]));
+ goto err;
+ }
tok = find_first_by_keyword(tokens, K_DIR_SOURCE);
tor_assert(tok);
@@ -1762,6 +1785,200 @@ networkstatus_parse_from_string(const char *s)
return ns;
}
+/** DOCDOC */
+networkstatus_vote_t *
+networkstatus_parse_vote_from_string(const char *s)
+{
+ smartlist_t *tokens = smartlist_create();
+ smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
+ networkstatus_vote_t *ns = NULL;
+ char ns_digest[DIGEST_LEN], declared_identity[DIGEST_LEN],
+ declared_digest[DIGEST_LEN];
+ const char *cert, *end_of_cert = NULL, *end_of_header, *end_of_footer;
+ directory_token_t *tok;
+ int ok;
+ struct in_addr in;
+
+ if (router_get_networkstatus_v3_hash(s, ns_digest)) {
+ log_warn(LD_DIR, "Unable to compute digest of network-status");
+ goto err;
+ }
+
+ end_of_header = find_start_of_next_routerstatus(s);
+ if (tokenize_string(s, end_of_header, tokens,
+ networkstatus_vote_token_table)) {
+ log_warn(LD_DIR, "Error tokenizing network-status vote header.");
+ goto err;
+ }
+
+ ns = tor_malloc_zero(sizeof(networkstatus_vote_t));
+ if (!(cert = strstr(s, "\ndir-key-certificate-version")))
+ goto err;
+ ++cert;
+ ns->cert = authority_cert_parse_from_string(cert, &end_of_cert);
+ if (!ns->cert || !end_of_cert || end_of_cert > end_of_header)
+ goto err;
+
+ tok = find_first_by_keyword(tokens, K_VOTE_STATUS);
+ tor_assert(tok);
+ tor_assert(tok->n_args);
+ if (strcmp(tok->args[0], "vote")) {
+ log_warn(LD_DIR, "Unrecognized vote status %s in network-status vote.",
+ escaped(tok->args[0]));
+ goto err;
+ }
+
+ tok = find_first_by_keyword(tokens, K_PUBLISHED);
+ if (parse_iso_time(tok->args[0], &ns->published))
+ goto err;
+
+ tok = find_first_by_keyword(tokens, K_VALID_AFTER);
+ if (parse_iso_time(tok->args[0], &ns->valid_after))
+ goto err;
+
+ tok = find_first_by_keyword(tokens, K_FRESH_UNTIL);
+ if (parse_iso_time(tok->args[0], &ns->fresh_until))
+ goto err;
+
+ tok = find_first_by_keyword(tokens, K_VALID_UNTIL);
+ if (parse_iso_time(tok->args[0], &ns->valid_until))
+ goto err;
+
+ tok = find_first_by_keyword(tokens, K_VOTING_DELAY);
+ tor_assert(tok->n_args >= 2);
+ ns->vote_seconds =
+ (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
+ if (!ok)
+ goto err;
+ ns->dist_seconds =
+ (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
+ if (!ok)
+ goto err;
+
+ if ((tok = find_first_by_keyword(tokens, K_CLIENT_VERSIONS))) {
+ ns->client_versions = tok->args[0];
+ tok->args[0] = NULL;
+ }
+ if ((tok = find_first_by_keyword(tokens, K_SERVER_VERSIONS))) {
+ ns->server_versions = tok->args[0];
+ tok->args[0] = NULL;
+ }
+
+ tok = find_first_by_keyword(tokens, K_KNOWN_FLAGS);
+ ns->known_flags = tor_malloc(sizeof(char*)*(tok->n_args+1));
+ memcpy(ns->known_flags, tok->args, sizeof(char*)*(tok->n_args));
+ ns->known_flags[tok->n_args] = NULL;
+ tok->n_args = 0; /* suppress free of args members, but not of args itself. */
+
+ tok = find_first_by_keyword(tokens, K_DIR_SOURCE);
+ tor_assert(tok);
+ tor_assert(tok->n_args >= 5);
+ ns->nickname = tor_strdup(tok->args[0]);
+ if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
+ base16_decode(ns->identity_digest, sizeof(ns->identity_digest),
+ tok->args[1], HEX_DIGEST_LEN) < 0) {
+ log_warn(LD_DIR, "Error decoding identity digest %s in "
+ "network-status vote.", escaped(tok->args[1]));
+ goto err;
+ }
+ ns->address = tor_strdup(tok->args[2]);
+ if (!tor_inet_aton(tok->args[3], &in)) {
+ log_warn(LD_DIR, "Error decoding IP address %s in network-status vote.",
+ escaped(tok->args[3]));
+ goto err;
+ }
+ ns->dir_port = (uint64_t)
+ (int) tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL);
+ if (!ok)
+ goto err;
+
+ tok = find_first_by_keyword(tokens, K_CONTACT);
+ if (tok) {
+ ns->contact = tor_strdup(tok->args[0]);
+ }
+
+ /* Parse routerstatus lines. */
+ rs_tokens = smartlist_create();
+ s = end_of_header;
+ ns->routerstatus_list = smartlist_create();
+ while (!strcmpstart(s, "r ")) {
+ vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ if (routerstatus_parse_entry_from_string(&s, tokens, ns, rs))
+ smartlist_add(ns->routerstatus_list, rs);
+ else {
+ tor_free(rs->version);
+ tor_free(rs);
+ }
+ }
+
+ /* Parse footer; check signature. */
+ footer_tokens = smartlist_create();
+ end_of_footer = s + strlen(s);
+ if (tokenize_string(s, end_of_footer, footer_tokens,
+ networkstatus_vote_footer_token_table)) {
+ log_warn(LD_DIR, "Error tokenizing network-status vote footer.");
+ goto err;
+ }
+ if (!(tok = find_first_by_keyword(footer_tokens, K_DIRECTORY_SIGNATURE))) {
+ log_warn(LD_DIR, "No signature on network-status vote.");
+ goto err;
+ }
+ tor_assert(tok->n_args >= 2);
+ if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
+ base16_decode(declared_identity, sizeof(ns->identity_digest),
+ tok->args[0], HEX_DIGEST_LEN) < 0) {
+ log_warn(LD_DIR, "Error decoding declared identity %s in "
+ "network-status vote.", escaped(tok->args[1]));
+ goto err;
+ }
+ if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
+ base16_decode(declared_digest, sizeof(ns->identity_digest),
+ tok->args[1], HEX_DIGEST_LEN) < 0) {
+ log_warn(LD_DIR, "Error decoding declared digest %s in "
+ "network-status vote.", escaped(tok->args[1]));
+ goto err;
+ }
+ if (memcmp(declared_identity, ns->cert->cache_info.identity_digest,
+ DIGEST_LEN)) {
+ log_warn(LD_DIR, "Digest mismatch between declared and actual on "
+ "network-status vote.");
+ goto err;
+ }
+ if (memcmp(declared_digest, ns_digest, DIGEST_LEN)) {
+ log_warn(LD_DIR, "Digest mismatch between declared and actual on "
+ "network-status vote.");
+ goto err;
+ }
+ if (check_signature_token(ns_digest, tok, ns->cert->signing_key, 0,
+ "network-status vote"))
+ goto err;
+
+ /* XXXX020 check dates for plausibility. ??? */
+
+ goto done;
+ err:
+ if (ns)
+ networkstatus_vote_free(ns);
+ ns = NULL;
+ done:
+ if (tokens) {
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ smartlist_free(tokens);
+ }
+ if (rs_tokens) {
+ SMARTLIST_FOREACH(rs_tokens, directory_token_t *, t, token_free(t));
+ smartlist_free(rs_tokens);
+ }
+ if (footer_tokens) {
+ SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_free(t));
+ smartlist_free(footer_tokens);
+ }
+
+
+ return ns;
+}
+
+
/** Parse the addr policy in the string <b>s</b> and return it. If
* assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
* ADDR_POLICY_REJECT) for items that specify no action.