[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] [tor/master] Merge remote branch 'origin/maint-0.2.1' into maint-0.2.2
commit 1393985768d760e11e45faabb537d28248306e8b
Merge: 9d13346 b97b0ef
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date: Sat Jan 15 13:25:13 2011 -0500
Merge remote branch 'origin/maint-0.2.1' into maint-0.2.2
Conflicts:
src/or/routerparse.c
src/or/test.c
changes/bug2352 | 6 ++++++
src/common/crypto.c | 14 ++++++++------
src/common/crypto.h | 2 +-
src/or/routerparse.c | 16 ++++++++++++----
src/test/test_dir.c | 6 +++---
5 files changed, 30 insertions(+), 14 deletions(-)
diff --combined src/common/crypto.c
index 15b5818,208e1c5..1d12a9d
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@@ -27,7 -27,6 +27,7 @@@
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
+#include <openssl/engine.h>
#include <openssl/rand.h>
#include <openssl/opensslv.h>
#include <openssl/bn.h>
@@@ -50,9 -49,9 +50,9 @@@
#define CRYPTO_PRIVATE
#include "crypto.h"
-#include "log.h"
+#include "../common/torlog.h"
#include "aes.h"
-#include "util.h"
+#include "../common/util.h"
#include "container.h"
#include "compat.h"
@@@ -62,33 -61,6 +62,33 @@@
#include <openssl/engine.h>
+#ifdef ANDROID
+/* Android's OpenSSL seems to have removed all of its Engine support. */
+#define DISABLE_ENGINES
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x00908000l
+/* On OpenSSL versions before 0.9.8, there is no working SHA256
+ * implementation, so we use Tom St Denis's nice speedy one, slightly adapted
+ * to our needs */
+#define SHA256_CTX sha256_state
+#define SHA256_Init sha256_init
+#define SHA256_Update sha256_process
+#define LTC_ARGCHK(x) tor_assert(x)
+#include "sha256.c"
+#define SHA256_Final(a,b) sha256_done(b,a)
+
+static unsigned char *
+SHA256(const unsigned char *m, size_t len, unsigned char *d)
+{
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, m, len);
+ SHA256_Final(d, &ctx);
+ return d;
+}
+#endif
+
/** Macro: is k a valid RSA public or private key? */
#define PUBLIC_KEY_OK(k) ((k) && (k)->key && (k)->key->n)
/** Macro: is k a valid RSA private key? */
@@@ -122,7 -94,7 +122,7 @@@ struct crypto_dh_env_t
};
static int setup_openssl_threading(void);
-static int tor_check_dh_key(BIGNUM *bn);
+static int tor_check_dh_key(int severity, BIGNUM *bn);
/** Return the number of bytes added by padding method <b>padding</b>.
*/
@@@ -179,7 -151,6 +179,7 @@@ crypto_log_errors(int severity, const c
}
}
+#ifndef DISABLE_ENGINES
/** Log any OpenSSL engines we're using at NOTICE. */
static void
log_engine(const char *fn, ENGINE *e)
@@@ -194,82 -165,37 +194,82 @@@
log(LOG_INFO, LD_CRYPTO, "Using default implementation for %s", fn);
}
}
+#endif
+
+#ifndef DISABLE_ENGINES
+/** Try to load an engine in a shared library via fully qualified path.
+ */
+static ENGINE *
+try_load_engine(const char *path, const char *engine)
+{
+ ENGINE *e = ENGINE_by_id("dynamic");
+ if (e) {
+ if (!ENGINE_ctrl_cmd_string(e, "ID", engine, 0) ||
+ !ENGINE_ctrl_cmd_string(e, "DIR_LOAD", "2", 0) ||
+ !ENGINE_ctrl_cmd_string(e, "DIR_ADD", path, 0) ||
+ !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) {
+ ENGINE_free(e);
+ e = NULL;
+ }
+ }
+ return e;
+}
+#endif
/** Initialize the crypto library. Return 0 on success, -1 on failure.
*/
int
-crypto_global_init(int useAccel)
+crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
{
if (!_crypto_global_initialized) {
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
_crypto_global_initialized = 1;
setup_openssl_threading();
- /* XXX the below is a bug, since we can't know if we're supposed
- * to be using hardware acceleration or not. we should arrange
- * for this function to be called before init_keys. But make it
- * not complain loudly, at least until we make acceleration work. */
- if (useAccel < 0) {
- log_info(LD_CRYPTO, "Initializing OpenSSL via tor_tls_init().");
- }
if (useAccel > 0) {
+#ifdef DISABLE_ENGINES
+ (void)accelName;
+ (void)accelDir;
+ log_warn(LD_CRYPTO, "No OpenSSL hardware acceleration support enabled.");
+#else
+ ENGINE *e = NULL;
+
log_info(LD_CRYPTO, "Initializing OpenSSL engine support.");
ENGINE_load_builtin_engines();
- if (!ENGINE_register_all_complete())
- return -1;
-
- /* XXXX make sure this isn't leaking. */
+ ENGINE_register_all_complete();
+
+ if (accelName) {
+ if (accelDir) {
+ log_info(LD_CRYPTO, "Trying to load dynamic OpenSSL engine \"%s\""
+ " via path \"%s\".", accelName, accelDir);
+ e = try_load_engine(accelName, accelDir);
+ } else {
+ log_info(LD_CRYPTO, "Initializing dynamic OpenSSL engine \"%s\""
+ " acceleration support.", accelName);
+ e = ENGINE_by_id(accelName);
+ }
+ if (!e) {
+ log_warn(LD_CRYPTO, "Unable to load dynamic OpenSSL engine \"%s\".",
+ accelName);
+ } else {
+ log_info(LD_CRYPTO, "Loaded dynamic OpenSSL engine \"%s\".",
+ accelName);
+ }
+ }
+ if (e) {
+ log_info(LD_CRYPTO, "Loaded OpenSSL hardware acceleration engine,"
+ " setting default ciphers.");
+ ENGINE_set_default(e, ENGINE_METHOD_ALL);
+ }
log_engine("RSA", ENGINE_get_default_RSA());
log_engine("DH", ENGINE_get_default_DH());
log_engine("RAND", ENGINE_get_default_RAND());
log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
log_engine("3DES", ENGINE_get_cipher_engine(NID_des_ede3_ecb));
log_engine("AES", ENGINE_get_cipher_engine(NID_aes_128_ecb));
+#endif
+ } else {
+ log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
}
return crypto_seed_rng(1);
}
@@@ -291,11 -217,7 +291,11 @@@ crypto_global_cleanup(void
EVP_cleanup();
ERR_remove_state(0);
ERR_free_strings();
+
+#ifndef DISABLE_ENGINES
ENGINE_cleanup();
+#endif
+
CONF_modules_unload(1);
CRYPTO_cleanup_all_ex_data();
#ifdef TOR_IS_MULTITHREADED
@@@ -337,8 -259,7 +337,8 @@@ _crypto_new_pk_env_evp_pkey(EVP_PKEY *p
return _crypto_new_pk_env_rsa(rsa);
}
-/** Helper, used by tor-checkkey.c. Return the RSA from a crypto_pk_env_t. */
+/** Helper, used by tor-checkkey.c and tor-gencert.c. Return the RSA from a
+ * crypto_pk_env_t. */
RSA *
_crypto_pk_env_get_rsa(crypto_pk_env_t *env)
{
@@@ -400,12 -321,10 +400,12 @@@ crypto_new_pk_env(void
void
crypto_free_pk_env(crypto_pk_env_t *env)
{
- tor_assert(env);
+ if (!env)
+ return;
if (--env->refs > 0)
return;
+ tor_assert(env->refs == 0);
if (env->key)
RSA_free(env->key);
@@@ -428,7 -347,10 +428,7 @@@ crypto_create_init_cipher(const char *k
return NULL;
}
- if (crypto_cipher_set_key(crypto, key)) {
- crypto_log_errors(LOG_WARN, "setting symmetric key");
- goto error;
- }
+ crypto_cipher_set_key(crypto, key);
if (encrypt_mode)
r = crypto_cipher_encrypt_init_cipher(crypto);
@@@ -462,8 -384,7 +462,8 @@@ crypto_new_cipher_env(void
void
crypto_free_cipher_env(crypto_cipher_env_t *env)
{
- tor_assert(env);
+ if (!env)
+ return;
tor_assert(env->cipher);
aes_free_cipher(env->cipher);
@@@ -473,11 -394,11 +473,11 @@@
/* public key crypto */
-/** Generate a new public/private keypair in <b>env</b>. Return 0 on
- * success, -1 on failure.
+/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
+ * Return 0 on success, -1 on failure.
*/
int
-crypto_pk_generate_key(crypto_pk_env_t *env)
+crypto_pk_generate_key_with_bits(crypto_pk_env_t *env, int bits)
{
tor_assert(env);
@@@ -485,7 -406,7 +485,7 @@@
RSA_free(env->key);
#if OPENSSL_VERSION_NUMBER < 0x00908000l
/* In OpenSSL 0.9.7, RSA_generate_key is all we have. */
- env->key = RSA_generate_key(PK_BYTES*8,65537, NULL, NULL);
+ env->key = RSA_generate_key(bits, 65537, NULL, NULL);
#else
/* In OpenSSL 0.9.8, RSA_generate_key is deprecated. */
{
@@@ -498,7 -419,7 +498,7 @@@
r = RSA_new();
if (!r)
goto done;
- if (RSA_generate_key_ex(r, PK_BYTES*8, e, NULL) == -1)
+ if (RSA_generate_key_ex(r, bits, e, NULL) == -1)
goto done;
env->key = r;
@@@ -518,21 -439,23 +518,23 @@@
return 0;
}
- /** Read a PEM-encoded private key from the string <b>s</b> into <b>env</b>.
- * Return 0 on success, -1 on failure.
+ /** Read a PEM-encoded private key from the <b>len</b>-byte string <b>s</b>
+ * into <b>env</b>. Return 0 on success, -1 on failure. If len is -1,
+ * the string is nul-terminated.
*/
/* Used here, and used for testing. */
int
crypto_pk_read_private_key_from_string(crypto_pk_env_t *env,
- const char *s)
+ const char *s, ssize_t len)
{
BIO *b;
tor_assert(env);
tor_assert(s);
+ tor_assert(len < INT_MAX && len < SIZE_T_CEILING);
- /* Create a read-only memory BIO, backed by the NUL-terminated string 's' */
- b = BIO_new_mem_buf((char*)s, -1);
+ /* Create a read-only memory BIO, backed by the string 's' */
+ b = BIO_new_mem_buf((char*)s, (int)len);
if (env->key)
RSA_free(env->key);
@@@ -566,7 -489,7 +568,7 @@@ crypto_pk_read_private_key_from_filenam
}
/* Try to parse it. */
- r = crypto_pk_read_private_key_from_string(env, contents);
+ r = crypto_pk_read_private_key_from_string(env, contents, -1);
tor_free(contents);
if (r)
return -1; /* read_private_key_from_string already warned, so we don't.*/
@@@ -780,25 -703,14 +782,25 @@@ crypto_pk_env_t
crypto_pk_copy_full(crypto_pk_env_t *env)
{
RSA *new_key;
+ int privatekey = 0;
tor_assert(env);
tor_assert(env->key);
if (PRIVATE_KEY_OK(env)) {
new_key = RSAPrivateKey_dup(env->key);
+ privatekey = 1;
} else {
new_key = RSAPublicKey_dup(env->key);
}
+ if (!new_key) {
+ log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.",
+ privatekey?"private":"public");
+ crypto_log_errors(LOG_ERR,
+ privatekey ? "Duplicating a private key" :
+ "Duplicating a public key");
+ tor_fragile_assert();
+ return NULL;
+ }
return _crypto_new_pk_env_rsa(new_key);
}
@@@ -1299,14 -1211,19 +1301,14 @@@ crypto_cipher_generate_key(crypto_ciphe
/** Set the symmetric key for the cipher in <b>env</b> to the first
* CIPHER_KEY_LEN bytes of <b>key</b>. Does not initialize the cipher.
- * Return 0 on success, -1 on failure.
*/
-int
+void
crypto_cipher_set_key(crypto_cipher_env_t *env, const char *key)
{
tor_assert(env);
tor_assert(key);
- if (!env->key)
- return -1;
-
memcpy(env->key, key, CIPHER_KEY_LEN);
- return 0;
}
/** Generate an initialization vector for our AES-CTR cipher; store it
@@@ -1483,69 -1400,9 +1485,69 @@@ crypto_digest(char *digest, const char
return (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
}
+int
+crypto_digest256(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm)
+{
+ tor_assert(m);
+ tor_assert(digest);
+ tor_assert(algorithm == DIGEST_SHA256);
+ return (SHA256((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
+}
+
+/** Set the digests_t in <b>ds_out</b> to contain every digest on the
+ * <b>len</b> bytes in <b>m</b> that we know how to compute. Return 0 on
+ * success, -1 on failure. */
+int
+crypto_digest_all(digests_t *ds_out, const char *m, size_t len)
+{
+ digest_algorithm_t i;
+ tor_assert(ds_out);
+ memset(ds_out, 0, sizeof(*ds_out));
+ if (crypto_digest(ds_out->d[DIGEST_SHA1], m, len) < 0)
+ return -1;
+ for (i = DIGEST_SHA256; i < N_DIGEST_ALGORITHMS; ++i) {
+ if (crypto_digest256(ds_out->d[i], m, len, i) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/** Return the name of an algorithm, as used in directory documents. */
+const char *
+crypto_digest_algorithm_get_name(digest_algorithm_t alg)
+{
+ switch (alg) {
+ case DIGEST_SHA1:
+ return "sha1";
+ case DIGEST_SHA256:
+ return "sha256";
+ default:
+ tor_fragile_assert();
+ return "??unknown_digest??";
+ }
+}
+
+/** Given the name of a digest algorithm, return its integer value, or -1 if
+ * the name is not recognized. */
+int
+crypto_digest_algorithm_parse_name(const char *name)
+{
+ if (!strcmp(name, "sha1"))
+ return DIGEST_SHA1;
+ else if (!strcmp(name, "sha256"))
+ return DIGEST_SHA256;
+ else
+ return -1;
+}
+
/** Intermediate information about the digest of a stream of data. */
struct crypto_digest_env_t {
- SHA_CTX d;
+ union {
+ SHA_CTX sha1;
+ SHA256_CTX sha2;
+ } d;
+ digest_algorithm_t algorithm : 8;
};
/** Allocate and return a new digest object.
@@@ -1555,19 -1412,7 +1557,19 @@@ crypto_new_digest_env(void
{
crypto_digest_env_t *r;
r = tor_malloc(sizeof(crypto_digest_env_t));
- SHA1_Init(&r->d);
+ SHA1_Init(&r->d.sha1);
+ r->algorithm = DIGEST_SHA1;
+ return r;
+}
+
+crypto_digest_env_t *
+crypto_new_digest256_env(digest_algorithm_t algorithm)
+{
+ crypto_digest_env_t *r;
+ tor_assert(algorithm == DIGEST_SHA256);
+ r = tor_malloc(sizeof(crypto_digest_env_t));
+ SHA256_Init(&r->d.sha2);
+ r->algorithm = algorithm;
return r;
}
@@@ -1576,8 -1421,6 +1578,8 @@@
void
crypto_free_digest_env(crypto_digest_env_t *digest)
{
+ if (!digest)
+ return;
memset(digest, 0, sizeof(crypto_digest_env_t));
tor_free(digest);
}
@@@ -1590,51 -1433,30 +1592,51 @@@ crypto_digest_add_bytes(crypto_digest_e
{
tor_assert(digest);
tor_assert(data);
- /* Using the SHA1_*() calls directly means we don't support doing
- * SHA1 in hardware. But so far the delay of getting the question
+ /* Using the SHA*_*() calls directly means we don't support doing
+ * SHA in hardware. But so far the delay of getting the question
* to the hardware, and hearing the answer, is likely higher than
* just doing it ourselves. Hashes are fast.
*/
- SHA1_Update(&digest->d, (void*)data, len);
+ switch (digest->algorithm) {
+ case DIGEST_SHA1:
+ SHA1_Update(&digest->d.sha1, (void*)data, len);
+ break;
+ case DIGEST_SHA256:
+ SHA256_Update(&digest->d.sha2, (void*)data, len);
+ break;
+ default:
+ tor_fragile_assert();
+ break;
+ }
}
/** Compute the hash of the data that has been passed to the digest
* object; write the first out_len bytes of the result to <b>out</b>.
- * <b>out_len</b> must be \<= DIGEST_LEN.
+ * <b>out_len</b> must be \<= DIGEST256_LEN.
*/
void
crypto_digest_get_digest(crypto_digest_env_t *digest,
char *out, size_t out_len)
{
- unsigned char r[DIGEST_LEN];
- SHA_CTX tmpctx;
+ unsigned char r[DIGEST256_LEN];
+ crypto_digest_env_t tmpenv;
tor_assert(digest);
tor_assert(out);
- tor_assert(out_len <= DIGEST_LEN);
- /* memcpy into a temporary ctx, since SHA1_Final clears the context */
- memcpy(&tmpctx, &digest->d, sizeof(SHA_CTX));
- SHA1_Final(r, &tmpctx);
+ /* memcpy into a temporary ctx, since SHA*_Final clears the context */
+ memcpy(&tmpenv, digest, sizeof(crypto_digest_env_t));
+ switch (digest->algorithm) {
+ case DIGEST_SHA1:
+ tor_assert(out_len <= DIGEST_LEN);
+ SHA1_Final(r, &tmpenv.d.sha1);
+ break;
+ case DIGEST_SHA256:
+ tor_assert(out_len <= DIGEST256_LEN);
+ SHA256_Final(r, &tmpenv.d.sha2);
+ break;
+ default:
+ tor_fragile_assert();
+ break;
+ }
memcpy(out, r, out_len);
memset(r, 0, sizeof(r));
}
@@@ -1770,7 -1592,7 +1772,7 @@@ crypto_dh_generate_public(crypto_dh_env
crypto_log_errors(LOG_WARN, "generating DH key");
return -1;
}
- if (tor_check_dh_key(dh->dh->pub_key)<0) {
+ if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) {
log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-"
"the-universe chances really do happen. Trying again.");
/* Free and clear the keys, so OpenSSL will actually try again. */
@@@ -1817,7 -1639,7 +1819,7 @@@ crypto_dh_get_public(crypto_dh_env_t *d
* See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips.
*/
static int
-tor_check_dh_key(BIGNUM *bn)
+tor_check_dh_key(int severity, BIGNUM *bn)
{
BIGNUM *x;
char *s;
@@@ -1828,13 -1650,13 +1830,13 @@@
init_dh_param();
BN_set_word(x, 1);
if (BN_cmp(bn,x)<=0) {
- log_warn(LD_CRYPTO, "DH key must be at least 2.");
+ log_fn(severity, LD_CRYPTO, "DH key must be at least 2.");
goto err;
}
BN_copy(x,dh_param_p);
BN_sub_word(x, 1);
if (BN_cmp(bn,x)>=0) {
- log_warn(LD_CRYPTO, "DH key must be at most p-2.");
+ log_fn(severity, LD_CRYPTO, "DH key must be at most p-2.");
goto err;
}
BN_free(x);
@@@ -1842,7 -1664,7 +1844,7 @@@
err:
BN_free(x);
s = BN_bn2hex(bn);
- log_warn(LD_CRYPTO, "Rejecting insecure DH key [%s]", s);
+ log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s);
OPENSSL_free(s);
return -1;
}
@@@ -1860,7 -1682,7 +1862,7 @@@
* where || is concatenation.)
*/
ssize_t
-crypto_dh_compute_secret(crypto_dh_env_t *dh,
+crypto_dh_compute_secret(int severity, crypto_dh_env_t *dh,
const char *pubkey, size_t pubkey_len,
char *secret_out, size_t secret_bytes_out)
{
@@@ -1875,9 -1697,9 +1877,9 @@@
if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey,
(int)pubkey_len, NULL)))
goto error;
- if (tor_check_dh_key(pubkey_bn)<0) {
+ if (tor_check_dh_key(severity, pubkey_bn)<0) {
/* Check for invalid public keys. */
- log_warn(LD_CRYPTO,"Rejected invalid g^x");
+ log_fn(severity, LD_CRYPTO,"Rejected invalid g^x");
goto error;
}
secret_tmp = tor_malloc(crypto_dh_get_bytes(dh));
@@@ -1949,8 -1771,7 +1951,8 @@@ crypto_expand_key_material(const char *
void
crypto_dh_free(crypto_dh_env_t *dh)
{
- tor_assert(dh);
+ if (!dh)
+ return;
tor_assert(dh->dh);
DH_free(dh->dh);
tor_free(dh);
@@@ -1975,14 -1796,6 +1977,14 @@@
OPENSSL_VERSION_NUMBER <= 0x00907fffl) || \
(OPENSSL_VERSION_NUMBER >= 0x0090803fl))
+static void
+seed_weak_rng(void)
+{
+ unsigned seed;
+ crypto_rand((void*)&seed, sizeof(seed));
+ tor_init_weak_random(seed);
+}
+
/** Seed OpenSSL's random number generator with bytes from the operating
* system. <b>startup</b> should be true iff we have just started Tor and
* have not yet allocated a bunch of fds. Return 0 on success, -1 on failure.
@@@ -1990,15 -1803,14 +1992,15 @@@
int
crypto_seed_rng(int startup)
{
- char buf[ADD_ENTROPY];
int rand_poll_status = 0;
/* local variables */
#ifdef MS_WINDOWS
+ unsigned char buf[ADD_ENTROPY];
static int provider_set = 0;
static HCRYPTPROV provider;
#else
+ char buf[ADD_ENTROPY];
static const char *filenames[] = {
"/dev/srandom", "/dev/urandom", "/dev/random", NULL
};
@@@ -2034,7 -1846,6 +2036,7 @@@
}
RAND_seed(buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
+ seed_weak_rng();
return 0;
#else
for (i = 0; filenames[i]; ++i) {
@@@ -2051,7 -1862,6 +2053,7 @@@
}
RAND_seed(buf, (int)sizeof(buf));
memset(buf, 0, sizeof(buf));
+ seed_weak_rng();
return 0;
}
@@@ -2119,26 -1929,6 +2121,26 @@@ crypto_rand_uint64(uint64_t max
}
}
+/** Return a pseudorandom double d, chosen uniformly from the range
+ * 0.0 <= d < 1.0.
+ */
+double
+crypto_rand_double(void)
+{
+ /* We just use an unsigned int here; we don't really care about getting
+ * more than 32 bits of resolution */
+ unsigned int uint;
+ crypto_rand((char*)&uint, sizeof(uint));
+#if SIZEOF_INT == 4
+#define UINT_MAX_AS_DOUBLE 4294967296.0
+#elif SIZEOF_INT == 8
+#define UINT_MAX_AS_DOUBLE 1.8446744073709552e+19
+#else
+#error SIZEOF_INT is neither 4 nor 8
+#endif
+ return ((double)uint) / UINT_MAX_AS_DOUBLE;
+}
+
/** Generate and return a new random hostname starting with <b>prefix</b>,
* ending with <b>suffix</b>, and containing no less than
* <b>min_rand_len</b> and no more than <b>max_rand_len</b> random base32
@@@ -2399,54 -2189,15 +2401,54 @@@ digest_from_base64(char *digest, const
#endif
}
+/** Base-64 encode DIGEST256_LINE bytes from <b>digest</b>, remove the
+ * trailing = and newline characters, and store the nul-terminated result in
+ * the first BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>. */
+int
+digest256_to_base64(char *d64, const char *digest)
+{
+ char buf[256];
+ base64_encode(buf, sizeof(buf), digest, DIGEST256_LEN);
+ buf[BASE64_DIGEST256_LEN] = '\0';
+ memcpy(d64, buf, BASE64_DIGEST256_LEN+1);
+ return 0;
+}
+
+/** Given a base-64 encoded, nul-terminated digest in <b>d64</b> (without
+ * trailing newline or = characters), decode it and store the result in the
+ * first DIGEST256_LEN bytes at <b>digest</b>. */
+int
+digest256_from_base64(char *digest, const char *d64)
+{
+#ifdef USE_OPENSSL_BASE64
+ char buf_in[BASE64_DIGEST256_LEN+3];
+ char buf[256];
+ if (strlen(d64) != BASE64_DIGEST256_LEN)
+ return -1;
+ memcpy(buf_in, d64, BASE64_DIGEST256_LEN);
+ memcpy(buf_in+BASE64_DIGEST256_LEN, "=\n\0", 3);
+ if (base64_decode(buf, sizeof(buf), buf_in, strlen(buf_in)) != DIGEST256_LEN)
+ return -1;
+ memcpy(digest, buf, DIGEST256_LEN);
+ return 0;
+#else
+ if (base64_decode(digest, DIGEST256_LEN, d64, strlen(d64)) == DIGEST256_LEN)
+ return 0;
+ else
+ return -1;
+#endif
+}
+
/** Implements base32 encoding as in rfc3548. Limitation: Requires
* that srclen*8 is a multiple of 5.
*/
void
base32_encode(char *dest, size_t destlen, const char *src, size_t srclen)
{
- unsigned int i, bit, v, u;
- size_t nbits = srclen * 8;
+ unsigned int i, v, u;
+ size_t nbits = srclen * 8, bit;
+ tor_assert(srclen < SIZE_T_CEILING/8);
tor_assert((nbits%5) == 0); /* We need an even multiple of 5 bits. */
tor_assert((nbits/5)+1 <= destlen); /* We need enough space. */
tor_assert(destlen < SIZE_T_CEILING);
@@@ -2470,12 -2221,11 +2472,12 @@@ base32_decode(char *dest, size_t destle
{
/* XXXX we might want to rewrite this along the lines of base64_decode, if
* it ever shows up in the profile. */
- unsigned int i, j, bit;
- size_t nbits;
+ unsigned int i;
+ size_t nbits, j, bit;
char *tmp;
nbits = srclen * 5;
+ tor_assert(srclen < SIZE_T_CEILING / 5);
tor_assert((nbits%8) == 0); /* We need an even multiple of 8 bits. */
tor_assert((nbits/8) <= destlen); /* We need enough space. */
tor_assert(destlen < SIZE_T_CEILING);
diff --combined src/common/crypto.h
index 29ba36c,d6f5555..c306bec
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@@ -18,9 -18,6 +18,9 @@@
/** Length of the output of our message digest. */
#define DIGEST_LEN 20
+/** Length of the output of our second (improved) message digests. (For now
+ * this is just sha256, but any it can be any other 256-byte digest). */
+#define DIGEST256_LEN 32
/** Length of our symmetric cipher's keys. */
#define CIPHER_KEY_LEN 16
/** Length of our symmetric cipher's IV. */
@@@ -30,12 -27,9 +30,12 @@@
/** Length of our DH keys. */
#define DH_BYTES (1024/8)
-/** Length of a message digest when encoded in base64 with trailing = signs
- * removed. */
+/** Length of a sha1 message digest when encoded in base64 with trailing =
+ * signs removed. */
#define BASE64_DIGEST_LEN 27
+/** Length of a sha256 message digest when encoded in base64 with trailing =
+ * signs removed. */
+#define BASE64_DIGEST256_LEN 43
/** Constants used to indicate no padding for public-key encryption */
#define PK_NO_PADDING 60000
@@@ -54,26 -48,6 +54,26 @@@
#define FINGERPRINT_LEN 49
/** Length of hex encoding of SHA1 digest, not including final NUL. */
#define HEX_DIGEST_LEN 40
+/** Length of hex encoding of SHA256 digest, not including final NUL. */
+#define HEX_DIGEST256_LEN 64
+
+typedef enum {
+ DIGEST_SHA1 = 0,
+ DIGEST_SHA256 = 1,
+} digest_algorithm_t;
+#define N_DIGEST_ALGORITHMS (DIGEST_SHA256+1)
+
+/** A set of all the digests we know how to compute, taken on a single
+ * string. Any digests that are shorter than 256 bits are right-padded
+ * with 0 bits.
+ *
+ * Note that this representation wastes 12 bytes for the SHA1 case, so
+ * don't use it for anything where we need to allocate a whole bunch at
+ * once.
+ **/
+typedef struct {
+ char d[N_DIGEST_ALGORITHMS][DIGEST256_LEN];
+} digests_t;
typedef struct crypto_pk_env_t crypto_pk_env_t;
typedef struct crypto_cipher_env_t crypto_cipher_env_t;
@@@ -81,9 -55,7 +81,9 @@@ typedef struct crypto_digest_env_t cryp
typedef struct crypto_dh_env_t crypto_dh_env_t;
/* global state */
-int crypto_global_init(int hardwareAccel);
+int crypto_global_init(int hardwareAccel,
+ const char *accelName,
+ const char *accelPath);
void crypto_thread_cleanup(void);
int crypto_global_cleanup(void);
@@@ -99,9 -71,7 +99,9 @@@ crypto_cipher_env_t *crypto_new_cipher_
void crypto_free_cipher_env(crypto_cipher_env_t *env);
/* public key crypto */
-int crypto_pk_generate_key(crypto_pk_env_t *env);
+int crypto_pk_generate_key_with_bits(crypto_pk_env_t *env, int bits);
+#define crypto_pk_generate_key(env) \
+ crypto_pk_generate_key_with_bits((env), (PK_BYTES*8))
int crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env,
const char *keyfile);
@@@ -112,7 -82,7 +112,7 @@@ int crypto_pk_write_private_key_to_stri
int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env,
const char *src, size_t len);
int crypto_pk_read_private_key_from_string(crypto_pk_env_t *env,
- const char *s);
+ const char *s, ssize_t len);
int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env,
const char *fname);
@@@ -153,7 -123,7 +153,7 @@@ int crypto_pk_check_fingerprint_syntax(
/* symmetric crypto */
int crypto_cipher_generate_key(crypto_cipher_env_t *env);
-int crypto_cipher_set_key(crypto_cipher_env_t *env, const char *key);
+void crypto_cipher_set_key(crypto_cipher_env_t *env, const char *key);
void crypto_cipher_generate_iv(char *iv_out);
int crypto_cipher_set_iv(crypto_cipher_env_t *env, const char *iv);
const char *crypto_cipher_get_key(crypto_cipher_env_t *env);
@@@ -173,15 -143,9 +173,15 @@@ int crypto_cipher_decrypt_with_iv(crypt
char *to, size_t tolen,
const char *from, size_t fromlen);
-/* SHA-1 */
+/* SHA-1 and other digests. */
int crypto_digest(char *digest, const char *m, size_t len);
+int crypto_digest256(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm);
+int crypto_digest_all(digests_t *ds_out, const char *m, size_t len);
+const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg);
+int crypto_digest_algorithm_parse_name(const char *name);
crypto_digest_env_t *crypto_new_digest_env(void);
+crypto_digest_env_t *crypto_new_digest256_env(digest_algorithm_t algorithm);
void crypto_free_digest_env(crypto_digest_env_t *digest);
void crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data,
size_t len);
@@@ -200,7 -164,7 +200,7 @@@ int crypto_dh_get_bytes(crypto_dh_env_
int crypto_dh_generate_public(crypto_dh_env_t *dh);
int crypto_dh_get_public(crypto_dh_env_t *dh, char *pubkey_out,
size_t pubkey_out_len);
-ssize_t crypto_dh_compute_secret(crypto_dh_env_t *dh,
+ssize_t crypto_dh_compute_secret(int severity, crypto_dh_env_t *dh,
const char *pubkey, size_t pubkey_len,
char *secret_out, size_t secret_out_len);
void crypto_dh_free(crypto_dh_env_t *dh);
@@@ -212,7 -176,6 +212,7 @@@ int crypto_seed_rng(int startup)
int crypto_rand(char *to, size_t n);
int crypto_rand_int(unsigned int max);
uint64_t crypto_rand_uint64(uint64_t max);
+double crypto_rand_double(void);
char *crypto_random_hostname(int min_rand_len, int max_rand_len,
const char *prefix, const char *suffix);
@@@ -230,8 -193,6 +230,8 @@@ int base32_decode(char *dest, size_t de
int digest_to_base64(char *d64, const char *digest);
int digest_from_base64(char *digest, const char *d64);
+int digest256_to_base64(char *d64, const char *digest);
+int digest256_from_base64(char *digest, const char *d64);
/** Length of RFC2440-style S2K specifier: the first 8 bytes are a salt, the
* 9th describes how much iteration to do. */
diff --combined src/or/routerparse.c
index 66d024e,3778509..a6eef2d
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@@ -10,20 -10,7 +10,20 @@@
**/
#include "or.h"
+#include "config.h"
+#include "dirserv.h"
+#include "dirvote.h"
+#include "policies.h"
+#include "rendcommon.h"
+#include "router.h"
+#include "routerlist.h"
#include "memarea.h"
+#include "microdesc.h"
+#include "networkstatus.h"
+#include "rephist.h"
+#include "routerparse.h"
+#undef log
+#include <math.h>
/****************************************************************************/
@@@ -68,7 -55,6 +68,7 @@@ typedef enum
K_S,
K_V,
K_W,
+ K_M,
K_EVENTDNS,
K_EXTRA_INFO,
K_EXTRA_INFO_DIGEST,
@@@ -76,31 -62,6 +76,31 @@@
K_HIDDEN_SERVICE_DIR,
K_ALLOW_SINGLE_HOP_EXITS,
+ K_DIRREQ_END,
+ K_DIRREQ_V2_IPS,
+ K_DIRREQ_V3_IPS,
+ K_DIRREQ_V2_REQS,
+ K_DIRREQ_V3_REQS,
+ K_DIRREQ_V2_SHARE,
+ K_DIRREQ_V3_SHARE,
+ K_DIRREQ_V2_RESP,
+ K_DIRREQ_V3_RESP,
+ K_DIRREQ_V2_DIR,
+ K_DIRREQ_V3_DIR,
+ K_DIRREQ_V2_TUN,
+ K_DIRREQ_V3_TUN,
+ K_ENTRY_END,
+ K_ENTRY_IPS,
+ K_CELL_END,
+ K_CELL_PROCESSED,
+ K_CELL_QUEUED,
+ K_CELL_TIME,
+ K_CELL_CIRCS,
+ K_EXIT_END,
+ K_EXIT_WRITTEN,
+ K_EXIT_READ,
+ K_EXIT_OPENED,
+
K_DIR_KEY_CERTIFICATE_VERSION,
K_DIR_IDENTITY_KEY,
K_DIR_KEY_PUBLISHED,
@@@ -117,18 -78,13 +117,18 @@@
K_KNOWN_FLAGS,
K_PARAMS,
+ K_BW_WEIGHTS,
K_VOTE_DIGEST,
K_CONSENSUS_DIGEST,
+ K_ADDITIONAL_DIGEST,
+ K_ADDITIONAL_SIGNATURE,
K_CONSENSUS_METHODS,
K_CONSENSUS_METHOD,
K_LEGACY_DIR_KEY,
+ K_DIRECTORY_FOOTER,
A_PURPOSE,
+ A_LAST_LISTED,
_A_UNKNOWN,
R_RENDEZVOUS_SERVICE_DESCRIPTOR,
@@@ -166,7 -122,7 +166,7 @@@
* type.
*
* This structure is only allocated in memareas; do not allocate it on
- * the heap, or token_free() won't work.
+ * the heap, or token_clear() won't work.
*/
typedef struct directory_token_t {
directory_keyword tp; /**< Type of the token. */
@@@ -302,31 -258,6 +302,31 @@@ static token_rule_t extrainfo_token_tab
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ),
T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ),
+ T01("dirreq-stats-end", K_DIRREQ_END, ARGS, NO_OBJ ),
+ T01("dirreq-v2-ips", K_DIRREQ_V2_IPS, ARGS, NO_OBJ ),
+ T01("dirreq-v3-ips", K_DIRREQ_V3_IPS, ARGS, NO_OBJ ),
+ T01("dirreq-v2-reqs", K_DIRREQ_V2_REQS, ARGS, NO_OBJ ),
+ T01("dirreq-v3-reqs", K_DIRREQ_V3_REQS, ARGS, NO_OBJ ),
+ T01("dirreq-v2-share", K_DIRREQ_V2_SHARE, ARGS, NO_OBJ ),
+ T01("dirreq-v3-share", K_DIRREQ_V3_SHARE, ARGS, NO_OBJ ),
+ T01("dirreq-v2-resp", K_DIRREQ_V2_RESP, ARGS, NO_OBJ ),
+ T01("dirreq-v3-resp", K_DIRREQ_V3_RESP, ARGS, NO_OBJ ),
+ T01("dirreq-v2-direct-dl", K_DIRREQ_V2_DIR, ARGS, NO_OBJ ),
+ T01("dirreq-v3-direct-dl", K_DIRREQ_V3_DIR, ARGS, NO_OBJ ),
+ T01("dirreq-v2-tunneled-dl", K_DIRREQ_V2_TUN, ARGS, NO_OBJ ),
+ T01("dirreq-v3-tunneled-dl", K_DIRREQ_V3_TUN, ARGS, NO_OBJ ),
+ T01("entry-stats-end", K_ENTRY_END, ARGS, NO_OBJ ),
+ T01("entry-ips", K_ENTRY_IPS, ARGS, NO_OBJ ),
+ T01("cell-stats-end", K_CELL_END, ARGS, NO_OBJ ),
+ T01("cell-processed-cells", K_CELL_PROCESSED, ARGS, NO_OBJ ),
+ T01("cell-queued-cells", K_CELL_QUEUED, ARGS, NO_OBJ ),
+ T01("cell-time-in-queue", K_CELL_TIME, ARGS, NO_OBJ ),
+ T01("cell-circuits-per-decile", K_CELL_CIRCS, ARGS, NO_OBJ ),
+ T01("exit-stats-end", K_EXIT_END, ARGS, NO_OBJ ),
+ T01("exit-kibibytes-written", K_EXIT_WRITTEN, ARGS, NO_OBJ ),
+ T01("exit-kibibytes-read", K_EXIT_READ, ARGS, NO_OBJ ),
+ T01("exit-streams-opened", K_EXIT_OPENED, ARGS, NO_OBJ ),
+
T1_START( "extra-info", K_EXTRA_INFO, GE(2), NO_OBJ ),
END_OF_TABLE
@@@ -336,11 -267,10 +336,11 @@@
* documents. */
static token_rule_t rtrstatus_token_table[] = {
T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
- T1( "r", K_R, GE(8), NO_OBJ ),
+ T1( "r", K_R, GE(7), NO_OBJ ),
T1( "s", K_S, ARGS, NO_OBJ ),
T01("v", K_V, CONCAT_ARGS, NO_OBJ ),
T01("w", K_W, ARGS, NO_OBJ ),
+ T0N("m", K_M, CONCAT_ARGS, NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
END_OF_TABLE
};
@@@ -445,7 -375,7 +445,7 @@@ static token_rule_t client_keys_token_t
/** List of tokens allowed in V3 networkstatus votes. */
static token_rule_t networkstatus_token_table[] = {
- T1("network-status-version", K_NETWORK_STATUS_VERSION,
+ T1_START("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 ),
@@@ -473,7 -403,7 +473,7 @@@
/** List of tokens allowed in V3 networkstatus consensuses. */
static token_rule_t networkstatus_consensus_token_table[] = {
- T1("network-status-version", K_NETWORK_STATUS_VERSION,
+ T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
GE(1), NO_OBJ ),
T1("vote-status", K_VOTE_STATUS, GE(1), NO_OBJ ),
T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
@@@ -500,29 -430,17 +500,29 @@@
/** List of tokens allowable in the footer of v1/v2 directory/networkstatus
* footers. */
static token_rule_t networkstatus_vote_footer_token_table[] = {
- T( "directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
+ T01("directory-footer", K_DIRECTORY_FOOTER, NO_ARGS, NO_OBJ ),
+ T01("bandwidth-weights", K_BW_WEIGHTS, ARGS, NO_OBJ ),
+ T( "directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
END_OF_TABLE
};
/** List of tokens allowable in detached networkstatus signature documents. */
static token_rule_t networkstatus_detached_signature_token_table[] = {
T1_START("consensus-digest", K_CONSENSUS_DIGEST, GE(1), NO_OBJ ),
+ T("additional-digest", K_ADDITIONAL_DIGEST,GE(3), 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 ),
- T1N("directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
+ T("additional-signature", K_ADDITIONAL_SIGNATURE, GE(4), NEED_OBJ ),
+ T1N("directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
+ END_OF_TABLE
+};
+
+static token_rule_t microdesc_token_table[] = {
+ T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024),
+ T01("family", K_FAMILY, ARGS, NO_OBJ ),
+ T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
+ A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ),
END_OF_TABLE
};
@@@ -535,13 -453,9 +535,13 @@@ static addr_policy_t *router_parse_addr
static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
const char *start_str, const char *end_str,
- char end_char);
-
-static void token_free(directory_token_t *tok);
+ char end_char,
+ digest_algorithm_t alg);
+static int router_get_hashes_impl(const char *s, size_t s_len,
+ digests_t *digests,
+ const char *start_str, const char *end_str,
+ char end_char);
+static void token_clear(directory_token_t *tok);
static smartlist_t *find_all_exitpolicy(smartlist_t *s);
static directory_token_t *_find_by_keyword(smartlist_t *s,
directory_keyword keyword,
@@@ -565,7 -479,6 +565,7 @@@ static directory_token_t *get_next_toke
#define CST_CHECK_AUTHORITY (1<<0)
#define CST_NO_CHECK_OBJTYPE (1<<1)
static int check_signature_token(const char *digest,
+ ssize_t digest_len,
directory_token_t *tok,
crypto_pk_env_t *pkey,
int flags,
@@@ -586,34 -499,6 +586,34 @@@ static int tor_version_same_series(tor_
#define DUMP_AREA(a,name) STMT_NIL
#endif
+/** Last time we dumped a descriptor to disk. */
+static time_t last_desc_dumped = 0;
+
+/** For debugging purposes, dump unparseable descriptor *<b>desc</b> of
+ * type *<b>type</b> to file $DATADIR/unparseable-desc. Do not write more
+ * than one descriptor to disk per minute. If there is already such a
+ * file in the data directory, overwrite it. */
+static void
+dump_desc(const char *desc, const char *type)
+{
+ time_t now = time(NULL);
+ tor_assert(desc);
+ tor_assert(type);
+ if (!last_desc_dumped || last_desc_dumped + 60 < now) {
+ char *debugfile = get_datadir_fname("unparseable-desc");
+ size_t filelen = 50 + strlen(type) + strlen(desc);
+ char *content = tor_malloc_zero(filelen);
+ tor_snprintf(content, filelen, "Unable to parse descriptor of type "
+ "%s:\n%s", type, desc);
+ write_str_to_file(debugfile, content, 0);
+ log_info(LD_DIR, "Unable to parse descriptor of type %s. See file "
+ "unparseable-desc in data directory for details.", type);
+ tor_free(content);
+ tor_free(debugfile);
+ last_desc_dumped = now;
+ }
+}
+
/** Set <b>digest</b> to the SHA-1 digest of the hash of the directory in
* <b>s</b>. Return 0 on success, -1 on failure.
*/
@@@ -621,8 -506,7 +621,8 @@@ in
router_get_dir_hash(const char *s, char *digest)
{
return router_get_hash_impl(s, strlen(s), digest,
- "signed-directory","\ndirectory-signature",'\n');
+ "signed-directory","\ndirectory-signature",'\n',
+ DIGEST_SHA1);
}
/** Set <b>digest</b> to the SHA-1 digest of the hash of the first router in
@@@ -632,8 -516,7 +632,8 @@@ in
router_get_router_hash(const char *s, size_t s_len, char *digest)
{
return router_get_hash_impl(s, s_len, digest,
- "router ","\nrouter-signature", '\n');
+ "router ","\nrouter-signature", '\n',
+ DIGEST_SHA1);
}
/** Set <b>digest</b> to the SHA-1 digest of the hash of the running-routers
@@@ -643,8 -526,7 +643,8 @@@ in
router_get_runningrouters_hash(const char *s, char *digest)
{
return router_get_hash_impl(s, strlen(s), digest,
- "network-status","\ndirectory-signature", '\n');
+ "network-status","\ndirectory-signature", '\n',
+ DIGEST_SHA1);
}
/** Set <b>digest</b> to the SHA-1 digest of the hash of the network-status
@@@ -654,31 -536,18 +654,31 @@@ router_get_networkstatus_v2_hash(const
{
return router_get_hash_impl(s, strlen(s), digest,
"network-status-version","\ndirectory-signature",
- '\n');
+ '\n',
+ DIGEST_SHA1);
+}
+
+/** Set <b>digests</b> to all the digests of the consensus document in
+ * <b>s</b> */
+int
+router_get_networkstatus_v3_hashes(const char *s, digests_t *digests)
+{
+ return router_get_hashes_impl(s,strlen(s),digests,
+ "network-status-version",
+ "\ndirectory-signature",
+ ' ');
}
/** Set <b>digest</b> to the SHA-1 digest of the hash of the network-status
* string in <b>s</b>. Return 0 on success, -1 on failure. */
int
-router_get_networkstatus_v3_hash(const char *s, char *digest)
+router_get_networkstatus_v3_hash(const char *s, char *digest,
+ digest_algorithm_t alg)
{
return router_get_hash_impl(s, strlen(s), digest,
"network-status-version",
"\ndirectory-signature",
- ' ');
+ ' ', alg);
}
/** Set <b>digest</b> to the SHA-1 digest of the hash of the extrainfo
@@@ -687,7 -556,7 +687,7 @@@ in
router_get_extrainfo_hash(const char *s, char *digest)
{
return router_get_hash_impl(s, strlen(s), digest, "extra-info",
- "\nrouter-signature",'\n');
+ "\nrouter-signature",'\n', DIGEST_SHA1);
}
/** Helper: used to generate signatures for routers, directories and
@@@ -699,17 -568,16 +699,17 @@@
*/
int
router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest,
- crypto_pk_env_t *private_key)
+ size_t digest_len, crypto_pk_env_t *private_key)
{
char *signature;
size_t i, keysize;
+ int siglen;
keysize = crypto_pk_keysize(private_key);
signature = tor_malloc(keysize);
- if (crypto_pk_private_sign(private_key, signature, keysize,
- digest, DIGEST_LEN) < 0) {
-
+ siglen = crypto_pk_private_sign(private_key, signature, keysize,
+ digest, digest_len);
+ if (siglen < 0) {
log_warn(LD_BUG,"Couldn't sign digest.");
goto err;
}
@@@ -717,7 -585,7 +717,7 @@@
goto truncated;
i = strlen(buf);
- if (base64_encode(buf+i, buf_len-i, signature, 128) < 0) {
+ if (base64_encode(buf+i, buf_len-i, signature, siglen) < 0) {
log_warn(LD_BUG,"couldn't base64-encode signature");
goto err;
}
@@@ -824,7 -692,7 +824,7 @@@ router_parse_directory(const char *str
char digest[DIGEST_LEN];
time_t published_on;
int r;
- const char *end, *cp;
+ const char *end, *cp, *str_dup = str;
smartlist_t *tokens = NULL;
crypto_pk_env_t *declared_key = NULL;
memarea_t *area = memarea_new();
@@@ -860,11 -728,11 +860,11 @@@
}
declared_key = find_dir_signing_key(str, str+strlen(str));
note_crypto_pk_op(VERIFY_DIR);
- if (check_signature_token(digest, tok, declared_key,
+ if (check_signature_token(digest, DIGEST_LEN, tok, declared_key,
CST_CHECK_AUTHORITY, "directory")<0)
goto err;
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_clear(tokens);
memarea_clear(area);
@@@ -897,12 -765,11 +897,12 @@@
r = 0;
goto done;
err:
+ dump_desc(str_dup, "v1 directory");
r = -1;
done:
if (declared_key) crypto_free_pk_env(declared_key);
if (tokens) {
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
}
if (area) {
@@@ -924,7 -791,7 +924,7 @@@ router_parse_runningrouters(const char
int r = -1;
crypto_pk_env_t *declared_key = NULL;
smartlist_t *tokens = NULL;
- const char *eos = str + strlen(str);
+ const char *eos = str + strlen(str), *str_dup = str;
memarea_t *area = NULL;
if (router_get_runningrouters_hash(str, digest)) {
@@@ -953,7 -820,7 +953,7 @@@
}
declared_key = find_dir_signing_key(str, eos);
note_crypto_pk_op(VERIFY_DIR);
- if (check_signature_token(digest, tok, declared_key,
+ if (check_signature_token(digest, DIGEST_LEN, tok, declared_key,
CST_CHECK_AUTHORITY, "running-routers")
< 0)
goto err;
@@@ -965,10 -832,9 +965,10 @@@
r = 0;
err:
+ dump_desc(str_dup, "v1 running-routers");
if (declared_key) crypto_free_pk_env(declared_key);
if (tokens) {
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
}
if (area) {
@@@ -1018,7 -884,7 +1018,7 @@@ find_dir_signing_key(const char *str, c
}
done:
- if (tok) token_free(tok);
+ if (tok) token_clear(tok);
if (area) {
DUMP_AREA(area, "dir-signing-key token");
memarea_drop_all(area);
@@@ -1054,7 -920,6 +1054,7 @@@ dir_signing_key_is_trusted(crypto_pk_en
*/
static int
check_signature_token(const char *digest,
+ ssize_t digest_len,
directory_token_t *tok,
crypto_pk_env_t *pkey,
int flags,
@@@ -1087,14 -952,14 +1087,14 @@@
signed_digest = tor_malloc(keysize);
if (crypto_pk_public_checksig(pkey, signed_digest, keysize,
tok->object_body, tok->object_size)
- != DIGEST_LEN) {
+ < DIGEST_LEN) {
log_warn(LD_DIR, "Error reading %s: invalid signature.", doctype);
tor_free(signed_digest);
return -1;
}
// log_debug(LD_DIR,"Signed %s hash starts %s", doctype,
// hex_str(signed_digest,4));
- if (memcmp(digest, signed_digest, DIGEST_LEN)) {
+ if (memcmp(digest, signed_digest, digest_len)) {
log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype);
tor_free(signed_digest);
return -1;
@@@ -1280,7 -1145,7 +1280,7 @@@ router_parse_entry_from_string(const ch
smartlist_t *tokens = NULL, *exit_policy_tokens = NULL;
directory_token_t *tok;
struct in_addr in;
- const char *start_of_annotations, *cp;
+ const char *start_of_annotations, *cp, *s_dup = s;
size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0;
int ok = 1;
memarea_t *area = NULL;
@@@ -1558,7 -1423,7 +1558,7 @@@
verified_digests = digestmap_new();
digestmap_set(verified_digests, signed_digest, (void*)(uintptr_t)1);
#endif
- if (check_signature_token(digest, tok, router->identity_pkey, 0,
+ if (check_signature_token(digest, DIGEST_LEN, tok, router->identity_pkey, 0,
"router descriptor") < 0)
goto err;
@@@ -1576,15 -1441,16 +1576,15 @@@
goto done;
err:
+ dump_desc(s_dup, "router descriptor");
routerinfo_free(router);
router = NULL;
done:
if (tokens) {
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
}
- if (exit_policy_tokens) {
- smartlist_free(exit_policy_tokens);
- }
+ smartlist_free(exit_policy_tokens);
if (area) {
DUMP_AREA(area, "routerinfo");
memarea_drop_all(area);
@@@ -1609,7 -1475,6 +1609,7 @@@ extrainfo_parse_entry_from_string(cons
crypto_pk_env_t *key = NULL;
routerinfo_t *router = NULL;
memarea_t *area = NULL;
+ const char *s_dup = s;
if (!end) {
end = s + strlen(s);
@@@ -1684,8 -1549,7 +1684,8 @@@
if (key) {
note_crypto_pk_op(VERIFY_RTR);
- if (check_signature_token(digest, tok, key, 0, "extra-info") < 0)
+ if (check_signature_token(digest, DIGEST_LEN, tok, key, 0,
+ "extra-info") < 0)
goto err;
if (router)
@@@ -1699,12 -1563,12 +1699,12 @@@
goto done;
err:
- if (extrainfo)
- extrainfo_free(extrainfo);
+ dump_desc(s_dup, "extra-info descriptor");
+ extrainfo_free(extrainfo);
extrainfo = NULL;
done:
if (tokens) {
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
}
if (area) {
@@@ -1728,7 -1592,6 +1728,7 @@@ authority_cert_parse_from_string(const
size_t len;
int found;
memarea_t *area = NULL;
+ const char *s_dup = s;
s = eat_whitespace(s);
eos = strstr(s, "\ndir-key-certification");
@@@ -1753,7 -1616,7 +1753,7 @@@
goto err;
}
if (router_get_hash_impl(s, strlen(s), digest, "dir-key-certificate-version",
- "\ndir-key-certification", '\n') < 0)
+ "\ndir-key-certification", '\n', DIGEST_SHA1) < 0)
goto err;
tok = smartlist_get(tokens, 0);
if (tok->tp != K_DIR_KEY_CERTIFICATE_VERSION || strcmp(tok->args[0], "3")) {
@@@ -1846,7 -1709,7 +1846,7 @@@
}
}
if (!found) {
- if (check_signature_token(digest, tok, cert->identity_key, 0,
+ if (check_signature_token(digest, DIGEST_LEN, tok, cert->identity_key, 0,
"key certificate")) {
goto err;
}
@@@ -1855,7 -1718,6 +1855,7 @@@
/* XXXX Once all authorities generate cross-certified certificates,
* make this field mandatory. */
if (check_signature_token(cert->cache_info.identity_digest,
+ DIGEST_LEN,
tok,
cert->signing_key,
CST_NO_CHECK_OBJTYPE,
@@@ -1875,7 -1737,7 +1875,7 @@@
if (end_of_string) {
*end_of_string = eat_whitespace(eos);
}
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
if (area) {
DUMP_AREA(area, "authority cert");
@@@ -1883,9 -1745,8 +1883,9 @@@
}
return cert;
err:
+ dump_desc(s_dup, "authority cert");
authority_cert_free(cert);
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
if (area) {
DUMP_AREA(area, "authority cert");
@@@ -1896,28 -1757,23 +1896,28 @@@
/** 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. */
+ * return the start of the directory footer, or 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) {
- const char *eos2 = tor_memstr(s, eos-s, "\ndirectory-signature");
- if (eos2 && eos2 < eos)
- return eos2;
- else
- return eos+1;
- } else {
- if ((eos = strstr(s, "\ndirectory-signature")))
- return eos+1;
- return s + strlen(s);
- }
+ const char *eos, *footer, *sig;
+ if ((eos = strstr(s, "\nr ")))
+ ++eos;
+ else
+ eos = s + strlen(s);
+
+ footer = tor_memstr(s, eos-s, "\ndirectory-footer");
+ sig = tor_memstr(s, eos-s, "\ndirectory-signature");
+
+ if (footer && sig)
+ return MIN(footer, sig) + 1;
+ else if (footer)
+ return footer+1;
+ else if (sig)
+ return sig+1;
+ else
+ return eos;
}
/** Given a string at *<b>s</b>, containing a routerstatus object, and an
@@@ -1931,29 -1787,22 +1931,29 @@@
* If <b>consensus_method</b> is nonzero, this routerstatus is part of a
* consensus, and we should parse it according to the method used to
* make that consensus.
+ *
+ * Parse according to the syntax used by the consensus flavor <b>flav</b>.
**/
static routerstatus_t *
routerstatus_parse_entry_from_string(memarea_t *area,
const char **s, smartlist_t *tokens,
networkstatus_t *vote,
vote_routerstatus_t *vote_rs,
- int consensus_method)
+ int consensus_method,
+ consensus_flavor_t flav)
{
- const char *eos;
+ const char *eos, *s_dup = *s;
routerstatus_t *rs = NULL;
directory_token_t *tok;
char timebuf[ISO_TIME_LEN+1];
struct in_addr in;
+ int offset = 0;
tor_assert(tokens);
tor_assert(bool_eq(vote, vote_rs));
+ if (!consensus_method)
+ flav = FLAV_NS;
+
eos = find_start_of_next_routerstatus(*s);
if (tokenize_string(area,*s, eos, tokens, rtrstatus_token_table,0)) {
@@@ -1965,15 -1814,7 +1965,15 @@@
goto err;
}
tok = find_by_keyword(tokens, K_R);
- tor_assert(tok->n_args >= 8);
+ tor_assert(tok->n_args >= 7);
+ if (flav == FLAV_NS) {
+ if (tok->n_args < 8) {
+ log_warn(LD_DIR, "Too few arguments to r");
+ goto err;
+ }
+ } else {
+ offset = -1;
+ }
if (vote_rs) {
rs = &vote_rs->status;
} else {
@@@ -1994,34 -1835,29 +1994,34 @@@
goto err;
}
- if (digest_from_base64(rs->descriptor_digest, tok->args[2])) {
- log_warn(LD_DIR, "Error decoding descriptor digest %s",
- escaped(tok->args[2]));
- goto err;
+ if (flav == FLAV_NS) {
+ if (digest_from_base64(rs->descriptor_digest, tok->args[2])) {
+ log_warn(LD_DIR, "Error decoding descriptor digest %s",
+ escaped(tok->args[2]));
+ goto err;
+ }
}
if (tor_snprintf(timebuf, sizeof(timebuf), "%s %s",
- tok->args[3], tok->args[4]) < 0 ||
+ tok->args[3+offset], tok->args[4+offset]) < 0 ||
parse_iso_time(timebuf, &rs->published_on)<0) {
- log_warn(LD_DIR, "Error parsing time '%s %s'",
- tok->args[3], tok->args[4]);
+ log_warn(LD_DIR, "Error parsing time '%s %s' [%d %d]",
+ tok->args[3+offset], tok->args[4+offset],
+ offset, (int)flav);
goto err;
}
- if (tor_inet_aton(tok->args[5], &in) == 0) {
+ if (tor_inet_aton(tok->args[5+offset], &in) == 0) {
log_warn(LD_DIR, "Error parsing router address in network-status %s",
- escaped(tok->args[5]));
+ escaped(tok->args[5+offset]));
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);
+ rs->or_port = (uint16_t) tor_parse_long(tok->args[6+offset],
+ 10,0,65535,NULL,NULL);
+ rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset],
+ 10,0,65535,NULL,NULL);
tok = find_opt_by_keyword(tokens, K_S);
if (tok && vote) {
@@@ -2107,17 -1943,6 +2107,17 @@@
goto err;
}
rs->has_bandwidth = 1;
+ } else if (!strcmpstart(tok->args[i], "Measured=")) {
+ int ok;
+ rs->measured_bw =
+ (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
+ 10, 0, UINT32_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_DIR, "Invalid Measured Bandwidth %s",
+ escaped(tok->args[i]));
+ goto err;
+ }
+ rs->has_measured_bw = 1;
}
}
}
@@@ -2139,29 -1964,16 +2139,29 @@@
rs->has_exitsummary = 1;
}
+ if (vote_rs) {
+ SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, t) {
+ if (t->tp == K_M && t->n_args) {
+ vote_microdesc_hash_t *line =
+ tor_malloc(sizeof(vote_microdesc_hash_t));
+ line->next = vote_rs->microdesc;
+ line->microdesc_hash_line = tor_strdup(t->args[0]);
+ vote_rs->microdesc = line;
+ }
+ } SMARTLIST_FOREACH_END(t);
+ }
+
if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
rs->is_named = 0;
goto done;
err:
+ dump_desc(s_dup, "routerstatus entry");
if (rs && !vote_rs)
routerstatus_free(rs);
rs = NULL;
done:
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_clear(tokens);
if (area) {
DUMP_AREA(area, "routerstatus entry");
@@@ -2173,8 -1985,8 +2173,8 @@@
}
/** Helper to sort a smartlist of pointers to routerstatus_t */
-static int
-_compare_routerstatus_entries(const void **_a, const void **_b)
+int
+compare_routerstatus_entries(const void **_a, const void **_b)
{
const routerstatus_t *a = *_a, *b = *_b;
return memcmp(a->identity_digest, b->identity_digest, DIGEST_LEN);
@@@ -2198,7 -2010,7 +2198,7 @@@ _free_duplicate_routerstatus_entry(voi
networkstatus_v2_t *
networkstatus_v2_parse_from_string(const char *s)
{
- const char *eos;
+ const char *eos, *s_dup = s;
smartlist_t *tokens = smartlist_create();
smartlist_t *footer_tokens = smartlist_create();
networkstatus_v2_t *ns = NULL;
@@@ -2312,17 -2124,17 +2312,17 @@@
ns->entries = smartlist_create();
s = eos;
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_clear(tokens);
memarea_clear(area);
while (!strcmpstart(s, "r ")) {
routerstatus_t *rs;
if ((rs = routerstatus_parse_entry_from_string(area, &s, tokens,
- NULL, NULL, 0)))
+ NULL, NULL, 0, 0)))
smartlist_add(ns->entries, rs);
}
- smartlist_sort(ns->entries, _compare_routerstatus_entries);
- smartlist_uniq(ns->entries, _compare_routerstatus_entries,
+ smartlist_sort(ns->entries, compare_routerstatus_entries);
+ smartlist_uniq(ns->entries, compare_routerstatus_entries,
_free_duplicate_routerstatus_entry);
if (tokenize_string(area,s, NULL, footer_tokens, dir_footer_token_table,0)) {
@@@ -2341,19 -2153,19 +2341,19 @@@
}
note_crypto_pk_op(VERIFY_DIR);
- if (check_signature_token(ns_digest, tok, ns->signing_key, 0,
+ if (check_signature_token(ns_digest, DIGEST_LEN, tok, ns->signing_key, 0,
"network-status") < 0)
goto err;
goto done;
err:
- if (ns)
- networkstatus_v2_free(ns);
+ dump_desc(s_dup, "v2 networkstatus");
+ networkstatus_v2_free(ns);
ns = NULL;
done:
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
- SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_clear(t));
smartlist_free(footer_tokens);
if (area) {
DUMP_AREA(area, "v2 networkstatus");
@@@ -2362,395 -2174,6 +2362,395 @@@
return ns;
}
+/** Verify the bandwidth weights of a network status document */
+int
+networkstatus_verify_bw_weights(networkstatus_t *ns)
+{
+ int64_t weight_scale;
+ int64_t G=0, M=0, E=0, D=0, T=0;
+ double Wgg, Wgm, Wgd, Wmg, Wmm, Wme, Wmd, Weg, Wem, Wee, Wed;
+ double Gtotal=0, Mtotal=0, Etotal=0;
+ const char *casename = NULL;
+ int valid = 1;
+
+ weight_scale = networkstatus_get_param(ns, "bwweightscale", BW_WEIGHT_SCALE);
+ Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
+ Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
+ Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
+ Wmg = networkstatus_get_bw_weight(ns, "Wmg", -1);
+ Wmm = networkstatus_get_bw_weight(ns, "Wmm", -1);
+ Wme = networkstatus_get_bw_weight(ns, "Wme", -1);
+ Wmd = networkstatus_get_bw_weight(ns, "Wmd", -1);
+ Weg = networkstatus_get_bw_weight(ns, "Weg", -1);
+ Wem = networkstatus_get_bw_weight(ns, "Wem", -1);
+ Wee = networkstatus_get_bw_weight(ns, "Wee", -1);
+ Wed = networkstatus_get_bw_weight(ns, "Wed", -1);
+
+ if (Wgg<0 || Wgm<0 || Wgd<0 || Wmg<0 || Wmm<0 || Wme<0 || Wmd<0 || Weg<0
+ || Wem<0 || Wee<0 || Wed<0) {
+ log_warn(LD_BUG, "No bandwidth weights produced in consensus!");
+ return 0;
+ }
+
+ // First, sanity check basic summing properties that hold for all cases
+ // We use > 1 as the check for these because they are computed as integers.
+ // Sometimes there are rounding errors.
+ if (fabs(Wmm - weight_scale) > 1) {
+ log_warn(LD_BUG, "Wmm=%lf != "I64_FORMAT,
+ Wmm, I64_PRINTF_ARG(weight_scale));
+ valid = 0;
+ }
+
+ if (fabs(Wem - Wee) > 1) {
+ log_warn(LD_BUG, "Wem=%lf != Wee=%lf", Wem, Wee);
+ valid = 0;
+ }
+
+ if (fabs(Wgm - Wgg) > 1) {
+ log_warn(LD_BUG, "Wgm=%lf != Wgg=%lf", Wgm, Wgg);
+ valid = 0;
+ }
+
+ if (fabs(Weg - Wed) > 1) {
+ log_warn(LD_BUG, "Wed=%lf != Weg=%lf", Wed, Weg);
+ valid = 0;
+ }
+
+ if (fabs(Wgg + Wmg - weight_scale) > 0.001*weight_scale) {
+ log_warn(LD_BUG, "Wgg=%lf != "I64_FORMAT" - Wmg=%lf", Wgg,
+ I64_PRINTF_ARG(weight_scale), Wmg);
+ valid = 0;
+ }
+
+ if (fabs(Wee + Wme - weight_scale) > 0.001*weight_scale) {
+ log_warn(LD_BUG, "Wee=%lf != "I64_FORMAT" - Wme=%lf", Wee,
+ I64_PRINTF_ARG(weight_scale), Wme);
+ valid = 0;
+ }
+
+ if (fabs(Wgd + Wmd + Wed - weight_scale) > 0.001*weight_scale) {
+ log_warn(LD_BUG, "Wgd=%lf + Wmd=%lf + Wed=%lf != "I64_FORMAT,
+ Wgd, Wmd, Wed, I64_PRINTF_ARG(weight_scale));
+ valid = 0;
+ }
+
+ Wgg /= weight_scale;
+ Wgm /= weight_scale;
+ Wgd /= weight_scale;
+
+ Wmg /= weight_scale;
+ Wmm /= weight_scale;
+ Wme /= weight_scale;
+ Wmd /= weight_scale;
+
+ Weg /= weight_scale;
+ Wem /= weight_scale;
+ Wee /= weight_scale;
+ Wed /= weight_scale;
+
+ // Then, gather G, M, E, D, T to determine case
+ SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
+ if (rs->has_bandwidth) {
+ T += rs->bandwidth;
+ if (rs->is_exit && rs->is_possible_guard) {
+ D += rs->bandwidth;
+ Gtotal += Wgd*rs->bandwidth;
+ Mtotal += Wmd*rs->bandwidth;
+ Etotal += Wed*rs->bandwidth;
+ } else if (rs->is_exit) {
+ E += rs->bandwidth;
+ Mtotal += Wme*rs->bandwidth;
+ Etotal += Wee*rs->bandwidth;
+ } else if (rs->is_possible_guard) {
+ G += rs->bandwidth;
+ Gtotal += Wgg*rs->bandwidth;
+ Mtotal += Wmg*rs->bandwidth;
+ } else {
+ M += rs->bandwidth;
+ Mtotal += Wmm*rs->bandwidth;
+ }
+ } else {
+ log_warn(LD_BUG, "Missing consensus bandwidth for router %s",
+ rs->nickname);
+ }
+ } SMARTLIST_FOREACH_END(rs);
+
+ // Finally, check equality conditions depending upon case 1, 2 or 3
+ // Full equality cases: 1, 3b
+ // Partial equality cases: 2b (E=G), 3a (M=E)
+ // Fully unknown: 2a
+ if (3*E >= T && 3*G >= T) {
+ // Case 1: Neither are scarce
+ casename = "Case 1";
+ if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Etotal %lf != Mtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Etotal, Mtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Etotal %lf != Gtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Etotal, Gtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Mtotal %lf != Gtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Mtotal, Gtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ } else if (3*E < T && 3*G < T) {
+ int64_t R = MIN(E, G);
+ int64_t S = MAX(E, G);
+ /*
+ * Case 2: Both Guards and Exits are scarce
+ * Balance D between E and G, depending upon
+ * D capacity and scarcity. Devote no extra
+ * bandwidth to middle nodes.
+ */
+ if (R+D < S) { // Subcase a
+ double Rtotal, Stotal;
+ if (E < G) {
+ Rtotal = Etotal;
+ Stotal = Gtotal;
+ } else {
+ Rtotal = Gtotal;
+ Stotal = Etotal;
+ }
+ casename = "Case 2a";
+ // Rtotal < Stotal
+ if (Rtotal > Stotal) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Rtotal %lf > Stotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Rtotal, Stotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ // Rtotal < T/3
+ if (3*Rtotal > T) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: 3*Rtotal %lf > T "
+ I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT
+ " D="I64_FORMAT" T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Rtotal*3, I64_PRINTF_ARG(T),
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ // Stotal < T/3
+ if (3*Stotal > T) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: 3*Stotal %lf > T "
+ I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT
+ " D="I64_FORMAT" T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Stotal*3, I64_PRINTF_ARG(T),
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ // Mtotal > T/3
+ if (3*Mtotal < T) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: 3*Mtotal %lf < T "
+ I64_FORMAT". "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Mtotal*3, I64_PRINTF_ARG(T),
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ } else { // Subcase b: R+D > S
+ casename = "Case 2b";
+
+ /* Check the rare-M redirect case. */
+ if (D != 0 && 3*M < T) {
+ casename = "Case 2b (balanced)";
+ if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Etotal %lf != Mtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Etotal, Mtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Etotal %lf != Gtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Etotal, Gtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Mtotal %lf != Gtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Mtotal, Gtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ } else {
+ if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Etotal %lf != Gtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Etotal, Gtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ }
+ }
+ } else { // if (E < T/3 || G < T/3) {
+ int64_t S = MIN(E, G);
+ int64_t NS = MAX(E, G);
+ if (3*(S+D) < T) { // Subcase a:
+ double Stotal;
+ double NStotal;
+ if (G < E) {
+ casename = "Case 3a (G scarce)";
+ Stotal = Gtotal;
+ NStotal = Etotal;
+ } else { // if (G >= E) {
+ casename = "Case 3a (E scarce)";
+ NStotal = Gtotal;
+ Stotal = Etotal;
+ }
+ // Stotal < T/3
+ if (3*Stotal > T) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: 3*Stotal %lf > T "
+ I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT
+ " D="I64_FORMAT" T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Stotal*3, I64_PRINTF_ARG(T),
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ if (NS >= M) {
+ if (fabs(NStotal-Mtotal) > 0.01*MAX(NStotal,Mtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: NStotal %lf != Mtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, NStotal, Mtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ } else {
+ // if NS < M, NStotal > T/3 because only one of G or E is scarce
+ if (3*NStotal < T) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: 3*NStotal %lf < T "
+ I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT
+ " E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, NStotal*3, I64_PRINTF_ARG(T),
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ }
+ } else { // Subcase b: S+D >= T/3
+ casename = "Case 3b";
+ if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Etotal %lf != Mtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Etotal, Mtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Etotal %lf != Gtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Etotal, Gtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
+ log_warn(LD_DIR,
+ "Bw Weight Failure for %s: Mtotal %lf != Gtotal %lf. "
+ "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+ " T="I64_FORMAT". "
+ "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+ casename, Mtotal, Gtotal,
+ I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
+ I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
+ Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+ valid = 0;
+ }
+ }
+ }
+
+ if (valid)
+ log_notice(LD_DIR, "Bandwidth-weight %s is verified and valid.",
+ casename);
+
+ return valid;
+}
+
/** Parse a v3 networkstatus vote, opinion, or consensus (depending on
* ns_type), from <b>s</b>, and return the result. Return NULL on failure. */
networkstatus_t *
@@@ -2761,21 -2184,19 +2761,21 @@@ networkstatus_parse_vote_from_string(co
smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
networkstatus_voter_info_t *voter = NULL;
networkstatus_t *ns = NULL;
- char ns_digest[DIGEST_LEN];
- const char *cert, *end_of_header, *end_of_footer;
+ digests_t ns_digests;
+ const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
directory_token_t *tok;
int ok;
struct in_addr in;
int i, inorder, n_signatures = 0;
memarea_t *area = NULL, *rs_area = NULL;
+ consensus_flavor_t flav = FLAV_NS;
+
tor_assert(s);
if (eos_out)
*eos_out = NULL;
- if (router_get_networkstatus_v3_hash(s, ns_digest)) {
+ if (router_get_networkstatus_v3_hashes(s, &ns_digests)) {
log_warn(LD_DIR, "Unable to compute digest of network-status");
goto err;
}
@@@ -2791,23 -2212,7 +2791,23 @@@
}
ns = tor_malloc_zero(sizeof(networkstatus_t));
- memcpy(ns->networkstatus_digest, ns_digest, DIGEST_LEN);
+ memcpy(&ns->digests, &ns_digests, sizeof(ns_digests));
+
+ tok = find_by_keyword(tokens, K_NETWORK_STATUS_VERSION);
+ tor_assert(tok);
+ if (tok->n_args > 1) {
+ int flavor = networkstatus_parse_flavor_name(tok->args[1]);
+ if (flavor < 0) {
+ log_warn(LD_DIR, "Can't parse document with unknown flavor %s",
+ escaped(tok->args[2]));
+ goto err;
+ }
+ ns->flavor = flav = flavor;
+ }
+ if (flav != FLAV_NS && ns_type != NS_TYPE_CONSENSUS) {
+ log_warn(LD_DIR, "Flavor found on non-consenus networkstatus.");
+ goto err;
+ }
if (ns_type != NS_TYPE_CONSENSUS) {
const char *end_of_cert = NULL;
@@@ -2961,9 -2366,8 +2961,9 @@@
if (voter)
smartlist_add(ns->voters, voter);
voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+ voter->sigs = smartlist_create();
if (ns->type != NS_TYPE_CONSENSUS)
- memcpy(voter->vote_digest, ns_digest, DIGEST_LEN);
+ memcpy(voter->vote_digest, ns_digests.d[DIGEST_SHA1], DIGEST_LEN);
voter->nickname = tor_strdup(tok->args[0]);
if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
@@@ -3055,7 -2459,7 +3055,7 @@@
if (ns->type != NS_TYPE_CONSENSUS) {
vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t));
if (routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens, ns,
- rs, 0))
+ rs, 0, 0))
smartlist_add(ns->routerstatus_list, rs);
else {
tor_free(rs->version);
@@@ -3065,8 -2469,7 +3065,8 @@@
routerstatus_t *rs;
if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens,
NULL, NULL,
- ns->consensus_method)))
+ ns->consensus_method,
+ flav)))
smartlist_add(ns->routerstatus_list, rs);
}
}
@@@ -3099,73 -2502,14 +3099,73 @@@
goto err;
}
- SMARTLIST_FOREACH(footer_tokens, directory_token_t *, _tok,
{
+ int found_sig = 0;
+ SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
+ tok = _tok;
+ if (tok->tp == K_DIRECTORY_SIGNATURE)
+ found_sig = 1;
+ else if (found_sig) {
+ log_warn(LD_DIR, "Extraneous token after first directory-signature");
+ goto err;
+ }
+ } SMARTLIST_FOREACH_END(_tok);
+ }
+
+ if ((tok = find_opt_by_keyword(footer_tokens, K_DIRECTORY_FOOTER))) {
+ if (tok != smartlist_get(footer_tokens, 0)) {
+ log_warn(LD_DIR, "Misplaced directory-footer token");
+ goto err;
+ }
+ }
+
+ tok = find_opt_by_keyword(footer_tokens, K_BW_WEIGHTS);
+ if (tok) {
+ ns->weight_params = smartlist_create();
+ for (i = 0; i < tok->n_args; ++i) {
+ int ok=0;
+ char *eq = strchr(tok->args[i], '=');
+ if (!eq) {
+ log_warn(LD_DIR, "Bad element '%s' in weight params",
+ escaped(tok->args[i]));
+ goto err;
+ }
+ tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
+ goto err;
+ }
+ smartlist_add(ns->weight_params, tor_strdup(tok->args[i]));
+ }
+ }
+
+ SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
char declared_identity[DIGEST_LEN];
networkstatus_voter_info_t *v;
+ document_signature_t *sig;
+ const char *id_hexdigest = NULL;
+ const char *sk_hexdigest = NULL;
+ digest_algorithm_t alg = DIGEST_SHA1;
tok = _tok;
if (tok->tp != K_DIRECTORY_SIGNATURE)
continue;
tor_assert(tok->n_args >= 2);
+ if (tok->n_args == 2) {
+ id_hexdigest = tok->args[0];
+ sk_hexdigest = tok->args[1];
+ } else {
+ const char *algname = tok->args[0];
+ int a;
+ id_hexdigest = tok->args[1];
+ sk_hexdigest = tok->args[2];
+ a = crypto_digest_algorithm_parse_name(algname);
+ if (a<0) {
+ log_warn(LD_DIR, "Unknown digest algorithm %s; skipping",
+ escaped(algname));
+ continue;
+ }
+ alg = a;
+ }
if (!tok->object_type ||
strcmp(tok->object_type, "SIGNATURE") ||
@@@ -3174,11 -2518,11 +3174,11 @@@
goto err;
}
- if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
+ if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
base16_decode(declared_identity, sizeof(declared_identity),
- tok->args[0], HEX_DIGEST_LEN) < 0) {
+ id_hexdigest, HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding declared identity %s in "
- "network-status vote.", escaped(tok->args[0]));
+ "network-status vote.", escaped(id_hexdigest));
goto err;
}
if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) {
@@@ -3186,15 -2530,11 +3186,15 @@@
"any declared directory source.");
goto err;
}
- if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
- base16_decode(v->signing_key_digest, sizeof(v->signing_key_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]));
+ sig = tor_malloc_zero(sizeof(document_signature_t));
+ memcpy(sig->identity_digest, v->identity_digest, DIGEST_LEN);
+ sig->alg = alg;
+ if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
+ base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest),
+ sk_hexdigest, HEX_DIGEST_LEN) < 0) {
+ log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
+ "network-status vote.", escaped(sk_hexdigest));
+ tor_free(sig);
goto err;
}
@@@ -3203,49 -2543,35 +3203,49 @@@
DIGEST_LEN)) {
log_warn(LD_DIR, "Digest mismatch between declared and actual on "
"network-status vote.");
+ tor_free(sig);
goto err;
}
}
+ if (voter_get_sig_by_algorithm(v, sig->alg)) {
+ /* We already parsed a vote with this algorithm from this voter. Use the
+ first one. */
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
+ "that contains two votes from the same voter with the same "
+ "algorithm. Ignoring the second vote.");
+ tor_free(sig);
+ continue;
+ }
+
if (ns->type != NS_TYPE_CONSENSUS) {
- if (check_signature_token(ns_digest, tok, ns->cert->signing_key, 0,
- "network-status vote"))
+ if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN,
+ tok, ns->cert->signing_key, 0,
+ "network-status vote")) {
+ tor_free(sig);
goto err;
- v->good_signature = 1;
+ }
+ sig->good_signature = 1;
} else {
- if (tok->object_size >= INT_MAX) {
- if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING)
++ if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
+ tor_free(sig);
goto err;
- /* We already parsed a vote from this voter. Use the first one. */
- if (v->signature) {
- log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
- "that contains two votes from the same voter. Ignoring "
- "the second vote.");
- continue;
}
-
- v->signature = tor_memdup(tok->object_body, tok->object_size);
- v->signature_len = (int) tok->object_size;
+ sig->signature = tor_memdup(tok->object_body, tok->object_size);
+ sig->signature_len = (int) tok->object_size;
}
+ smartlist_add(v->sigs, sig);
+
++n_signatures;
- });
+ } SMARTLIST_FOREACH_END(_tok);
if (! n_signatures) {
log_warn(LD_DIR, "No signatures on networkstatus vote.");
goto err;
+ } else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) {
+ log_warn(LD_DIR, "Received more than one signature on a "
+ "network-status vote.");
+ goto err;
}
if (eos_out)
@@@ -3253,31 -2579,27 +3253,31 @@@
goto done;
err:
- if (ns)
- networkstatus_vote_free(ns);
+ dump_desc(s_dup, "v3 networkstatus");
+ networkstatus_vote_free(ns);
ns = NULL;
done:
if (tokens) {
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
}
if (voter) {
+ if (voter->sigs) {
+ SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
+ document_signature_free(sig));
+ smartlist_free(voter->sigs);
+ }
tor_free(voter->nickname);
tor_free(voter->address);
tor_free(voter->contact);
tor_free(voter);
}
if (rs_tokens) {
- SMARTLIST_FOREACH(rs_tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(rs_tokens, directory_token_t *, t, token_clear(t));
smartlist_free(rs_tokens);
}
if (footer_tokens) {
- SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_clear(t));
smartlist_free(footer_tokens);
}
if (area) {
@@@ -3290,35 -2612,6 +3290,35 @@@
return ns;
}
+/** Return the digests_t that holds the digests of the
+ * <b>flavor_name</b>-flavored networkstatus according to the detached
+ * signatures document <b>sigs</b>, allocating a new digests_t as neeeded. */
+static digests_t *
+detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
+{
+ digests_t *d = strmap_get(sigs->digests, flavor_name);
+ if (!d) {
+ d = tor_malloc_zero(sizeof(digests_t));
+ strmap_set(sigs->digests, flavor_name, d);
+ }
+ return d;
+}
+
+/** Return the list of signatures of the <b>flavor_name</b>-flavored
+ * networkstatus according to the detached signatures document <b>sigs</b>,
+ * allocating a new digests_t as neeeded. */
+static smartlist_t *
+detached_get_signatures(ns_detached_signatures_t *sigs,
+ const char *flavor_name)
+{
+ smartlist_t *sl = strmap_get(sigs->signatures, flavor_name);
+ if (!sl) {
+ sl = smartlist_create();
+ strmap_set(sigs->signatures, flavor_name, sl);
+ }
+ return sl;
+}
+
/** Parse a detached v3 networkstatus signature document between <b>s</b> and
* <b>eos</b> and return the result. Return -1 on failure. */
ns_detached_signatures_t *
@@@ -3328,13 -2621,10 +3328,13 @@@ networkstatus_parse_detached_signatures
* networkstatus_parse_vote_from_string(). */
directory_token_t *tok;
memarea_t *area = NULL;
+ digests_t *digests;
smartlist_t *tokens = smartlist_create();
ns_detached_signatures_t *sigs =
tor_malloc_zero(sizeof(ns_detached_signatures_t));
+ sigs->digests = strmap_new();
+ sigs->signatures = strmap_new();
if (!eos)
eos = s + strlen(s);
@@@ -3346,57 -2636,18 +3346,57 @@@
goto err;
}
- tok = find_by_keyword(tokens, K_CONSENSUS_DIGEST);
- if (strlen(tok->args[0]) != HEX_DIGEST_LEN) {
- log_warn(LD_DIR, "Wrong length on consensus-digest in detached "
- "networkstatus signatures");
- goto err;
- }
- if (base16_decode(sigs->networkstatus_digest, DIGEST_LEN,
- tok->args[0], strlen(tok->args[0])) < 0) {
- log_warn(LD_DIR, "Bad encoding on on consensus-digest in detached "
- "networkstatus signatures");
- goto err;
- }
+ /* Grab all the digest-like tokens. */
+ SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
+ const char *algname;
+ digest_algorithm_t alg;
+ const char *flavor;
+ const char *hexdigest;
+ size_t expected_length;
+
+ tok = _tok;
+
+ if (tok->tp == K_CONSENSUS_DIGEST) {
+ algname = "sha1";
+ alg = DIGEST_SHA1;
+ flavor = "ns";
+ hexdigest = tok->args[0];
+ } else if (tok->tp == K_ADDITIONAL_DIGEST) {
+ int a = crypto_digest_algorithm_parse_name(tok->args[1]);
+ if (a<0) {
+ log_warn(LD_DIR, "Unrecognized algorithm name %s", tok->args[0]);
+ continue;
+ }
+ alg = (digest_algorithm_t) a;
+ flavor = tok->args[0];
+ algname = tok->args[1];
+ hexdigest = tok->args[2];
+ } else {
+ continue;
+ }
+
+ expected_length =
+ (alg == DIGEST_SHA1) ? HEX_DIGEST_LEN : HEX_DIGEST256_LEN;
+
+ if (strlen(hexdigest) != expected_length) {
+ log_warn(LD_DIR, "Wrong length on consensus-digest in detached "
+ "networkstatus signatures");
+ goto err;
+ }
+ digests = detached_get_digests(sigs, flavor);
+ tor_assert(digests);
+ if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) {
+ log_warn(LD_DIR, "Multiple digests for %s with %s on detached "
+ "signatures document", flavor, algname);
+ continue;
+ }
+ if (base16_decode(digests->d[alg], DIGEST256_LEN,
+ hexdigest, strlen(hexdigest)) < 0) {
+ log_warn(LD_DIR, "Bad encoding on consensus-digest in detached "
+ "networkstatus signatures");
+ goto err;
+ }
+ } SMARTLIST_FOREACH_END(_tok);
tok = find_by_keyword(tokens, K_VALID_AFTER);
if (parse_iso_time(tok->args[0], &sigs->valid_after)) {
@@@ -3416,102 -2667,57 +3416,102 @@@
goto err;
}
- sigs->signatures = smartlist_create();
- SMARTLIST_FOREACH(tokens, directory_token_t *, _tok,
- {
- char id_digest[DIGEST_LEN];
- char sk_digest[DIGEST_LEN];
- networkstatus_voter_info_t *voter;
+ SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
+ const char *id_hexdigest;
+ const char *sk_hexdigest;
+ const char *algname;
+ const char *flavor;
+ digest_algorithm_t alg;
+
+ char id_digest[DIGEST_LEN];
+ char sk_digest[DIGEST_LEN];
+ smartlist_t *siglist;
+ document_signature_t *sig;
+ int is_duplicate;
- tok = _tok;
- if (tok->tp != K_DIRECTORY_SIGNATURE)
- continue;
+ tok = _tok;
+ if (tok->tp == K_DIRECTORY_SIGNATURE) {
tor_assert(tok->n_args >= 2);
+ flavor = "ns";
+ algname = "sha1";
+ id_hexdigest = tok->args[0];
+ sk_hexdigest = tok->args[1];
+ } else if (tok->tp == K_ADDITIONAL_SIGNATURE) {
+ tor_assert(tok->n_args >= 4);
+ flavor = tok->args[0];
+ algname = tok->args[1];
+ id_hexdigest = tok->args[2];
+ sk_hexdigest = tok->args[3];
+ } else {
+ continue;
+ }
- if (!tok->object_type ||
- strcmp(tok->object_type, "SIGNATURE") ||
- tok->object_size < 128 || tok->object_size > 512) {
- log_warn(LD_DIR, "Bad object type or length on directory-signature");
- goto err;
+ {
+ int a = crypto_digest_algorithm_parse_name(algname);
+ if (a<0) {
+ log_warn(LD_DIR, "Unrecognized algorithm name %s", algname);
+ continue;
}
+ alg = (digest_algorithm_t) a;
+ }
- if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
- base16_decode(id_digest, sizeof(id_digest),
- tok->args[0], HEX_DIGEST_LEN) < 0) {
- log_warn(LD_DIR, "Error decoding declared identity %s in "
- "network-status vote.", escaped(tok->args[0]));
- goto err;
- }
- if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
- base16_decode(sk_digest, sizeof(sk_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 (!tok->object_type ||
+ strcmp(tok->object_type, "SIGNATURE") ||
+ tok->object_size < 128 || tok->object_size > 512) {
+ log_warn(LD_DIR, "Bad object type or length on directory-signature");
+ goto err;
+ }
- voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
- memcpy(voter->identity_digest, id_digest, DIGEST_LEN);
- memcpy(voter->signing_key_digest, sk_digest, DIGEST_LEN);
- if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING)
- goto err;
- voter->signature = tor_memdup(tok->object_body, tok->object_size);
- voter->signature_len = (int) tok->object_size;
+ if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
+ base16_decode(id_digest, sizeof(id_digest),
+ id_hexdigest, HEX_DIGEST_LEN) < 0) {
+ log_warn(LD_DIR, "Error decoding declared identity %s in "
+ "network-status vote.", escaped(id_hexdigest));
+ goto err;
+ }
+ if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
+ base16_decode(sk_digest, sizeof(sk_digest),
+ sk_hexdigest, HEX_DIGEST_LEN) < 0) {
+ log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
+ "network-status vote.", escaped(sk_hexdigest));
+ goto err;
+ }
- smartlist_add(sigs->signatures, voter);
+ siglist = detached_get_signatures(sigs, flavor);
+ is_duplicate = 0;
+ SMARTLIST_FOREACH(siglist, document_signature_t *, s, {
+ if (s->alg == alg &&
+ !memcmp(id_digest, s->identity_digest, DIGEST_LEN) &&
+ !memcmp(sk_digest, s->signing_key_digest, DIGEST_LEN)) {
+ is_duplicate = 1;
+ }
});
+ if (is_duplicate) {
+ log_warn(LD_DIR, "Two signatures with identical keys and algorithm "
+ "found.");
+ continue;
+ }
+
+ sig = tor_malloc_zero(sizeof(document_signature_t));
+ sig->alg = alg;
+ memcpy(sig->identity_digest, id_digest, DIGEST_LEN);
+ memcpy(sig->signing_key_digest, sk_digest, DIGEST_LEN);
- if (tok->object_size >= INT_MAX) {
++ if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
+ tor_free(sig);
+ goto err;
+ }
+ sig->signature = tor_memdup(tok->object_body, tok->object_size);
+ sig->signature_len = (int) tok->object_size;
+
+ smartlist_add(siglist, sig);
+ } SMARTLIST_FOREACH_END(_tok);
goto done;
err:
ns_detached_signatures_free(sigs);
sigs = NULL;
done:
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
if (area) {
DUMP_AREA(area, "detached signatures");
@@@ -3567,7 -2773,7 +3567,7 @@@ router_parse_addr_policy_item_from_stri
err:
r = NULL;
done:
- token_free(tok);
+ token_clear(tok);
if (area) {
DUMP_AREA(area, "policy item");
memarea_drop_all(area);
@@@ -3690,8 -2896,9 +3690,8 @@@ assert_addr_policy_ok(smartlist_t *lst
/** Free all resources allocated for <b>tok</b> */
static void
-token_free(directory_token_t *tok)
+token_clear(directory_token_t *tok)
{
- tor_assert(tok);
if (tok->key)
crypto_free_pk_env(tok->key);
}
@@@ -3703,7 -2910,7 +3703,7 @@@
#define RET_ERR(msg) \
STMT_BEGIN \
- if (tok) token_free(tok); \
+ if (tok) token_clear(tok); \
tok = ALLOC_ZERO(sizeof(directory_token_t)); \
tok->tp = _ERR; \
tok->error = STRDUP(msg); \
@@@ -3814,6 -3021,10 +3814,10 @@@ static directory_token_t
get_next_token(memarea_t *area,
const char **s, const char *eos, token_rule_t *table)
{
+ /** Reject any object at least this big; it is probably an overflow, an
+ * attack, a bug, or some other nonsense. */
+ #define MAX_UNPARSED_OBJECT_SIZE (128*1024)
+
const char *next, *eol, *obstart;
size_t obname_len;
int i;
@@@ -3898,7 -3109,8 +3902,8 @@@
obstart = *s; /* Set obstart to start of object spec */
if (*s+16 >= eol || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */
- strcmp_len(eol-5, "-----", 5)) { /* nuls or invalid endings */
+ strcmp_len(eol-5, "-----", 5) || /* nuls or invalid endings */
+ (eol-*s) > MAX_UNPARSED_OBJECT_SIZE) { /* name too long */
RET_ERR("Malformed object: bad begin line");
}
tok->object_type = STRNDUP(*s+11, eol-*s-16);
@@@ -3923,13 -3135,16 +3928,16 @@@
ebuf[sizeof(ebuf)-1] = '\0';
RET_ERR(ebuf);
}
+ if (next - *s > MAX_UNPARSED_OBJECT_SIZE)
+ RET_ERR("Couldn't parse object: missing footer or object much too big.");
+
if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a public key */
tok->key = crypto_new_pk_env();
if (crypto_pk_read_public_key_from_string(tok->key, obstart, eol-obstart))
RET_ERR("Couldn't parse public key.");
} else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */
tok->key = crypto_new_pk_env();
- if (crypto_pk_read_private_key_from_string(tok->key, obstart))
+ if (crypto_pk_read_private_key_from_string(tok->key, obstart, eol-obstart))
RET_ERR("Couldn't parse private key.");
} else { /* If it's something else, try to base64-decode it */
int r;
@@@ -3984,7 -3199,7 +3992,7 @@@ tokenize_string(memarea_t *area
tok = get_next_token(area, s, end, table);
if (tok->tp == _ERR) {
log_warn(LD_DIR, "parse error: %s", tok->error);
- token_free(tok);
+ token_clear(tok);
return -1;
}
++counts[tok->tp];
@@@ -4098,11 -3313,17 +4106,11 @@@ find_all_exitpolicy(smartlist_t *s
return out;
}
-/** Compute the SHA-1 digest of the substring of <b>s</b> taken from the first
- * occurrence of <b>start_str</b> through the first instance of c after the
- * first subsequent occurrence of <b>end_str</b>; store the 20-byte result in
- * <b>digest</b>; return 0 on success.
- *
- * If no such substring exists, return -1.
- */
static int
-router_get_hash_impl(const char *s, size_t s_len, char *digest,
+router_get_hash_impl_helper(const char *s, size_t s_len,
const char *start_str,
- const char *end_str, char end_c)
+ const char *end_str, char end_c,
+ const char **start_out, const char **end_out)
{
const char *start, *end;
start = tor_memstr(s, s_len, start_str);
@@@ -4129,214 -3350,14 +4137,214 @@@
}
++end;
- if (crypto_digest(digest, start, end-start)) {
- log_warn(LD_BUG,"couldn't compute digest");
+ *start_out = start;
+ *end_out = end;
+ return 0;
+}
+
+/** Compute the digest of the substring of <b>s</b> taken from the first
+ * occurrence of <b>start_str</b> through the first instance of c after the
+ * first subsequent occurrence of <b>end_str</b>; store the 20-byte result in
+ * <b>digest</b>; return 0 on success.
+ *
+ * If no such substring exists, return -1.
+ */
+static int
+router_get_hash_impl(const char *s, size_t s_len, char *digest,
+ const char *start_str,
+ const char *end_str, char end_c,
+ digest_algorithm_t alg)
+{
+ const char *start=NULL, *end=NULL;
+ if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,
+ &start,&end)<0)
return -1;
+
+ if (alg == DIGEST_SHA1) {
+ if (crypto_digest(digest, start, end-start)) {
+ log_warn(LD_BUG,"couldn't compute digest");
+ return -1;
+ }
+ } else {
+ if (crypto_digest256(digest, start, end-start, alg)) {
+ log_warn(LD_BUG,"couldn't compute digest");
+ return -1;
+ }
}
return 0;
}
+/** As router_get_hash_impl, but compute all hashes. */
+static int
+router_get_hashes_impl(const char *s, size_t s_len, digests_t *digests,
+ const char *start_str,
+ const char *end_str, char end_c)
+{
+ const char *start=NULL, *end=NULL;
+ if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,
+ &start,&end)<0)
+ return -1;
+
+ if (crypto_digest_all(digests, start, end-start)) {
+ log_warn(LD_BUG,"couldn't compute digests");
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Assuming that s starts with a microdesc, return the start of the
+ * *NEXT* one. Return NULL on "not found." */
+static const char *
+find_start_of_next_microdesc(const char *s, const char *eos)
+{
+ int started_with_annotations;
+ s = eat_whitespace_eos(s, eos);
+ if (!s)
+ return NULL;
+
+#define CHECK_LENGTH() STMT_BEGIN \
+ if (s+32 > eos) \
+ return NULL; \
+ STMT_END
+
+#define NEXT_LINE() STMT_BEGIN \
+ s = memchr(s, '\n', eos-s); \
+ if (!s || s+1 >= eos) \
+ return NULL; \
+ s++; \
+ STMT_END
+
+ CHECK_LENGTH();
+
+ started_with_annotations = (*s == '@');
+
+ if (started_with_annotations) {
+ /* Start by advancing to the first non-annotation line. */
+ while (*s == '@')
+ NEXT_LINE();
+ }
+ CHECK_LENGTH();
+
+ /* Now we should be pointed at an onion-key line. If we are, then skip
+ * it. */
+ if (!strcmpstart(s, "onion-key"))
+ NEXT_LINE();
+
+ /* Okay, now we're pointed at the first line of the microdescriptor which is
+ not an annotation or onion-key. The next line that _is_ an annotation or
+ onion-key is the start of the next microdescriptor. */
+ while (s+32 < eos) {
+ if (*s == '@' || !strcmpstart(s, "onion-key"))
+ return s;
+ NEXT_LINE();
+ }
+ return NULL;
+
+#undef CHECK_LENGTH
+#undef NEXT_LINE
+}
+
+/** Parse as many microdescriptors as are found from the string starting at
+ * <b>s</b> and ending at <b>eos</b>. If allow_annotations is set, read any
+ * annotations we recognize and ignore ones we don't. If <b>copy_body</b> is
+ * true, then strdup the bodies of the microdescriptors. Return all newly
+ * parsed microdescriptors in a newly allocated smartlist_t. */
+smartlist_t *
+microdescs_parse_from_string(const char *s, const char *eos,
+ int allow_annotations, int copy_body)
+{
+ smartlist_t *tokens;
+ smartlist_t *result;
+ microdesc_t *md = NULL;
+ memarea_t *area;
+ const char *start = s;
+ const char *start_of_next_microdesc;
+ int flags = allow_annotations ? TS_ANNOTATIONS_OK : 0;
+
+ directory_token_t *tok;
+
+ if (!eos)
+ eos = s + strlen(s);
+
+ s = eat_whitespace_eos(s, eos);
+ area = memarea_new();
+ result = smartlist_create();
+ tokens = smartlist_create();
+
+ while (s < eos) {
+ start_of_next_microdesc = find_start_of_next_microdesc(s, eos);
+ if (!start_of_next_microdesc)
+ start_of_next_microdesc = eos;
+
+ if (tokenize_string(area, s, start_of_next_microdesc, tokens,
+ microdesc_token_table, flags)) {
+ log_warn(LD_DIR, "Unparseable microdescriptor");
+ goto next;
+ }
+
+ md = tor_malloc_zero(sizeof(microdesc_t));
+ {
+ const char *cp = tor_memstr(s, start_of_next_microdesc-s,
+ "onion-key");
+ tor_assert(cp);
+
+ md->bodylen = start_of_next_microdesc - cp;
+ if (copy_body)
+ md->body = tor_strndup(cp, md->bodylen);
+ else
+ md->body = (char*)cp;
+ md->off = cp - start;
+ }
+
+ if ((tok = find_opt_by_keyword(tokens, A_LAST_LISTED))) {
+ if (parse_iso_time(tok->args[0], &md->last_listed)) {
+ log_warn(LD_DIR, "Bad last-listed time in microdescriptor");
+ goto next;
+ }
+ }
+
+ tok = find_by_keyword(tokens, K_ONION_KEY);
+ md->onion_pkey = tok->key;
+ tok->key = NULL;
+
+ if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) {
+ int i;
+ md->family = smartlist_create();
+ for (i=0;i<tok->n_args;++i) {
+ if (!is_legal_nickname_or_hexdigest(tok->args[i])) {
+ log_warn(LD_DIR, "Illegal nickname %s in family line",
+ escaped(tok->args[i]));
+ goto next;
+ }
+ smartlist_add(md->family, tor_strdup(tok->args[i]));
+ }
+ }
+
+ if ((tok = find_opt_by_keyword(tokens, K_P))) {
+ md->exitsummary = tor_strdup(tok->args[0]);
+ }
+
+ crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);
+
+ smartlist_add(result, md);
+
+ md = NULL;
+ next:
+ microdesc_free(md);
+
+ memarea_clear(area);
+ smartlist_clear(tokens);
+ s = start_of_next_microdesc;
+ }
+
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+
+ return result;
+}
+
/** Parse the Tor version of the platform string <b>platform</b>,
* and compare it to the version in <b>cutoff</b>. Return 1 if
* the router is at least as new as the cutoff, else return 0.
@@@ -4361,7 -3382,7 +4369,7 @@@ tor_version_as_new_as(const char *platf
if (!*start) return 0;
s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
s2 = (char*)eat_whitespace(s);
- if (!strcmpstart(s2, "(r"))
+ if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-"))
s = (char*)find_whitespace(s2);
if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
@@@ -4457,23 -3478,6 +4465,23 @@@ tor_version_parse(const char *s, tor_ve
if (!strcmpstart(cp, "(r")) {
cp += 2;
out->svn_revision = (int) strtol(cp,&eos,10);
+ } else if (!strcmpstart(cp, "(git-")) {
+ char *close_paren = strchr(cp, ')');
+ int hexlen;
+ char digest[DIGEST_LEN];
+ if (! close_paren)
+ return -1;
+ cp += 5;
+ if (close_paren-cp > HEX_DIGEST_LEN)
+ return -1;
+ hexlen = (int)(close_paren-cp);
+ memset(digest, 0, sizeof(digest));
+ if ( hexlen == 0 || (hexlen % 2) == 1)
+ return -1;
+ if (base16_decode(digest, hexlen/2, cp, hexlen))
+ return -1;
+ memcpy(out->git_tag, digest, hexlen/2);
+ out->git_tag_len = hexlen/2;
}
return 0;
@@@ -4499,14 -3503,8 +4507,14 @@@ tor_version_compare(tor_version_t *a, t
return i;
else if ((i = strcmp(a->status_tag, b->status_tag)))
return i;
+ else if ((i = a->svn_revision - b->svn_revision))
+ return i;
+ else if ((i = a->git_tag_len - b->git_tag_len))
+ return i;
+ else if (a->git_tag_len)
+ return memcmp(a->git_tag, b->git_tag, a->git_tag_len);
else
- return a->svn_revision - b->svn_revision;
+ return 0;
}
/** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
@@@ -4595,7 -3593,7 +4603,7 @@@ rend_parse_v2_service_descriptor(rend_s
/* Compute descriptor hash for later validation. */
if (router_get_hash_impl(desc, strlen(desc), desc_hash,
"rendezvous-service-descriptor ",
- "\nsignature", '\n') < 0) {
+ "\nsignature", '\n', DIGEST_SHA1) < 0) {
log_warn(LD_REND, "Couldn't compute descriptor hash.");
goto err;
}
@@@ -4714,7 -3712,7 +4722,7 @@@
/* Parse and verify signature. */
tok = find_by_keyword(tokens, R_SIGNATURE);
note_crypto_pk_op(VERIFY_RTR);
- if (check_signature_token(desc_hash, tok, result->pk, 0,
+ if (check_signature_token(desc_hash, DIGEST_LEN, tok, result->pk, 0,
"v2 rendezvous service descriptor") < 0)
goto err;
/* Verify that descriptor ID belongs to public key and secret ID part. */
@@@ -4728,11 -3726,12 +4736,11 @@@
}
goto done;
err:
- if (result)
- rend_service_descriptor_free(result);
+ rend_service_descriptor_free(result);
result = NULL;
done:
if (tokens) {
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
}
if (area)
@@@ -4890,7 -3889,7 +4898,7 @@@ rend_parse_introduction_points(rend_ser
eos = eos+1;
tor_assert(eos <= intro_points_encoded+intro_points_encoded_size);
/* Free tokens and clear token list. */
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_clear(tokens);
memarea_clear(area);
/* Tokenize string. */
@@@ -4963,7 -3962,7 +4971,7 @@@
done:
/* Free tokens and clear token list. */
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
if (area)
memarea_drop_all(area);
@@@ -5002,7 -4001,7 +5010,7 @@@ rend_parse_client_keys(strmap_t *parsed
else
eos = eos + 1;
/* Free tokens and clear token list. */
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_clear(tokens);
memarea_clear(area);
/* Tokenize string. */
@@@ -5074,7 -4073,7 +5082,7 @@@
result = -1;
done:
/* Free tokens and clear token list. */
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
if (area)
memarea_drop_all(area);
diff --combined src/test/test_dir.c
index c7c5a0b,0000000..e618150
mode 100644,000000..100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@@ -1,1317 -1,0 +1,1317 @@@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define DIRSERV_PRIVATE
+#define DIRVOTE_PRIVATE
+#define ROUTER_PRIVATE
+#include "or.h"
+#include "directory.h"
+#include "dirserv.h"
+#include "dirvote.h"
+#include "networkstatus.h"
+#include "router.h"
+#include "routerlist.h"
+#include "routerparse.h"
+#include "test.h"
+
+static void
+test_dir_nicknames(void)
+{
+ test_assert( is_legal_nickname("a"));
+ test_assert(!is_legal_nickname(""));
+ test_assert(!is_legal_nickname("abcdefghijklmnopqrst")); /* 20 chars */
+ test_assert(!is_legal_nickname("hyphen-")); /* bad char */
+ test_assert( is_legal_nickname("abcdefghijklmnopqrs")); /* 19 chars */
+ test_assert(!is_legal_nickname("$AAAAAAAA01234AAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+ /* valid */
+ test_assert( is_legal_nickname_or_hexdigest(
+ "$AAAAAAAA01234AAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+ test_assert( is_legal_nickname_or_hexdigest(
+ "$AAAAAAAA01234AAAAAAAAAAAAAAAAAAAAAAAAAAA=fred"));
+ test_assert( is_legal_nickname_or_hexdigest(
+ "$AAAAAAAA01234AAAAAAAAAAAAAAAAAAAAAAAAAAA~fred"));
+ /* too short */
+ test_assert(!is_legal_nickname_or_hexdigest(
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+ /* illegal char */
+ test_assert(!is_legal_nickname_or_hexdigest(
+ "$AAAAAAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+ /* hex part too long */
+ test_assert(!is_legal_nickname_or_hexdigest(
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+ test_assert(!is_legal_nickname_or_hexdigest(
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=fred"));
+ /* Bad nickname */
+ test_assert(!is_legal_nickname_or_hexdigest(
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="));
+ test_assert(!is_legal_nickname_or_hexdigest(
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~"));
+ test_assert(!is_legal_nickname_or_hexdigest(
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~hyphen-"));
+ test_assert(!is_legal_nickname_or_hexdigest(
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~"
+ "abcdefghijklmnoppqrst"));
+ /* Bad extra char. */
+ test_assert(!is_legal_nickname_or_hexdigest(
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!"));
+ test_assert(is_legal_nickname_or_hexdigest("xyzzy"));
+ test_assert(is_legal_nickname_or_hexdigest("abcdefghijklmnopqrs"));
+ test_assert(!is_legal_nickname_or_hexdigest("abcdefghijklmnopqrst"));
+ done:
+ ;
+}
+
+/** Run unit tests for router descriptor generation logic. */
+static void
+test_dir_formats(void)
+{
+ char buf[8192], buf2[8192];
+ char platform[256];
+ char fingerprint[FINGERPRINT_LEN+1];
+ char *pk1_str = NULL, *pk2_str = NULL, *pk3_str = NULL, *cp;
+ size_t pk1_str_len, pk2_str_len, pk3_str_len;
+ routerinfo_t *r1=NULL, *r2=NULL;
+ crypto_pk_env_t *pk1 = NULL, *pk2 = NULL, *pk3 = NULL;
+ routerinfo_t *rp1 = NULL;
+ addr_policy_t *ex1, *ex2;
+ routerlist_t *dir1 = NULL, *dir2 = NULL;
+
+ pk1 = pk_generate(0);
+ pk2 = pk_generate(1);
+ pk3 = pk_generate(2);
+
+ test_assert(pk1 && pk2 && pk3);
+
+ get_platform_str(platform, sizeof(platform));
+ r1 = tor_malloc_zero(sizeof(routerinfo_t));
+ r1->address = tor_strdup("18.244.0.1");
+ r1->addr = 0xc0a80001u; /* 192.168.0.1 */
+ r1->cache_info.published_on = 0;
+ r1->or_port = 9000;
+ r1->dir_port = 9003;
+ r1->onion_pkey = crypto_pk_dup_key(pk1);
+ r1->identity_pkey = crypto_pk_dup_key(pk2);
+ r1->bandwidthrate = 1000;
+ r1->bandwidthburst = 5000;
+ r1->bandwidthcapacity = 10000;
+ r1->exit_policy = NULL;
+ r1->nickname = tor_strdup("Magri");
+ r1->platform = tor_strdup(platform);
+
+ ex1 = tor_malloc_zero(sizeof(addr_policy_t));
+ ex2 = tor_malloc_zero(sizeof(addr_policy_t));
+ ex1->policy_type = ADDR_POLICY_ACCEPT;
+ tor_addr_from_ipv4h(&ex1->addr, 0);
+ ex1->maskbits = 0;
+ ex1->prt_min = ex1->prt_max = 80;
+ ex2->policy_type = ADDR_POLICY_REJECT;
+ tor_addr_from_ipv4h(&ex2->addr, 18<<24);
+ ex2->maskbits = 8;
+ ex2->prt_min = ex2->prt_max = 24;
+ r2 = tor_malloc_zero(sizeof(routerinfo_t));
+ r2->address = tor_strdup("1.1.1.1");
+ r2->addr = 0x0a030201u; /* 10.3.2.1 */
+ r2->platform = tor_strdup(platform);
+ r2->cache_info.published_on = 5;
+ r2->or_port = 9005;
+ r2->dir_port = 0;
+ r2->onion_pkey = crypto_pk_dup_key(pk2);
+ r2->identity_pkey = crypto_pk_dup_key(pk1);
+ r2->bandwidthrate = r2->bandwidthburst = r2->bandwidthcapacity = 3000;
+ r2->exit_policy = smartlist_create();
+ smartlist_add(r2->exit_policy, ex2);
+ smartlist_add(r2->exit_policy, ex1);
+ r2->nickname = tor_strdup("Fred");
+
+ test_assert(!crypto_pk_write_public_key_to_string(pk1, &pk1_str,
+ &pk1_str_len));
+ test_assert(!crypto_pk_write_public_key_to_string(pk2 , &pk2_str,
+ &pk2_str_len));
+ test_assert(!crypto_pk_write_public_key_to_string(pk3 , &pk3_str,
+ &pk3_str_len));
+
+ memset(buf, 0, 2048);
+ test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
+
+ strlcpy(buf2, "router Magri 18.244.0.1 9000 0 9003\n"
+ "platform Tor "VERSION" on ", sizeof(buf2));
+ strlcat(buf2, get_uname(), sizeof(buf2));
+ strlcat(buf2, "\n"
+ "opt protocols Link 1 2 Circuit 1\n"
+ "published 1970-01-01 00:00:00\n"
+ "opt fingerprint ", sizeof(buf2));
+ test_assert(!crypto_pk_get_fingerprint(pk2, fingerprint, 1));
+ strlcat(buf2, fingerprint, sizeof(buf2));
+ strlcat(buf2, "\nuptime 0\n"
+ /* XXX the "0" above is hard-coded, but even if we made it reflect
+ * uptime, that still wouldn't make it right, because the two
+ * descriptors might be made on different seconds... hm. */
+ "bandwidth 1000 5000 10000\n"
+ "onion-key\n", sizeof(buf2));
+ strlcat(buf2, pk1_str, sizeof(buf2));
+ strlcat(buf2, "signing-key\n", sizeof(buf2));
+ strlcat(buf2, pk2_str, sizeof(buf2));
+ strlcat(buf2, "opt hidden-service-dir\n", sizeof(buf2));
+ strlcat(buf2, "reject *:*\nrouter-signature\n", sizeof(buf2));
+ buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
+ * twice */
+
+ test_streq(buf, buf2);
+
+ test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
+ cp = buf;
+ rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL);
+ test_assert(rp1);
+ test_streq(rp1->address, r1->address);
+ test_eq(rp1->or_port, r1->or_port);
+ //test_eq(rp1->dir_port, r1->dir_port);
+ test_eq(rp1->bandwidthrate, r1->bandwidthrate);
+ test_eq(rp1->bandwidthburst, r1->bandwidthburst);
+ test_eq(rp1->bandwidthcapacity, r1->bandwidthcapacity);
+ test_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0);
+ test_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0);
+ //test_assert(rp1->exit_policy == NULL);
+
+#if 0
+ /* XXX Once we have exit policies, test this again. XXX */
+ strlcpy(buf2, "router tor.tor.tor 9005 0 0 3000\n", sizeof(buf2));
+ strlcat(buf2, pk2_str, sizeof(buf2));
+ strlcat(buf2, "signing-key\n", sizeof(buf2));
+ strlcat(buf2, pk1_str, sizeof(buf2));
+ strlcat(buf2, "accept *:80\nreject 18.*:24\n\n", sizeof(buf2));
+ test_assert(router_dump_router_to_string(buf, 2048, &r2, pk2)>0);
+ test_streq(buf, buf2);
+
+ cp = buf;
+ rp2 = router_parse_entry_from_string(&cp,1);
+ test_assert(rp2);
+ test_streq(rp2->address, r2.address);
+ test_eq(rp2->or_port, r2.or_port);
+ test_eq(rp2->dir_port, r2.dir_port);
+ test_eq(rp2->bandwidth, r2.bandwidth);
+ test_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0);
+ test_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0);
+ test_eq(rp2->exit_policy->policy_type, EXIT_POLICY_ACCEPT);
+ test_streq(rp2->exit_policy->string, "accept *:80");
+ test_streq(rp2->exit_policy->address, "*");
+ test_streq(rp2->exit_policy->port, "80");
+ test_eq(rp2->exit_policy->next->policy_type, EXIT_POLICY_REJECT);
+ test_streq(rp2->exit_policy->next->string, "reject 18.*:24");
+ test_streq(rp2->exit_policy->next->address, "18.*");
+ test_streq(rp2->exit_policy->next->port, "24");
+ test_assert(rp2->exit_policy->next->next == NULL);
+
+ /* Okay, now for the directories. */
+ {
+ fingerprint_list = smartlist_create();
+ crypto_pk_get_fingerprint(pk2, buf, 1);
+ add_fingerprint_to_dir("Magri", buf, fingerprint_list);
+ crypto_pk_get_fingerprint(pk1, buf, 1);
+ add_fingerprint_to_dir("Fred", buf, fingerprint_list);
+ }
+
+ {
+ char d[DIGEST_LEN];
+ const char *m;
+ /* XXXX NM re-enable. */
+ /* Make sure routers aren't too far in the past any more. */
+ r1->cache_info.published_on = time(NULL);
+ r2->cache_info.published_on = time(NULL)-3*60*60;
+ test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
+ test_eq(dirserv_add_descriptor(buf,&m,""), ROUTER_ADDED_NOTIFY_GENERATOR);
+ test_assert(router_dump_router_to_string(buf, 2048, r2, pk1)>0);
+ test_eq(dirserv_add_descriptor(buf,&m,""), ROUTER_ADDED_NOTIFY_GENERATOR);
+ get_options()->Nickname = tor_strdup("DirServer");
+ test_assert(!dirserv_dump_directory_to_string(&cp,pk3, 0));
+ crypto_pk_get_digest(pk3, d);
+ test_assert(!router_parse_directory(cp));
+ test_eq(2, smartlist_len(dir1->routers));
+ tor_free(cp);
+ }
+#endif
+ dirserv_free_fingerprint_list();
+
+ done:
+ if (r1)
+ routerinfo_free(r1);
+ if (r2)
+ routerinfo_free(r2);
+
+ tor_free(pk1_str);
+ tor_free(pk2_str);
+ tor_free(pk3_str);
+ if (pk1) crypto_free_pk_env(pk1);
+ if (pk2) crypto_free_pk_env(pk2);
+ if (pk3) crypto_free_pk_env(pk3);
+ if (rp1) routerinfo_free(rp1);
+ tor_free(dir1); /* XXXX And more !*/
+ tor_free(dir2); /* And more !*/
+}
+
+static void
+test_dir_versions(void)
+{
+ tor_version_t ver1;
+
+ /* Try out version parsing functionality */
+ test_eq(0, tor_version_parse("0.3.4pre2-cvs", &ver1));
+ test_eq(0, ver1.major);
+ test_eq(3, ver1.minor);
+ test_eq(4, ver1.micro);
+ test_eq(VER_PRE, ver1.status);
+ test_eq(2, ver1.patchlevel);
+ test_eq(0, tor_version_parse("0.3.4rc1", &ver1));
+ test_eq(0, ver1.major);
+ test_eq(3, ver1.minor);
+ test_eq(4, ver1.micro);
+ test_eq(VER_RC, ver1.status);
+ test_eq(1, ver1.patchlevel);
+ test_eq(0, tor_version_parse("1.3.4", &ver1));
+ test_eq(1, ver1.major);
+ test_eq(3, ver1.minor);
+ test_eq(4, ver1.micro);
+ test_eq(VER_RELEASE, ver1.status);
+ test_eq(0, ver1.patchlevel);
+ test_eq(0, tor_version_parse("1.3.4.999", &ver1));
+ test_eq(1, ver1.major);
+ test_eq(3, ver1.minor);
+ test_eq(4, ver1.micro);
+ test_eq(VER_RELEASE, ver1.status);
+ test_eq(999, ver1.patchlevel);
+ test_eq(0, tor_version_parse("0.1.2.4-alpha", &ver1));
+ test_eq(0, ver1.major);
+ test_eq(1, ver1.minor);
+ test_eq(2, ver1.micro);
+ test_eq(4, ver1.patchlevel);
+ test_eq(VER_RELEASE, ver1.status);
+ test_streq("alpha", ver1.status_tag);
+ test_eq(0, tor_version_parse("0.1.2.4", &ver1));
+ test_eq(0, ver1.major);
+ test_eq(1, ver1.minor);
+ test_eq(2, ver1.micro);
+ test_eq(4, ver1.patchlevel);
+ test_eq(VER_RELEASE, ver1.status);
+ test_streq("", ver1.status_tag);
+
+#define tt_versionstatus_op(vs1, op, vs2) \
+ tt_assert_test_type(vs1,vs2,#vs1" "#op" "#vs2,version_status_t, \
+ (_val1 op _val2),"%d")
+#define test_v_i_o(val, ver, lst) \
+ tt_versionstatus_op(val, ==, tor_version_is_obsolete(ver, lst))
+
+ /* make sure tor_version_is_obsolete() works */
+ test_v_i_o(VS_OLD, "0.0.1", "Tor 0.0.2");
+ test_v_i_o(VS_OLD, "0.0.1", "0.0.2, Tor 0.0.3");
+ test_v_i_o(VS_OLD, "0.0.1", "0.0.2,Tor 0.0.3");
+ test_v_i_o(VS_OLD, "0.0.1","0.0.3,BetterTor 0.0.1");
+ test_v_i_o(VS_RECOMMENDED, "0.0.2", "Tor 0.0.2,Tor 0.0.3");
+ test_v_i_o(VS_NEW_IN_SERIES, "0.0.2", "Tor 0.0.2pre1,Tor 0.0.3");
+ test_v_i_o(VS_OLD, "0.0.2", "Tor 0.0.2.1,Tor 0.0.3");
+ test_v_i_o(VS_NEW, "0.1.0", "Tor 0.0.2,Tor 0.0.3");
+ test_v_i_o(VS_RECOMMENDED, "0.0.7rc2", "0.0.7,Tor 0.0.7rc2,Tor 0.0.8");
+ test_v_i_o(VS_OLD, "0.0.5.0", "0.0.5.1-cvs");
+ test_v_i_o(VS_NEW_IN_SERIES, "0.0.5.1-cvs", "0.0.5, 0.0.6");
+ /* Not on list, but newer than any in same series. */
+ test_v_i_o(VS_NEW_IN_SERIES, "0.1.0.3",
+ "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+ /* Series newer than any on list. */
+ test_v_i_o(VS_NEW, "0.1.2.3", "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+ /* Series older than any on list. */
+ test_v_i_o(VS_OLD, "0.0.1.3", "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+ /* Not on list, not newer than any on same series. */
+ test_v_i_o(VS_UNRECOMMENDED, "0.1.0.1",
+ "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+ /* On list, not newer than any on same series. */
+ test_v_i_o(VS_UNRECOMMENDED,
+ "0.1.0.1", "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+ test_eq(0, tor_version_as_new_as("Tor 0.0.5", "0.0.9pre1-cvs"));
+ test_eq(1, tor_version_as_new_as(
+ "Tor 0.0.8 on Darwin 64-121-192-100.c3-0."
+ "sfpo-ubr1.sfrn-sfpo.ca.cable.rcn.com Power Macintosh",
+ "0.0.8rc2"));
+ test_eq(0, tor_version_as_new_as(
+ "Tor 0.0.8 on Darwin 64-121-192-100.c3-0."
+ "sfpo-ubr1.sfrn-sfpo.ca.cable.rcn.com Power Macintosh", "0.0.8.2"));
+
+ /* Now try svn revisions. */
+ test_eq(1, tor_version_as_new_as("Tor 0.2.1.0-dev (r100)",
+ "Tor 0.2.1.0-dev (r99)"));
+ test_eq(1, tor_version_as_new_as("Tor 0.2.1.0-dev (r100) on Banana Jr",
+ "Tor 0.2.1.0-dev (r99) on Hal 9000"));
+ test_eq(1, tor_version_as_new_as("Tor 0.2.1.0-dev (r100)",
+ "Tor 0.2.1.0-dev on Colossus"));
+ test_eq(0, tor_version_as_new_as("Tor 0.2.1.0-dev (r99)",
+ "Tor 0.2.1.0-dev (r100)"));
+ test_eq(0, tor_version_as_new_as("Tor 0.2.1.0-dev (r99) on MCP",
+ "Tor 0.2.1.0-dev (r100) on AM"));
+ test_eq(0, tor_version_as_new_as("Tor 0.2.1.0-dev",
+ "Tor 0.2.1.0-dev (r99)"));
+ test_eq(1, tor_version_as_new_as("Tor 0.2.1.1",
+ "Tor 0.2.1.0-dev (r99)"));
+
+ /* Now try git revisions */
+ test_eq(0, tor_version_parse("0.5.6.7 (git-ff00ff)", &ver1));
+ test_eq(0, ver1.major);
+ test_eq(5, ver1.minor);
+ test_eq(6, ver1.micro);
+ test_eq(7, ver1.patchlevel);
+ test_eq(3, ver1.git_tag_len);
+ test_memeq(ver1.git_tag, "\xff\x00\xff", 3);
+ test_eq(-1, tor_version_parse("0.5.6.7 (git-ff00xx)", &ver1));
+ test_eq(-1, tor_version_parse("0.5.6.7 (git-ff00fff)", &ver1));
+ test_eq(0, tor_version_parse("0.5.6.7 (git ff00fff)", &ver1));
+ done:
+ ;
+}
+
+/** Run unit tests for directory fp_pair functions. */
+static void
+test_dir_fp_pairs(void)
+{
+ smartlist_t *sl = smartlist_create();
+ fp_pair_t *pair;
+
+ dir_split_resource_into_fingerprint_pairs(
+ /* Two pairs, out of order, with one duplicate. */
+ "73656372657420646174612E0000000000FFFFFF-"
+ "557365204145532d32353620696e73746561642e+"
+ "73656372657420646174612E0000000000FFFFFF-"
+ "557365204145532d32353620696e73746561642e+"
+ "48657861646563696d616c2069736e277420736f-"
+ "676f6f6420666f7220686964696e6720796f7572.z", sl);
+
+ test_eq(smartlist_len(sl), 2);
+ pair = smartlist_get(sl, 0);
+ test_memeq(pair->first, "Hexadecimal isn't so", DIGEST_LEN);
+ test_memeq(pair->second, "good for hiding your", DIGEST_LEN);
+ pair = smartlist_get(sl, 1);
+ test_memeq(pair->first, "secret data.\0\0\0\0\0\xff\xff\xff", DIGEST_LEN);
+ test_memeq(pair->second, "Use AES-256 instead.", DIGEST_LEN);
+
+ done:
+ SMARTLIST_FOREACH(sl, fp_pair_t *, pair, tor_free(pair));
+ smartlist_free(sl);
+}
+
+static void
+test_dir_split_fps(void *testdata)
+{
+ smartlist_t *sl = smartlist_create();
+ char *mem_op_hex_tmp = NULL;
+ (void)testdata;
+
+ /* Some example hex fingerprints and their base64 equivalents */
+#define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+#define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+#define HEX3 "b33ff00db33ff00db33ff00db33ff00db33ff00d"
+#define HEX256_1 \
+ "f3f3f3f3fbbbbf3f3f3f3fbbbf3f3f3f3fbbbbf3f3f3f3fbbbf3f3f3f3fbbbbf"
+#define HEX256_2 \
+ "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccCCc"
+#define HEX256_3 \
+ "0123456789ABCdef0123456789ABCdef0123456789ABCdef0123456789ABCdef"
+#define B64_1 "/g2v+JEnOJvGdVhpEjEjRVEZPu4"
+#define B64_2 "3q2+75mZmZERERmZmRERERHwC6Q"
+#define B64_3 "sz/wDbM/8A2zP/ANsz/wDbM/8A0"
+#define B64_256_1 "8/Pz8/u7vz8/Pz+7vz8/Pz+7u/Pz8/P7u/Pz8/P7u78"
+#define B64_256_2 "zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMw"
+#define B64_256_3 "ASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8"
+
+ /* no flags set */
+ dir_split_resource_into_fingerprints("A+C+B", sl, NULL, 0);
+ tt_int_op(smartlist_len(sl), ==, 3);
+ tt_str_op(smartlist_get(sl, 0), ==, "A");
+ tt_str_op(smartlist_get(sl, 1), ==, "C");
+ tt_str_op(smartlist_get(sl, 2), ==, "B");
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ /* uniq strings. */
+ dir_split_resource_into_fingerprints("A+C+B+A+B+B", sl, NULL, DSR_SORT_UNIQ);
+ tt_int_op(smartlist_len(sl), ==, 3);
+ tt_str_op(smartlist_get(sl, 0), ==, "A");
+ tt_str_op(smartlist_get(sl, 1), ==, "B");
+ tt_str_op(smartlist_get(sl, 2), ==, "C");
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ /* Decode hex. */
+ dir_split_resource_into_fingerprints(HEX1"+"HEX2, sl, NULL, DSR_HEX);
+ tt_int_op(smartlist_len(sl), ==, 2);
+ test_mem_op_hex(smartlist_get(sl, 0), ==, HEX1);
+ test_mem_op_hex(smartlist_get(sl, 1), ==, HEX2);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ /* decode hex and drop weirdness. */
+ dir_split_resource_into_fingerprints(HEX1"+bogus+"HEX2"+"HEX256_1,
+ sl, NULL, DSR_HEX);
+ tt_int_op(smartlist_len(sl), ==, 2);
+ test_mem_op_hex(smartlist_get(sl, 0), ==, HEX1);
+ test_mem_op_hex(smartlist_get(sl, 1), ==, HEX2);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ /* Decode long hex */
+ dir_split_resource_into_fingerprints(HEX256_1"+"HEX256_2"+"HEX2"+"HEX256_3,
+ sl, NULL, DSR_HEX|DSR_DIGEST256);
+ tt_int_op(smartlist_len(sl), ==, 3);
+ test_mem_op_hex(smartlist_get(sl, 0), ==, HEX256_1);
+ test_mem_op_hex(smartlist_get(sl, 1), ==, HEX256_2);
+ test_mem_op_hex(smartlist_get(sl, 2), ==, HEX256_3);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ /* Decode hex and sort. */
+ dir_split_resource_into_fingerprints(HEX1"+"HEX2"+"HEX3"+"HEX2,
+ sl, NULL, DSR_HEX|DSR_SORT_UNIQ);
+ tt_int_op(smartlist_len(sl), ==, 3);
+ test_mem_op_hex(smartlist_get(sl, 0), ==, HEX3);
+ test_mem_op_hex(smartlist_get(sl, 1), ==, HEX2);
+ test_mem_op_hex(smartlist_get(sl, 2), ==, HEX1);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ /* Decode long hex and sort */
+ dir_split_resource_into_fingerprints(HEX256_1"+"HEX256_2"+"HEX256_3
+ "+"HEX256_1,
+ sl, NULL,
+ DSR_HEX|DSR_DIGEST256|DSR_SORT_UNIQ);
+ tt_int_op(smartlist_len(sl), ==, 3);
+ test_mem_op_hex(smartlist_get(sl, 0), ==, HEX256_3);
+ test_mem_op_hex(smartlist_get(sl, 1), ==, HEX256_2);
+ test_mem_op_hex(smartlist_get(sl, 2), ==, HEX256_1);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ /* Decode base64 */
+ dir_split_resource_into_fingerprints(B64_1"-"B64_2, sl, NULL, DSR_BASE64);
+ tt_int_op(smartlist_len(sl), ==, 2);
+ test_mem_op_hex(smartlist_get(sl, 0), ==, HEX1);
+ test_mem_op_hex(smartlist_get(sl, 1), ==, HEX2);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ /* Decode long base64 */
+ dir_split_resource_into_fingerprints(B64_256_1"-"B64_256_2,
+ sl, NULL, DSR_BASE64|DSR_DIGEST256);
+ tt_int_op(smartlist_len(sl), ==, 2);
+ test_mem_op_hex(smartlist_get(sl, 0), ==, HEX256_1);
+ test_mem_op_hex(smartlist_get(sl, 1), ==, HEX256_2);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ dir_split_resource_into_fingerprints(B64_256_1,
+ sl, NULL, DSR_BASE64|DSR_DIGEST256);
+ tt_int_op(smartlist_len(sl), ==, 1);
+ test_mem_op_hex(smartlist_get(sl, 0), ==, HEX256_1);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_clear(sl);
+
+ done:
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_free(sl);
+ tor_free(mem_op_hex_tmp);
+}
+
+static void
+test_dir_measured_bw(void)
+{
+ measured_bw_line_t mbwl;
+ int i;
+ const char *lines_pass[] = {
+ "node_id=$557365204145532d32353620696e73746561642e bw=1024\n",
+ "node_id=$557365204145532d32353620696e73746561642e\t bw=1024 \n",
+ " node_id=$557365204145532d32353620696e73746561642e bw=1024\n",
+ "\tnoise\tnode_id=$557365204145532d32353620696e73746561642e "
+ "bw=1024 junk=007\n",
+ "misc=junk node_id=$557365204145532d32353620696e73746561642e "
+ "bw=1024 junk=007\n",
+ "end"
+ };
+ const char *lines_fail[] = {
+ /* Test possible python stupidity on input */
+ "node_id=None bw=1024\n",
+ "node_id=$None bw=1024\n",
+ "node_id=$557365204145532d32353620696e73746561642e bw=None\n",
+ "node_id=$557365204145532d32353620696e73746561642e bw=1024.0\n",
+ "node_id=$557365204145532d32353620696e73746561642e bw=.1024\n",
+ "node_id=$557365204145532d32353620696e73746561642e bw=1.024\n",
+ "node_id=$557365204145532d32353620696e73746561642e bw=1024 bw=0\n",
+ "node_id=$557365204145532d32353620696e73746561642e bw=1024 bw=None\n",
+ "node_id=$557365204145532d32353620696e73746561642e bw=-1024\n",
+ /* Test incomplete writes due to race conditions, partial copies, etc */
+ "node_i",
+ "node_i\n",
+ "node_id=",
+ "node_id=\n",
+ "node_id=$557365204145532d32353620696e73746561642e bw=",
+ "node_id=$557365204145532d32353620696e73746561642e bw=1024",
+ "node_id=$557365204145532d32353620696e73746561642e bw=\n",
+ "node_id=$557365204145532d32353620696e7374",
+ "node_id=$557365204145532d32353620696e7374\n",
+ "",
+ "\n",
+ " \n ",
+ " \n\n",
+ /* Test assorted noise */
+ " node_id= ",
+ "node_id==$557365204145532d32353620696e73746561642e bw==1024\n",
+ "node_id=$55736520414552d32353620696e73746561642e bw=1024\n",
+ "node_id=557365204145532d32353620696e73746561642e bw=1024\n",
+ "node_id= $557365204145532d32353620696e73746561642e bw=0.23\n",
+ "end"
+ };
+
+ for (i = 0; strcmp(lines_fail[i], "end"); i++) {
+ //fprintf(stderr, "Testing: %s\n", lines_fail[i]);
+ test_assert(measured_bw_line_parse(&mbwl, lines_fail[i]) == -1);
+ }
+
+ for (i = 0; strcmp(lines_pass[i], "end"); i++) {
+ //fprintf(stderr, "Testing: %s %d\n", lines_pass[i], TOR_ISSPACE('\n'));
+ test_assert(measured_bw_line_parse(&mbwl, lines_pass[i]) == 0);
+ test_assert(mbwl.bw == 1024);
+ test_assert(strcmp(mbwl.node_hex,
+ "557365204145532d32353620696e73746561642e") == 0);
+ }
+
+ done:
+ return;
+}
+
+static void
+test_dir_param_voting(void)
+{
+ networkstatus_t vote1, vote2, vote3, vote4;
+ smartlist_t *votes = smartlist_create();
+ char *res = NULL;
+
+ /* dirvote_compute_params only looks at the net_params field of the votes,
+ so that's all we need to set.
+ */
+ memset(&vote1, 0, sizeof(vote1));
+ memset(&vote2, 0, sizeof(vote2));
+ memset(&vote3, 0, sizeof(vote3));
+ memset(&vote4, 0, sizeof(vote4));
+ vote1.net_params = smartlist_create();
+ vote2.net_params = smartlist_create();
+ vote3.net_params = smartlist_create();
+ vote4.net_params = smartlist_create();
+ smartlist_split_string(vote1.net_params,
+ "ab=90 abcd=20 cw=50 x-yz=-99", NULL, 0, 0);
+ smartlist_split_string(vote2.net_params,
+ "ab=27 cw=5 x-yz=88", NULL, 0, 0);
+ smartlist_split_string(vote3.net_params,
+ "abcd=20 c=60 cw=500 x-yz=-9 zzzzz=101", NULL, 0, 0);
+ smartlist_split_string(vote4.net_params,
+ "ab=900 abcd=200 c=1 cw=51 x-yz=100", NULL, 0, 0);
+ test_eq(100, networkstatus_get_param(&vote4, "x-yz", 50));
+ test_eq(222, networkstatus_get_param(&vote4, "foobar", 222));
+
+ smartlist_add(votes, &vote1);
+ smartlist_add(votes, &vote2);
+ smartlist_add(votes, &vote3);
+ smartlist_add(votes, &vote4);
+
+ res = dirvote_compute_params(votes);
+ test_streq(res,
+ "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101");
+
+ done:
+ tor_free(res);
+ SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(vote2.net_params, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(vote3.net_params, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(vote4.net_params, char *, cp, tor_free(cp));
+ smartlist_free(vote1.net_params);
+ smartlist_free(vote2.net_params);
+ smartlist_free(vote3.net_params);
+ smartlist_free(vote4.net_params);
+ smartlist_free(votes);
+
+ return;
+}
+
+extern const char AUTHORITY_CERT_1[];
+extern const char AUTHORITY_SIGNKEY_1[];
+extern const char AUTHORITY_CERT_2[];
+extern const char AUTHORITY_SIGNKEY_2[];
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_3[];
+
+/** Helper: Test that two networkstatus_voter_info_t do in fact represent the
+ * same voting authority, and that they do in fact have all the same
+ * information. */
+static void
+test_same_voter(networkstatus_voter_info_t *v1,
+ networkstatus_voter_info_t *v2)
+{
+ test_streq(v1->nickname, v2->nickname);
+ test_memeq(v1->identity_digest, v2->identity_digest, DIGEST_LEN);
+ test_streq(v1->address, v2->address);
+ test_eq(v1->addr, v2->addr);
+ test_eq(v1->dir_port, v2->dir_port);
+ test_eq(v1->or_port, v2->or_port);
+ test_streq(v1->contact, v2->contact);
+ test_memeq(v1->vote_digest, v2->vote_digest, DIGEST_LEN);
+ done:
+ ;
+}
+
+/** Helper: Make a new routerinfo containing the right information for a
+ * given vote_routerstatus_t. */
+static routerinfo_t *
+generate_ri_from_rs(const vote_routerstatus_t *vrs)
+{
+ routerinfo_t *r;
+ const routerstatus_t *rs = &vrs->status;
+ static time_t published = 0;
+
+ r = tor_malloc_zero(sizeof(routerinfo_t));
+ memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN);
+ memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest,
+ DIGEST_LEN);
+ r->cache_info.do_not_cache = 1;
+ r->cache_info.routerlist_index = -1;
+ r->cache_info.signed_descriptor_body =
+ tor_strdup("123456789012345678901234567890123");
+ r->cache_info.signed_descriptor_len =
+ strlen(r->cache_info.signed_descriptor_body);
+ r->exit_policy = smartlist_create();
+ r->cache_info.published_on = ++published + time(NULL);
+ return r;
+}
+
+/** Helper: get a detached signatures document for one or two
+ * consensuses. */
+static char *
+get_detached_sigs(networkstatus_t *ns, networkstatus_t *ns2)
+{
+ char *r;
+ smartlist_t *sl;
+ tor_assert(ns && ns->flavor == FLAV_NS);
+ sl = smartlist_create();
+ smartlist_add(sl,ns);
+ if (ns2)
+ smartlist_add(sl,ns2);
+ r = networkstatus_get_detached_signatures(sl);
+ smartlist_free(sl);
+ return r;
+}
+
+/** Run unit tests for generating and parsing V3 consensus networkstatus
+ * documents. */
+static void
+test_dir_v3_networkstatus(void)
+{
+ authority_cert_t *cert1=NULL, *cert2=NULL, *cert3=NULL;
+ crypto_pk_env_t *sign_skey_1=NULL, *sign_skey_2=NULL, *sign_skey_3=NULL;
+ crypto_pk_env_t *sign_skey_leg1=NULL;
+ const char *msg=NULL;
+
+ time_t now = time(NULL);
+ networkstatus_voter_info_t *voter;
+ document_signature_t *sig;
+ networkstatus_t *vote=NULL, *v1=NULL, *v2=NULL, *v3=NULL, *con=NULL,
+ *con_md=NULL;
+ vote_routerstatus_t *vrs;
+ routerstatus_t *rs;
+ char *v1_text=NULL, *v2_text=NULL, *v3_text=NULL, *consensus_text=NULL, *cp;
+ smartlist_t *votes = smartlist_create();
+
+ /* For generating the two other consensuses. */
+ char *detached_text1=NULL, *detached_text2=NULL;
+ char *consensus_text2=NULL, *consensus_text3=NULL;
+ char *consensus_text_md2=NULL, *consensus_text_md3=NULL;
+ char *consensus_text_md=NULL;
+ networkstatus_t *con2=NULL, *con_md2=NULL, *con3=NULL, *con_md3=NULL;
+ ns_detached_signatures_t *dsig1=NULL, *dsig2=NULL;
+
+ /* Parse certificates and keys. */
+ cert1 = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ test_assert(cert1);
+ test_assert(cert1->is_cross_certified);
+ cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
+ test_assert(cert2);
+ cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+ test_assert(cert3);
+ sign_skey_1 = crypto_new_pk_env();
+ sign_skey_2 = crypto_new_pk_env();
+ sign_skey_3 = crypto_new_pk_env();
+ sign_skey_leg1 = pk_generate(4);
+
+ test_assert(!crypto_pk_read_private_key_from_string(sign_skey_1,
- AUTHORITY_SIGNKEY_1));
++ AUTHORITY_SIGNKEY_1, -1));
+ test_assert(!crypto_pk_read_private_key_from_string(sign_skey_2,
- AUTHORITY_SIGNKEY_2));
++ AUTHORITY_SIGNKEY_2, -1));
+ test_assert(!crypto_pk_read_private_key_from_string(sign_skey_3,
- AUTHORITY_SIGNKEY_3));
++ AUTHORITY_SIGNKEY_3, -1));
+
+ test_assert(!crypto_pk_cmp_keys(sign_skey_1, cert1->signing_key));
+ test_assert(!crypto_pk_cmp_keys(sign_skey_2, cert2->signing_key));
+
+ /*
+ * Set up a vote; generate it; try to parse it.
+ */
+ vote = tor_malloc_zero(sizeof(networkstatus_t));
+ vote->type = NS_TYPE_VOTE;
+ vote->published = now;
+ vote->valid_after = now+1000;
+ vote->fresh_until = now+2000;
+ vote->valid_until = now+3000;
+ vote->vote_seconds = 100;
+ vote->dist_seconds = 200;
+ vote->supported_methods = smartlist_create();
+ smartlist_split_string(vote->supported_methods, "1 2 3", NULL, 0, -1);
+ vote->client_versions = tor_strdup("0.1.2.14,0.1.2.15");
+ vote->server_versions = tor_strdup("0.1.2.14,0.1.2.15,0.1.2.16");
+ vote->known_flags = smartlist_create();
+ smartlist_split_string(vote->known_flags,
+ "Authority Exit Fast Guard Running Stable V2Dir Valid",
+ 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ vote->voters = smartlist_create();
+ voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+ voter->nickname = tor_strdup("Voter1");
+ voter->address = tor_strdup("1.2.3.4");
+ voter->addr = 0x01020304;
+ voter->dir_port = 80;
+ voter->or_port = 9000;
+ voter->contact = tor_strdup("voter@xxxxxxxxxxx");
+ crypto_pk_get_digest(cert1->identity_key, voter->identity_digest);
+ smartlist_add(vote->voters, voter);
+ vote->cert = authority_cert_dup(cert1);
+ vote->net_params = smartlist_create();
+ smartlist_split_string(vote->net_params, "circuitwindow=101 foo=990",
+ NULL, 0, 0);
+ vote->routerstatus_list = smartlist_create();
+ /* add the first routerstatus. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.1.2.14");
+ rs->published_on = now-1500;
+ strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
+ memset(rs->identity_digest, 3, DIGEST_LEN);
+ memset(rs->descriptor_digest, 78, DIGEST_LEN);
+ rs->addr = 0x99008801;
+ rs->or_port = 443;
+ rs->dir_port = 8000;
+ /* all flags but running cleared */
+ rs->is_running = 1;
+ smartlist_add(vote->routerstatus_list, vrs);
+ test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+ /* add the second routerstatus. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.2.0.5");
+ rs->published_on = now-1000;
+ strlcpy(rs->nickname, "router1", sizeof(rs->nickname));
+ memset(rs->identity_digest, 5, DIGEST_LEN);
+ memset(rs->descriptor_digest, 77, DIGEST_LEN);
+ rs->addr = 0x99009901;
+ rs->or_port = 443;
+ rs->dir_port = 0;
+ rs->is_exit = rs->is_stable = rs->is_fast = rs->is_running =
+ rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
+ smartlist_add(vote->routerstatus_list, vrs);
+ test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+ /* add the third routerstatus. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.1.0.3");
+ rs->published_on = now-1000;
+ strlcpy(rs->nickname, "router3", sizeof(rs->nickname));
+ memset(rs->identity_digest, 33, DIGEST_LEN);
+ memset(rs->descriptor_digest, 79, DIGEST_LEN);
+ rs->addr = 0xAA009901;
+ rs->or_port = 400;
+ rs->dir_port = 9999;
+ rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
+ rs->is_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
+ smartlist_add(vote->routerstatus_list, vrs);
+ test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+ /* add a fourth routerstatus that is not running. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.1.6.3");
+ rs->published_on = now-1000;
+ strlcpy(rs->nickname, "router4", sizeof(rs->nickname));
+ memset(rs->identity_digest, 34, DIGEST_LEN);
+ memset(rs->descriptor_digest, 47, DIGEST_LEN);
+ rs->addr = 0xC0000203;
+ rs->or_port = 500;
+ rs->dir_port = 1999;
+ /* Running flag (and others) cleared */
+ smartlist_add(vote->routerstatus_list, vrs);
+ test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+ /* dump the vote and try to parse it. */
+ v1_text = format_networkstatus_vote(sign_skey_1, vote);
+ test_assert(v1_text);
+ v1 = networkstatus_parse_vote_from_string(v1_text, NULL, NS_TYPE_VOTE);
+ test_assert(v1);
+
+ /* Make sure the parsed thing was right. */
+ test_eq(v1->type, NS_TYPE_VOTE);
+ test_eq(v1->published, vote->published);
+ test_eq(v1->valid_after, vote->valid_after);
+ test_eq(v1->fresh_until, vote->fresh_until);
+ test_eq(v1->valid_until, vote->valid_until);
+ test_eq(v1->vote_seconds, vote->vote_seconds);
+ test_eq(v1->dist_seconds, vote->dist_seconds);
+ test_streq(v1->client_versions, vote->client_versions);
+ test_streq(v1->server_versions, vote->server_versions);
+ test_assert(v1->voters && smartlist_len(v1->voters));
+ voter = smartlist_get(v1->voters, 0);
+ test_streq(voter->nickname, "Voter1");
+ test_streq(voter->address, "1.2.3.4");
+ test_eq(voter->addr, 0x01020304);
+ test_eq(voter->dir_port, 80);
+ test_eq(voter->or_port, 9000);
+ test_streq(voter->contact, "voter@xxxxxxxxxxx");
+ test_assert(v1->cert);
+ test_assert(!crypto_pk_cmp_keys(sign_skey_1, v1->cert->signing_key));
+ cp = smartlist_join_strings(v1->known_flags, ":", 0, NULL);
+ test_streq(cp, "Authority:Exit:Fast:Guard:Running:Stable:V2Dir:Valid");
+ tor_free(cp);
+ test_eq(smartlist_len(v1->routerstatus_list), 4);
+ /* Check the first routerstatus. */
+ vrs = smartlist_get(v1->routerstatus_list, 0);
+ rs = &vrs->status;
+ test_streq(vrs->version, "0.1.2.14");
+ test_eq(rs->published_on, now-1500);
+ test_streq(rs->nickname, "router2");
+ test_memeq(rs->identity_digest,
+ "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3",
+ DIGEST_LEN);
+ test_memeq(rs->descriptor_digest, "NNNNNNNNNNNNNNNNNNNN", DIGEST_LEN);
+ test_eq(rs->addr, 0x99008801);
+ test_eq(rs->or_port, 443);
+ test_eq(rs->dir_port, 8000);
+ test_eq(vrs->flags, U64_LITERAL(16)); // no flags except "running"
+ /* Check the second routerstatus. */
+ vrs = smartlist_get(v1->routerstatus_list, 1);
+ rs = &vrs->status;
+ test_streq(vrs->version, "0.2.0.5");
+ test_eq(rs->published_on, now-1000);
+ test_streq(rs->nickname, "router1");
+ test_memeq(rs->identity_digest,
+ "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5",
+ DIGEST_LEN);
+ test_memeq(rs->descriptor_digest, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN);
+ test_eq(rs->addr, 0x99009901);
+ test_eq(rs->or_port, 443);
+ test_eq(rs->dir_port, 0);
+ test_eq(vrs->flags, U64_LITERAL(254)); // all flags except "authority."
+
+ {
+ measured_bw_line_t mbw;
+ memset(mbw.node_id, 33, sizeof(mbw.node_id));
+ mbw.bw = 1024;
+ test_assert(measured_bw_line_apply(&mbw,
+ v1->routerstatus_list) == 1);
+ vrs = smartlist_get(v1->routerstatus_list, 2);
+ test_assert(vrs->status.has_measured_bw &&
+ vrs->status.measured_bw == 1024);
+ }
+
+ /* Generate second vote. It disagrees on some of the times,
+ * and doesn't list versions, and knows some crazy flags */
+ vote->published = now+1;
+ vote->fresh_until = now+3005;
+ vote->dist_seconds = 300;
+ authority_cert_free(vote->cert);
+ vote->cert = authority_cert_dup(cert2);
+ vote->net_params = smartlist_create();
+ smartlist_split_string(vote->net_params, "bar=2000000000 circuitwindow=20",
+ NULL, 0, 0);
+ tor_free(vote->client_versions);
+ tor_free(vote->server_versions);
+ voter = smartlist_get(vote->voters, 0);
+ tor_free(voter->nickname);
+ tor_free(voter->address);
+ voter->nickname = tor_strdup("Voter2");
+ voter->address = tor_strdup("2.3.4.5");
+ voter->addr = 0x02030405;
+ crypto_pk_get_digest(cert2->identity_key, voter->identity_digest);
+ smartlist_add(vote->known_flags, tor_strdup("MadeOfCheese"));
+ smartlist_add(vote->known_flags, tor_strdup("MadeOfTin"));
+ smartlist_sort_strings(vote->known_flags);
+ vrs = smartlist_get(vote->routerstatus_list, 2);
+ smartlist_del_keeporder(vote->routerstatus_list, 2);
+ tor_free(vrs->version);
+ tor_free(vrs);
+ vrs = smartlist_get(vote->routerstatus_list, 0);
+ vrs->status.is_fast = 1;
+ /* generate and parse. */
+ v2_text = format_networkstatus_vote(sign_skey_2, vote);
+ test_assert(v2_text);
+ v2 = networkstatus_parse_vote_from_string(v2_text, NULL, NS_TYPE_VOTE);
+ test_assert(v2);
+ /* Check that flags come out right.*/
+ cp = smartlist_join_strings(v2->known_flags, ":", 0, NULL);
+ test_streq(cp, "Authority:Exit:Fast:Guard:MadeOfCheese:MadeOfTin:"
+ "Running:Stable:V2Dir:Valid");
+ tor_free(cp);
+ vrs = smartlist_get(v2->routerstatus_list, 1);
+ /* 1023 - authority(1) - madeofcheese(16) - madeoftin(32) */
+ test_eq(vrs->flags, U64_LITERAL(974));
+
+ /* Generate the third vote. */
+ vote->published = now;
+ vote->fresh_until = now+2003;
+ vote->dist_seconds = 250;
+ authority_cert_free(vote->cert);
+ vote->cert = authority_cert_dup(cert3);
+ vote->net_params = smartlist_create();
+ smartlist_split_string(vote->net_params, "circuitwindow=80 foo=660",
+ NULL, 0, 0);
+ smartlist_add(vote->supported_methods, tor_strdup("4"));
+ vote->client_versions = tor_strdup("0.1.2.14,0.1.2.17");
+ vote->server_versions = tor_strdup("0.1.2.10,0.1.2.15,0.1.2.16");
+ voter = smartlist_get(vote->voters, 0);
+ tor_free(voter->nickname);
+ tor_free(voter->address);
+ voter->nickname = tor_strdup("Voter3");
+ voter->address = tor_strdup("3.4.5.6");
+ voter->addr = 0x03040506;
+ crypto_pk_get_digest(cert3->identity_key, voter->identity_digest);
+ /* This one has a legacy id. */
+ memset(voter->legacy_id_digest, (int)'A', DIGEST_LEN);
+ vrs = smartlist_get(vote->routerstatus_list, 0);
+ smartlist_del_keeporder(vote->routerstatus_list, 0);
+ tor_free(vrs->version);
+ tor_free(vrs);
+ vrs = smartlist_get(vote->routerstatus_list, 0);
+ memset(vrs->status.descriptor_digest, (int)'Z', DIGEST_LEN);
+ test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+ v3_text = format_networkstatus_vote(sign_skey_3, vote);
+ test_assert(v3_text);
+
+ v3 = networkstatus_parse_vote_from_string(v3_text, NULL, NS_TYPE_VOTE);
+ test_assert(v3);
+
+ /* Compute a consensus as voter 3. */
+ smartlist_add(votes, v3);
+ smartlist_add(votes, v1);
+ smartlist_add(votes, v2);
+ consensus_text = networkstatus_compute_consensus(votes, 3,
+ cert3->identity_key,
+ sign_skey_3,
+ "AAAAAAAAAAAAAAAAAAAA",
+ sign_skey_leg1,
+ FLAV_NS);
+ test_assert(consensus_text);
+ con = networkstatus_parse_vote_from_string(consensus_text, NULL,
+ NS_TYPE_CONSENSUS);
+ test_assert(con);
+ //log_notice(LD_GENERAL, "<<%s>>\n<<%s>>\n<<%s>>\n",
+ // v1_text, v2_text, v3_text);
+ consensus_text_md = networkstatus_compute_consensus(votes, 3,
+ cert3->identity_key,
+ sign_skey_3,
+ "AAAAAAAAAAAAAAAAAAAA",
+ sign_skey_leg1,
+ FLAV_MICRODESC);
+ test_assert(consensus_text_md);
+ con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
+ NS_TYPE_CONSENSUS);
+ test_assert(con_md);
+ test_eq(con_md->flavor, FLAV_MICRODESC);
+
+ /* Check consensus contents. */
+ test_assert(con->type == NS_TYPE_CONSENSUS);
+ test_eq(con->published, 0); /* this field only appears in votes. */
+ test_eq(con->valid_after, now+1000);
+ test_eq(con->fresh_until, now+2003); /* median */
+ test_eq(con->valid_until, now+3000);
+ test_eq(con->vote_seconds, 100);
+ test_eq(con->dist_seconds, 250); /* median */
+ test_streq(con->client_versions, "0.1.2.14");
+ test_streq(con->server_versions, "0.1.2.15,0.1.2.16");
+ cp = smartlist_join_strings(v2->known_flags, ":", 0, NULL);
+ test_streq(cp, "Authority:Exit:Fast:Guard:MadeOfCheese:MadeOfTin:"
+ "Running:Stable:V2Dir:Valid");
+ tor_free(cp);
+ cp = smartlist_join_strings(con->net_params, ":", 0, NULL);
+ test_streq(cp, "bar=2000000000:circuitwindow=80:foo=660");
+ tor_free(cp);
+
+ test_eq(4, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/
+ /* The voter id digests should be in this order. */
+ test_assert(memcmp(cert2->cache_info.identity_digest,
+ cert1->cache_info.identity_digest,DIGEST_LEN)<0);
+ test_assert(memcmp(cert1->cache_info.identity_digest,
+ cert3->cache_info.identity_digest,DIGEST_LEN)<0);
+ test_same_voter(smartlist_get(con->voters, 1),
+ smartlist_get(v2->voters, 0));
+ test_same_voter(smartlist_get(con->voters, 2),
+ smartlist_get(v1->voters, 0));
+ test_same_voter(smartlist_get(con->voters, 3),
+ smartlist_get(v3->voters, 0));
+
+ test_assert(!con->cert);
+ test_eq(2, smartlist_len(con->routerstatus_list));
+ /* There should be two listed routers: one with identity 3, one with
+ * identity 5. */
+ /* This one showed up in 2 digests. */
+ rs = smartlist_get(con->routerstatus_list, 0);
+ test_memeq(rs->identity_digest,
+ "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3",
+ DIGEST_LEN);
+ test_memeq(rs->descriptor_digest, "NNNNNNNNNNNNNNNNNNNN", DIGEST_LEN);
+ test_assert(!rs->is_authority);
+ test_assert(!rs->is_exit);
+ test_assert(!rs->is_fast);
+ test_assert(!rs->is_possible_guard);
+ test_assert(!rs->is_stable);
+ test_assert(rs->is_running); /* If it wasn't running it wouldn't be here */
+ test_assert(!rs->is_v2_dir);
+ test_assert(!rs->is_valid);
+ test_assert(!rs->is_named);
+ /* XXXX check version */
+
+ rs = smartlist_get(con->routerstatus_list, 1);
+ /* This one showed up in 3 digests. Twice with ID 'M', once with 'Z'. */
+ test_memeq(rs->identity_digest,
+ "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5",
+ DIGEST_LEN);
+ test_streq(rs->nickname, "router1");
+ test_memeq(rs->descriptor_digest, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN);
+ test_eq(rs->published_on, now-1000);
+ test_eq(rs->addr, 0x99009901);
+ test_eq(rs->or_port, 443);
+ test_eq(rs->dir_port, 0);
+ test_assert(!rs->is_authority);
+ test_assert(rs->is_exit);
+ test_assert(rs->is_fast);
+ test_assert(rs->is_possible_guard);
+ test_assert(rs->is_stable);
+ test_assert(rs->is_running);
+ test_assert(rs->is_v2_dir);
+ test_assert(rs->is_valid);
+ test_assert(!rs->is_named);
+ /* XXXX check version */
+
+ /* Check signatures. the first voter is a pseudo-entry with a legacy key.
+ * The second one hasn't signed. The fourth one has signed: validate it. */
+ voter = smartlist_get(con->voters, 1);
+ test_eq(smartlist_len(voter->sigs), 0);
+
+ voter = smartlist_get(con->voters, 3);
+ test_eq(smartlist_len(voter->sigs), 1);
+ sig = smartlist_get(voter->sigs, 0);
+ test_assert(sig->signature);
+ test_assert(!sig->good_signature);
+ test_assert(!sig->bad_signature);
+
+ test_assert(!networkstatus_check_document_signature(con, sig, cert3));
+ test_assert(sig->signature);
+ test_assert(sig->good_signature);
+ test_assert(!sig->bad_signature);
+
+ {
+ const char *msg=NULL;
+ /* Compute the other two signed consensuses. */
+ smartlist_shuffle(votes);
+ consensus_text2 = networkstatus_compute_consensus(votes, 3,
+ cert2->identity_key,
+ sign_skey_2, NULL,NULL,
+ FLAV_NS);
+ consensus_text_md2 = networkstatus_compute_consensus(votes, 3,
+ cert2->identity_key,
+ sign_skey_2, NULL,NULL,
+ FLAV_MICRODESC);
+ smartlist_shuffle(votes);
+ consensus_text3 = networkstatus_compute_consensus(votes, 3,
+ cert1->identity_key,
+ sign_skey_1, NULL,NULL,
+ FLAV_NS);
+ consensus_text_md3 = networkstatus_compute_consensus(votes, 3,
+ cert1->identity_key,
+ sign_skey_1, NULL,NULL,
+ FLAV_MICRODESC);
+ test_assert(consensus_text2);
+ test_assert(consensus_text3);
+ test_assert(consensus_text_md2);
+ test_assert(consensus_text_md3);
+ con2 = networkstatus_parse_vote_from_string(consensus_text2, NULL,
+ NS_TYPE_CONSENSUS);
+ con3 = networkstatus_parse_vote_from_string(consensus_text3, NULL,
+ NS_TYPE_CONSENSUS);
+ con_md2 = networkstatus_parse_vote_from_string(consensus_text_md2, NULL,
+ NS_TYPE_CONSENSUS);
+ con_md3 = networkstatus_parse_vote_from_string(consensus_text_md3, NULL,
+ NS_TYPE_CONSENSUS);
+ test_assert(con2);
+ test_assert(con3);
+ test_assert(con_md2);
+ test_assert(con_md3);
+
+ /* All three should have the same digest. */
+ test_memeq(&con->digests, &con2->digests, sizeof(digests_t));
+ test_memeq(&con->digests, &con3->digests, sizeof(digests_t));
+
+ test_memeq(&con_md->digests, &con_md2->digests, sizeof(digests_t));
+ test_memeq(&con_md->digests, &con_md3->digests, sizeof(digests_t));
+
+ /* Extract a detached signature from con3. */
+ detached_text1 = get_detached_sigs(con3, con_md3);
+ tor_assert(detached_text1);
+ /* Try to parse it. */
+ dsig1 = networkstatus_parse_detached_signatures(detached_text1, NULL);
+ tor_assert(dsig1);
+
+ /* Are parsed values as expected? */
+ test_eq(dsig1->valid_after, con3->valid_after);
+ test_eq(dsig1->fresh_until, con3->fresh_until);
+ test_eq(dsig1->valid_until, con3->valid_until);
+ {
+ digests_t *dsig_digests = strmap_get(dsig1->digests, "ns");
+ test_assert(dsig_digests);
+ test_memeq(dsig_digests->d[DIGEST_SHA1], con3->digests.d[DIGEST_SHA1],
+ DIGEST_LEN);
+ dsig_digests = strmap_get(dsig1->digests, "microdesc");
+ test_assert(dsig_digests);
+ test_memeq(dsig_digests->d[DIGEST_SHA256],
+ con_md3->digests.d[DIGEST_SHA256],
+ DIGEST256_LEN);
+ }
+ {
+ smartlist_t *dsig_signatures = strmap_get(dsig1->signatures, "ns");
+ test_assert(dsig_signatures);
+ test_eq(1, smartlist_len(dsig_signatures));
+ sig = smartlist_get(dsig_signatures, 0);
+ test_memeq(sig->identity_digest, cert1->cache_info.identity_digest,
+ DIGEST_LEN);
+ test_eq(sig->alg, DIGEST_SHA1);
+
+ dsig_signatures = strmap_get(dsig1->signatures, "microdesc");
+ test_assert(dsig_signatures);
+ test_eq(1, smartlist_len(dsig_signatures));
+ sig = smartlist_get(dsig_signatures, 0);
+ test_memeq(sig->identity_digest, cert1->cache_info.identity_digest,
+ DIGEST_LEN);
+ test_eq(sig->alg, DIGEST_SHA256);
+ }
+
+ /* Try adding it to con2. */
+ detached_text2 = get_detached_sigs(con2,con_md2);
+ test_eq(1, networkstatus_add_detached_signatures(con2, dsig1, &msg));
+ tor_free(detached_text2);
+ test_eq(1, networkstatus_add_detached_signatures(con_md2, dsig1, &msg));
+ tor_free(detached_text2);
+ detached_text2 = get_detached_sigs(con2,con_md2);
+ //printf("\n<%s>\n", detached_text2);
+ dsig2 = networkstatus_parse_detached_signatures(detached_text2, NULL);
+ test_assert(dsig2);
+ /*
+ printf("\n");
+ SMARTLIST_FOREACH(dsig2->signatures, networkstatus_voter_info_t *, vi, {
+ char hd[64];
+ base16_encode(hd, sizeof(hd), vi->identity_digest, DIGEST_LEN);
+ printf("%s\n", hd);
+ });
+ */
+ test_eq(2,
+ smartlist_len((smartlist_t*)strmap_get(dsig2->signatures, "ns")));
+ test_eq(2,
+ smartlist_len((smartlist_t*)strmap_get(dsig2->signatures,
+ "microdesc")));
+
+ /* Try adding to con2 twice; verify that nothing changes. */
+ test_eq(0, networkstatus_add_detached_signatures(con2, dsig1, &msg));
+
+ /* Add to con. */
+ test_eq(2, networkstatus_add_detached_signatures(con, dsig2, &msg));
+ /* Check signatures */
+ voter = smartlist_get(con->voters, 1);
+ sig = smartlist_get(voter->sigs, 0);
+ test_assert(sig);
+ test_assert(!networkstatus_check_document_signature(con, sig, cert2));
+ voter = smartlist_get(con->voters, 2);
+ sig = smartlist_get(voter->sigs, 0);
+ test_assert(sig);
+ test_assert(!networkstatus_check_document_signature(con, sig, cert1));
+ }
+
+ done:
+ smartlist_free(votes);
+ tor_free(v1_text);
+ tor_free(v2_text);
+ tor_free(v3_text);
+ tor_free(consensus_text);
+ tor_free(consensus_text_md);
+
+ if (vote)
+ networkstatus_vote_free(vote);
+ if (v1)
+ networkstatus_vote_free(v1);
+ if (v2)
+ networkstatus_vote_free(v2);
+ if (v3)
+ networkstatus_vote_free(v3);
+ if (con)
+ networkstatus_vote_free(con);
+ if (con_md)
+ networkstatus_vote_free(con_md);
+ if (sign_skey_1)
+ crypto_free_pk_env(sign_skey_1);
+ if (sign_skey_2)
+ crypto_free_pk_env(sign_skey_2);
+ if (sign_skey_3)
+ crypto_free_pk_env(sign_skey_3);
+ if (sign_skey_leg1)
+ crypto_free_pk_env(sign_skey_leg1);
+ if (cert1)
+ authority_cert_free(cert1);
+ if (cert2)
+ authority_cert_free(cert2);
+ if (cert3)
+ authority_cert_free(cert3);
+
+ tor_free(consensus_text2);
+ tor_free(consensus_text3);
+ tor_free(consensus_text_md2);
+ tor_free(consensus_text_md3);
+ tor_free(detached_text1);
+ tor_free(detached_text2);
+ if (con2)
+ networkstatus_vote_free(con2);
+ if (con3)
+ networkstatus_vote_free(con3);
+ if (con_md2)
+ networkstatus_vote_free(con_md2);
+ if (con_md3)
+ networkstatus_vote_free(con_md3);
+ if (dsig1)
+ ns_detached_signatures_free(dsig1);
+ if (dsig2)
+ ns_detached_signatures_free(dsig2);
+}
+
+#define DIR_LEGACY(name) \
+ { #name, legacy_test_helper, 0, &legacy_setup, test_dir_ ## name }
+
+#define DIR(name) \
+ { #name, test_dir_##name, 0, NULL, NULL }
+
+struct testcase_t dir_tests[] = {
+ DIR_LEGACY(nicknames),
+ DIR_LEGACY(formats),
+ DIR_LEGACY(versions),
+ DIR_LEGACY(fp_pairs),
+ DIR(split_fps),
+ DIR_LEGACY(measured_bw),
+ DIR_LEGACY(param_voting),
+ DIR_LEGACY(v3_networkstatus),
+ END_OF_TESTCASES
+};
+