[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r10461: First cut at code to parse and validate v3 networkstatus vot (in tor/trunk: . doc/spec src/or)



Author: nickm
Date: 2007-06-02 23:05:10 -0400 (Sat, 02 Jun 2007)
New Revision: 10461

Added:
   tor/trunk/src/or/dirvote.c
Modified:
   tor/trunk/
   tor/trunk/doc/spec/dir-spec.txt
   tor/trunk/src/or/Makefile.am
   tor/trunk/src/or/or.h
   tor/trunk/src/or/routerparse.c
Log:
 r13166@catbus:  nickm | 2007-06-02 23:02:40 -0400
 First cut at code to parse and validate v3 networkstatus votes.



Property changes on: tor/trunk
___________________________________________________________________
 svk:merge ticket from /tor/trunk [r13166] on 8246c3cf-6607-4228-993b-4d95d33730f1

Modified: tor/trunk/doc/spec/dir-spec.txt
===================================================================
--- tor/trunk/doc/spec/dir-spec.txt	2007-06-03 03:05:07 UTC (rev 10460)
+++ tor/trunk/doc/spec/dir-spec.txt	2007-06-03 03:05:10 UTC (rev 10461)
@@ -704,14 +704,26 @@
 
         [Exactly once.]
 
-        The start of the Interval for this vote.
+        The start of the Interval for this vote. XXXX
 
+    "fresh-until" SP YYYY-MM-DD SP HH:MM:SS NL
+
+        [Exactly once.]
+
+        XXXX
+
     "valid-until" SP YYYY-MM-DD SP HH:MM:SS NL
 
         [Exactly once.]
 
-        The end of the Interval for this vote, plus CONSENSUS_DELAY.
+        The end of the Interval for this vote. XXXX
 
+    "voting-delay" SP VoteSeconds SP DistSeconds NL
+
+        [Exactly once.]
+
+        XXXX
+
     "client-versions" SP VersionList NL
 
         [At most once.]
@@ -968,6 +980,7 @@
         The digest of the consensus being signed.
 
     "valid-after" SP YYYY-MM-DD SP HH:MM:SS NL
+    "fresh-until" SP YYYY-MM-DD SP HH:MM:SS NL
     "valid-until" SP YYYY-MM-DD SP HH:MM:SS NL
 
         [As in the consensus]

Modified: tor/trunk/src/or/Makefile.am
===================================================================
--- tor/trunk/src/or/Makefile.am	2007-06-03 03:05:07 UTC (rev 10460)
+++ tor/trunk/src/or/Makefile.am	2007-06-03 03:05:10 UTC (rev 10461)
@@ -7,7 +7,8 @@
 tor_SOURCES = buffers.c circuitbuild.c circuitlist.c \
 	circuituse.c command.c config.c \
 	connection.c connection_edge.c connection_or.c control.c \
-	cpuworker.c directory.c dirserv.c dns.c dnsserv.c hibernate.c main.c \
+	cpuworker.c directory.c dirserv.c dirvote.c \
+	dns.c dnsserv.c hibernate.c main.c \
 	onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \
 	rendservice.c rephist.c router.c routerlist.c routerparse.c \
 	eventdns.c \
@@ -23,7 +24,8 @@
 test_SOURCES = buffers.c circuitbuild.c circuitlist.c \
 	circuituse.c command.c config.c \
 	connection.c connection_edge.c connection_or.c control.c \
-	cpuworker.c directory.c dirserv.c dns.c dnsserv.c hibernate.c main.c \
+	cpuworker.c directory.c dirserv.c dirvote.c \
+	dns.c dnsserv.c hibernate.c main.c \
 	onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \
 	rendservice.c rephist.c router.c routerlist.c routerparse.c \
 	eventdns.c \

Added: tor/trunk/src/or/dirvote.c
===================================================================
--- tor/trunk/src/or/dirvote.c	                        (rev 0)
+++ tor/trunk/src/or/dirvote.c	2007-06-03 03:05:10 UTC (rev 10461)
@@ -0,0 +1,48 @@
+/* Copyright 2001-2004 Roger Dingledine.
+ * Copyright 2004-2007 Roger Dingledine, Nick Mathewson. */
+/* See LICENSE for licensing information */
+/* $Id$ */
+const char dirvote_c_id[] =
+  "$Id$";
+
+#include "or.h"
+
+/**
+ * \file dirvote.c
+ **/
+
+/** DOCDOC */
+void
+networkstatus_vote_free(networkstatus_vote_t *ns)
+{
+  int i;
+  if (!ns)
+    return;
+
+  tor_free(ns->client_versions);
+  tor_free(ns->server_versions);
+  if (ns->known_flags) {
+    for (i=0; ns->known_flags[i]; ++i)
+      tor_free(ns->known_flags[i]);
+    tor_free(ns->known_flags);
+  }
+  tor_free(ns->nickname);
+  tor_free(ns->address);
+  tor_free(ns->contact);
+  if (ns->cert)
+    authority_cert_free(ns->cert);
+
+  if (ns->routerstatus_list) {
+    SMARTLIST_FOREACH(ns->routerstatus_list, vote_routerstatus_t *, rs,
+    {
+      tor_free(rs->version);
+      tor_free(rs);
+    });
+
+    smartlist_free(ns->routerstatus_list);
+  }
+
+  memset(ns, 11, sizeof(*ns));
+  tor_free(ns);
+}
+


Property changes on: tor/trunk/src/or/dirvote.c
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Modified: tor/trunk/src/or/or.h
===================================================================
--- tor/trunk/src/or/or.h	2007-06-03 03:05:07 UTC (rev 10460)
+++ tor/trunk/src/or/or.h	2007-06-03 03:05:10 UTC (rev 10461)
@@ -1294,11 +1294,11 @@
 } networkstatus_t;
 
 /** DOCDOC */
-typedef struct ns_vote_routerstatus_t {
+typedef struct vote_routerstatus_t {
   routerstatus_t status;
   uint64_t flags;
   char *version;
-} ns_vote_routerstatus_t;
+} vote_routerstatus_t;
 
 /** DOCDOC */
 typedef struct networkstatus_vote_t {
@@ -1306,16 +1306,24 @@
   time_t valid_after;
   time_t fresh_until;
   time_t valid_until;
+  int vote_seconds;
+  int dist_seconds;
+
   char *client_versions;
   char *server_versions;
-  char **known_flags;
+  char **known_flags; /* NULL-terminated */
+
+  char *nickname;
   char identity_digest[DIGEST_LEN];
   char *address;
   uint32_t addr;
   uint16_t dir_port;
   uint16_t or_port;
+
   struct authority_cert_t *cert;
+
   char *contact;
+
   smartlist_t *routerstatus_list;
 } networkstatus_vote_t;
 
@@ -2701,6 +2709,10 @@
 void dirserv_free_all(void);
 void cached_dir_decref(cached_dir_t *d);
 
+/********************************* dirvote.c ************************/
+
+void networkstatus_vote_free(networkstatus_vote_t *ns);
+
 /********************************* dns.c ***************************/
 
 int dns_init(void);
@@ -3365,6 +3377,7 @@
 void dump_distinct_digest_count(int severity);
 
 networkstatus_t *networkstatus_parse_from_string(const char *s);
+networkstatus_vote_t *networkstatus_parse_vote_from_string(const char *s);
 
 void authority_cert_free(authority_cert_t *cert);
 authority_cert_t *authority_cert_parse_from_string(const char *s,

Modified: tor/trunk/src/or/routerparse.c
===================================================================
--- tor/trunk/src/or/routerparse.c	2007-06-03 03:05:07 UTC (rev 10460)
+++ tor/trunk/src/or/routerparse.c	2007-06-03 03:05:10 UTC (rev 10461)
@@ -67,7 +67,11 @@
   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 @@
 } 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 @@
 /** 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 @@
   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 @@
   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 @@
   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,14 +344,14 @@
 
   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
 
 /* static function prototypes */
@@ -1450,7 +1459,7 @@
 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 @@
   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 @@
   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 @@
   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 @@
       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 @@
 
   goto done;
  err:
-  if (rs)
+  if (rs && !vote_rs)
     routerstatus_free(rs);
   rs = NULL;
  done:
@@ -1597,7 +1615,7 @@
   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 @@
   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 @@
   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.