diff options
Diffstat (limited to 'src/test')
129 files changed, 16751 insertions, 2201 deletions
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake index 0ba56d7036..605f1a92c3 100644 --- a/src/test/Makefile.nmake +++ b/src/test/Makefile.nmake @@ -12,11 +12,12 @@ LIBS = ..\..\..\build-alpha\lib\libevent.lib \ crypt32.lib gdi32.lib user32.lib TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \ - test_containers.obj \ + test_consdiff.obj test_containers.obj \ test_controller_events.obj test_crypto.obj test_data.obj test_dir.obj \ test_checkdir.obj test_microdesc.obj test_pt.obj test_util.obj \ test_config.obj test_connection.obj \ test_cell_formats.obj test_relay.obj test_replay.obj \ + test_channelpadding.obj \ test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj tinytest.obj: ..\ext\tinytest.c diff --git a/src/test/bench.c b/src/test/bench.c index 30984fda70..a44dc94a61 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ extern const char tor_git_revision[]; @@ -28,6 +28,7 @@ const char tor_git_revision[] = ""; #include "crypto_curve25519.h" #include "onion_ntor.h" #include "crypto_ed25519.h" +#include "consdiff.h" #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) static uint64_t nanostart; @@ -120,7 +121,7 @@ bench_onion_TAP(void) uint64_t start, end; char os[TAP_ONIONSKIN_CHALLENGE_LEN]; char or[TAP_ONIONSKIN_REPLY_LEN]; - crypto_dh_t *dh_out; + crypto_dh_t *dh_out = NULL; key = crypto_pk_new(); key2 = crypto_pk_new(); @@ -175,6 +176,7 @@ bench_onion_TAP(void) NANOCOUNT(start, end, iters)/1e3); done: + crypto_dh_free(dh_out); crypto_pk_free(key); crypto_pk_free(key2); } @@ -672,6 +674,28 @@ main(int argc, const char **argv) or_options_t *options; tor_threads_init(); + tor_compress_init(); + + if (argc == 4 && !strcmp(argv[1], "diff")) { + init_logging(1); + const int N = 200; + char *f1 = read_file_to_str(argv[2], RFTS_BIN, NULL); + char *f2 = read_file_to_str(argv[3], RFTS_BIN, NULL); + if (! f1 || ! f2) { + perror("X"); + return 1; + } + for (i = 0; i < N; ++i) { + char *diff = consensus_diff_generate(f1, f2); + tor_free(diff); + } + char *diff = consensus_diff_generate(f1, f2); + printf("%s", diff); + tor_free(f1); + tor_free(f2); + tor_free(diff); + return 0; + } for (i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--list")) { diff --git a/src/test/bt_test.py b/src/test/bt_test.py index 30591453b9..4cb3326042 100755 --- a/src/test/bt_test.py +++ b/src/test/bt_test.py @@ -1,4 +1,4 @@ -# Copyright 2013-2015, The Tor Project, Inc +# Copyright 2013-2017, The Tor Project, Inc # See LICENSE for licensing information """ diff --git a/src/test/ed25519_exts_ref.py b/src/test/ed25519_exts_ref.py index d5a3a79910..af5010415e 100644 --- a/src/test/ed25519_exts_ref.py +++ b/src/test/ed25519_exts_ref.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 2014-2015, The Tor Project, Inc +# Copyright 2014-2017, The Tor Project, Inc # See LICENSE for licensing information """ diff --git a/src/test/fakechans.h b/src/test/fakechans.h index fa0e37dbe6..c0de430e3d 100644 --- a/src/test/fakechans.h +++ b/src/test/fakechans.h @@ -1,4 +1,4 @@ - /* Copyright (c) 2014-2016, The Tor Project, Inc. */ + /* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_FAKECHANS_H diff --git a/src/test/fuzz/dict/consensus b/src/test/fuzz/dict/consensus new file mode 100644 index 0000000000..3fcd9ee7ff --- /dev/null +++ b/src/test/fuzz/dict/consensus @@ -0,0 +1,52 @@ +"a" +"additional-digest" +"additional-signature" +"bandwidth-weights" +"client-versions" +"consensus-digest" +"consensus-method" +"consensus-methods" +"contact" +"dir-address" +"directory-footer" +"directory-signature" +"dir-identity-key" +"dir-key-certificate-version" +"dir-key-certification" +"dir-key-crosscert" +"dir-key-expires" +"dir-key-published" +"dir-signing-key" +"dir-source" +"fingerprint" +"fresh-until" +"id" +"known-flags" +"legacy-dir-key" +"m" +"network-status-version" +"opt" +"p" +"package" +"params" +"pr" +"published" +"r" +"recommended-client-protocols" +"recommended-relay-protocols" +"required-client-protocols" +"required-relay-protocols" +"s" +"server-versions" +"shared-rand-commit" +"shared-rand-current-value" +"shared-rand-participate" +"shared-rand-previous-value" +"signing-ed25519" +"v" +"valid-after" +"valid-until" +"vote-digest" +"vote-status" +"voting-delay" +"w" diff --git a/src/test/fuzz/dict/descriptor b/src/test/fuzz/dict/descriptor new file mode 100644 index 0000000000..110ee3e820 --- /dev/null +++ b/src/test/fuzz/dict/descriptor @@ -0,0 +1,41 @@ +"reject" +"accept" +"reject6" +"accept6" +"router" +"ipv6-policy" +"signing-key" +"onion-key" +"ntor-onion-key" +"router-signature" +"published" +"uptime" +"fingerprint" +"hibernating" +"platform" +"proto" +"contact" +"read-history" +"write-history" +"extra-info-digest" +"hidden-service-dir" +"identity-ed25519" +"master-key-ed25519" +"router-sig-ed25519" +"onion-key-crosscert" +"ntor-onion-key-crosscert" +"allow-single-hop-exits" +"family" +"caches-extra-info" +"or-address" +"opt" + "bandwidth" +"@purpose" +"tunnelled-dir-server" +"-----BEGIN" +"-----END" +"-----" +"ED25519 CERT" +"RSA PUBLIC KEY" +"CROSSCERT" +"SIGNATURE" diff --git a/src/test/fuzz/dict/extrainfo b/src/test/fuzz/dict/extrainfo new file mode 100644 index 0000000000..eba7a1e4ce --- /dev/null +++ b/src/test/fuzz/dict/extrainfo @@ -0,0 +1,32 @@ +"cell-circuits-per-decile" +"cell-processed-cells" +"cell-queued-cells" +"cell-stats-end" +"cell-time-in-queue" +"dirreq-stats-end" +"dirreq-v2-direct-dl" +"dirreq-v2-ips" +"dirreq-v2-reqs" +"dirreq-v2-resp" +"dirreq-v2-share" +"dirreq-v2-tunneled-dl" +"dirreq-v3-direct-dl" +"dirreq-v3-ips" +"dirreq-v3-reqs" +"dirreq-v3-resp" +"dirreq-v3-share" +"dirreq-v3-tunneled-dl" +"entry-ips" +"entry-stats-end" +"exit-kibibytes-read" +"exit-kibibytes-written" +"exit-stats-end" +"exit-streams-opened" +"extra-info" +"identity-ed25519" +"opt" +"published" +"read-history" +"router-sig-ed25519" +"router-signature" +"write-history" diff --git a/src/test/fuzz/dict/hsdescv2 b/src/test/fuzz/dict/hsdescv2 new file mode 100644 index 0000000000..48788301dc --- /dev/null +++ b/src/test/fuzz/dict/hsdescv2 @@ -0,0 +1,8 @@ +"introduction-points" +"permanent-key" +"protocol-versions" +"publication-time" +"rendezvous-service-descriptor" +"secret-id-part" +"signature" +"version" diff --git a/src/test/fuzz/dict/http b/src/test/fuzz/dict/http new file mode 100644 index 0000000000..3b0531579d --- /dev/null +++ b/src/test/fuzz/dict/http @@ -0,0 +1,24 @@ +# +# AFL dictionary for the Tor Directory protocol's HTTP headers +# ------------------------------------------------------------ +# +# Extracted from directory_handle_command() in the tor source code +# +# Copyright (c) 2016-2017, The Tor Project, Inc. +# See LICENSE for licensing information +# +# Usage: +# Select the dictionaries relevant to the part of the directory protocol you +# are fuzzing, and feed them to your fuzzer (if it supports dictionaries). + +http_header_body_delimiter = "\x0d\x0a\x0d\x0a" +http_header_header_delimiter = "\x0d\x0a" +# multi-character tokens only +#http_header_value_delimiter = " " + +content_length_header = "Content-Length:" +forwarded_for_header = "Forwarded-For:" +x_forwarded_for_header = "X-Forwarded-For:" + +get_command = "GET" +post_command = "POST" diff --git a/src/test/fuzz/dict/iptsv2 b/src/test/fuzz/dict/iptsv2 new file mode 100644 index 0000000000..57791c5e3c --- /dev/null +++ b/src/test/fuzz/dict/iptsv2 @@ -0,0 +1,6 @@ +"introduction-point" +"ip-address" +"onion-port" +"onion-key" +"service-key" + diff --git a/src/test/fuzz/dict/microdesc b/src/test/fuzz/dict/microdesc new file mode 100644 index 0000000000..fdd0567b65 --- /dev/null +++ b/src/test/fuzz/dict/microdesc @@ -0,0 +1,7 @@ +"onion-key" +"ntor-onion-key" +"id" +"a" +"family" +"p" +"p6" diff --git a/src/test/fuzz/fixup_filenames.sh b/src/test/fuzz/fixup_filenames.sh new file mode 100755 index 0000000000..68efc1abc5 --- /dev/null +++ b/src/test/fuzz/fixup_filenames.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +if [ ! -d "$1" ] ; then + echo "I need a directory" + exit 1 +fi + +for fn in "$1"/* ; do + prev=`basename "$fn"` + post=`sha256sum "$fn" | sed -e 's/ .*//;'` + if [ "$prev" == "$post" ] ; then + echo "OK $prev" + else + echo "mv $prev $post" + mv "$fn" "$1/$post" + fi +done diff --git a/src/test/fuzz/fuzz_consensus.c b/src/test/fuzz/fuzz_consensus.c new file mode 100644 index 0000000000..6610ade7ad --- /dev/null +++ b/src/test/fuzz/fuzz_consensus.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "networkstatus.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static int +mock_router_produce_hash_final__nohash(char *digest, + const char *start, size_t len, + digest_algorithm_t alg) +{ + (void)start; + (void)len; + /* we could look at start[..] */ + if (alg == DIGEST_SHA1) + memset(digest, 0x01, 20); + else + memset(digest, 0x02, 32); + return 0; +} + +static int +mock_signed_digest_equals__yes(const uint8_t *d1, const uint8_t *d2, + size_t len) +{ + (void) tor_memeq(d1, d2, len); + return 1; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + MOCK(router_compute_hash_final, mock_router_produce_hash_final__nohash); + MOCK(signed_digest_equals, mock_signed_digest_equals__yes); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + networkstatus_t *ns; + char *str = tor_memdup_nulterm(data, sz); + const char *eos = NULL; + networkstatus_type_t tp = NS_TYPE_CONSENSUS; + if (tor_memstr(data, MIN(sz, 1024), "tus vote")) + tp = NS_TYPE_VOTE; + const char *what = (tp == NS_TYPE_CONSENSUS) ? "consensus" : "vote"; + ns = networkstatus_parse_vote_from_string(str, + &eos, + tp); + if (ns) { + log_debug(LD_GENERAL, "Parsing as %s okay", what); + networkstatus_vote_free(ns); + } else { + log_debug(LD_GENERAL, "Parsing as %s failed", what); + } + tor_free(str); + return 0; +} + diff --git a/src/test/fuzz/fuzz_descriptor.c b/src/test/fuzz/fuzz_descriptor.c new file mode 100644 index 0000000000..1a50beae17 --- /dev/null +++ b/src/test/fuzz/fuzz_descriptor.c @@ -0,0 +1,79 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "routerlist.h" +#include "routerkeys.h" +#include "fuzzing.h" + +static int +mock_check_tap_onion_key_crosscert__nocheck(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest) +{ + tor_assert(crosscert && onion_pkey && master_id_pkey && rsa_id_digest); + /* we could look at crosscert[..] */ + (void) crosscert_len; + return 0; +} + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static int +mock_router_produce_hash_final__nohash(char *digest, + const char *start, size_t len, + digest_algorithm_t alg) +{ + (void)start; + (void)len; + /* we could look at start[..] */ + if (alg == DIGEST_SHA1) + memset(digest, 0x01, 20); + else + memset(digest, 0x02, 32); + return 0; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(check_tap_onion_key_crosscert, + mock_check_tap_onion_key_crosscert__nocheck); + MOCK(dump_desc, mock_dump_desc__nodump); + MOCK(router_compute_hash_final, mock_router_produce_hash_final__nohash); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + routerinfo_t *ri; + const char *str = (const char*) data; + ri = router_parse_entry_from_string((const char *)str, + str+sz, + 0, 0, 0, NULL); + if (ri) { + log_debug(LD_GENERAL, "Parsing okay"); + routerinfo_free(ri); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + return 0; +} + diff --git a/src/test/fuzz/fuzz_diff.c b/src/test/fuzz/fuzz_diff.c new file mode 100644 index 0000000000..642380b512 --- /dev/null +++ b/src/test/fuzz/fuzz_diff.c @@ -0,0 +1,69 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONSDIFF_PRIVATE + +#include "orconfig.h" +#include "or.h" +#include "consdiff.h" + +#include "fuzzing.h" + +static int +mock_consensus_compute_digest_(const char *c, consensus_digest_t *d) +{ + (void)c; + memset(d->sha3_256, 3, sizeof(d->sha3_256)); + return 0; +} + +int +fuzz_init(void) +{ + MOCK(consensus_compute_digest, mock_consensus_compute_digest_); + MOCK(consensus_compute_digest_as_signed, mock_consensus_compute_digest_); + return 0; +} + +int +fuzz_cleanup(void) +{ + UNMOCK(consensus_compute_digest); + UNMOCK(consensus_compute_digest_as_signed); + return 0; +} + +int +fuzz_main(const uint8_t *stdin_buf, size_t data_size) +{ +#define SEP "=====\n" +#define SEPLEN strlen(SEP) + const uint8_t *separator = tor_memmem(stdin_buf, data_size, SEP, SEPLEN); + if (! separator) + return 0; + size_t c1_len = separator - stdin_buf; + char *c1 = tor_memdup_nulterm(stdin_buf, c1_len); + size_t c2_len = data_size - c1_len - SEPLEN; + char *c2 = tor_memdup_nulterm(separator + SEPLEN, c2_len); + + char *c3 = consensus_diff_generate(c1, c2); + + if (c3) { + char *c4 = consensus_diff_apply(c1, c3); + tor_assert(c4); + if (strcmp(c2, c4)) { + printf("%s\n", escaped(c1)); + printf("%s\n", escaped(c2)); + printf("%s\n", escaped(c3)); + printf("%s\n", escaped(c4)); + } + tor_assert(! strcmp(c2, c4)); + tor_free(c3); + tor_free(c4); + } + tor_free(c1); + tor_free(c2); + + return 0; +} + diff --git a/src/test/fuzz/fuzz_diff_apply.c b/src/test/fuzz/fuzz_diff_apply.c new file mode 100644 index 0000000000..8d7bf751bf --- /dev/null +++ b/src/test/fuzz/fuzz_diff_apply.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONSDIFF_PRIVATE + +#include "orconfig.h" +#include "or.h" +#include "consdiff.h" + +#include "fuzzing.h" + +static int +mock_consensus_compute_digest_(const char *c, consensus_digest_t *d) +{ + (void)c; + memset(d->sha3_256, 3, sizeof(d->sha3_256)); + return 0; +} + +static int +mock_consensus_digest_eq_(const uint8_t *a, const uint8_t *b) +{ + (void)a; + (void)b; + return 1; +} + +int +fuzz_init(void) +{ + MOCK(consensus_compute_digest, mock_consensus_compute_digest_); + MOCK(consensus_digest_eq, mock_consensus_digest_eq_); + return 0; +} + +int +fuzz_cleanup(void) +{ + UNMOCK(consensus_compute_digest); + UNMOCK(consensus_digest_eq); + return 0; +} + +int +fuzz_main(const uint8_t *stdin_buf, size_t data_size) +{ +#define SEP "=====\n" +#define SEPLEN strlen(SEP) + const uint8_t *separator = tor_memmem(stdin_buf, data_size, SEP, SEPLEN); + if (! separator) + return 0; + size_t c1_len = separator - stdin_buf; + char *c1 = tor_memdup_nulterm(stdin_buf, c1_len); + size_t c2_len = data_size - c1_len - SEPLEN; + char *c2 = tor_memdup_nulterm(separator + SEPLEN, c2_len); + + char *c3 = consensus_diff_apply(c1, c2); + + tor_free(c1); + tor_free(c2); + tor_free(c3); + + return 0; +} + diff --git a/src/test/fuzz/fuzz_extrainfo.c b/src/test/fuzz/fuzz_extrainfo.c new file mode 100644 index 0000000000..2a3de7ecf7 --- /dev/null +++ b/src/test/fuzz/fuzz_extrainfo.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "routerlist.h" +#include "routerkeys.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static int +mock_router_produce_hash_final__nohash(char *digest, + const char *start, size_t len, + digest_algorithm_t alg) +{ + (void)start; + (void)len; + /* we could look at start[..] */ + if (alg == DIGEST_SHA1) + memset(digest, 0x01, 20); + else + memset(digest, 0x02, 32); + return 0; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + MOCK(router_compute_hash_final, mock_router_produce_hash_final__nohash); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + extrainfo_t *ei; + const char *str = (const char*) data; + int again = 0; + ei = extrainfo_parse_entry_from_string((const char *)str, + str+sz, + 0, NULL, &again); + if (ei) { + log_debug(LD_GENERAL, "Parsing okay"); + extrainfo_free(ei); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + return 0; +} + diff --git a/src/test/fuzz/fuzz_hsdescv2.c b/src/test/fuzz/fuzz_hsdescv2.c new file mode 100644 index 0000000000..19db265716 --- /dev/null +++ b/src/test/fuzz/fuzz_hsdescv2.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "rendcommon.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + rend_service_descriptor_t *desc = NULL; + char desc_id[64]; + char *ipts = NULL; + size_t ipts_size, esize; + const char *next; + char *str = tor_memdup_nulterm(data, sz); + (void) rend_parse_v2_service_descriptor(&desc, desc_id, &ipts, &ipts_size, + &esize, &next, str, 1); + if (desc) { + log_debug(LD_GENERAL, "Parsing okay"); + rend_service_descriptor_free(desc); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + tor_free(ipts); + tor_free(str); + return 0; +} + diff --git a/src/test/fuzz/fuzz_http.c b/src/test/fuzz/fuzz_http.c new file mode 100644 index 0000000000..2ffeb60244 --- /dev/null +++ b/src/test/fuzz/fuzz_http.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define BUFFERS_PRIVATE +#define DIRECTORY_PRIVATE + +#include "or.h" +#include "backtrace.h" +#include "buffers.h" +#include "config.h" +#include "connection.h" +#include "directory.h" +#include "torlog.h" + +#include "fuzzing.h" + +static void +mock_connection_write_to_buf_impl_(const char *string, size_t len, + connection_t *conn, int compressed) +{ + log_debug(LD_GENERAL, "%sResponse:\n%u\nConnection: %p\n%s\n", + compressed ? "Compressed " : "", (unsigned)len, conn, string); +} + +static int +mock_directory_handle_command_get(dir_connection_t *conn, + const char *headers, + const char *body, + size_t body_len) +{ + (void)conn; + + log_debug(LD_GENERAL, "Method:\nGET\n"); + + if (headers) { + log_debug(LD_GENERAL, "Header-Length:\n%u\n", (unsigned)strlen(headers)); + log_debug(LD_GENERAL, "Headers:\n%s\n", headers); + } + + log_debug(LD_GENERAL, "Body-Length:\n%u\n", (unsigned)body_len); + if (body) { + log_debug(LD_GENERAL, "Body:\n%s\n", body); + } + + /* Always tell the caller we succeeded */ + return 0; +} + +static int +mock_directory_handle_command_post(dir_connection_t *conn, + const char *headers, + const char *body, + size_t body_len) +{ + (void)conn; + + log_debug(LD_GENERAL, "Method:\nPOST\n"); + + if (headers) { + log_debug(LD_GENERAL, "Header-Length:\n%u\n", (unsigned)strlen(headers)); + log_debug(LD_GENERAL, "Headers:\n%s\n", headers); + } + + log_debug(LD_GENERAL, "Body-Length:\n%u\n", (unsigned)body_len); + if (body) { + log_debug(LD_GENERAL, "Body:\n%s\n", body); + } + + /* Always tell the caller we succeeded */ + return 0; +} + +int +fuzz_init(void) +{ + /* Set up fake response handler */ + MOCK(connection_write_to_buf_impl_, mock_connection_write_to_buf_impl_); + /* Set up the fake handler functions */ + MOCK(directory_handle_command_get, mock_directory_handle_command_get); + MOCK(directory_handle_command_post, mock_directory_handle_command_post); + + return 0; +} + +int +fuzz_cleanup(void) +{ + UNMOCK(connection_write_to_buf_impl_); + UNMOCK(directory_handle_command_get); + UNMOCK(directory_handle_command_post); + return 0; +} + +int +fuzz_main(const uint8_t *stdin_buf, size_t data_size) +{ + dir_connection_t dir_conn; + + /* Set up the fake connection */ + memset(&dir_conn, 0, sizeof(dir_connection_t)); + dir_conn.base_.type = CONN_TYPE_DIR; + /* Apparently tor sets this before directory_handle_command() is called. */ + dir_conn.base_.address = tor_strdup("replace-this-address.example.com"); + + dir_conn.base_.inbuf = buf_new_with_data((char*)stdin_buf, data_size); + if (!dir_conn.base_.inbuf) { + log_debug(LD_GENERAL, "Zero-Length-Input\n"); + goto done; + } + + /* Parse the headers */ + int rv = directory_handle_command(&dir_conn); + + /* TODO: check the output is correctly parsed based on the input */ + + /* Report the parsed origin address */ + if (dir_conn.base_.address) { + log_debug(LD_GENERAL, "Address:\n%s\n", dir_conn.base_.address); + } + + log_debug(LD_GENERAL, "Result:\n%d\n", rv); + + done: + /* Reset. */ + tor_free(dir_conn.base_.address); + buf_free(dir_conn.base_.inbuf); + dir_conn.base_.inbuf = NULL; + + return 0; +} + diff --git a/src/test/fuzz/fuzz_iptsv2.c b/src/test/fuzz/fuzz_iptsv2.c new file mode 100644 index 0000000000..4abde0c16d --- /dev/null +++ b/src/test/fuzz/fuzz_iptsv2.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "rendcommon.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + rend_service_descriptor_t *desc = + tor_malloc_zero(sizeof(rend_service_descriptor_t)); + const char *str = (const char*) data; + int r = rend_parse_introduction_points(desc, str, sz); + if (r >= 0) { + log_debug(LD_GENERAL, "Parsing okay: %d", r); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + rend_service_descriptor_free(desc); + return 0; +} + diff --git a/src/test/fuzz/fuzz_microdesc.c b/src/test/fuzz/fuzz_microdesc.c new file mode 100644 index 0000000000..396115026e --- /dev/null +++ b/src/test/fuzz/fuzz_microdesc.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "microdesc.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + const char *str = (const char*) data; + smartlist_t *result = microdescs_parse_from_string((const char *)str, + str+sz, + 0, SAVED_NOWHERE, NULL); + if (result) { + log_debug(LD_GENERAL, "Parsing okay: %d", smartlist_len(result)); + SMARTLIST_FOREACH(result, microdesc_t *, md, microdesc_free(md)); + smartlist_free(result); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + return 0; +} + diff --git a/src/test/fuzz/fuzz_multi.sh b/src/test/fuzz/fuzz_multi.sh new file mode 100755 index 0000000000..b4a17ed8cb --- /dev/null +++ b/src/test/fuzz/fuzz_multi.sh @@ -0,0 +1,34 @@ +MEMLIMIT_BYTES=21990500990976 + +N_CPUS=1 +if [ $# -ge 1 ]; then + N_CPUS="$1" + shift +fi + +FILTER=echo + +for i in `seq -w "$N_CPUS"`; do + if [ "$i" -eq 1 ]; then + if [ "$N_CPUS" -eq 1 ]; then + INSTANCE="" + NUMBER="" + else + INSTANCE="-M" + NUMBER="$i" + fi + else + INSTANCE="-S" + NUMBER="$i" + fi + # use whatever remains on the command-line to prefix the fuzzer command + # you have to copy and paste and run these commands yourself + "$FILTER" "$@" \ + ../afl/afl-fuzz \ + -i src/test/fuzz/fuzz_dir_testcase \ + -o src/test/fuzz/fuzz_dir_findings \ + -x src/test/fuzz/fuzz_dir_dictionary/fuzz_dir_http_header.dct \ + -m "$MEMLIMIT_BYTES" \ + "$INSTANCE" "$NUMBER" \ + -- src/test/fuzz_dir +done diff --git a/src/test/fuzz/fuzz_vrs.c b/src/test/fuzz/fuzz_vrs.c new file mode 100644 index 0000000000..baf0610a0b --- /dev/null +++ b/src/test/fuzz/fuzz_vrs.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#define NETWORKSTATUS_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "memarea.h" +#include "microdesc.h" +#include "networkstatus.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static networkstatus_t *dummy_vote = NULL; +static memarea_t *area = NULL; + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + area = memarea_new(); + dummy_vote = tor_malloc_zero(sizeof(*dummy_vote)); + dummy_vote->known_flags = smartlist_new(); + smartlist_split_string(dummy_vote->known_flags, + "Authority BadExit Exit Fast Guard HSDir " + "NoEdConsensus Running Stable V2Dir Valid", + " ", 0, 0); + return 0; +} + +int +fuzz_cleanup(void) +{ + SMARTLIST_FOREACH(dummy_vote->known_flags, char *, cp, tor_free(cp)); + smartlist_free(dummy_vote->known_flags); + tor_free(dummy_vote); + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + char *str = tor_memdup_nulterm(data, sz); + const char *s; + routerstatus_t *rs_ns = NULL, *rs_md = NULL, *rs_vote = NULL; + vote_routerstatus_t *vrs = tor_malloc_zero(sizeof(*vrs)); + smartlist_t *tokens = smartlist_new(); + + s = str; + rs_ns = routerstatus_parse_entry_from_string(area, &s, tokens, + NULL, NULL, 26, FLAV_NS); + tor_assert(smartlist_len(tokens) == 0); + + s = str; + rs_md = routerstatus_parse_entry_from_string(area, &s, tokens, + NULL, NULL, 26, FLAV_MICRODESC); + tor_assert(smartlist_len(tokens) == 0); + + s = str; + rs_vote = routerstatus_parse_entry_from_string(area, &s, tokens, + dummy_vote, vrs, 26, FLAV_NS); + tor_assert(smartlist_len(tokens) == 0); + + log_debug(LD_GENERAL, + "ns=%p, md=%p, vote=%p", rs_ns, rs_md, rs_vote); + + routerstatus_free(rs_md); + routerstatus_free(rs_ns); + vote_routerstatus_free(vrs); + memarea_clear(area); + smartlist_free(tokens); + tor_free(str); + return 0; +} + diff --git a/src/test/fuzz/fuzzing.h b/src/test/fuzz/fuzzing.h new file mode 100644 index 0000000000..aecdbb4e52 --- /dev/null +++ b/src/test/fuzz/fuzzing.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#ifndef FUZZING_H +#define FUZZING_H + +int fuzz_init(void); +int fuzz_cleanup(void); +int fuzz_main(const uint8_t *data, size_t sz); + +void disable_signature_checking(void); + +#endif /* FUZZING_H */ + diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c new file mode 100644 index 0000000000..7aee92df63 --- /dev/null +++ b/src/test/fuzz/fuzzing_common.c @@ -0,0 +1,191 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define CRYPTO_ED25519_PRIVATE +#include "orconfig.h" +#include "or.h" +#include "backtrace.h" +#include "config.h" +#include "fuzzing.h" +#include "crypto.h" +#include "crypto_ed25519.h" + +extern const char tor_git_revision[]; +const char tor_git_revision[] = ""; + +static or_options_t *mock_options = NULL; +static const or_options_t * +mock_get_options(void) +{ + return mock_options; +} + +static int +mock_crypto_pk_public_checksig__nocheck(const crypto_pk_t *env, char *to, + size_t tolen, + const char *from, size_t fromlen) +{ + tor_assert(env && to && from); + (void)fromlen; + /* We could look at from[0..fromlen-1] ... */ + tor_assert(tolen >= crypto_pk_keysize(env)); + memset(to, 0x01, 20); + return 20; +} + +static int +mock_crypto_pk_public_checksig_digest__nocheck(crypto_pk_t *env, + const char *data, + size_t datalen, + const char *sig, + size_t siglen) +{ + tor_assert(env && data && sig); + (void)datalen; + (void)siglen; + /* We could look at data[..] and sig[..] */ + return 0; +} + +static int +mock_ed25519_checksig__nocheck(const ed25519_signature_t *signature, + const uint8_t *msg, size_t len, + const ed25519_public_key_t *pubkey) +{ + tor_assert(signature && msg && pubkey); + /* We could look at msg[0..len-1] ... */ + (void)len; + return 0; +} + +static int +mock_ed25519_checksig_batch__nocheck(int *okay_out, + const ed25519_checkable_t *checkable, + int n_checkable) +{ + tor_assert(checkable); + int i; + for (i = 0; i < n_checkable; ++i) { + /* We could look at messages and signatures XXX */ + tor_assert(checkable[i].pubkey); + tor_assert(checkable[i].msg); + if (okay_out) + okay_out[i] = 1; + } + return 0; +} + +static int +mock_ed25519_impl_spot_check__nocheck(void) +{ + return 0; +} + +void +disable_signature_checking(void) +{ + MOCK(crypto_pk_public_checksig, + mock_crypto_pk_public_checksig__nocheck); + MOCK(crypto_pk_public_checksig_digest, + mock_crypto_pk_public_checksig_digest__nocheck); + MOCK(ed25519_checksig, mock_ed25519_checksig__nocheck); + MOCK(ed25519_checksig_batch, mock_ed25519_checksig_batch__nocheck); + MOCK(ed25519_impl_spot_check, mock_ed25519_impl_spot_check__nocheck); +} + +static void +global_init(void) +{ + tor_threads_init(); + tor_compress_init(); + { + struct sipkey sipkey = { 1337, 7331 }; + siphash_set_global_key(&sipkey); + } + + /* Initialise logging first */ + init_logging(1); + configure_backtrace_handler(get_version()); + + /* set up the options. */ + mock_options = tor_malloc(sizeof(or_options_t)); + MOCK(get_options, mock_get_options); + + /* Make BUG() and nonfatal asserts crash */ + tor_set_failed_assertion_callback(abort); +} + +#ifdef LLVM_FUZZ +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +int +LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + static int initialized = 0; + if (!initialized) { + global_init(); + if (fuzz_init() < 0) + abort(); + initialized = 1; + } + + return fuzz_main(Data, Size); +} + +#else /* Not LLVM_FUZZ, so AFL. */ + +int +main(int argc, char **argv) +{ + size_t size; + + global_init(); + + /* Disable logging by default to speed up fuzzing. */ + int loglevel = LOG_ERR; + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--warn")) { + loglevel = LOG_WARN; + } else if (!strcmp(argv[i], "--notice")) { + loglevel = LOG_NOTICE; + } else if (!strcmp(argv[i], "--info")) { + loglevel = LOG_INFO; + } else if (!strcmp(argv[i], "--debug")) { + loglevel = LOG_DEBUG; + } + } + + { + log_severity_list_t s; + memset(&s, 0, sizeof(s)); + set_log_severity_config(loglevel, LOG_ERR, &s); + /* ALWAYS log bug warnings. */ + s.masks[LOG_WARN-LOG_ERR] |= LD_BUG; + add_stream_log(&s, "", fileno(stdout)); + } + + if (fuzz_init() < 0) + abort(); + +#ifdef __AFL_HAVE_MANUAL_CONTROL + /* Tell AFL to pause and fork here - ignored if not using AFL */ + __AFL_INIT(); +#endif + +#define MAX_FUZZ_SIZE (128*1024) + char *input = read_file_to_str_until_eof(0, MAX_FUZZ_SIZE, &size); + tor_assert(input); + char *raw = tor_memdup(input, size); /* Because input is nul-terminated */ + tor_free(input); + fuzz_main((const uint8_t*)raw, size); + tor_free(raw); + + if (fuzz_cleanup() < 0) + abort(); + + tor_free(mock_options); + UNMOCK(get_options); + return 0; +} + +#endif + diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am new file mode 100644 index 0000000000..2961dab56f --- /dev/null +++ b/src/test/fuzz/include.am @@ -0,0 +1,305 @@ +# This file was generated by fuzzing_include_am.py; do not hand-edit unless +# you enjoy having your changes erased. +FUZZING_CPPFLAGS = \ + $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) +FUZZING_CFLAGS = \ + $(AM_CFLAGS) $(TEST_CFLAGS) +FUZZING_LDFLAG = \ + @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ +FUZZING_LIBS = \ + src/or/libtor-testing.a \ + src/common/libor-crypto-testing.a \ + $(LIBKECCAK_TINY) \ + $(LIBDONNA) \ + src/common/libor-testing.a \ + src/common/libor-ctime-testing.a \ + src/common/libor-event-testing.a \ + src/trunnel/libor-trunnel-testing.a \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ + @TOR_LIBEVENT_LIBS@ \ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_SYSTEMD_LIBS@ \ + @TOR_LZMA_LIBS@ \ + @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) + +oss-fuzz-prereqs: \ + src/or/libtor-testing.a \ + src/common/libor-crypto-testing.a \ + $(LIBKECCAK_TINY) \ + $(LIBDONNA) \ + src/common/libor-testing.a \ + src/common/libor-ctime-testing.a \ + src/common/libor-event-testing.a \ + src/trunnel/libor-trunnel-testing.a + +noinst_HEADERS += \ + src/test/fuzz/fuzzing.h + +LIBFUZZER = -lFuzzer +LIBFUZZER_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ +LIBFUZZER_CFLAGS = $(FUZZING_CFLAGS) +LIBFUZZER_LDFLAG = $(FUZZING_LDFLAG) +LIBFUZZER_LIBS = $(FUZZING_LIBS) $(LIBFUZZER) -lstdc++ + +LIBOSS_FUZZ_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ +LIBOSS_FUZZ_CFLAGS = $(FUZZING_CFLAGS) + +# ===== AFL fuzzers +src_test_fuzz_fuzz_consensus_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_consensus.c +src_test_fuzz_fuzz_consensus_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_consensus_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_consensus_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_consensus_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_descriptor_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_descriptor.c +src_test_fuzz_fuzz_descriptor_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_descriptor_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_descriptor_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_descriptor_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_diff_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_diff.c +src_test_fuzz_fuzz_diff_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_diff_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_diff_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_diff_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_diff_apply_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_diff_apply.c +src_test_fuzz_fuzz_diff_apply_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_diff_apply_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_diff_apply_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_diff_apply_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_extrainfo_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_extrainfo.c +src_test_fuzz_fuzz_extrainfo_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_extrainfo_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_extrainfo_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_extrainfo_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_hsdescv2_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_hsdescv2.c +src_test_fuzz_fuzz_hsdescv2_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_hsdescv2_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_hsdescv2_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_hsdescv2_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_http_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_http.c +src_test_fuzz_fuzz_http_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_http_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_http_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_http_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_iptsv2_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_iptsv2.c +src_test_fuzz_fuzz_iptsv2_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_iptsv2_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_iptsv2_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_iptsv2_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_microdesc_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_microdesc.c +src_test_fuzz_fuzz_microdesc_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_microdesc_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_microdesc_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_microdesc_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_vrs_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_vrs.c +src_test_fuzz_fuzz_vrs_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_vrs_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_vrs_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_vrs_LDADD = $(FUZZING_LIBS) + +FUZZERS = \ + src/test/fuzz/fuzz-consensus \ + src/test/fuzz/fuzz-descriptor \ + src/test/fuzz/fuzz-diff \ + src/test/fuzz/fuzz-diff-apply \ + src/test/fuzz/fuzz-extrainfo \ + src/test/fuzz/fuzz-hsdescv2 \ + src/test/fuzz/fuzz-http \ + src/test/fuzz/fuzz-iptsv2 \ + src/test/fuzz/fuzz-microdesc \ + src/test/fuzz/fuzz-vrs + +# ===== libfuzzer + +if LIBFUZZER_ENABLED +src_test_fuzz_lf_fuzz_consensus_SOURCES = \ + $(src_test_fuzz_fuzz_consensus_SOURCES) +src_test_fuzz_lf_fuzz_consensus_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_consensus_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_consensus_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_consensus_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_descriptor_SOURCES = \ + $(src_test_fuzz_fuzz_descriptor_SOURCES) +src_test_fuzz_lf_fuzz_descriptor_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_descriptor_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_descriptor_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_descriptor_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_diff_SOURCES = \ + $(src_test_fuzz_fuzz_diff_SOURCES) +src_test_fuzz_lf_fuzz_diff_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_diff_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_diff_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_diff_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_diff_apply_SOURCES = \ + $(src_test_fuzz_fuzz_diff_apply_SOURCES) +src_test_fuzz_lf_fuzz_diff_apply_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_diff_apply_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_diff_apply_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_diff_apply_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_extrainfo_SOURCES = \ + $(src_test_fuzz_fuzz_extrainfo_SOURCES) +src_test_fuzz_lf_fuzz_extrainfo_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_extrainfo_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_extrainfo_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_extrainfo_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_hsdescv2_SOURCES = \ + $(src_test_fuzz_fuzz_hsdescv2_SOURCES) +src_test_fuzz_lf_fuzz_hsdescv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_hsdescv2_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_hsdescv2_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_hsdescv2_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_http_SOURCES = \ + $(src_test_fuzz_fuzz_http_SOURCES) +src_test_fuzz_lf_fuzz_http_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_http_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_http_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_http_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_iptsv2_SOURCES = \ + $(src_test_fuzz_fuzz_iptsv2_SOURCES) +src_test_fuzz_lf_fuzz_iptsv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_iptsv2_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_iptsv2_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_iptsv2_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_microdesc_SOURCES = \ + $(src_test_fuzz_fuzz_microdesc_SOURCES) +src_test_fuzz_lf_fuzz_microdesc_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_microdesc_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_microdesc_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_microdesc_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_vrs_SOURCES = \ + $(src_test_fuzz_fuzz_vrs_SOURCES) +src_test_fuzz_lf_fuzz_vrs_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_vrs_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_vrs_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_vrs_LDADD = $(LIBFUZZER_LIBS) + +LIBFUZZER_FUZZERS = \ + src/test/fuzz/lf-fuzz-consensus \ + src/test/fuzz/lf-fuzz-descriptor \ + src/test/fuzz/lf-fuzz-diff \ + src/test/fuzz/lf-fuzz-diff-apply \ + src/test/fuzz/lf-fuzz-extrainfo \ + src/test/fuzz/lf-fuzz-hsdescv2 \ + src/test/fuzz/lf-fuzz-http \ + src/test/fuzz/lf-fuzz-iptsv2 \ + src/test/fuzz/lf-fuzz-microdesc \ + src/test/fuzz/lf-fuzz-vrs + +else +LIBFUZZER_FUZZERS = +endif + +# ===== oss-fuzz + +if OSS_FUZZ_ENABLED +src_test_fuzz_liboss_fuzz_consensus_a_SOURCES = \ + $(src_test_fuzz_fuzz_consensus_SOURCES) +src_test_fuzz_liboss_fuzz_consensus_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_consensus_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_descriptor_a_SOURCES = \ + $(src_test_fuzz_fuzz_descriptor_SOURCES) +src_test_fuzz_liboss_fuzz_descriptor_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_descriptor_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_diff_a_SOURCES = \ + $(src_test_fuzz_fuzz_diff_SOURCES) +src_test_fuzz_liboss_fuzz_diff_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_diff_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_diff_apply_a_SOURCES = \ + $(src_test_fuzz_fuzz_diff_apply_SOURCES) +src_test_fuzz_liboss_fuzz_diff_apply_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_diff_apply_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_extrainfo_a_SOURCES = \ + $(src_test_fuzz_fuzz_extrainfo_SOURCES) +src_test_fuzz_liboss_fuzz_extrainfo_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_extrainfo_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_hsdescv2_a_SOURCES = \ + $(src_test_fuzz_fuzz_hsdescv2_SOURCES) +src_test_fuzz_liboss_fuzz_hsdescv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_hsdescv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_http_a_SOURCES = \ + $(src_test_fuzz_fuzz_http_SOURCES) +src_test_fuzz_liboss_fuzz_http_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_http_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_iptsv2_a_SOURCES = \ + $(src_test_fuzz_fuzz_iptsv2_SOURCES) +src_test_fuzz_liboss_fuzz_iptsv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_iptsv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_microdesc_a_SOURCES = \ + $(src_test_fuzz_fuzz_microdesc_SOURCES) +src_test_fuzz_liboss_fuzz_microdesc_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_microdesc_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_vrs_a_SOURCES = \ + $(src_test_fuzz_fuzz_vrs_SOURCES) +src_test_fuzz_liboss_fuzz_vrs_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_vrs_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +OSS_FUZZ_FUZZERS = \ + src/test/fuzz/liboss-fuzz-consensus.a \ + src/test/fuzz/liboss-fuzz-descriptor.a \ + src/test/fuzz/liboss-fuzz-diff.a \ + src/test/fuzz/liboss-fuzz-diff-apply.a \ + src/test/fuzz/liboss-fuzz-extrainfo.a \ + src/test/fuzz/liboss-fuzz-hsdescv2.a \ + src/test/fuzz/liboss-fuzz-http.a \ + src/test/fuzz/liboss-fuzz-iptsv2.a \ + src/test/fuzz/liboss-fuzz-microdesc.a \ + src/test/fuzz/liboss-fuzz-vrs.a + +else +OSS_FUZZ_FUZZERS = +endif + +noinst_PROGRAMS += $(FUZZERS) $(LIBFUZZER_FUZZERS) +noinst_LIBRARIES += $(OSS_FUZZ_FUZZERS) +oss-fuzz-fuzzers: oss-fuzz-prereqs $(OSS_FUZZ_FUZZERS) +fuzzers: $(FUZZERS) $(LIBFUZZER_FUZZERS) + +test-fuzz-corpora: $(FUZZERS) + $(top_srcdir)/src/test/fuzz_static_testcases.sh diff --git a/src/test/fuzz/minimize.sh b/src/test/fuzz/minimize.sh new file mode 100755 index 0000000000..87d3dda13c --- /dev/null +++ b/src/test/fuzz/minimize.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +if [ ! -d "$1" ] ; then + echo "I need a directory" + exit 1 +fi + +which=`basename "$1"` + +mkdir "$1.out" +afl-cmin -i "$1" -o "$1.out" -m none "./src/test/fuzz/fuzz-${which}" + diff --git a/src/test/fuzz_static_testcases.sh b/src/test/fuzz_static_testcases.sh new file mode 100755 index 0000000000..3cb45ad5e6 --- /dev/null +++ b/src/test/fuzz_static_testcases.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Copyright (c) 2016-2017, The Tor Project, Inc. +# See LICENSE for licensing information + +set -e + +if [ -z "${TOR_FUZZ_CORPORA}" ] || [ ! -d "${TOR_FUZZ_CORPORA}" ] ; then + echo "You need to set TOR_FUZZ_CORPORA to point to a checkout of " + echo "the 'fuzzing-corpora' repository." + exit 77 +fi + + + +for fuzzer in "${builddir:-.}"/src/test/fuzz/fuzz-* ; do + f=`basename $fuzzer` + case="${f#fuzz-}" + if [ -d "${TOR_FUZZ_CORPORA}/${case}" ]; then + echo "Running tests for ${case}" + for entry in "${TOR_FUZZ_CORPORA}/${case}/"*; do + "${fuzzer}" "--err" < "$entry" + done + else + echo "No tests found for ${case}" + fi +done diff --git a/src/test/hs_ntor_ref.py b/src/test/hs_ntor_ref.py new file mode 100644 index 0000000000..2ed9324e1f --- /dev/null +++ b/src/test/hs_ntor_ref.py @@ -0,0 +1,425 @@ +#!/usr/bin/python +# Copyright 2017, The Tor Project, Inc +# See LICENSE for licensing information + +""" +hs_ntor_ref.py + +This module is a reference implementation of the modified ntor protocol +proposed for Tor hidden services in proposal 224 (Next Generation Hidden +Services) in section [NTOR-WITH-EXTRA-DATA]. + +The modified ntor protocol is a single-round protocol, with three steps in total: + + 1: Client generates keys and sends them to service via INTRODUCE cell + + 2: Service computes key material based on client's keys, and sends its own + keys to client via RENDEZVOUS cell + + 3: Client computes key material as well. + +It's meant to be used to validate Tor's HS ntor implementation by conducting +various integration tests. Specifically it conducts the following three tests: + +- Tests our Python implementation by running the whole protocol in Python and + making sure that results are consistent. + +- Tests little-t-tor ntor implementation. We use this Python code to instrument + little-t-tor and carry out the handshake by using little-t-tor code. The + small C wrapper at src/test/test-hs-ntor-cl is used for this Python module to + interface with little-t-tor. + +- Cross-tests Python and little-t-tor implementation by running half of the + protocol in Python code and the other in little-t-tor. This is actually two + tests so that all parts of the protocol are run both by little-t-tor and + Python. + +It requires the curve25519 python module from the curve25519-donna package. + +The whole logic and concept for this test suite was taken from ntor_ref.py. + + *** DO NOT USE THIS IN PRODUCTION. *** +""" + +import struct +import os, sys +import binascii +import subprocess + +try: + import curve25519 + curve25519mod = curve25519.keys +except ImportError: + curve25519 = None + import slownacl_curve25519 + curve25519mod = slownacl_curve25519 + +import hashlib +try: + import sha3 +except ImportError: + # In python 3.6, the sha3 functions are in hashlib whether we + # import sha3 or not. + sha3 = None + +try: + # Pull the sha3 functions in. + from hashlib import sha3_256, shake_256 + shake_squeeze = shake_256.digest +except ImportError: + if hasattr(sha3, "SHA3256"): + # If this happens, then we have the old "sha3" module which + # hashlib and pysha3 superseded. + sha3_256 = sha3.SHA3256 + shake_256 = sha3.SHAKE256 + shake_squeeze = shake_256.squeeze + else: + # error code 77 tells automake to skip this test + sys.exit(77) + +# Import Nick's ntor reference implementation in Python +# We are gonna use a few of its utilities. +from ntor_ref import hash_nil +from ntor_ref import PrivateKey + +# String constants used in this protocol +PROTOID = b"tor-hs-ntor-curve25519-sha3-256-1" +T_HSENC = PROTOID + b":hs_key_extract" +T_HSVERIFY = PROTOID + b":hs_verify" +T_HSMAC = PROTOID + b":hs_mac" +M_HSEXPAND = PROTOID + b":hs_key_expand" + +INTRO_SECRET_LEN = 161 +REND_SECRET_LEN = 225 +AUTH_INPUT_LEN = 199 + +# Implements MAC(k,m) = H(htonll(len(k)) | k | m) +def mac(k,m): + def htonll(num): + return struct.pack('!q', num) + + s = sha3_256() + s.update(htonll(len(k))) + s.update(k) + s.update(m) + return s.digest() + +###################################################################### + +# Functions that implement the modified HS ntor protocol + +"""As client compute key material for INTRODUCE cell as follows: + + intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID + info = m_hsexpand | subcredential + hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) + ENC_KEY = hs_keys[0:S_KEY_LEN] + MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] +""" +def intro2_ntor_client(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential): + + dh_result = client_ephemeral_enc_privkey.get_shared_key(intro_enc_pubkey, hash_nil) + secret = dh_result + intro_auth_pubkey_str + client_ephemeral_enc_pubkey.serialize() + intro_enc_pubkey.serialize() + PROTOID + assert(len(secret) == INTRO_SECRET_LEN) + info = M_HSEXPAND + subcredential + + kdf = shake_256() + kdf.update(secret + T_HSENC + info) + key_material = shake_squeeze(kdf, 64*8) + + enc_key = key_material[0:32] + mac_key = key_material[32:64] + + return enc_key, mac_key + +"""Wrapper over intro2_ntor_client()""" +def client_part1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential): + enc_key, mac_key = intro2_ntor_client(intro_auth_pubkey_str, intro_enc_pubkey, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential) + assert(enc_key) + assert(mac_key) + + return enc_key, mac_key + +"""As service compute key material for INTRODUCE cell as follows: + + intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID + info = m_hsexpand | subcredential + hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) + HS_DEC_KEY = hs_keys[0:S_KEY_LEN] + HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] +""" +def intro2_ntor_service(intro_auth_pubkey_str, client_enc_pubkey, service_enc_privkey, service_enc_pubkey, subcredential): + dh_result = service_enc_privkey.get_shared_key(client_enc_pubkey, hash_nil) + secret = dh_result + intro_auth_pubkey_str + client_enc_pubkey.serialize() + service_enc_pubkey.serialize() + PROTOID + assert(len(secret) == INTRO_SECRET_LEN) + info = M_HSEXPAND + subcredential + + kdf = shake_256() + kdf.update(secret + T_HSENC + info) + key_material = shake_squeeze(kdf, 64*8) + + enc_key = key_material[0:32] + mac_key = key_material[32:64] + + return enc_key, mac_key + +"""As service compute key material for INTRODUCE and REDNEZVOUS cells. + + Use intro2_ntor_service() to calculate the INTRODUCE key material, and use + the following computations to do the RENDEZVOUS ones: + + rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID + NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc) + verify = MAC(rend_secret_hs_input, t_hsverify) + auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" + AUTH_INPUT_MAC = MAC(auth_input, t_hsmac) +""" +def service_part1(intro_auth_pubkey_str, client_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential): + intro_enc_key, intro_mac_key = intro2_ntor_service(intro_auth_pubkey_str, client_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential) + assert(intro_enc_key) + assert(intro_mac_key) + + service_ephemeral_privkey = PrivateKey() + service_ephemeral_pubkey = service_ephemeral_privkey.get_public() + + dh_result1 = service_ephemeral_privkey.get_shared_key(client_enc_pubkey, hash_nil) + dh_result2 = intro_enc_privkey.get_shared_key(client_enc_pubkey, hash_nil) + rend_secret_hs_input = dh_result1 + dh_result2 + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + client_enc_pubkey.serialize() + service_ephemeral_pubkey.serialize() + PROTOID + assert(len(rend_secret_hs_input) == REND_SECRET_LEN) + + ntor_key_seed = mac(rend_secret_hs_input, T_HSENC) + verify = mac(rend_secret_hs_input, T_HSVERIFY) + auth_input = verify + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + service_ephemeral_pubkey.serialize() + client_enc_pubkey.serialize() + PROTOID + b"Server" + assert(len(auth_input) == AUTH_INPUT_LEN) + auth_input_mac = mac(auth_input, T_HSMAC) + + assert(ntor_key_seed) + assert(auth_input_mac) + assert(service_ephemeral_pubkey) + + return intro_enc_key, intro_mac_key, ntor_key_seed, auth_input_mac, service_ephemeral_pubkey + +"""As client compute key material for rendezvous cells as follows: + + rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID + NTOR_KEY_SEED = MAC(ntor_secret_input, t_hsenc) + verify = MAC(ntor_secret_input, t_hsverify) + auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" + AUTH_INPUT_MAC = MAC(auth_input, t_hsmac) +""" +def client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_ephemeral_rend_pubkey): + dh_result1 = client_ephemeral_enc_privkey.get_shared_key(service_ephemeral_rend_pubkey, hash_nil) + dh_result2 = client_ephemeral_enc_privkey.get_shared_key(intro_enc_pubkey, hash_nil) + rend_secret_hs_input = dh_result1 + dh_result2 + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + client_ephemeral_enc_pubkey.serialize() + service_ephemeral_rend_pubkey.serialize() + PROTOID + assert(len(rend_secret_hs_input) == REND_SECRET_LEN) + + ntor_key_seed = mac(rend_secret_hs_input, T_HSENC) + verify = mac(rend_secret_hs_input, T_HSVERIFY) + auth_input = verify + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + service_ephemeral_rend_pubkey.serialize() + client_ephemeral_enc_pubkey.serialize() + PROTOID + b"Server" + assert(len(auth_input) == AUTH_INPUT_LEN) + auth_input_mac = mac(auth_input, T_HSMAC) + + assert(ntor_key_seed) + assert(auth_input_mac) + + return ntor_key_seed, auth_input_mac + +################################################################################# + +""" +Utilities for communicating with the little-t-tor ntor wrapper to conduct the +integration tests +""" + +PROG = b"./src/test/test-hs-ntor-cl" +enhex=lambda s: binascii.b2a_hex(s) +dehex=lambda s: binascii.a2b_hex(s.strip()) + +def tor_client1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_privkey, subcredential): + p = subprocess.Popen([PROG, "client1", + enhex(intro_auth_pubkey_str), + enhex(intro_enc_pubkey.serialize()), + enhex(client_ephemeral_enc_privkey.serialize()), + enhex(subcredential)], + stdout=subprocess.PIPE) + return map(dehex, p.stdout.readlines()) + +def tor_server1(intro_auth_pubkey_str, intro_enc_privkey, + client_ephemeral_enc_pubkey, subcredential): + p = subprocess.Popen([PROG, "server1", + enhex(intro_auth_pubkey_str), + enhex(intro_enc_privkey.serialize()), + enhex(client_ephemeral_enc_pubkey.serialize()), + enhex(subcredential)], + stdout=subprocess.PIPE) + return map(dehex, p.stdout.readlines()) + +def tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_ephemeral_rend_pubkey, subcredential): + p = subprocess.Popen([PROG, "client2", + enhex(intro_auth_pubkey_str), + enhex(client_ephemeral_enc_privkey.serialize()), + enhex(intro_enc_pubkey.serialize()), + enhex(service_ephemeral_rend_pubkey.serialize()), + enhex(subcredential)], + stdout=subprocess.PIPE) + return map(dehex, p.stdout.readlines()) + +################################################################################## + +# Perform a pure python ntor test +def do_pure_python_ntor_test(): + # Initialize all needed key material + client_ephemeral_enc_privkey = PrivateKey() + client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public() + intro_enc_privkey = PrivateKey() + intro_enc_pubkey = intro_enc_privkey.get_public() + intro_auth_pubkey_str = os.urandom(32) + subcredential = os.urandom(32) + + client_enc_key, client_mac_key = client_part1(intro_auth_pubkey_str, intro_enc_pubkey, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential) + + service_enc_key, service_mac_key, service_ntor_key_seed, service_auth_input_mac, service_ephemeral_pubkey = service_part1(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential) + + assert(client_enc_key == service_enc_key) + assert(client_mac_key == service_mac_key) + + client_ntor_key_seed, client_auth_input_mac = client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_ephemeral_pubkey) + + assert(client_ntor_key_seed == service_ntor_key_seed) + assert(client_auth_input_mac == service_auth_input_mac) + + print("DONE: python dance [%s]" % repr(client_auth_input_mac)) + +# Perform a pure little-t-tor integration test. +def do_little_t_tor_ntor_test(): + # Initialize all needed key material + subcredential = os.urandom(32) + client_ephemeral_enc_privkey = PrivateKey() + client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public() + intro_enc_privkey = PrivateKey() + intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key + intro_auth_pubkey_str = os.urandom(32) + + client_enc_key, client_mac_key = tor_client1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_privkey, subcredential) + assert(client_enc_key) + assert(client_mac_key) + + service_enc_key, service_mac_key, service_ntor_auth_mac, service_ntor_key_seed, service_eph_pubkey = tor_server1(intro_auth_pubkey_str, + intro_enc_privkey, + client_ephemeral_enc_pubkey, + subcredential) + assert(service_enc_key) + assert(service_mac_key) + assert(service_ntor_auth_mac) + assert(service_ntor_key_seed) + + assert(client_enc_key == service_enc_key) + assert(client_mac_key == service_mac_key) + + # Turn from bytes to key + service_eph_pubkey = curve25519mod.Public(service_eph_pubkey) + + client_ntor_auth_mac, client_ntor_key_seed = tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_eph_pubkey, subcredential) + assert(client_ntor_auth_mac) + assert(client_ntor_key_seed) + + assert(client_ntor_key_seed == service_ntor_key_seed) + assert(client_ntor_auth_mac == service_ntor_auth_mac) + + print("DONE: tor dance [%s]" % repr(client_ntor_auth_mac)) + +""" +Do mixed test as follows: + 1. C -> S (python mode) + 2. C <- S (tor mode) + 3. Client computes keys (python mode) +""" +def do_first_mixed_test(): + subcredential = os.urandom(32) + + client_ephemeral_enc_privkey = PrivateKey() + client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public() + intro_enc_privkey = PrivateKey() + intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key + + intro_auth_pubkey_str = os.urandom(32) + + # Let's do mixed + client_enc_key, client_mac_key = client_part1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, + subcredential) + + service_enc_key, service_mac_key, service_ntor_auth_mac, service_ntor_key_seed, service_eph_pubkey = tor_server1(intro_auth_pubkey_str, + intro_enc_privkey, + client_ephemeral_enc_pubkey, + subcredential) + assert(service_enc_key) + assert(service_mac_key) + assert(service_ntor_auth_mac) + assert(service_ntor_key_seed) + assert(service_eph_pubkey) + + assert(client_enc_key == service_enc_key) + assert(client_mac_key == service_mac_key) + + # Turn from bytes to key + service_eph_pubkey = curve25519mod.Public(service_eph_pubkey) + + client_ntor_key_seed, client_auth_input_mac = client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_eph_pubkey) + + assert(client_auth_input_mac == service_ntor_auth_mac) + assert(client_ntor_key_seed == service_ntor_key_seed) + + print("DONE: 1st mixed dance [%s]" % repr(client_auth_input_mac)) + +""" +Do mixed test as follows: + 1. C -> S (tor mode) + 2. C <- S (python mode) + 3. Client computes keys (tor mode) +""" +def do_second_mixed_test(): + subcredential = os.urandom(32) + + client_ephemeral_enc_privkey = PrivateKey() + client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public() + intro_enc_privkey = PrivateKey() + intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key + + intro_auth_pubkey_str = os.urandom(32) + + # Let's do mixed + client_enc_key, client_mac_key = tor_client1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_privkey, subcredential) + assert(client_enc_key) + assert(client_mac_key) + + service_enc_key, service_mac_key, service_ntor_key_seed, service_ntor_auth_mac, service_ephemeral_pubkey = service_part1(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential) + + client_ntor_auth_mac, client_ntor_key_seed = tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_ephemeral_pubkey, subcredential) + assert(client_ntor_auth_mac) + assert(client_ntor_key_seed) + + assert(client_ntor_key_seed == service_ntor_key_seed) + assert(client_ntor_auth_mac == service_ntor_auth_mac) + + print("DONE: 2nd mixed dance [%s]" % repr(client_ntor_auth_mac)) + +def do_mixed_tests(): + do_first_mixed_test() + do_second_mixed_test() + +if __name__ == '__main__': + do_pure_python_ntor_test() + do_little_t_tor_ntor_test() + do_mixed_tests() diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c new file mode 100644 index 0000000000..dcd58bc5fd --- /dev/null +++ b/src/test/hs_test_helpers.c @@ -0,0 +1,263 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "crypto_ed25519.h" +#include "test.h" +#include "torcert.h" + +#include "hs_test_helpers.h" + +hs_desc_intro_point_t * +hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now, + const char *addr, int legacy) +{ + int ret; + ed25519_keypair_t auth_kp; + hs_desc_intro_point_t *intro_point = NULL; + hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip)); + ip->link_specifiers = smartlist_new(); + + { + hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls)); + if (legacy) { + ls->type = LS_LEGACY_ID; + memcpy(ls->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8", + DIGEST_LEN); + } else { + ls->u.ap.port = 9001; + int family = tor_addr_parse(&ls->u.ap.addr, addr); + switch (family) { + case AF_INET: + ls->type = LS_IPV4; + break; + case AF_INET6: + ls->type = LS_IPV6; + break; + default: + /* Stop the test, not suppose to have an error. */ + tt_int_op(family, OP_EQ, AF_INET); + } + } + smartlist_add(ip->link_specifiers, ls); + } + + ret = ed25519_keypair_generate(&auth_kp, 0); + tt_int_op(ret, ==, 0); + ip->auth_key_cert = tor_cert_create(signing_kp, CERT_TYPE_AUTH_HS_IP_KEY, + &auth_kp.pubkey, now, + HS_DESC_CERT_LIFETIME, + CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(ip->auth_key_cert); + + if (legacy) { + ip->legacy.key = crypto_pk_new(); + tt_assert(ip->legacy.key); + ret = crypto_pk_generate_key(ip->legacy.key); + tt_int_op(ret, ==, 0); + ssize_t cert_len = tor_make_rsa_ed25519_crosscert( + &signing_kp->pubkey, ip->legacy.key, + now + HS_DESC_CERT_LIFETIME, + &ip->legacy.cert.encoded); + tt_assert(ip->legacy.cert.encoded); + tt_u64_op(cert_len, OP_GT, 0); + ip->legacy.cert.len = cert_len; + } + + /* Encryption key. */ + { + int signbit; + curve25519_keypair_t curve25519_kp; + ed25519_keypair_t ed25519_kp; + tor_cert_t *cross_cert; + + ret = curve25519_keypair_generate(&curve25519_kp, 0); + tt_int_op(ret, ==, 0); + ed25519_keypair_from_curve25519_keypair(&ed25519_kp, &signbit, + &curve25519_kp); + cross_cert = tor_cert_create(signing_kp, CERT_TYPE_CROSS_HS_IP_KEYS, + &ed25519_kp.pubkey, time(NULL), + HS_DESC_CERT_LIFETIME, + CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(cross_cert); + ip->enc_key_cert = cross_cert; + } + + intro_point = ip; + done: + if (intro_point == NULL) + tor_free(ip); + + return intro_point; +} + +/* Return a valid hs_descriptor_t object. If no_ip is set, no introduction + * points are added. */ +static hs_descriptor_t * +hs_helper_build_hs_desc_impl(unsigned int no_ip, + const ed25519_keypair_t *signing_kp) +{ + int ret; + time_t now = time(NULL); + ed25519_keypair_t blinded_kp; + hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc)); + + desc->plaintext_data.version = HS_DESC_SUPPORTED_FORMAT_VERSION_MAX; + + /* Copy only the public key into the descriptor. */ + memcpy(&desc->plaintext_data.signing_pubkey, &signing_kp->pubkey, + sizeof(ed25519_public_key_t)); + + ret = ed25519_keypair_generate(&blinded_kp, 0); + tt_int_op(ret, ==, 0); + /* Copy only the public key into the descriptor. */ + memcpy(&desc->plaintext_data.blinded_pubkey, &blinded_kp.pubkey, + sizeof(ed25519_public_key_t)); + + desc->plaintext_data.signing_key_cert = + tor_cert_create(&blinded_kp, CERT_TYPE_SIGNING_HS_DESC, + &signing_kp->pubkey, now, 3600, + CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(desc->plaintext_data.signing_key_cert); + desc->plaintext_data.revision_counter = 42; + desc->plaintext_data.lifetime_sec = 3 * 60 * 60; + + /* Setup encrypted data section. */ + desc->encrypted_data.create2_ntor = 1; + desc->encrypted_data.intro_auth_types = smartlist_new(); + desc->encrypted_data.single_onion_service = 1; + smartlist_add(desc->encrypted_data.intro_auth_types, tor_strdup("ed25519")); + desc->encrypted_data.intro_points = smartlist_new(); + if (!no_ip) { + /* Add four intro points. */ + smartlist_add(desc->encrypted_data.intro_points, + hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0)); + smartlist_add(desc->encrypted_data.intro_points, + hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0)); + smartlist_add(desc->encrypted_data.intro_points, + hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1)); + smartlist_add(desc->encrypted_data.intro_points, + hs_helper_build_intro_point(signing_kp, now, "", 1)); + } + + descp = desc; + done: + if (descp == NULL) + tor_free(desc); + + return descp; +} + +/* Build a descriptor with introduction points. */ +hs_descriptor_t * +hs_helper_build_hs_desc_with_ip(const ed25519_keypair_t *signing_kp) +{ + return hs_helper_build_hs_desc_impl(0, signing_kp); +} + +/* Build a descriptor without any introduction points. */ +hs_descriptor_t * +hs_helper_build_hs_desc_no_ip(const ed25519_keypair_t *signing_kp) +{ + return hs_helper_build_hs_desc_impl(1, signing_kp); +} + +void +hs_helper_desc_equal(const hs_descriptor_t *desc1, + const hs_descriptor_t *desc2) +{ + char *addr1 = NULL, *addr2 = NULL; + /* Plaintext data section. */ + tt_int_op(desc1->plaintext_data.version, OP_EQ, + desc2->plaintext_data.version); + tt_uint_op(desc1->plaintext_data.lifetime_sec, OP_EQ, + desc2->plaintext_data.lifetime_sec); + tt_assert(tor_cert_eq(desc1->plaintext_data.signing_key_cert, + desc2->plaintext_data.signing_key_cert)); + tt_mem_op(desc1->plaintext_data.signing_pubkey.pubkey, OP_EQ, + desc2->plaintext_data.signing_pubkey.pubkey, + ED25519_PUBKEY_LEN); + tt_mem_op(desc1->plaintext_data.blinded_pubkey.pubkey, OP_EQ, + desc2->plaintext_data.blinded_pubkey.pubkey, + ED25519_PUBKEY_LEN); + tt_u64_op(desc1->plaintext_data.revision_counter, ==, + desc2->plaintext_data.revision_counter); + + /* NOTE: We can't compare the encrypted blob because when encoding the + * descriptor, the object is immutable thus we don't update it with the + * encrypted blob. As contrast to the decoding process where we populate a + * descriptor object. */ + + /* Encrypted data section. */ + tt_uint_op(desc1->encrypted_data.create2_ntor, ==, + desc2->encrypted_data.create2_ntor); + + /* Authentication type. */ + tt_int_op(!!desc1->encrypted_data.intro_auth_types, ==, + !!desc2->encrypted_data.intro_auth_types); + if (desc1->encrypted_data.intro_auth_types && + desc2->encrypted_data.intro_auth_types) { + tt_int_op(smartlist_len(desc1->encrypted_data.intro_auth_types), ==, + smartlist_len(desc2->encrypted_data.intro_auth_types)); + for (int i = 0; + i < smartlist_len(desc1->encrypted_data.intro_auth_types); + i++) { + tt_str_op(smartlist_get(desc1->encrypted_data.intro_auth_types, i),OP_EQ, + smartlist_get(desc2->encrypted_data.intro_auth_types, i)); + } + } + + /* Introduction points. */ + { + tt_assert(desc1->encrypted_data.intro_points); + tt_assert(desc2->encrypted_data.intro_points); + tt_int_op(smartlist_len(desc1->encrypted_data.intro_points), ==, + smartlist_len(desc2->encrypted_data.intro_points)); + for (int i=0; i < smartlist_len(desc1->encrypted_data.intro_points); i++) { + hs_desc_intro_point_t *ip1 = smartlist_get(desc1->encrypted_data + .intro_points, i), + *ip2 = smartlist_get(desc2->encrypted_data + .intro_points, i); + tt_assert(tor_cert_eq(ip1->auth_key_cert, ip2->auth_key_cert)); + if (ip1->legacy.key) { + tt_int_op(crypto_pk_cmp_keys(ip1->legacy.key, ip2->legacy.key), + OP_EQ, 0); + } else { + tt_mem_op(&ip1->enc_key, OP_EQ, &ip2->enc_key, CURVE25519_PUBKEY_LEN); + } + + tt_int_op(smartlist_len(ip1->link_specifiers), ==, + smartlist_len(ip2->link_specifiers)); + for (int j = 0; j < smartlist_len(ip1->link_specifiers); j++) { + hs_desc_link_specifier_t *ls1 = smartlist_get(ip1->link_specifiers, j), + *ls2 = smartlist_get(ip2->link_specifiers, j); + tt_int_op(ls1->type, ==, ls2->type); + switch (ls1->type) { + case LS_IPV4: + case LS_IPV6: + { + addr1 = tor_addr_to_str_dup(&ls1->u.ap.addr); + addr2 = tor_addr_to_str_dup(&ls2->u.ap.addr); + tt_str_op(addr1, OP_EQ, addr2); + tor_free(addr1); + tor_free(addr2); + tt_int_op(ls1->u.ap.port, ==, ls2->u.ap.port); + } + break; + case LS_LEGACY_ID: + tt_mem_op(ls1->u.legacy_id, OP_EQ, ls2->u.legacy_id, + sizeof(ls1->u.legacy_id)); + break; + default: + /* Unknown type, caught it and print its value. */ + tt_int_op(ls1->type, OP_EQ, -1); + } + } + } + } + + done: + tor_free(addr1); + tor_free(addr2); +} + diff --git a/src/test/hs_test_helpers.h b/src/test/hs_test_helpers.h new file mode 100644 index 0000000000..a7fedab136 --- /dev/null +++ b/src/test/hs_test_helpers.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_HS_TEST_HELPERS_H +#define TOR_HS_TEST_HELPERS_H + +#include "ed25519_cert.h" +#include "hs_descriptor.h" + +/* Set of functions to help build and test descriptors. */ +hs_desc_intro_point_t *hs_helper_build_intro_point( + const ed25519_keypair_t *signing_kp, time_t now, + const char *addr, int legacy); +hs_descriptor_t *hs_helper_build_hs_desc_no_ip( + const ed25519_keypair_t *signing_kp); +hs_descriptor_t *hs_helper_build_hs_desc_with_ip( + const ed25519_keypair_t *signing_kp); +void hs_helper_desc_equal(const hs_descriptor_t *desc1, + const hs_descriptor_t *desc2); + +#endif /* TOR_HS_TEST_HELPERS_H */ + diff --git a/src/test/include.am b/src/test/include.am index 0ee3d1169f..723b4964e1 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -5,10 +5,15 @@ TESTS_ENVIRONMENT = \ export PYTHON="$(PYTHON)"; \ export SHELL="$(SHELL)"; \ export abs_top_srcdir="$(abs_top_srcdir)"; \ + export abs_top_builddir="$(abs_top_builddir)"; \ export builddir="$(builddir)"; \ - export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; + export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; \ + export CARGO="$(CARGO)"; \ + export CARGO_ONLINE="$(CARGO_ONLINE)"; -TESTSCRIPTS = src/test/test_zero_length_keys.sh \ +TESTSCRIPTS = \ + src/test/fuzz_static_testcases.sh \ + src/test/test_zero_length_keys.sh \ src/test/test_workqueue_cancel.sh \ src/test/test_workqueue_efd.sh \ src/test/test_workqueue_efd2.sh \ @@ -17,8 +22,13 @@ TESTSCRIPTS = src/test/test_zero_length_keys.sh \ src/test/test_workqueue_socketpair.sh \ src/test/test_switch_id.sh +if USE_RUST +TESTSCRIPTS += \ + src/test/test_rust.sh +endif + if USEPYTHON -TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh +TESTSCRIPTS += src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh endif TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ @@ -67,6 +77,7 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ src_test_test_SOURCES = \ src/test/log_test_helpers.c \ + src/test/hs_test_helpers.c \ src/test/rend_test_helpers.c \ src/test/test.c \ src/test/test_accounting.c \ @@ -77,17 +88,24 @@ src_test_test_SOURCES = \ src/test/test_cell_formats.c \ src/test/test_cell_queue.c \ src/test/test_channel.c \ + src/test/test_channelpadding.c \ src/test/test_channeltls.c \ src/test/test_checkdir.c \ src/test/test_circuitlist.c \ src/test/test_circuitmux.c \ + src/test/test_circuitbuild.c \ + src/test/test_circuituse.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ src/test/test_connection.c \ + src/test/test_conscache.c \ + src/test/test_consdiff.c \ + src/test/test_consdiffmgr.c \ src/test/test_containers.c \ src/test/test_controller.c \ src/test/test_controller_events.c \ src/test/test_crypto.c \ + src/test/test_crypto_openssl.c \ src/test/test_dos.c \ src/test/test_data.c \ src/test/test_dir.c \ @@ -98,7 +116,11 @@ src_test_test_SOURCES = \ src/test/test_guardfraction.c \ src/test/test_extorport.c \ src/test/test_hs.c \ + src/test/test_hs_service.c \ + src/test/test_hs_intropoint.c \ src/test/test_handles.c \ + src/test/test_hs_cache.c \ + src/test/test_hs_descriptor.c \ src/test/test_introduce.c \ src/test/test_keypin.c \ src/test/test_link_handshake.c \ @@ -120,10 +142,12 @@ src_test_test_SOURCES = \ src/test/test_routerkeys.c \ src/test/test_routerlist.c \ src/test/test_routerset.c \ + src/test/test_rust.c \ src/test/test_scheduler.c \ src/test/test_shared_random.c \ src/test/test_socks.c \ src/test/test_status.c \ + src/test/test_storagedir.c \ src/test/test_threads.c \ src/test/test_tortls.c \ src/test/test_util.c \ @@ -132,6 +156,7 @@ src_test_test_SOURCES = \ src/test/test_helpers.c \ src/test/test_dns.c \ src/test/testing_common.c \ + src/test/testing_rsakeys.c \ src/ext/tinytest.c src_test_test_slow_SOURCES = \ @@ -139,6 +164,7 @@ src_test_test_slow_SOURCES = \ src/test/test_crypto_slow.c \ src/test/test_util_slow.c \ src/test/testing_common.c \ + src/test/testing_rsakeys.c \ src/ext/tinytest.c src_test_test_memwipe_SOURCES = \ @@ -167,7 +193,9 @@ src_test_test_switch_id_LDFLAGS = @TOR_LDFLAGS_zlib@ src_test_test_switch_id_LDADD = \ src/common/libor-testing.a \ src/common/libor-ctime-testing.a \ - @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ @@ -179,9 +207,11 @@ src_test_test_LDADD = src/or/libtor-testing.a \ src/common/libor-ctime-testing.a \ src/common/libor-event-testing.a \ src/trunnel/libor-trunnel-testing.a \ + src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_SYSTEMD_LIBS@ + @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS) src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS) @@ -202,9 +232,11 @@ src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \ src/common/libor-ctime.a \ src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \ src/common/libor-event.a src/trunnel/libor-trunnel.a \ + src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_SYSTEMD_LIBS@ + @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ @@ -213,8 +245,11 @@ src_test_test_workqueue_LDADD = src/or/libtor-testing.a \ src/common/libor-ctime-testing.a \ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \ src/common/libor-event-testing.a \ + src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \ + $(rust_ldadd) src_test_test_timers_CPPFLAGS = $(src_test_test_CPPFLAGS) src_test_test_timers_CFLAGS = $(src_test_test_CFLAGS) @@ -224,11 +259,14 @@ src_test_test_timers_LDADD = \ src/common/libor-event-testing.a \ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_LZMA_LIBS@ \ + $(rust_ldadd) src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS) noinst_HEADERS+= \ src/test/fakechans.h \ + src/test/hs_test_helpers.h \ src/test/log_test_helpers.h \ src/test/rend_test_helpers.h \ src/test/test.h \ @@ -239,37 +277,58 @@ noinst_HEADERS+= \ src/test/failing_routerdescs.inc \ src/test/ed25519_vectors.inc \ src/test/test_descriptors.inc \ + src/test/test_hs_descriptor.inc \ src/test/vote_descriptors.inc noinst_PROGRAMS+= src/test/test-ntor-cl +noinst_PROGRAMS+= src/test/test-hs-ntor-cl src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \ src/common/libor-ctime.a \ src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \ + src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_LZMA_LIBS@ \ + $(rust_ldadd) src_test_test_ntor_cl_AM_CPPFLAGS = \ -I"$(top_srcdir)/src/or" +src_test_test_hs_ntor_cl_SOURCES = src/test/test_hs_ntor_cl.c +src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_test_test_hs_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \ + src/common/libor-ctime.a \ + src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ +src_test_test_hs_ntor_cl_AM_CPPFLAGS = \ + -I"$(top_srcdir)/src/or" + + noinst_PROGRAMS += src/test/test-bt-cl src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c src_test_test_bt_cl_LDADD = src/common/libor-testing.a \ src/common/libor-ctime-testing.a \ + src/trace/libor-trace.a \ @TOR_LIB_MATH@ \ - @TOR_LIB_WS32@ @TOR_LIB_GDI@ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ \ + $(rust_ldadd) src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) EXTRA_DIST += \ src/test/bt_test.py \ src/test/ntor_ref.py \ + src/test/hs_ntor_ref.py \ + src/test/fuzz_static_testcases.sh \ src/test/slownacl_curve25519.py \ src/test/zero_length_keys.sh \ src/test/test_keygen.sh \ src/test/test_zero_length_keys.sh \ - src/test/test_ntor.sh src/test/test_bt.sh \ + src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh \ src/test/test-network.sh \ + src/test/test_rust.sh \ src/test/test_switch_id.sh \ src/test/test_workqueue_cancel.sh \ src/test/test_workqueue_efd.sh \ diff --git a/src/test/log_test_helpers.c b/src/test/log_test_helpers.c index c788a33c17..d5a39cfeee 100644 --- a/src/test/log_test_helpers.c +++ b/src/test/log_test_helpers.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define LOG_PRIVATE #include "torlog.h" diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h index 922c68b42f..f7798c0249 100644 --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" @@ -71,14 +71,14 @@ void mock_dump_saved_logs(void); \ assert_log_predicate(mock_saved_log_has_message_containing(str) && \ mock_saved_log_n_entries() == 1, \ - "expected log to contain exactly 1 message: " # str); \ + "expected log to contain exactly 1 message " # str); \ } while (0); #define expect_single_log_msg_containing(str) \ do { \ assert_log_predicate(mock_saved_log_has_message_containing(str)&& \ mock_saved_log_n_entries() == 1 , \ - "expected log to contain 1 message, containing" # str); \ + "expected log to contain 1 message, containing " # str); \ } while (0); #define expect_no_log_msg(str) \ diff --git a/src/test/ntor_ref.py b/src/test/ntor_ref.py index df065853f3..c753588f97 100755 --- a/src/test/ntor_ref.py +++ b/src/test/ntor_ref.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 2012-2015, The Tor Project, Inc +# Copyright 2012-2017, The Tor Project, Inc # See LICENSE for licensing information """ diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c index 377337bcb9..f7880046fb 100644 --- a/src/test/rend_test_helpers.c +++ b/src/test/rend_test_helpers.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h index 180a4e8fde..486adba436 100644 --- a/src/test/rend_test_helpers.h +++ b/src/test/rend_test_helpers.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test-child.c b/src/test/test-child.c index fdf3ccec0a..f0bdb3ea26 100644 --- a/src/test/test-child.c +++ b/src/test/test-child.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Tor Project, Inc. */ +/* Copyright (c) 2011-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index fd6457416a..484f13dd05 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -36,7 +36,7 @@ const char *s = NULL; sum += (unsigned char)buf[i]; \ } -#ifdef __OpenBSD__ +#ifdef OpenBSD /* Disable some of OpenBSD's malloc protections for this test. This helps * us do bad things, such as access freed buffers, without crashing. */ const char *malloc_options="sufjj"; diff --git a/src/test/test-network.sh b/src/test/test-network.sh index 4d9776822b..6e0f286573 100755 --- a/src/test/test-network.sh +++ b/src/test/test-network.sh @@ -1,103 +1,45 @@ -#! /bin/sh +#!/bin/sh -# Please do not modify this script, it has been moved to chutney/tools +# This script calls the equivalent script in chutney/tools -ECHO_N="/bin/echo -n" +# If we already know CHUTNEY_PATH, don't bother with argument parsing +TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh" +# Call the chutney version of this script, if it exists, and we can find it +if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then + # we can't produce any output, because we might be --quiet + # this preserves arguments with spaces correctly + exec "$TEST_NETWORK" "$@" +fi + +# We need to go looking for CHUTNEY_PATH +# Do we output anything at all? +ECHO="${ECHO:-echo}" # Output is prefixed with the name of the script myname=$(basename $0) -# We need to find CHUTNEY_PATH, so that we can call the version of this script -# in chutney/tools. And we want to pass any arguments to that script as well. -# So we source this script, which processes its arguments to find CHUTNEY_PATH. - -# Avoid recursively sourcing this script, and don't call the chutney version -# while recursing, either -if [ "$TEST_NETWORK_RECURSING" != true ]; then - # Process the arguments into environmental variables with this script - # to make sure $CHUTNEY_PATH is set - # When we switch to using test-network.sh in chutney/tools, --dry-run - # can be removed, because this script will find chutney, then pass all - # arguments to chutney's test-network.sh - echo "$myname: Parsing command-line arguments to find \$CHUTNEY_PATH" - export TEST_NETWORK_RECURSING=true - . "$0" --dry-run "$@" - - # Call the chutney version of this script, if it exists, and we can find it - if [ -d "$CHUTNEY_PATH" -a -x "$CHUTNEY_PATH/tools/test-network.sh" ]; then - unset NETWORK_DRY_RUN - echo "$myname: Calling newer chutney script \ -$CHUTNEY_PATH/tools/test-network.sh" - "$CHUTNEY_PATH/tools/test-network.sh" "$@" - exit $? - else - echo "$myname: This script has moved to chutney/tools." - echo "$myname: Please update your chutney using 'git pull'." - # When we switch to using test-network.sh in chutney/tools, we should - # exit with a very loud failure here - echo "$myname: Falling back to the old tor version of the script." - fi -fi +# Save the arguments before we destroy them +# This might not preserve arguments with spaces in them +ORIGINAL_ARGS="$@" +# We need to find CHUTNEY_PATH, so that we can call the version of this script +# in chutney/tools with the same arguments. We also need to respect --quiet. until [ -z "$1" ] do case "$1" in --chutney-path) - export CHUTNEY_PATH="$2" + CHUTNEY_PATH="$2" shift ;; --tor-path) - export TOR_DIR="$2" - shift - ;; - # When we switch to using test-network.sh in chutney/tools, only the - # --chutney-path and --tor-path arguments need to be processed by this - # script, everything else can be handled by chutney's test-network.sh - --flavor|--flavour|--network-flavor|--network-flavour) - export NETWORK_FLAVOUR="$2" - shift - ;; - --delay|--sleep|--bootstrap-time|--time) - export BOOTSTRAP_TIME="$2" - shift - ;; - # Environmental variables used by chutney verify performance tests - # Send this many bytes per client connection (10 KBytes) - --data|--data-bytes|--data-byte|--bytes|--byte) - export CHUTNEY_DATA_BYTES="$2" + TOR_DIR="$2" shift ;; - # Make this many connections per client (1) - # Note: If you create 7 or more connections to a hidden service from - # a single Tor 0.2.7 client, you'll likely get a verification failure due - # to #15937. This is fixed in 0.2.8. - --connections|--connection|--connection-count|--count) - export CHUTNEY_CONNECTIONS="$2" - shift + --quiet) + ECHO=true ;; - # Make each client connect to each HS (0) - # 0 means a single client connects to each HS - # 1 means every client connects to every HS - --hs-multi-client|--hs-multi-clients|--hs-client|--hs-clients) - export CHUTNEY_HS_MULTI_CLIENT="$2" - shift - ;; - --coverage) - export USE_COVERAGE_BINARY=true - ;; - --dry-run) - # process arguments, but don't call any other scripts - export NETWORK_DRY_RUN=true - ;; *) - echo "$myname: Sorry, I don't know what to do with '$1'." - echo "$myname: Maybe chutney's test-network.sh understands '$1'." - echo "$myname: Please update your chutney using 'git pull', and set \ -\$CHUTNEY_PATH" - # continue processing arguments during a dry run - if [ "$NETWORK_DRY_RUN" != true ]; then - exit 2 - fi + # maybe chutney's test-network.sh can handle it ;; esac shift @@ -106,22 +48,22 @@ done # optional: $TOR_DIR is the tor build directory # it's used to find the location of tor binaries # if it's not set: -# - set it ro $BUILDDIR, or +# - set it to $BUILDDIR, or # - if $PWD looks like a tor build directory, set it to $PWD, or # - unset $TOR_DIR, and let chutney fall back to finding tor binaries in $PATH if [ ! -d "$TOR_DIR" ]; then if [ -d "$BUILDDIR/src/or" -a -d "$BUILDDIR/src/tools" ]; then # Choose the build directory # But only if it looks like one - echo "$myname: \$TOR_DIR not set, trying \$BUILDDIR" - export TOR_DIR="$BUILDDIR" + $ECHO "$myname: \$TOR_DIR not set, trying \$BUILDDIR" + TOR_DIR="$BUILDDIR" elif [ -d "$PWD/src/or" -a -d "$PWD/src/tools" ]; then # Guess the tor directory is the current directory # But only if it looks like one - echo "$myname: \$TOR_DIR not set, trying \$PWD" - export TOR_DIR="$PWD" + $ECHO "$myname: \$TOR_DIR not set, trying \$PWD" + TOR_DIR="$PWD" else - echo "$myname: no \$TOR_DIR, chutney will use \$PATH for tor binaries" + $ECHO "$myname: no \$TOR_DIR, chutney will use \$PATH for tor binaries" unset TOR_DIR fi fi @@ -133,63 +75,34 @@ fi # - fail and tell the user how to clone the chutney repository if [ ! -d "$CHUTNEY_PATH" -o ! -x "$CHUTNEY_PATH/chutney" ]; then if [ -x "$PWD/chutney" ]; then - echo "$myname: \$CHUTNEY_PATH not valid, trying \$PWD" - export CHUTNEY_PATH="$PWD" + $ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$PWD" + CHUTNEY_PATH="$PWD" elif [ -d "$TOR_DIR" -a -d "$TOR_DIR/../chutney" -a \ -x "$TOR_DIR/../chutney/chutney" ]; then - echo "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney" - export CHUTNEY_PATH="$TOR_DIR/../chutney" + $ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney" + CHUTNEY_PATH="$TOR_DIR/../chutney" else - # TODO: work out how to package and install chutney, - # so users can find it in $PATH - echo "$myname: missing 'chutney' in \$CHUTNEY_PATH ($CHUTNEY_PATH)" - echo "$myname: Get chutney: git clone https://git.torproject.org/\ + $ECHO "$myname: missing 'chutney' in \$CHUTNEY_PATH ($CHUTNEY_PATH)" + $ECHO "$myname: Get chutney: git clone https://git.torproject.org/\ chutney.git" - echo "$myname: Set \$CHUTNEY_PATH to a non-standard location: export \ + $ECHO "$myname: Set \$CHUTNEY_PATH to a non-standard location: export \ CHUTNEY_PATH=\`pwd\`/chutney" unset CHUTNEY_PATH exit 1 fi fi -# When we switch to using test-network.sh in chutney/tools, this comment and -# everything below it can be removed - -# For picking up the right tor binaries. -# If these varibles aren't set, chutney looks for tor binaries in $PATH -if [ -d "$TOR_DIR" ]; then - tor_name=tor - tor_gencert_name=tor-gencert - if [ "$USE_COVERAGE_BINARY" = true ]; then - tor_name=tor-cov - fi - export CHUTNEY_TOR="${TOR_DIR}/src/or/${tor_name}" - export CHUTNEY_TOR_GENCERT="${TOR_DIR}/src/tools/${tor_gencert_name}" -fi - -# Set the variables for the chutney network flavour -export NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-"bridges+hs"} -export CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR - -# And finish up if we're doing a dry run -if [ "$NETWORK_DRY_RUN" = true ]; then - # we can't exit here, it breaks argument processing - return +TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh" +# Call the chutney version of this script, if it exists, and we can find it +if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then + $ECHO "$myname: Calling newer chutney script $TEST_NETWORK" + # this may fail if some arguments have spaces in them + # if so, set CHUTNEY_PATH before calling test-network.sh, and spaces + # will be handled correctly + exec "$TEST_NETWORK" $ORIGINAL_ARGS +else + $ECHO "$myname: Could not find tools/test-network.sh in CHUTNEY_PATH." + $ECHO "$myname: Please update your chutney using 'git pull'." + # We have failed to do what the user asked + exit 1 fi - -cd "$CHUTNEY_PATH" -./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 2 - -# Sleep some, waiting for the network to bootstrap. -# TODO: Add chutney command 'bootstrap-status' and use that instead. -BOOTSTRAP_TIME=${BOOTSTRAP_TIME:-35} -$ECHO_N "$myname: sleeping for $BOOTSTRAP_TIME seconds" -n=$BOOTSTRAP_TIME; while [ $n -gt 0 ]; do - sleep 1; n=$(expr $n - 1); $ECHO_N . -done; echo "" -./chutney verify $CHUTNEY_NETWORK -VERIFY_EXIT_STATUS=$? -# work around a bug/feature in make -j2 (or more) -# where make hangs if any child processes are still alive -./chutney stop $CHUTNEY_NETWORK -exit $VERIFY_EXIT_STATUS diff --git a/src/test/test-timers.c b/src/test/test-timers.c index b5fcade7f8..99715f4333 100644 --- a/src/test/test-timers.c +++ b/src/test/test-timers.c @@ -1,4 +1,4 @@ -/* Copyright 2016, The Tor Project, Inc. */ +/* Copyright 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test.c b/src/test/test.c index 0fef697909..911ef0c24e 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -44,13 +44,13 @@ double fabs(double x); #include "buffers.h" #include "circuitlist.h" #include "circuitstats.h" +#include "compress.h" #include "config.h" #include "connection_edge.h" #include "geoip.h" #include "rendcommon.h" #include "rendcache.h" #include "test.h" -#include "torgzip.h" #include "main.h" #include "memarea.h" #include "onion.h" @@ -1205,17 +1205,24 @@ struct testgroup_t testgroups[] = { { "cellfmt/", cell_format_tests }, { "cellqueue/", cell_queue_tests }, { "channel/", channel_tests }, + { "channelpadding/", channelpadding_tests }, { "channeltls/", channeltls_tests }, { "checkdir/", checkdir_tests }, + { "circuitbuild/", circuitbuild_tests }, { "circuitlist/", circuitlist_tests }, { "circuitmux/", circuitmux_tests }, + { "circuituse/", circuituse_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, { "connection/", connection_tests }, + { "conscache/", conscache_tests }, + { "consdiff/", consdiff_tests }, + { "consdiffmgr/", consdiffmgr_tests }, { "container/", container_tests }, { "control/", controller_tests }, { "control/event/", controller_event_tests }, { "crypto/", crypto_tests }, + { "crypto/openssl/", crypto_openssl_tests }, { "dos/", dos_tests }, { "dir/", dir_tests }, { "dir_handle_get/", dir_handle_get_tests }, @@ -1224,7 +1231,11 @@ struct testgroup_t testgroups[] = { { "entrynodes/", entrynodes_tests }, { "guardfraction/", guardfraction_tests }, { "extorport/", extorport_tests }, - { "hs/", hs_tests }, + { "legacy_hs/", hs_tests }, + { "hs_cache/", hs_cache }, + { "hs_descriptor/", hs_descriptor }, + { "hs_service/", hs_service_tests }, + { "hs_intropoint/", hs_intropoint_tests }, { "introduce/", introduce_tests }, { "keypin/", keypin_tests }, { "link-handshake/", link_handshake_tests }, @@ -1243,10 +1254,12 @@ struct testgroup_t testgroups[] = { { "routerkeys/", routerkeys_tests }, { "routerlist/", routerlist_tests }, { "routerset/" , routerset_tests }, + { "rust/", rust_tests }, { "scheduler/", scheduler_tests }, { "socks/", socks_tests }, { "shared-random/", sr_tests }, { "status/" , status_tests }, + { "storagedir/", storagedir_tests }, { "tortls/", tortls_tests }, { "util/", util_tests }, { "util/format/", util_format_tests }, diff --git a/src/test/test.h b/src/test/test.h index 028082386e..ea1b16adee 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2003, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_TEST_H @@ -75,6 +75,8 @@ const char *get_fname(const char *name); const char *get_fname_rnd(const char *name); struct crypto_pk_t *pk_generate(int idx); +void init_pregenerated_keys(void); +void free_pregenerated_keys(void); #define US2_CONCAT_2__(a, b) a ## __ ## b #define US_CONCAT_2__(a, b) a ## _ ## b @@ -180,17 +182,24 @@ extern struct testcase_t buffer_tests[]; extern struct testcase_t cell_format_tests[]; extern struct testcase_t cell_queue_tests[]; extern struct testcase_t channel_tests[]; +extern struct testcase_t channelpadding_tests[]; extern struct testcase_t channeltls_tests[]; extern struct testcase_t checkdir_tests[]; +extern struct testcase_t circuitbuild_tests[]; extern struct testcase_t circuitlist_tests[]; extern struct testcase_t circuitmux_tests[]; +extern struct testcase_t circuituse_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; extern struct testcase_t connection_tests[]; +extern struct testcase_t conscache_tests[]; +extern struct testcase_t consdiff_tests[]; +extern struct testcase_t consdiffmgr_tests[]; extern struct testcase_t container_tests[]; extern struct testcase_t controller_tests[]; extern struct testcase_t controller_event_tests[]; extern struct testcase_t crypto_tests[]; +extern struct testcase_t crypto_openssl_tests[]; extern struct testcase_t dos_tests[]; extern struct testcase_t dir_tests[]; extern struct testcase_t dir_handle_get_tests[]; @@ -199,6 +208,10 @@ extern struct testcase_t entrynodes_tests[]; extern struct testcase_t guardfraction_tests[]; extern struct testcase_t extorport_tests[]; extern struct testcase_t hs_tests[]; +extern struct testcase_t hs_cache[]; +extern struct testcase_t hs_descriptor[]; +extern struct testcase_t hs_service_tests[]; +extern struct testcase_t hs_intropoint_tests[]; extern struct testcase_t introduce_tests[]; extern struct testcase_t keypin_tests[]; extern struct testcase_t link_handshake_tests[]; @@ -221,7 +234,9 @@ extern struct testcase_t router_tests[]; extern struct testcase_t routerkeys_tests[]; extern struct testcase_t routerlist_tests[]; extern struct testcase_t routerset_tests[]; +extern struct testcase_t rust_tests[]; extern struct testcase_t scheduler_tests[]; +extern struct testcase_t storagedir_tests[]; extern struct testcase_t socks_tests[]; extern struct testcase_t status_tests[]; extern struct testcase_t thread_tests[]; diff --git a/src/test/test_addr.c b/src/test/test_addr.c index be440a0925..2f591bdfe7 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ADDRESSMAP_PRIVATE @@ -9,6 +9,24 @@ #include "test.h" #include "addressmap.h" +/** Mocking replacement: only handles localhost. */ +static int +mock_tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr_out) +{ + if (!strcmp(name, "localhost")) { + if (family == AF_INET || family == AF_UNSPEC) { + tor_addr_from_ipv4h(addr_out, 0x7f000001); + return 0; + } else if (family == AF_INET6) { + char bytes[16] = { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 }; + tor_addr_from_ipv6_bytes(addr_out, bytes); + return 0; + } + } + return -1; +} + static void test_addr_basic(void *arg) { @@ -29,6 +47,9 @@ test_addr_basic(void *arg) tt_int_op(u32,OP_EQ, 0x04030201u); tt_int_op(u16,OP_EQ, 99); tor_free(cp); + + MOCK(tor_addr_lookup, mock_tor_addr_lookup); + tt_assert(!addr_port_lookup(LOG_WARN, "nonexistent.address:4040", &cp, NULL, &u16)); tt_str_op(cp,OP_EQ, "nonexistent.address"); @@ -36,8 +57,8 @@ test_addr_basic(void *arg) tor_free(cp); tt_assert(!addr_port_lookup(LOG_WARN, "localhost:9999", &cp, &u32, &u16)); tt_str_op(cp,OP_EQ, "localhost"); - tt_int_op(u32,OP_EQ, 0x7f000001u); tt_int_op(u16,OP_EQ, 9999); + tt_int_op(u32,OP_EQ, 0x7f000001u); tor_free(cp); u32 = 3; tt_assert(!addr_port_lookup(LOG_WARN, "localhost", NULL, &u32, &u16)); @@ -75,6 +96,7 @@ test_addr_basic(void *arg) } done: + UNMOCK(tor_addr_lookup); tor_free(cp); } diff --git a/src/test/test_address.c b/src/test/test_address.c index 0d142ad483..50a0574522 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ADDRESS_PRIVATE diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c index 95b4f48f11..ed588ecc5b 100644 --- a/src/test/test_bt_cl.c +++ b/src/test/test_bt_cl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Tor Project, Inc. */ +/* Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -19,14 +19,12 @@ static int crashtype = 0; #ifdef __GNUC__ #define NOINLINE __attribute__((noinline)) -#define NORETURN __attribute__((noreturn)) #endif int crash(int x) NOINLINE; int oh_what(int x) NOINLINE; int a_tangled_web(int x) NOINLINE; int we_weave(int x) NOINLINE; -static void abort_handler(int s) NORETURN; #ifdef HAVE_CFLAG_WNULL_DEREFERENCE DISABLE_GCC_WARNING(null-dereference) @@ -76,13 +74,6 @@ we_weave(int x) return a_tangled_web(x) + a_tangled_web(x+1); } -static void -abort_handler(int s) -{ - (void)s; - exit(0); -} - int main(int argc, char **argv) { @@ -120,8 +111,6 @@ main(int argc, char **argv) configure_backtrace_handler(NULL); - signal(SIGABRT, abort_handler); - printf("%d\n", we_weave(2)); clean_up_backtrace_handler(); diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index 3408da3aa9..07114a8571 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define BUFFERS_PRIVATE @@ -578,120 +578,150 @@ test_buffer_time_tracking(void *arg) } static void -test_buffers_zlib_impl(int finalize_with_nil) +test_buffers_compress_fin_at_chunk_end_impl(compress_method_t method, + compression_level_t level) { char *msg = NULL; char *contents = NULL; char *expanded = NULL; buf_t *buf = NULL; - tor_zlib_state_t *zlib_state = NULL; + tor_compress_state_t *compress_state = NULL; size_t out_len, in_len; - int done; + size_t sz, headerjunk; buf = buf_new_with_capacity(128); /* will round up */ - zlib_state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); + sz = buf_get_default_chunk_size(buf); + msg = tor_malloc_zero(sz); - msg = tor_malloc(512); - crypto_rand(msg, 512); - tt_int_op(write_to_buf_zlib(buf, zlib_state, msg, 128, 0), OP_EQ, 0); - tt_int_op(write_to_buf_zlib(buf, zlib_state, msg+128, 128, 0), OP_EQ, 0); - tt_int_op(write_to_buf_zlib(buf, zlib_state, msg+256, 256, 0), OP_EQ, 0); - done = !finalize_with_nil; - tt_int_op(write_to_buf_zlib(buf, zlib_state, "all done", 9, done), OP_EQ, 0); - if (finalize_with_nil) { - tt_int_op(write_to_buf_zlib(buf, zlib_state, "", 0, 1), OP_EQ, 0); - } + write_to_buf(msg, 1, buf); + tt_assert(buf->head); + + /* Fill up the chunk so the compression stuff won't fit in one chunk. */ + tt_uint_op(buf->head->memlen, OP_LT, sz); + headerjunk = buf->head->memlen - 7; + write_to_buf(msg, headerjunk-1, buf); + tt_uint_op(buf->head->datalen, OP_EQ, headerjunk); + tt_uint_op(buf_datalen(buf), OP_EQ, headerjunk); + /* Write an empty string, with finalization on. */ + compress_state = tor_compress_new(1, method, level); + tt_int_op(write_to_buf_compress(buf, compress_state, "", 0, 1), OP_EQ, 0); in_len = buf_datalen(buf); contents = tor_malloc(in_len); tt_int_op(fetch_from_buf(contents, in_len, buf), OP_EQ, 0); - tt_int_op(0, OP_EQ, tor_gzip_uncompress(&expanded, &out_len, - contents, in_len, - ZLIB_METHOD, 1, - LOG_WARN)); + if (method == NO_METHOD) { + tt_uint_op(in_len, OP_EQ, headerjunk); + } else { + tt_uint_op(in_len, OP_GT, headerjunk); + } - tt_int_op(out_len, OP_GE, 128); - tt_mem_op(msg, OP_EQ, expanded, 128); - tt_int_op(out_len, OP_GE, 512); - tt_mem_op(msg, OP_EQ, expanded, 512); - tt_int_op(out_len, OP_EQ, 512+9); - tt_mem_op("all done", OP_EQ, expanded+512, 9); + tt_int_op(0, OP_EQ, tor_uncompress(&expanded, &out_len, + contents + headerjunk, + in_len - headerjunk, + method, 1, + LOG_WARN)); + + tt_int_op(out_len, OP_EQ, 0); + tt_assert(expanded); done: buf_free(buf); - tor_zlib_free(zlib_state); + tor_compress_free(compress_state); tor_free(contents); tor_free(expanded); tor_free(msg); } static void -test_buffers_zlib(void *arg) -{ - (void) arg; - test_buffers_zlib_impl(0); -} -static void -test_buffers_zlib_fin_with_nil(void *arg) -{ - (void) arg; - test_buffers_zlib_impl(1); -} - -static void -test_buffers_zlib_fin_at_chunk_end(void *arg) +test_buffers_compress_impl(compress_method_t method, + compression_level_t level, + int finalize_with_nil) { char *msg = NULL; char *contents = NULL; char *expanded = NULL; buf_t *buf = NULL; - tor_zlib_state_t *zlib_state = NULL; + tor_compress_state_t *compress_state = NULL; size_t out_len, in_len; - size_t sz, headerjunk; - (void) arg; + int done; buf = buf_new_with_capacity(128); /* will round up */ - sz = buf_get_default_chunk_size(buf); - msg = tor_malloc_zero(sz); + compress_state = tor_compress_new(1, method, level); - write_to_buf(msg, 1, buf); - tt_assert(buf->head); - - /* Fill up the chunk so the zlib stuff won't fit in one chunk. */ - tt_uint_op(buf->head->memlen, OP_LT, sz); - headerjunk = buf->head->memlen - 7; - write_to_buf(msg, headerjunk-1, buf); - tt_uint_op(buf->head->datalen, OP_EQ, headerjunk); - tt_uint_op(buf_datalen(buf), OP_EQ, headerjunk); - /* Write an empty string, with finalization on. */ - zlib_state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); - tt_int_op(write_to_buf_zlib(buf, zlib_state, "", 0, 1), OP_EQ, 0); + msg = tor_malloc(512); + crypto_rand(msg, 512); + tt_int_op(write_to_buf_compress(buf, compress_state, + msg, 128, 0), OP_EQ, 0); + tt_int_op(write_to_buf_compress(buf, compress_state, + msg+128, 128, 0), OP_EQ, 0); + tt_int_op(write_to_buf_compress(buf, compress_state, + msg+256, 256, 0), OP_EQ, 0); + done = !finalize_with_nil; + tt_int_op(write_to_buf_compress(buf, compress_state, + "all done", 9, done), OP_EQ, 0); + if (finalize_with_nil) { + tt_int_op(write_to_buf_compress(buf, compress_state, "", 0, 1), OP_EQ, 0); + } in_len = buf_datalen(buf); contents = tor_malloc(in_len); tt_int_op(fetch_from_buf(contents, in_len, buf), OP_EQ, 0); - tt_uint_op(in_len, OP_GT, headerjunk); - - tt_int_op(0, OP_EQ, tor_gzip_uncompress(&expanded, &out_len, - contents + headerjunk, in_len - headerjunk, - ZLIB_METHOD, 1, - LOG_WARN)); + tt_int_op(0, OP_EQ, tor_uncompress(&expanded, &out_len, + contents, in_len, + method, 1, + LOG_WARN)); - tt_int_op(out_len, OP_EQ, 0); - tt_assert(expanded); + tt_int_op(out_len, OP_GE, 128); + tt_mem_op(msg, OP_EQ, expanded, 128); + tt_int_op(out_len, OP_GE, 512); + tt_mem_op(msg, OP_EQ, expanded, 512); + tt_int_op(out_len, OP_EQ, 512+9); + tt_mem_op("all done", OP_EQ, expanded+512, 9); done: buf_free(buf); - tor_zlib_free(zlib_state); + tor_compress_free(compress_state); tor_free(contents); tor_free(expanded); tor_free(msg); } +static void +test_buffers_compress(void *arg) +{ + const char *methodname = arg; + tt_assert(methodname); + + compress_method_t method = compression_method_get_by_name(methodname); + tt_int_op(method, OP_NE, UNKNOWN_METHOD); + + if (! tor_compress_supports_method(method)) { + tt_skip(); + } + + compression_level_t levels[] = { + BEST_COMPRESSION, + HIGH_COMPRESSION, + MEDIUM_COMPRESSION, + LOW_COMPRESSION + }; + + for (unsigned l = 0; l < ARRAY_LENGTH(levels); ++l) { + compression_level_t level = levels[l]; + + test_buffers_compress_impl(method, level, 0); + test_buffers_compress_impl(method, level, 1); + test_buffers_compress_fin_at_chunk_end_impl(method, level); + } + + done: + ; +} + static const uint8_t *tls_read_ptr; static int n_remaining; static int next_reply_val[16]; @@ -765,6 +795,49 @@ test_buffers_chunk_size(void *arg) ; } +static void +test_buffers_find_contentlen(void *arg) +{ + static const struct { + const char *headers; + int r; + int contentlen; + } results[] = { + { "Blah blah\r\nContent-Length: 1\r\n\r\n", 1, 1 }, + { "Blah blah\r\n\r\n", 0, 0 }, /* no content-len */ + { "Blah blah Content-Length: 1\r\n", 0, 0 }, /* no content-len. */ + { "Blah blah\r\nContent-Length: 100000\r\n", 1, 100000}, + { "Blah blah\r\nContent-Length: 1000000000000000000000000\r\n", -1, 0}, + { "Blah blah\r\nContent-Length: 0\r\n", 1, 0}, + { "Blah blah\r\nContent-Length: -1\r\n", -1, 0}, + { "Blah blah\r\nContent-Length: 1x\r\n", -1, 0}, + { "Blah blah\r\nContent-Length: 1 x\r\n", -1, 0}, + { "Blah blah\r\nContent-Length: 1 \r\n", 1, 1}, + { "Blah blah\r\nContent-Length: \r\n", -1, 0}, + { "Blah blah\r\nContent-Length: ", -1, 0}, + { "Blah blah\r\nContent-Length: 5050", -1, 0}, + { NULL, 0, 0 } + }; + int i; + + (void)arg; + + for (i = 0; results[i].headers; ++i) { + int r; + size_t sz; + size_t headerlen = strlen(results[i].headers); + char * tmp = tor_memdup(results[i].headers, headerlen);/* ensure no eos */ + sz = 999; /* to ensure it gets set */ + r = buf_http_find_content_length(tmp, headerlen, &sz); + tor_free(tmp); + log_debug(LD_DIR, "%d: %s", i, escaped(results[i].headers)); + tt_int_op(r, ==, results[i].r); + tt_int_op(sz, ==, results[i].contentlen); + } + done: + ; +} + struct testcase_t buffer_tests[] = { { "basic", test_buffers_basic, TT_FORK, NULL, NULL }, { "copy", test_buffer_copy, TT_FORK, NULL, NULL }, @@ -773,13 +846,22 @@ struct testcase_t buffer_tests[] = { { "allocation_tracking", test_buffer_allocation_tracking, TT_FORK, NULL, NULL }, { "time_tracking", test_buffer_time_tracking, TT_FORK, NULL, NULL }, - { "zlib", test_buffers_zlib, TT_FORK, NULL, NULL }, - { "zlib_fin_with_nil", test_buffers_zlib_fin_with_nil, TT_FORK, NULL, NULL }, - { "zlib_fin_at_chunk_end", test_buffers_zlib_fin_at_chunk_end, TT_FORK, - NULL, NULL}, { "tls_read_mocked", test_buffers_tls_read_mocked, 0, NULL, NULL }, { "chunk_size", test_buffers_chunk_size, 0, NULL, NULL }, + { "find_contentlen", test_buffers_find_contentlen, 0, NULL, NULL }, + + { "compress/zlib", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"deflate" }, + { "compress/gzip", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"gzip" }, + { "compress/zstd", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"x-zstd" }, + { "compress/lzma", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"x-tor-lzma" }, + { "compress/none", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"identity" }, + END_OF_TESTCASES }; diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index f429f4291d..007f7e3d3e 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -11,6 +11,7 @@ #include "channel.h" #include "connection_edge.h" #include "connection_or.h" +#include "config.h" #include "onion.h" #include "onion_tap.h" #include "onion_fast.h" @@ -698,6 +699,7 @@ test_cfmt_extend_cells(void *arg) tt_int_op(61681, OP_EQ, ec.orport_ipv4.port); tt_str_op("2002::f0:c51e", OP_EQ, fmt_addr(&ec.orport_ipv6.addr)); tt_int_op(4370, OP_EQ, ec.orport_ipv6.port); + tt_assert(ed25519_public_key_is_zero(&ec.ed_pubkey)); tt_mem_op(ec.node_id,OP_EQ, "anthropomorphization", 20); tt_int_op(cc->cell_type, OP_EQ, CELL_CREATE2); tt_int_op(cc->handshake_type, OP_EQ, 0x105); @@ -717,6 +719,37 @@ test_cfmt_extend_cells(void *arg) tt_mem_op(p2+1+8+22+4,OP_EQ, b, 99+20); tt_int_op(0, OP_EQ, create_cell_format_relayed(&cell, cc)); + /* Now let's add an ed25519 key to that extend2 cell. */ + memcpy(ec.ed_pubkey.pubkey, + "brownshoesdontmakeit/brownshoesd", 32); + + /* As before, since we aren't extending by ed25519. */ + get_options_mutable()->ExtendByEd25519ID = 0; + tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_len, OP_EQ, 89+99-34-20); + test_memeq_hex(p2, + "02000612F40001F0F1" + "0214616e7468726f706f6d6f727068697a6174696f6e" + "01050063"); + + /* Now try with the ed25519 ID. */ + get_options_mutable()->ExtendByEd25519ID = 1; + tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_len, OP_EQ, 89+99-34-20 + 34); + test_memeq_hex(p2, + "03000612F40001F0F1" + "0214616e7468726f706f6d6f727068697a6174696f6e" + // ed digest follows: + "0320" "62726f776e73686f6573646f6e746d616b656" + "9742f62726f776e73686f657364" + "01050063"); + /* Can we parse that? Did the key come through right? */ + memset(&ec, 0, sizeof(ec)); + tt_int_op(0, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p2, p2_len)); + tt_mem_op("brownshoesdontmakeit/brownshoesd", OP_EQ, + ec.ed_pubkey.pubkey, 32); + /* == Now try parsing some junk */ /* Try a too-long handshake */ @@ -1257,7 +1290,7 @@ struct testcase_t cell_format_tests[] = { TEST(connected_cells, 0), TEST(create_cells, 0), TEST(created_cells, 0), - TEST(extend_cells, 0), + TEST(extend_cells, TT_FORK), TEST(extended_cells, 0), TEST(resolved_cells, 0), TEST(is_destroy, 0), diff --git a/src/test/test_cell_queue.c b/src/test/test_cell_queue.c index 93ac9854d8..69e89b69b0 100644 --- a/src/test/test_cell_queue.c +++ b/src/test/test_cell_queue.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CIRCUITLIST_PRIVATE diff --git a/src/test/test_channel.c b/src/test/test_channel.c index a9e0634d9e..f5999b8e67 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TOR_CHANNEL_INTERNAL_ @@ -1405,10 +1405,14 @@ test_channel_queue_impossible(void *arg) /* Let it drain and check that the bad entry is discarded */ test_chan_accept_cells = 1; + tor_capture_bugs_(1); channel_change_state(ch, CHANNEL_STATE_OPEN); tt_assert(test_cells_written == old_count); tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), ==, 1); + tor_end_capture_bugs_(); + done: free_fake_channel(ch); @@ -1764,6 +1768,112 @@ test_channel_write(void *arg) return; } +static void +test_channel_id_map(void *arg) +{ + (void)arg; +#define N_CHAN 6 + char rsa_id[N_CHAN][DIGEST_LEN]; + ed25519_public_key_t *ed_id[N_CHAN]; + channel_t *chan[N_CHAN]; + int i; + ed25519_public_key_t ed_zero; + memset(&ed_zero, 0, sizeof(ed_zero)); + + tt_assert(sizeof(rsa_id[0]) == DIGEST_LEN); // Do I remember C? + + for (i = 0; i < N_CHAN; ++i) { + crypto_rand(rsa_id[i], DIGEST_LEN); + ed_id[i] = tor_malloc_zero(sizeof(*ed_id[i])); + crypto_rand((char*)ed_id[i]->pubkey, sizeof(ed_id[i]->pubkey)); + } + + /* For channel 3, have no Ed identity. */ + tor_free(ed_id[3]); + + /* Channel 2 and 4 have same ROSA identity */ + memcpy(rsa_id[4], rsa_id[2], DIGEST_LEN); + + /* Channel 2 and 4 and 5 have same RSA identity */ + memcpy(rsa_id[4], rsa_id[2], DIGEST_LEN); + memcpy(rsa_id[5], rsa_id[2], DIGEST_LEN); + + /* Channels 2 and 5 have same Ed25519 identity */ + memcpy(ed_id[5], ed_id[2], sizeof(*ed_id[2])); + + for (i = 0; i < N_CHAN; ++i) { + chan[i] = new_fake_channel(); + channel_register(chan[i]); + channel_set_identity_digest(chan[i], rsa_id[i], ed_id[i]); + } + + /* Lookup by RSA id only */ + tt_ptr_op(chan[0], OP_EQ, + channel_find_by_remote_identity(rsa_id[0], NULL)); + tt_ptr_op(chan[1], OP_EQ, + channel_find_by_remote_identity(rsa_id[1], NULL)); + tt_ptr_op(chan[3], OP_EQ, + channel_find_by_remote_identity(rsa_id[3], NULL)); + channel_t *ch; + ch = channel_find_by_remote_identity(rsa_id[2], NULL); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == NULL); + + /* As above, but with zero Ed25519 ID (meaning "any ID") */ + tt_ptr_op(chan[0], OP_EQ, + channel_find_by_remote_identity(rsa_id[0], &ed_zero)); + tt_ptr_op(chan[1], OP_EQ, + channel_find_by_remote_identity(rsa_id[1], &ed_zero)); + tt_ptr_op(chan[3], OP_EQ, + channel_find_by_remote_identity(rsa_id[3], &ed_zero)); + ch = channel_find_by_remote_identity(rsa_id[2], &ed_zero); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == NULL); + + /* Lookup nonexistent RSA identity */ + tt_ptr_op(NULL, OP_EQ, + channel_find_by_remote_identity("!!!!!!!!!!!!!!!!!!!!", NULL)); + + /* Look up by full identity pair */ + tt_ptr_op(chan[0], OP_EQ, + channel_find_by_remote_identity(rsa_id[0], ed_id[0])); + tt_ptr_op(chan[1], OP_EQ, + channel_find_by_remote_identity(rsa_id[1], ed_id[1])); + tt_ptr_op(chan[3], OP_EQ, + channel_find_by_remote_identity(rsa_id[3], ed_id[3] /*NULL*/)); + tt_ptr_op(chan[4], OP_EQ, + channel_find_by_remote_identity(rsa_id[4], ed_id[4])); + ch = channel_find_by_remote_identity(rsa_id[2], ed_id[2]); + tt_assert(ch == chan[2] || ch == chan[5]); + + /* Look up RSA identity with wrong ed25519 identity */ + tt_ptr_op(NULL, OP_EQ, + channel_find_by_remote_identity(rsa_id[4], ed_id[0])); + tt_ptr_op(NULL, OP_EQ, + channel_find_by_remote_identity(rsa_id[2], ed_id[1])); + tt_ptr_op(NULL, OP_EQ, + channel_find_by_remote_identity(rsa_id[3], ed_id[1])); + + done: + for (i = 0; i < N_CHAN; ++i) { + channel_clear_identity_digest(chan[i]); + channel_unregister(chan[i]); + free_fake_channel(chan[i]); + tor_free(ed_id[i]); + } +#undef N_CHAN +} + struct testcase_t channel_tests[] = { { "dumpstats", test_channel_dumpstats, TT_FORK, NULL, NULL }, { "flush", test_channel_flush, TT_FORK, NULL, NULL }, @@ -1776,6 +1886,7 @@ struct testcase_t channel_tests[] = { { "queue_incoming", test_channel_queue_incoming, TT_FORK, NULL, NULL }, { "queue_size", test_channel_queue_size, TT_FORK, NULL, NULL }, { "write", test_channel_write, TT_FORK, NULL, NULL }, + { "id_map", test_channel_id_map, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c new file mode 100644 index 0000000000..d399738ce6 --- /dev/null +++ b/src/test/test_channelpadding.c @@ -0,0 +1,1129 @@ +#define TOR_CHANNEL_INTERNAL_ +#define MAIN_PRIVATE +#define NETWORKSTATUS_PRIVATE +#define TOR_TIMERS_PRIVATE +#include "or.h" +#include "test.h" +#include "testsupport.h" +#include "connection.h" +#include "connection_or.h" +#include "channel.h" +#include "channeltls.h" +#include "channelpadding.h" +#include "compat_libevent.h" +#include "config.h" +#include <event2/event.h> +#include "compat_time.h" +#include "main.h" +#include "networkstatus.h" +#include "log_test_helpers.h" + +int channelpadding_get_netflow_inactive_timeout_ms(channel_t *chan); +int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *chan); +int channelpadding_send_disable_command(channel_t*); +int channelpadding_find_timerslot(channel_t *chan); + +void test_channelpadding_timers(void *arg); +void test_channelpadding_consensus(void *arg); +void test_channelpadding_negotiation(void *arg); +void test_channelpadding_decide_to_pad_channel(void *arg); +void test_channelpadding_killonehop(void *arg); + +void dummy_nop_timer(void); + +#define NSEC_PER_MSEC (1000*1000) + +/* Thing to cast to fake tor_tls_t * to appease assert_connection_ok() */ +static int fake_tortls = 0; /* Bleh... */ + +static int dont_stop_libevent = 0; + +// From test_channel.c +channel_t * new_fake_channel(void); +void free_fake_channel(channel_t*); + +static int +mock_channel_has_queued_writes(channel_t *chan) +{ + (void)chan; + return 0; +} + +static int tried_to_write_cell = 0; + +static channel_t *relay1_relay2; +static channel_t *relay2_relay1; +static channel_t *relay3_client; +static channel_t *client_relay3; + +static int +mock_channel_write_cell_relay2(channel_t *chan, cell_t *cell) +{ + (void)chan; + tried_to_write_cell++; + channel_tls_handle_cell(cell, ((channel_tls_t*)relay1_relay2)->conn); + event_base_loopbreak(tor_libevent_get_base()); + return 0; +} + +static int +mock_channel_write_cell_relay1(channel_t *chan, cell_t *cell) +{ + (void)chan; + tried_to_write_cell++; + channel_tls_handle_cell(cell, ((channel_tls_t*)relay2_relay1)->conn); + event_base_loopbreak(tor_libevent_get_base()); + return 0; +} + +static int +mock_channel_write_cell_relay3(channel_t *chan, cell_t *cell) +{ + (void)chan; + tried_to_write_cell++; + channel_tls_handle_cell(cell, ((channel_tls_t*)client_relay3)->conn); + event_base_loopbreak(tor_libevent_get_base()); + return 0; +} + +static int +mock_channel_write_cell_client(channel_t *chan, cell_t *cell) +{ + (void)chan; + tried_to_write_cell++; + channel_tls_handle_cell(cell, ((channel_tls_t*)relay3_client)->conn); + event_base_loopbreak(tor_libevent_get_base()); + return 0; +} + +static int +mock_channel_write_cell(channel_t *chan, cell_t *cell) +{ + tried_to_write_cell++; + channel_tls_handle_cell(cell, ((channel_tls_t*)chan)->conn); + if (!dont_stop_libevent) + event_base_loopbreak(tor_libevent_get_base()); + return 0; +} + +static void +setup_fake_connection_for_channel(channel_tls_t *chan) +{ + or_connection_t *conn = (or_connection_t*)connection_new(CONN_TYPE_OR, + AF_INET); + + conn->base_.conn_array_index = smartlist_len(connection_array); + smartlist_add(connection_array, conn); + + conn->chan = chan; + chan->conn = conn; + + conn->base_.magic = OR_CONNECTION_MAGIC; + conn->base_.state = OR_CONN_STATE_OPEN; + conn->base_.type = CONN_TYPE_OR; + conn->base_.socket_family = AF_INET; + conn->base_.address = tor_strdup("<fake>"); + + conn->base_.port = 4242; + + conn->tls = (tor_tls_t *)((void *)(&fake_tortls)); + + conn->link_proto = MIN_LINK_PROTO_FOR_CHANNEL_PADDING; + + connection_or_set_canonical(conn, 1); +} + +static channel_tls_t * +new_fake_channeltls(uint8_t id) +{ + channel_tls_t *chan = tor_realloc(new_fake_channel(), sizeof(channel_tls_t)); + chan->base_.magic = TLS_CHAN_MAGIC; + setup_fake_connection_for_channel(chan); + chan->base_.channel_usage = CHANNEL_USED_FOR_FULL_CIRCS; + chan->base_.has_queued_writes = mock_channel_has_queued_writes; + chan->base_.write_cell = mock_channel_write_cell; + chan->base_.padding_enabled = 1; + + chan->base_.identity_digest[0] = id; + channel_register(&chan->base_); + + return chan; +} + +static void +free_fake_channeltls(channel_tls_t *chan) +{ + channel_unregister(&chan->base_); + + tor_free(((channel_tls_t*)chan)->conn->base_.address); + buf_free(((channel_tls_t*)chan)->conn->base_.inbuf); + buf_free(((channel_tls_t*)chan)->conn->base_.outbuf); + tor_free(((channel_tls_t*)chan)->conn); + + timer_free(chan->base_.padding_timer); + channel_handle_free(chan->base_.timer_handle); + channel_handles_clear(&chan->base_); + + free_fake_channel(&chan->base_); + + return; +} + +static void +setup_mock_consensus(void) +{ + current_md_consensus = current_ns_consensus + = tor_malloc_zero(sizeof(networkstatus_t)); + current_md_consensus->net_params = smartlist_new(); + current_md_consensus->routerstatus_list = smartlist_new(); + channelpadding_new_consensus_params(current_md_consensus); +} + +static void +free_mock_consensus(void) +{ + SMARTLIST_FOREACH(current_md_consensus->routerstatus_list, void *, r, + tor_free(r)); + smartlist_free(current_md_consensus->routerstatus_list); + smartlist_free(current_ns_consensus->net_params); + tor_free(current_ns_consensus); +} + +static void +setup_mock_network(void) +{ + routerstatus_t *relay; + connection_array = smartlist_new(); + + relay1_relay2 = (channel_t*)new_fake_channeltls(2); + relay1_relay2->write_cell = mock_channel_write_cell_relay1; + channel_timestamp_active(relay1_relay2); + relay = tor_malloc_zero(sizeof(routerstatus_t)); + relay->identity_digest[0] = 1; + smartlist_add(current_md_consensus->routerstatus_list, relay); + + relay2_relay1 = (channel_t*)new_fake_channeltls(1); + relay2_relay1->write_cell = mock_channel_write_cell_relay2; + channel_timestamp_active(relay2_relay1); + relay = tor_malloc_zero(sizeof(routerstatus_t)); + relay->identity_digest[0] = 2; + smartlist_add(current_md_consensus->routerstatus_list, relay); + + relay3_client = (channel_t*)new_fake_channeltls(0); + relay3_client->write_cell = mock_channel_write_cell_relay3; + relay3_client->is_client = 1; + channel_timestamp_active(relay3_client); + relay = tor_malloc_zero(sizeof(routerstatus_t)); + relay->identity_digest[0] = 3; + smartlist_add(current_md_consensus->routerstatus_list, relay); + + client_relay3 = (channel_t*)new_fake_channeltls(3); + client_relay3->write_cell = mock_channel_write_cell_client; + channel_timestamp_active(client_relay3); + + channel_do_open_actions(relay1_relay2); + channel_do_open_actions(relay2_relay1); + channel_do_open_actions(relay3_client); + channel_do_open_actions(client_relay3); +} + +static void +free_mock_network(void) +{ + free_fake_channeltls((channel_tls_t*)relay1_relay2); + free_fake_channeltls((channel_tls_t*)relay2_relay1); + free_fake_channeltls((channel_tls_t*)relay3_client); + free_fake_channeltls((channel_tls_t*)client_relay3); + + smartlist_free(connection_array); +} + +static void +dummy_timer_cb(tor_timer_t *t, void *arg, const monotime_t *now_mono) +{ + (void)t; (void)arg; (void)now_mono; + event_base_loopbreak(tor_libevent_get_base()); + return; +} + +// This hack adds a dummy timer so that the libevent base loop +// actually returns when we don't expect any timers to fire. Otherwise, +// the global_timer_event gets scheduled an hour from now, and the +// base loop never returns. +void +dummy_nop_timer(void) +{ + tor_timer_t *dummy_timer = timer_new(dummy_timer_cb, NULL); + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + timer_schedule(dummy_timer, &timeout); + + event_base_loop(tor_libevent_get_base(), 0); + timer_free(dummy_timer); +} + +#define CHANNELPADDING_MAX_TIMERS 25 +#define CHANNELS_TO_TEST (CHANNELPADDING_MAX_TIMERS*4) +/** + * Tests to ensure that we handle more than the max number of pending + * timers properly. + */ +void +test_channelpadding_timers(void *arg) +{ + channelpadding_decision_t decision; + channel_t *chans[CHANNELS_TO_TEST]; + int64_t new_time; + (void)arg; + + tor_libevent_postfork(); + + connection_array = smartlist_new(); + + monotime_init(); + monotime_enable_test_mocking(); + monotime_set_mock_time_nsec(1); + monotime_coarse_set_mock_time_nsec(1); + + timers_initialize(); + channelpadding_new_consensus_params(NULL); + + for (int i = 0; i < CHANNELS_TO_TEST; i++) { + chans[i] = (channel_t*)new_fake_channeltls(0); + channel_timestamp_active(chans[i]); + } + + for (int j = 0; j < 2; j++) { + tried_to_write_cell = 0; + int i = 0; + + /* This loop fills our timerslot array with timers of increasing time + * until they fire */ + for (; i < CHANNELPADDING_MAX_TIMERS; i++) { + chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + + 10 + i*4; + decision = channelpadding_decide_to_pad_channel(chans[i]); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(chans[i]->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + } + + /* This loop should add timers to the first position in the timerslot + * array, since its timeout is before all other timers. */ + for (; i < CHANNELS_TO_TEST/3; i++) { + chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 1; + decision = channelpadding_decide_to_pad_channel(chans[i]); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(chans[i]->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + } + + /* This loop should add timers to our existing lists in a weak + * pseudorandom pattern. It ensures that the lists can grow with multiple + * timers in them. */ + for (; i < CHANNELS_TO_TEST/2; i++) { + chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 10 + + i*3 % CHANNELPADDING_MAX_TIMERS; + decision = channelpadding_decide_to_pad_channel(chans[i]); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(chans[i]->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + } + + /* This loop should add timers to the last position in the timerslot + * array, since its timeout is after all other timers. */ + for (; i < CHANNELS_TO_TEST; i++) { + chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 500 + + i % CHANNELPADDING_MAX_TIMERS; + decision = channelpadding_decide_to_pad_channel(chans[i]); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(chans[i]->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + } + + // Wait for the timers and then kill the event loop. + new_time = (monotime_coarse_absolute_msec()+1001)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + + tt_int_op(tried_to_write_cell, OP_EQ, CHANNELS_TO_TEST); + + // Test that we have no pending callbacks and all empty slots now + for (i = 0; i < CHANNELS_TO_TEST; i++) { + tt_assert(!chans[i]->pending_padding_callback); + } + } + + done: + for (int i = 0; i < CHANNELS_TO_TEST; i++) { + free_fake_channeltls((channel_tls_t*)chans[i]); + } + smartlist_free(connection_array); + + timers_shutdown(); + monotime_disable_test_mocking(); + channel_free_all(); + + return; +} + +void +test_channelpadding_killonehop(void *arg) +{ + channelpadding_decision_t decision; + int64_t new_time; + (void)arg; + tor_libevent_postfork(); + + routerstatus_t *relay = tor_malloc_zero(sizeof(routerstatus_t)); + monotime_init(); + monotime_enable_test_mocking(); + monotime_set_mock_time_nsec(1); + monotime_coarse_set_mock_time_nsec(1); + + timers_initialize(); + setup_mock_consensus(); + setup_mock_network(); + + /* Do we disable padding if tor2webmode or rsos are enabled, and + * the consensus says don't pad? */ + + /* Ensure we can kill tor2web and rsos padding if we want. */ + // First, test that padding works if either is enabled + smartlist_clear(current_md_consensus->net_params); + channelpadding_new_consensus_params(current_md_consensus); + + tried_to_write_cell = 0; + get_options_mutable()->Tor2webMode = 1; + client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + decision = channelpadding_decide_to_pad_channel(client_relay3); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(client_relay3->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + + decision = channelpadding_decide_to_pad_channel(client_relay3); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); + + // Wait for the timer + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!client_relay3->pending_padding_callback); + + // Then test disabling each via consensus param + smartlist_add(current_md_consensus->net_params, + (void*)"nf_pad_tor2web=0"); + channelpadding_new_consensus_params(current_md_consensus); + + // Before the client tries to pad, the relay will still pad: + tried_to_write_cell = 0; + relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + get_options_mutable()->ORPort_set = 1; + get_options_mutable()->Tor2webMode = 0; + decision = channelpadding_decide_to_pad_channel(relay3_client); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(relay3_client->pending_padding_callback); + + // Wait for the timer + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!client_relay3->pending_padding_callback); + + // Test client side (it should stop immediately, but send a negotiate) + tried_to_write_cell = 0; + tt_assert(relay3_client->padding_enabled); + tt_assert(client_relay3->padding_enabled); + get_options_mutable()->Tor2webMode = 1; + /* For the relay to recieve the negotiate: */ + get_options_mutable()->ORPort_set = 1; + decision = channelpadding_decide_to_pad_channel(client_relay3); + tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!client_relay3->pending_padding_callback); + tt_assert(!relay3_client->padding_enabled); + + // Test relay side (it should have gotten the negotiation to disable) + get_options_mutable()->ORPort_set = 1; + get_options_mutable()->Tor2webMode = 0; + tt_int_op(channelpadding_decide_to_pad_channel(relay3_client), OP_EQ, + CHANNELPADDING_WONTPAD); + tt_assert(!relay3_client->padding_enabled); + + /* Repeat for SOS */ + // First, test that padding works if either is enabled + smartlist_clear(current_md_consensus->net_params); + channelpadding_new_consensus_params(current_md_consensus); + + relay3_client->padding_enabled = 1; + client_relay3->padding_enabled = 1; + + tried_to_write_cell = 0; + get_options_mutable()->ORPort_set = 0; + get_options_mutable()->HiddenServiceSingleHopMode = 1; + get_options_mutable()->HiddenServiceNonAnonymousMode = 1; + client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + decision = channelpadding_decide_to_pad_channel(client_relay3); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(client_relay3->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + + decision = channelpadding_decide_to_pad_channel(client_relay3); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); + + // Wait for the timer + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!client_relay3->pending_padding_callback); + + // Then test disabling each via consensus param + smartlist_add(current_md_consensus->net_params, + (void*)"nf_pad_single_onion=0"); + channelpadding_new_consensus_params(current_md_consensus); + + // Before the client tries to pad, the relay will still pad: + tried_to_write_cell = 0; + relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + get_options_mutable()->ORPort_set = 1; + get_options_mutable()->HiddenServiceSingleHopMode = 0; + get_options_mutable()->HiddenServiceNonAnonymousMode = 0; + decision = channelpadding_decide_to_pad_channel(relay3_client); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(relay3_client->pending_padding_callback); + + // Wait for the timer + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!client_relay3->pending_padding_callback); + + // Test client side (it should stop immediately) + get_options_mutable()->HiddenServiceSingleHopMode = 1; + get_options_mutable()->HiddenServiceNonAnonymousMode = 1; + /* For the relay to recieve the negotiate: */ + get_options_mutable()->ORPort_set = 1; + decision = channelpadding_decide_to_pad_channel(client_relay3); + tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); + tt_assert(!client_relay3->pending_padding_callback); + + // Test relay side (it should have gotten the negotiation to disable) + get_options_mutable()->ORPort_set = 1; + get_options_mutable()->HiddenServiceSingleHopMode = 0; + get_options_mutable()->HiddenServiceNonAnonymousMode = 0; + tt_int_op(channelpadding_decide_to_pad_channel(relay3_client), OP_EQ, + CHANNELPADDING_WONTPAD); + tt_assert(!relay3_client->padding_enabled); + + done: + free_mock_consensus(); + free_mock_network(); + tor_free(relay); + + timers_shutdown(); + monotime_disable_test_mocking(); + channel_free_all(); +} + +void +test_channelpadding_consensus(void *arg) +{ + channelpadding_decision_t decision; + or_options_t *options = get_options_mutable(); + int64_t val; + int64_t new_time; + (void)arg; + + tor_libevent_postfork(); + + /* + * Params tested: + * nf_pad_before_usage + * nf_pad_relays + * nf_ito_low + * nf_ito_high + * + * Plan: + * 1. Padding can be completely disabled via consensus + * 2. Negotiation can't re-enable consensus-disabled padding + * 3. Negotiation can't increase padding from relays beyond + * consensus defaults + * 4. Relay-to-relay padding can be enabled/disabled in consensus + * 5. Can enable/disable padding before actually using a connection + * 6. Can we control circ and TLS conn lifetime from the consensus? + */ + channel_t *chan; + routerstatus_t *relay = tor_malloc_zero(sizeof(routerstatus_t)); + monotime_enable_test_mocking(); + monotime_set_mock_time_nsec(1); + monotime_coarse_set_mock_time_nsec(1); + timers_initialize(); + + connection_array = smartlist_new(); + chan = (channel_t*)new_fake_channeltls(0); + channel_timestamp_active(chan); + + setup_mock_consensus(); + + get_options_mutable()->ORPort_set = 1; + + /* Test 1: Padding can be completely disabled via consensus */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(chan->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); + + // Wait for the timer + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!chan->pending_padding_callback); + + smartlist_add(current_md_consensus->net_params, + (void*)"nf_ito_low=0"); + smartlist_add(current_md_consensus->net_params, + (void*)"nf_ito_high=0"); + get_options_mutable()->ConnectionPadding = 1; + channelpadding_new_consensus_params(current_md_consensus); + + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); + tt_assert(!chan->pending_padding_callback); + val = channelpadding_get_netflow_inactive_timeout_ms(chan); + tt_i64_op(val, OP_EQ, 0); + val = channelpadding_compute_time_until_pad_for_netflow(chan); + tt_i64_op(val, OP_EQ, -2); + + /* Test 2: Negotiation can't re-enable consensus-disabled padding */ + channelpadding_send_enable_command(chan, 100, 200); + tried_to_write_cell = 0; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); + tt_assert(!chan->pending_padding_callback); + val = channelpadding_get_netflow_inactive_timeout_ms(chan); + tt_i64_op(val, OP_EQ, 0); + val = channelpadding_compute_time_until_pad_for_netflow(chan); + tt_i64_op(val, OP_EQ, -2); + tt_assert(!chan->next_padding_time_ms); + + smartlist_clear(current_md_consensus->net_params); + + /* Test 3: Negotiation can't increase padding from relays beyond consensus + * values */ + smartlist_add(current_md_consensus->net_params, + (void*)"nf_ito_low=100"); + smartlist_add(current_md_consensus->net_params, + (void*)"nf_ito_high=200"); + channelpadding_new_consensus_params(current_md_consensus); + + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(chan->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + val = channelpadding_get_netflow_inactive_timeout_ms(chan); + tt_i64_op(val, OP_GE, 100); + tt_i64_op(val, OP_LE, 200); + val = channelpadding_compute_time_until_pad_for_netflow(chan); + tt_i64_op(val, OP_LE, 200); + + // Wait for the timer + new_time = (monotime_coarse_absolute_msec()+201)*NSEC_PER_MSEC; + monotime_set_mock_time_nsec(new_time); + monotime_coarse_set_mock_time_nsec(new_time); + timers_run_pending(); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!chan->pending_padding_callback); + + smartlist_clear(current_md_consensus->net_params); + smartlist_add(current_md_consensus->net_params, + (void*)"nf_ito_low=1500"); + smartlist_add(current_md_consensus->net_params, + (void*)"nf_ito_high=4500"); + channelpadding_new_consensus_params(current_md_consensus); + + channelpadding_send_enable_command(chan, 100, 200); + tried_to_write_cell = 0; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER); + tt_assert(!chan->pending_padding_callback); + val = channelpadding_get_netflow_inactive_timeout_ms(chan); + tt_i64_op(val, OP_GE, 1500); + tt_i64_op(val, OP_LE, 4500); + val = channelpadding_compute_time_until_pad_for_netflow(chan); + tt_i64_op(val, OP_LE, 4500); + + /* Test 4: Relay-to-relay padding can be enabled/disabled in consensus */ + /* Make this channel a relay's channel */ + memcpy(relay->identity_digest, + ((channel_tls_t *)chan)->conn->identity_digest, DIGEST_LEN); + smartlist_add(current_md_consensus->routerstatus_list, relay); + relay = NULL; /* Prevent double-free */ + + tried_to_write_cell = 0; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); + tt_assert(!chan->pending_padding_callback); + + smartlist_add(current_md_consensus->net_params, + (void*)"nf_pad_relays=1"); + channelpadding_new_consensus_params(current_md_consensus); + + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER); + tt_assert(!chan->pending_padding_callback); + val = channelpadding_get_netflow_inactive_timeout_ms(chan); + tt_i64_op(val, OP_GE, 1500); + tt_i64_op(val, OP_LE, 4500); + val = channelpadding_compute_time_until_pad_for_netflow(chan); + tt_i64_op(val, OP_LE, 4500); + + /* Test 5: If we disable padding before channel usage, does that work? */ + smartlist_add(current_md_consensus->net_params, + (void*)"nf_pad_before_usage=0"); + channelpadding_new_consensus_params(current_md_consensus); + tried_to_write_cell = 0; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); + tt_assert(!chan->pending_padding_callback); + + /* Test 6: Can we control circ and TLS conn lifetime from the consensus? */ + val = channelpadding_get_channel_idle_timeout(NULL, 0); + tt_i64_op(val, OP_GE, 180); + tt_i64_op(val, OP_LE, 180+90); + val = channelpadding_get_channel_idle_timeout(chan, 0); + tt_i64_op(val, OP_GE, 180); + tt_i64_op(val, OP_LE, 180+90); + options->ReducedConnectionPadding = 1; + val = channelpadding_get_channel_idle_timeout(chan, 0); + tt_i64_op(val, OP_GE, 180/2); + tt_i64_op(val, OP_LE, (180+90)/2); + + options->ReducedConnectionPadding = 0; + options->ORPort_set = 1; + smartlist_add(current_md_consensus->net_params, + (void*)"nf_conntimeout_relays=600"); + channelpadding_new_consensus_params(current_md_consensus); + val = channelpadding_get_channel_idle_timeout(chan, 1); + tt_i64_op(val, OP_GE, 450); + tt_i64_op(val, OP_LE, 750); + + val = channelpadding_get_circuits_available_timeout(); + tt_i64_op(val, OP_GE, 30*60); + tt_i64_op(val, OP_LE, 30*60*2); + + options->ReducedConnectionPadding = 1; + smartlist_add(current_md_consensus->net_params, + (void*)"nf_conntimeout_clients=600"); + channelpadding_new_consensus_params(current_md_consensus); + val = channelpadding_get_circuits_available_timeout(); + tt_i64_op(val, OP_GE, 600/2); + tt_i64_op(val, OP_LE, 600*2/2); + + options->ReducedConnectionPadding = 0; + options->CircuitsAvailableTimeout = 24*60*60; + val = channelpadding_get_circuits_available_timeout(); + tt_i64_op(val, OP_GE, 24*60*60); + tt_i64_op(val, OP_LE, 24*60*60*2); + + done: + tor_free(relay); + + free_mock_consensus(); + free_fake_channeltls((channel_tls_t*)chan); + smartlist_free(connection_array); + + timers_shutdown(); + monotime_disable_test_mocking(); + channel_free_all(); + + return; +} + +void +test_channelpadding_negotiation(void *arg) +{ + channelpadding_negotiate_t disable; + cell_t cell; + channelpadding_decision_t decision; + int val; + (void)arg; + + /* Plan: + * 1. Clients reject negotiation, relays accept it. + * * Bridges accept negotiation from their clients, + * but not from relays. + * 2. Torrc options can override client-side negotiation + * 3. Test a version issue in channelpadidng cell + * 4. Test channelpadding_reduced_padding + */ + monotime_init(); + monotime_enable_test_mocking(); + monotime_set_mock_time_nsec(1); + monotime_coarse_set_mock_time_nsec(1); + timers_initialize(); + setup_mock_consensus(); + setup_mock_network(); + + /* Test case #1: Do the right things ignore negotiation? */ + /* relay-to-client case: */ + channelpadding_send_disable_command(relay3_client); + tt_assert(client_relay3->padding_enabled); + + /* client-to-relay case: */ + get_options_mutable()->ORPort_set = 1; + channelpadding_disable_padding_on_channel(client_relay3); + tt_int_op(channelpadding_decide_to_pad_channel(relay3_client), OP_EQ, + CHANNELPADDING_WONTPAD); + tt_assert(!relay3_client->padding_enabled); + relay3_client->padding_enabled = 1; + client_relay3->padding_enabled = 1; + + /* Bridge case from relay */ + get_options_mutable()->BridgeRelay = 1; + channelpadding_disable_padding_on_channel(relay2_relay1); + tt_assert(relay1_relay2->padding_enabled); + + /* Bridge case from client */ + channelpadding_disable_padding_on_channel(client_relay3); + tt_assert(!relay3_client->padding_enabled); + tt_int_op(channelpadding_decide_to_pad_channel(relay3_client), OP_EQ, + CHANNELPADDING_WONTPAD); + relay3_client->padding_enabled = 1; + client_relay3->padding_enabled = 1; + get_options_mutable()->BridgeRelay = 0; + get_options_mutable()->ORPort_set = 0; + + /* Test case #2: Torrc options */ + /* ConnectionPadding auto; Relay doesn't suport us */ + ((channel_tls_t*)relay3_client)->conn->link_proto = 4; + relay3_client->padding_enabled = 0; + tried_to_write_cell = 0; + decision = channelpadding_decide_to_pad_channel(relay3_client); + tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); + tt_assert(!relay3_client->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + ((channel_tls_t*)relay3_client)->conn->link_proto = 5; + relay3_client->padding_enabled = 1; + + /* ConnectionPadding 1; Relay doesn't suport us */ + get_options_mutable()->ConnectionPadding = 1; + tried_to_write_cell = 0; + decision = channelpadding_decide_to_pad_channel(client_relay3); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER); + tt_assert(!client_relay3->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + get_options_mutable()->ConnectionPadding = 0; + + /* Test case #3: Test a version issue in channelpadding cell */ + get_options_mutable()->ORPort_set = 1; + client_relay3->padding_enabled = 1; + relay3_client->padding_enabled = 1; + memset(&cell, 0, sizeof(cell_t)); + memset(&disable, 0, sizeof(channelpadding_negotiate_t)); + cell.command = CELL_PADDING_NEGOTIATE; + + channelpadding_negotiate_set_command(&disable, CHANNELPADDING_COMMAND_STOP); + disable.version = 1; + channelpadding_negotiate_encode(cell.payload, CELL_PAYLOAD_SIZE, &disable); + client_relay3->write_cell(client_relay3, &cell); + tt_assert(relay3_client->padding_enabled); + tt_int_op(channelpadding_update_padding_for_channel(client_relay3, &disable), + OP_EQ, -1); + tt_assert(client_relay3->padding_enabled); + + disable.version = 0; + channelpadding_negotiate_encode(cell.payload, CELL_PAYLOAD_SIZE, &disable); + client_relay3->write_cell(client_relay3, &cell); + tt_assert(!relay3_client->padding_enabled); + + /* Test case 4: Reducing padding actually reduces it */ + relay3_client->padding_enabled = 1; + client_relay3->padding_enabled = 1; + + decision = channelpadding_decide_to_pad_channel(relay3_client); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER); + + channelpadding_reduce_padding_on_channel(client_relay3); + + tried_to_write_cell = 0; + decision = channelpadding_decide_to_pad_channel(relay3_client); + tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); + + get_options_mutable()->ORPort_set = 0; + decision = channelpadding_decide_to_pad_channel(client_relay3); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER); + + tt_assert(!client_relay3->pending_padding_callback); + val = channelpadding_get_netflow_inactive_timeout_ms(client_relay3); + tt_int_op(val, OP_GE, 9000); + tt_int_op(val, OP_LE, 14000); + int64_t val64 = + channelpadding_compute_time_until_pad_for_netflow(client_relay3); + tt_i64_op(val64, OP_LE, 14000); + + done: + free_mock_network(); + free_mock_consensus(); + + timers_shutdown(); + monotime_disable_test_mocking(); + channel_free_all(); + + return; +} + +void +test_channelpadding_decide_to_pad_channel(void *arg) +{ + channelpadding_decision_t decision; + /** + * Test case plan: + * + * 1. Channel that has "sent a packet" before the timeout. + * + We should decide to pad later + * 2. Channel that has not "sent a packet" before the timeout: + * 2a. Not within 1.1s of the timeout. + * + We should decide to pad later + * 2b. Within 1.1s of the timemout. + * + We should schedule padding + * + We should get feedback that we wrote a cell + * 2c. Within 0.1s of the timeout. + * + We should schedule padding + * + We should get feedback that we wrote a cell + * 2d. Channel that asks to pad while timeout is scheduled + * + We should schedule padding + * + We should get feedback that we wrote a cell + * 2e. 0s of the timeout + * + We should send padding immediately + * + We should get feedback that we wrote a cell + * 2f. <0s of the timeout + * + We should send padding immediately + * + We should get feedback that we wrote a cell + * 3. Channel that sends a packet while timeout is scheduled + * + We should not get feedback that we wrote a cell + * 4. Channel that closes while timeout is scheduled + * + We should not get feedback that we wrote a cell + * 5. Make sure the channel still would work if repaired + * + We should be able to schedule padding and resend + * 6. Channel is not used for full circuits + * 7. Channel that disappears while timeout is scheduled + * + We should not send padding + */ + channel_t *chan; + int64_t new_time; + connection_array = smartlist_new(); + (void)arg; + + tor_libevent_postfork(); + + monotime_init(); + monotime_enable_test_mocking(); + monotime_set_mock_time_nsec(1); + monotime_coarse_set_mock_time_nsec(1); + timers_initialize(); + setup_full_capture_of_logs(LOG_WARN); + channelpadding_new_consensus_params(NULL); + + chan = (channel_t*)new_fake_channeltls(0); + channel_timestamp_active(chan); + + /* Test case #1: Channel that has "sent a packet" before the timeout. */ + tried_to_write_cell = 0; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER); + tt_assert(!chan->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + + /* Test case #2a: > 1.1s until timeout */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1200; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER); + tt_assert(!chan->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + + /* Test case #2b: >= 1.0s until timeout */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1000; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(chan->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + + // Wait for the timer from case #2b + new_time = (monotime_coarse_absolute_msec() + 1000)*NSEC_PER_MSEC; + monotime_set_mock_time_nsec(new_time); + monotime_coarse_set_mock_time_nsec(new_time); + timers_run_pending(); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!chan->pending_padding_callback); + + /* Test case #2c: > 0.1s until timeout */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(chan->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + + /* Test case #2d: Channel that asks to pad while timeout is scheduled */ + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); + + // Wait for the timer + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!chan->pending_padding_callback); + + /* Test case #2e: 0s until timeout */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec(); + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!chan->pending_padding_callback); + + /* Test case #2f: <0s until timeout */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() - 100; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT); + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!chan->pending_padding_callback); + + /* Test case #3: Channel that sends a packet while timeout is scheduled */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + tt_assert(chan->pending_padding_callback); + + // Pretend the channel sent a packet + channel_timestamp_active(chan); + + // We don't expect any timer callbacks here. Make a dummy one to be sure. + // Wait for the timer + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + + tt_int_op(tried_to_write_cell, OP_EQ, 0); + tt_assert(!chan->pending_padding_callback); + + /* Test case #4: Channel that closes while a timeout is scheduled */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + tt_assert(chan->pending_padding_callback); + + // Pretend the channel is temporarily down + chan->state = CHANNEL_STATE_MAINT; + + // We don't expect any timer callbacks here. Make a dummy one to be sure. + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + + tt_int_op(tried_to_write_cell, OP_EQ, 0); + tt_assert(!chan->pending_padding_callback); + chan->state = CHANNEL_STATE_OPEN; + + /* Test case #5: Make sure previous test case didn't break everything */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_assert(chan->pending_padding_callback); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + + // Wait for the timer + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + + tt_int_op(tried_to_write_cell, OP_EQ, 1); + tt_assert(!chan->pending_padding_callback); + + /* Test case #6. Channel is not used for full circuits */ + chan->channel_usage = CHANNEL_USED_NOT_USED_FOR_FULL_CIRCS; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); + tt_assert(!chan->pending_padding_callback); + chan->channel_usage = CHANNEL_USED_FOR_FULL_CIRCS; + + /* Test case #7. Channel is closed while timeout is scheduled. + * + * NOTE: This test deliberately breaks the channel callback mechanism. + * It must be last. + */ + tried_to_write_cell = 0; + chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + decision = channelpadding_decide_to_pad_channel(chan); + tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); + tt_int_op(tried_to_write_cell, OP_EQ, 0); + tt_assert(chan->pending_padding_callback); + + // Close the connection while the timer is scheduled + free_fake_channeltls((channel_tls_t*)chan); + + // We don't expect any timer callbacks here. Make a dummy one to be sure. + new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(new_time); + monotime_set_mock_time_nsec(new_time); + timers_run_pending(); + + tt_int_op(tried_to_write_cell, OP_EQ, 0); + + done: + smartlist_free(connection_array); + + teardown_capture_of_logs(); + monotime_disable_test_mocking(); + timers_shutdown(); + channel_free_all(); + + return; +} + +#define TEST_CHANNELPADDING(name, flags) \ + { #name, test_##name, (flags), NULL, NULL } + +struct testcase_t channelpadding_tests[] = { + //TEST_CHANNELPADDING(channelpadding_decide_to_pad_channel, 0), + TEST_CHANNELPADDING(channelpadding_decide_to_pad_channel, TT_FORK), + TEST_CHANNELPADDING(channelpadding_negotiation, TT_FORK), + TEST_CHANNELPADDING(channelpadding_consensus, TT_FORK), + TEST_CHANNELPADDING(channelpadding_killonehop, TT_FORK), + TEST_CHANNELPADDING(channelpadding_timers, TT_FORK), + END_OF_TESTCASES +}; + diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c index 08442e01b6..96c5eba9a5 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -32,6 +32,7 @@ static or_connection_t * tlschan_connection_or_connect_mock( const tor_addr_t *addr, uint16_t port, const char *digest, + const ed25519_public_key_t *ed_id, channel_tls_t *tlschan); static int tlschan_is_local_addr_mock(const tor_addr_t *addr); @@ -70,7 +71,7 @@ test_channeltls_create(void *arg) MOCK(connection_or_connect, tlschan_connection_or_connect_mock); /* Try connecting */ - ch = channel_tls_connect(&test_addr, 567, test_digest); + ch = channel_tls_connect(&test_addr, 567, test_digest, NULL); tt_assert(ch != NULL); done: @@ -119,7 +120,7 @@ test_channeltls_num_bytes_queued(void *arg) MOCK(connection_or_connect, tlschan_connection_or_connect_mock); /* Try connecting */ - ch = channel_tls_connect(&test_addr, 567, test_digest); + ch = channel_tls_connect(&test_addr, 567, test_digest, NULL); tt_assert(ch != NULL); /* @@ -204,7 +205,7 @@ test_channeltls_overhead_estimate(void *arg) MOCK(connection_or_connect, tlschan_connection_or_connect_mock); /* Try connecting */ - ch = channel_tls_connect(&test_addr, 567, test_digest); + ch = channel_tls_connect(&test_addr, 567, test_digest, NULL); tt_assert(ch != NULL); /* First case: silly low ratios should get clamped to 1.0 */ @@ -266,9 +267,11 @@ static or_connection_t * tlschan_connection_or_connect_mock(const tor_addr_t *addr, uint16_t port, const char *digest, + const ed25519_public_key_t *ed_id, channel_tls_t *tlschan) { or_connection_t *result = NULL; + (void) ed_id; // XXXX Not yet used. tt_assert(addr != NULL); tt_assert(port != 0); diff --git a/src/test/test_checkdir.c b/src/test/test_checkdir.c index fbb33f87f6..38f3360b61 100644 --- a/src/test/test_checkdir.c +++ b/src/test/test_checkdir.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c new file mode 100644 index 0000000000..a5282df69d --- /dev/null +++ b/src/test/test_circuitbuild.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CIRCUITBUILD_PRIVATE + +#include "or.h" +#include "test.h" +#include "test_helpers.h" +#include "log_test_helpers.h" +#include "config.h" +#include "circuitbuild.h" + +/* Dummy nodes smartlist for testing */ +static smartlist_t dummy_nodes; +/* Dummy exit extend_info for testing */ +static extend_info_t dummy_ei; + +static int +mock_count_acceptable_nodes(smartlist_t *nodes) +{ + (void)nodes; + + return DEFAULT_ROUTE_LEN + 1; +} + +/* Test route lengths when the caller of new_route_len() doesn't + * specify exit_ei. */ +static void +test_new_route_len_noexit(void *arg) +{ + int r; + + (void)arg; + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); + + r = new_route_len(CIRCUIT_PURPOSE_C_GENERAL, NULL, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + r = new_route_len(CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT, NULL, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + r = new_route_len(CIRCUIT_PURPOSE_S_CONNECT_REND, NULL, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + done: + UNMOCK(count_acceptable_nodes); +} + +/* Test route lengths where someone else chose the "exit" node, which + * require an extra hop for safety. */ +static void +test_new_route_len_unsafe_exit(void *arg) +{ + int r; + + (void)arg; + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); + + /* connecting to hidden service directory */ + r = new_route_len(CIRCUIT_PURPOSE_C_GENERAL, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN + 1, OP_EQ, r); + + /* client connecting to introduction point */ + r = new_route_len(CIRCUIT_PURPOSE_C_INTRODUCING, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN + 1, OP_EQ, r); + + /* hidden service connecting to rendezvous point */ + r = new_route_len(CIRCUIT_PURPOSE_S_CONNECT_REND, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN + 1, OP_EQ, r); + + done: + UNMOCK(count_acceptable_nodes); +} + +/* Test route lengths where we chose the "exit" node, which don't + * require an extra hop for safety. */ +static void +test_new_route_len_safe_exit(void *arg) +{ + int r; + + (void)arg; + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); + + /* hidden service connecting to introduction point */ + r = new_route_len(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, &dummy_ei, + &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + /* router testing its own reachability */ + r = new_route_len(CIRCUIT_PURPOSE_TESTING, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + done: + UNMOCK(count_acceptable_nodes); +} + +/* Make sure a non-fatal assertion fails when new_route_len() gets an + * unexpected circuit purpose. */ +static void +test_new_route_len_unhandled_exit(void *arg) +{ + int r; + + (void)arg; + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); + + tor_capture_bugs_(1); + setup_full_capture_of_logs(LOG_WARN); + r = new_route_len(CIRCUIT_PURPOSE_CONTROLLER, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN + 1, OP_EQ, r); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(exit_ei && !known_purpose)"); + expect_single_log_msg_containing("Unhandled purpose"); + expect_single_log_msg_containing("with a chosen exit; assuming routelen"); + teardown_capture_of_logs(); + tor_end_capture_bugs_(); + + done: + UNMOCK(count_acceptable_nodes); +} + +struct testcase_t circuitbuild_tests[] = { + { "noexit", test_new_route_len_noexit, 0, NULL, NULL }, + { "safe_exit", test_new_route_len_safe_exit, 0, NULL, NULL }, + { "unsafe_exit", test_new_route_len_unsafe_exit, 0, NULL, NULL }, + { "unhandled_exit", test_new_route_len_unhandled_exit, 0, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c index e996c42115..344ab27921 100644 --- a/src/test/test_circuitlist.c +++ b/src/test/test_circuitlist.c @@ -1,13 +1,15 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TOR_CHANNEL_INTERNAL_ #define CIRCUITBUILD_PRIVATE #define CIRCUITLIST_PRIVATE +#define HS_CIRCUITMAP_PRIVATE #include "or.h" #include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" +#include "hs_circuitmap.h" #include "test.h" #include "log_test_helpers.h" @@ -185,6 +187,9 @@ test_rend_token_maps(void *arg) (void)arg; (void)tok1; //xxxx + + hs_circuitmap_init(); + c1 = or_circuit_new(0, NULL); c2 = or_circuit_new(0, NULL); c3 = or_circuit_new(0, NULL); @@ -196,68 +201,68 @@ test_rend_token_maps(void *arg) tt_int_op(tok3[REND_TOKEN_LEN-1], OP_EQ, '.'); /* No maps; nothing there. */ - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok1)); - circuit_set_rendezvous_cookie(c1, tok1); - circuit_set_intro_point_digest(c2, tok2); + hs_circuitmap_register_rend_circ_relay_side(c1, tok1); + hs_circuitmap_register_intro_circ_v2_relay_side(c2, tok2); - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok3)); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok3)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok3)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok1)); /* Without purpose set, we don't get the circuits */ - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); c1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; c2->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; /* Okay, make sure they show up now. */ - tt_ptr_op(c1, OP_EQ, circuit_get_rendezvous(tok1)); - tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); + tt_ptr_op(c1, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + tt_ptr_op(c2, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); /* Two items at the same place with the same token. */ c3->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; - circuit_set_rendezvous_cookie(c3, tok2); - tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); - tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); + hs_circuitmap_register_rend_circ_relay_side(c3, tok2); + tt_ptr_op(c2, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); + tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); /* Marking a circuit makes it not get returned any more */ circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED); - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); circuit_free(TO_CIRCUIT(c1)); c1 = NULL; /* Freeing a circuit makes it not get returned any more. */ circuit_free(TO_CIRCUIT(c2)); c2 = NULL; - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); /* c3 -- are you still there? */ - tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); + tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); /* Change its cookie. This never happens in Tor per se, but hey. */ c3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; - circuit_set_intro_point_digest(c3, tok3); + hs_circuitmap_register_intro_circ_v2_relay_side(c3, tok3); - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); - tt_ptr_op(c3, OP_EQ, circuit_get_intro_point(tok3)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); + tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok3)); /* Now replace c3 with c4. */ c4->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; - circuit_set_intro_point_digest(c4, tok3); + hs_circuitmap_register_intro_circ_v2_relay_side(c4, tok3); - tt_ptr_op(c4, OP_EQ, circuit_get_intro_point(tok3)); + tt_ptr_op(c4, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok3)); - tt_ptr_op(c3->rendinfo, OP_EQ, NULL); - tt_ptr_op(c4->rendinfo, OP_NE, NULL); - tt_mem_op(c4->rendinfo, OP_EQ, tok3, REND_TOKEN_LEN); + tt_ptr_op(TO_CIRCUIT(c3)->hs_token, OP_EQ, NULL); + tt_ptr_op(TO_CIRCUIT(c4)->hs_token, OP_NE, NULL); + tt_mem_op(TO_CIRCUIT(c4)->hs_token->token, OP_EQ, tok3, REND_TOKEN_LEN); /* Now clear c4's cookie. */ - circuit_set_intro_point_digest(c4, NULL); - tt_ptr_op(c4->rendinfo, OP_EQ, NULL); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); + hs_circuitmap_remove_circuit(TO_CIRCUIT(c4)); + tt_ptr_op(TO_CIRCUIT(c4)->hs_token, OP_EQ, NULL); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok3)); done: if (c1) @@ -365,10 +370,89 @@ test_pick_circid(void *arg) UNMOCK(channel_dump_statistics); } +/** Test that the circuit pools of our HS circuitmap are isolated based on + * their token type. */ +static void +test_hs_circuitmap_isolation(void *arg) +{ + or_circuit_t *circ1 = NULL; + origin_circuit_t *circ2 = NULL; + or_circuit_t *circ3 = NULL; + origin_circuit_t *circ4 = NULL; + + (void)arg; + + hs_circuitmap_init(); + + { + const uint8_t tok1[REND_TOKEN_LEN] = "bet i got some of th"; + + circ1 = or_circuit_new(0, NULL); + tt_assert(circ1); + circ1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; + + /* check that circuitmap is empty right? */ + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + + /* Register circ1 with tok1 as relay-side rend circ */ + hs_circuitmap_register_rend_circ_relay_side(circ1, tok1); + + /* check that service-side getters don't work */ + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_service_side(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_service_side(tok1)); + + /* Check that the right getter works. */ + tt_ptr_op(circ1, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + } + + { + const uint8_t tok2[REND_TOKEN_LEN] = "you dont know anythi"; + + circ2 = origin_circuit_new(); + tt_assert(circ2); + circ2->base_.purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO; + circ3 = or_circuit_new(0, NULL); + tt_assert(circ3); + circ3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; + circ4 = origin_circuit_new(); + tt_assert(circ4); + circ4->base_.purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO; + + /* Register circ2 with tok2 as service-side intro v2 circ */ + hs_circuitmap_register_intro_circ_v2_service_side(circ2, tok2); + /* Register circ3 with tok2 again but for different purpose */ + hs_circuitmap_register_intro_circ_v2_relay_side(circ3, tok2); + + /* Check that the getters work */ + tt_ptr_op(circ2, OP_EQ, + hs_circuitmap_get_intro_circ_v2_service_side(tok2)); + tt_ptr_op(circ3, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); + + /* Register circ4 with tok2: it should override circ2 */ + hs_circuitmap_register_intro_circ_v2_service_side(circ4, tok2); + + /* check that relay-side getters don't work */ + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); + + /* Check that the getter returns circ4; the last circuit registered with + * that token. */ + tt_ptr_op(circ4, OP_EQ, + hs_circuitmap_get_intro_circ_v2_service_side(tok2)); + } + + done: + circuit_free(TO_CIRCUIT(circ1)); + circuit_free(TO_CIRCUIT(circ2)); + circuit_free(TO_CIRCUIT(circ3)); + circuit_free(TO_CIRCUIT(circ4)); +} + struct testcase_t circuitlist_tests[] = { { "maps", test_clist_maps, TT_FORK, NULL, NULL }, { "rend_token_maps", test_rend_token_maps, TT_FORK, NULL, NULL }, { "pick_circid", test_pick_circid, TT_FORK, NULL, NULL }, + { "hs_circuitmap_isolation", test_hs_circuitmap_isolation, + TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c index 1ffa17247d..99abdbc685 100644 --- a/src/test/test_circuitmux.c +++ b/src/test/test_circuitmux.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TOR_CHANNEL_INTERNAL_ diff --git a/src/test/test_circuituse.c b/src/test/test_circuituse.c new file mode 100644 index 0000000000..5cc9fe571e --- /dev/null +++ b/src/test/test_circuituse.c @@ -0,0 +1,304 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CIRCUITLIST_PRIVATE + +#include "or.h" +#include "test.h" +#include "test_helpers.h" +#include "config.h" +#include "circuitlist.h" +#include "circuituse.h" +#include "circuitbuild.h" +#include "nodelist.h" + +static void +test_circuit_is_available_for_use_ret_false_when_marked_for_close(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->marked_for_close = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_when_timestamp_dirty(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->timestamp_dirty = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_general_purpose(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_general_origin(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_origin_purpose(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_OR; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_unusable_for_new_conns(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + mark_circuit_unusable_for_new_conns(TO_ORIGIN_CIRCUIT(circ)); + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + circuit_free(circ); +} + +static void +test_circuit_is_available_for_use_returns_false_for_onehop_tunnel(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + oc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + oc->build_state->onehop_tunnel = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + circuit_free(circ); +} + +static void +test_circuit_is_available_for_use_returns_true_for_clean_circuit(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + oc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + oc->build_state->onehop_tunnel = 0; + + tt_int_op(1, ==, circuit_is_available_for_use(circ)); + + done: + circuit_free(circ); +} + +static int +mock_circuit_all_predicted_ports_handled(time_t now, + int *need_uptime, + int *need_capacity) +{ + (void)now; + + if (need_uptime && need_capacity) + return 0; + return 1; +} + +static consensus_path_type_t +mock_router_have_unknown_consensus_path(void) +{ + return CONSENSUS_PATH_UNKNOWN; +} + +static consensus_path_type_t +mock_router_have_exit_consensus_path(void) +{ + return CONSENSUS_PATH_EXIT; +} + +static void +test_needs_exit_circuits_ret_false_for_predicted_ports_and_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 0; + + time_t now = time(NULL); + tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); +} + +static void +test_needs_exit_circuits_ret_false_for_non_exit_consensus_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 1; + MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path); + + time_t now = time(NULL); + tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_exit_circuits_ret_true_for_predicted_ports_and_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 1; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + + time_t now = time(NULL); + tt_int_op(1, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_circuits_for_build_ret_false_consensus_path_unknown(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path); + tt_int_op(0, ==, needs_circuits_for_build(0)); + done: ; +} + +static void +test_needs_circuits_for_build_ret_false_if_num_less_than_max(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + tt_int_op(0, ==, needs_circuits_for_build(13)); + done: + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_circuits_for_build_returns_true_when_more_are_needed(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + tt_int_op(1, ==, needs_circuits_for_build(0)); + done: + UNMOCK(router_have_consensus_path); +} + +struct testcase_t circuituse_tests[] = { + { "marked", + test_circuit_is_available_for_use_ret_false_when_marked_for_close, + TT_FORK, NULL, NULL + }, + { "timestamp", + test_circuit_is_available_for_use_ret_false_when_timestamp_dirty, + TT_FORK, NULL, NULL + }, + { "non_general", + test_circuit_is_available_for_use_ret_false_for_non_general_purpose, + TT_FORK, NULL, NULL + }, + { "non_general", + test_circuit_is_available_for_use_ret_false_for_non_general_origin, + TT_FORK, NULL, NULL + }, + { "origin", + test_circuit_is_available_for_use_ret_false_for_non_origin_purpose, + TT_FORK, NULL, NULL + }, + { "clean", + test_circuit_is_available_for_use_ret_false_unusable_for_new_conns, + TT_FORK, NULL, NULL + }, + { "onehop", + test_circuit_is_available_for_use_returns_false_for_onehop_tunnel, + TT_FORK, NULL, NULL + }, + { "clean_circ", + test_circuit_is_available_for_use_returns_true_for_clean_circuit, + TT_FORK, NULL, NULL + }, + { "exit_f", + test_needs_exit_circuits_ret_false_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "exit_t", + test_needs_exit_circuits_ret_true_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "non_exit", + test_needs_exit_circuits_ret_false_for_non_exit_consensus_path, + TT_FORK, NULL, NULL + }, + { "true", + test_needs_exit_circuits_ret_true_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "consensus_path_unknown", + test_needs_circuits_for_build_ret_false_consensus_path_unknown, + TT_FORK, NULL, NULL + }, + { "less_than_max", + test_needs_circuits_for_build_ret_false_if_num_less_than_max, + TT_FORK, NULL, NULL + }, + { "more_needed", + test_needs_circuits_for_build_returns_true_when_more_are_needed, + TT_FORK, NULL, NULL + }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c index 0443cc0b1c..7dd8e65194 100644 --- a/src/test/test_compat_libevent.c +++ b/src/test/test_compat_libevent.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define COMPAT_LIBEVENT_PRIVATE diff --git a/src/test/test_config.c b/src/test/test_config.c index 89f9b3e2aa..beaab00f73 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -11,6 +11,7 @@ #include "or.h" #include "address.h" #include "addressmap.h" +#include "bridges.h" #include "circuitmux_ewma.h" #include "circuitbuild.h" #include "config.h" @@ -45,6 +46,8 @@ #include "transports.h" #include "util.h" +#include "test_helpers.h" + static void test_config_addressmap(void *arg) { @@ -851,9 +854,23 @@ static void test_config_fix_my_family(void *arg) { char *err = NULL; - const char *family = "$1111111111111111111111111111111111111111, " - "1111111111111111111111111111111111111112, " - "$1111111111111111111111111111111111111113"; + config_line_t *family = tor_malloc_zero(sizeof(config_line_t)); + family->key = tor_strdup("MyFamily"); + family->value = tor_strdup("$1111111111111111111111111111111111111111, " + "1111111111111111111111111111111111111112, " + "$1111111111111111111111111111111111111113"); + + config_line_t *family2 = tor_malloc_zero(sizeof(config_line_t)); + family2->key = tor_strdup("MyFamily"); + family2->value = tor_strdup("1111111111111111111111111111111111111114"); + + config_line_t *family3 = tor_malloc_zero(sizeof(config_line_t)); + family3->key = tor_strdup("MyFamily"); + family3->value = tor_strdup("$1111111111111111111111111111111111111115"); + + family->next = family2; + family2->next = family3; + family3->next = NULL; or_options_t* options = options_new(); or_options_t* defaults = options_new(); @@ -861,7 +878,7 @@ test_config_fix_my_family(void *arg) options_init(options); options_init(defaults); - options->MyFamily = tor_strdup(family); + options->MyFamily_lines = family; options_validate(NULL, options, defaults, 0, &err) ; @@ -869,18 +886,23 @@ test_config_fix_my_family(void *arg) TT_FAIL(("options_validate failed: %s", err)); } - tt_str_op(options->MyFamily,OP_EQ, - "$1111111111111111111111111111111111111111, " - "$1111111111111111111111111111111111111112, " - "$1111111111111111111111111111111111111113"); - - done: - if (err != NULL) { - tor_free(err); - } + const char *valid[] = { "$1111111111111111111111111111111111111111", + "$1111111111111111111111111111111111111112", + "$1111111111111111111111111111111111111113", + "$1111111111111111111111111111111111111114", + "$1111111111111111111111111111111111111115" }; + int ret_size = 0; + config_line_t *ret; + for (ret = options->MyFamily; ret && ret_size < 5; ret = ret->next) { + tt_str_op(ret->value, OP_EQ, valid[ret_size]); + ret_size++; + } + tt_int_op(ret_size, OP_EQ, 5); - or_options_free(options); - or_options_free(defaults); + done: + tor_free(err); + or_options_free(options); + or_options_free(defaults); } static int n_hostname_01010101 = 0; @@ -3861,144 +3883,6 @@ mock_config_line(const char *key, const char *val) } static void -test_config_parse_port_config__listenaddress(void *data) -{ - (void)data; - int ret; - config_line_t *config_listen_address = NULL, *config_listen_address2 = NULL, - *config_listen_address3 = NULL; - config_line_t *config_port1 = NULL, *config_port2 = NULL, - *config_port3 = NULL, *config_port4 = NULL, *config_port5 = NULL; - smartlist_t *slout = NULL; - port_cfg_t *port_cfg = NULL; - - // Test basic invocation with no arguments - ret = parse_port_config(NULL, NULL, NULL, NULL, 0, NULL, 0, 0); - tt_int_op(ret, OP_EQ, 0); - - // Setup some test data - config_listen_address = mock_config_line("DNSListenAddress", "127.0.0.1"); - config_listen_address2 = mock_config_line("DNSListenAddress", "x$$$:::345"); - config_listen_address3 = mock_config_line("DNSListenAddress", - "127.0.0.1:1442"); - config_port1 = mock_config_line("DNSPort", "42"); - config_port2 = mock_config_line("DNSPort", "43"); - config_port1->next = config_port2; - config_port3 = mock_config_line("DNSPort", "auto"); - config_port4 = mock_config_line("DNSPort", "55542"); - config_port5 = mock_config_line("DNSPort", "666777"); - - // Test failure when we have a ListenAddress line and several - // Port lines for the same portname - ret = parse_port_config(NULL, config_port1, config_listen_address, "DNS", 0, - NULL, 0, 0); - - tt_int_op(ret, OP_EQ, -1); - - // Test case when we have a listen address, no default port and allow - // spurious listen address lines - ret = parse_port_config(NULL, NULL, config_listen_address, "DNS", 0, NULL, - 0, CL_PORT_ALLOW_EXTRA_LISTENADDR); - tt_int_op(ret, OP_EQ, 1); - - // Test case when we have a listen address, no default port but doesn't - // allow spurious listen address lines - ret = parse_port_config(NULL, NULL, config_listen_address, "DNS", 0, NULL, - 0, 0); - tt_int_op(ret, OP_EQ, -1); - - // Test case when we have a listen address, and a port that points to auto, - // should use the AUTO port - slout = smartlist_new(); - ret = parse_port_config(slout, config_port3, config_listen_address, "DNS", - 0, NULL, 0, 0); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(smartlist_len(slout), OP_EQ, 1); - port_cfg = (port_cfg_t *)smartlist_get(slout, 0); - tt_int_op(port_cfg->port, OP_EQ, CFG_AUTO_PORT); - - // Test when we have a listen address and a custom port - ret = parse_port_config(slout, config_port4, config_listen_address, "DNS", - 0, NULL, 0, 0); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(smartlist_len(slout), OP_EQ, 2); - port_cfg = (port_cfg_t *)smartlist_get(slout, 1); - tt_int_op(port_cfg->port, OP_EQ, 55542); - - // Test when we have a listen address and an invalid custom port - ret = parse_port_config(slout, config_port5, config_listen_address, "DNS", - 0, NULL, 0, 0); - tt_int_op(ret, OP_EQ, -1); - - // Test we get a server port configuration when asked for it - ret = parse_port_config(slout, NULL, config_listen_address, "DNS", 0, NULL, - 123, CL_PORT_SERVER_OPTIONS); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(smartlist_len(slout), OP_EQ, 4); - port_cfg = (port_cfg_t *)smartlist_get(slout, 2); - tt_int_op(port_cfg->port, OP_EQ, 123); - tt_int_op(port_cfg->server_cfg.no_listen, OP_EQ, 1); - tt_int_op(port_cfg->server_cfg.bind_ipv4_only, OP_EQ, 1); - - // Test an invalid ListenAddress configuration - ret = parse_port_config(NULL, NULL, config_listen_address2, "DNS", 0, NULL, - 222, 0); - tt_int_op(ret, OP_EQ, -1); - - // Test default to the port in the listen address if available - ret = parse_port_config(slout, config_port2, config_listen_address3, "DNS", - 0, NULL, 0, 0); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(smartlist_len(slout), OP_EQ, 5); - port_cfg = (port_cfg_t *)smartlist_get(slout, 4); - tt_int_op(port_cfg->port, OP_EQ, 1442); - - // Test we work correctly without an out, but with a listen address - // and a port - ret = parse_port_config(NULL, config_port2, config_listen_address, "DNS", - 0, NULL, 0, 0); - tt_int_op(ret, OP_EQ, 0); - - // Test warning nonlocal control - ret = parse_port_config(slout, config_port2, config_listen_address, "DNS", - CONN_TYPE_CONTROL_LISTENER, NULL, 0, - CL_PORT_WARN_NONLOCAL); - tt_int_op(ret, OP_EQ, 0); - - // Test warning nonlocal ext or listener - ret = parse_port_config(slout, config_port2, config_listen_address, "DNS", - CONN_TYPE_EXT_OR_LISTENER, NULL, 0, - CL_PORT_WARN_NONLOCAL); - tt_int_op(ret, OP_EQ, 0); - - // Test warning nonlocal other - SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); - smartlist_clear(slout); - ret = parse_port_config(slout, config_port2, config_listen_address, "DNS", - 0, NULL, 0, CL_PORT_WARN_NONLOCAL); - tt_int_op(ret, OP_EQ, 0); - - // Test warning nonlocal control without an out - ret = parse_port_config(NULL, config_port2, config_listen_address, "DNS", - CONN_TYPE_CONTROL_LISTENER, NULL, 0, - CL_PORT_WARN_NONLOCAL); - tt_int_op(ret, OP_EQ, 0); - - done: - config_free_lines(config_listen_address); - config_free_lines(config_listen_address2); - config_free_lines(config_listen_address3); - config_free_lines(config_port1); - /* 2 was linked from 1. */ - config_free_lines(config_port3); - config_free_lines(config_port4); - config_free_lines(config_port5); - if (slout) - SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); - smartlist_free(slout); -} - -static void test_config_parse_port_config__ports__no_ports_given(void *data) { (void)data; @@ -4009,40 +3893,40 @@ test_config_parse_port_config__ports__no_ports_given(void *data) slout = smartlist_new(); // Test no defaultport, no defaultaddress and no out - ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, NULL, 0, 0); + ret = parse_port_config(NULL, NULL, "DNS", 0, NULL, 0, 0); tt_int_op(ret, OP_EQ, 0); // Test with defaultport, no defaultaddress and no out - ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, NULL, 42, 0); + ret = parse_port_config(NULL, NULL, "DNS", 0, NULL, 42, 0); tt_int_op(ret, OP_EQ, 0); // Test no defaultport, with defaultaddress and no out - ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, "127.0.0.2", 0, 0); + ret = parse_port_config(NULL, NULL, "DNS", 0, "127.0.0.2", 0, 0); tt_int_op(ret, OP_EQ, 0); // Test with defaultport, with defaultaddress and no out - ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, "127.0.0.2", 42, 0); + ret = parse_port_config(NULL, NULL, "DNS", 0, "127.0.0.2", 42, 0); tt_int_op(ret, OP_EQ, 0); // Test no defaultport, no defaultaddress and with out - ret = parse_port_config(slout, NULL, NULL, "DNS", 0, NULL, 0, 0); + ret = parse_port_config(slout, NULL, "DNS", 0, NULL, 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 0); // Test with defaultport, no defaultaddress and with out - ret = parse_port_config(slout, NULL, NULL, "DNS", 0, NULL, 42, 0); + ret = parse_port_config(slout, NULL, "DNS", 0, NULL, 42, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 0); // Test no defaultport, with defaultaddress and with out - ret = parse_port_config(slout, NULL, NULL, "DNS", 0, "127.0.0.2", 0, 0); + ret = parse_port_config(slout, NULL, "DNS", 0, "127.0.0.2", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 0); // Test with defaultport, with defaultaddress and out, adds a new port cfg SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - ret = parse_port_config(slout, NULL, NULL, "DNS", 0, "127.0.0.2", 42, 0); + ret = parse_port_config(slout, NULL, "DNS", 0, "127.0.0.2", 42, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); port_cfg = (port_cfg_t *)smartlist_get(slout, 0); @@ -4053,7 +3937,7 @@ test_config_parse_port_config__ports__no_ports_given(void *data) // for a unix address SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - ret = parse_port_config(slout, NULL, NULL, "DNS", 0, "/foo/bar/unixdomain", + ret = parse_port_config(slout, NULL, "DNS", 0, "/foo/bar/unixdomain", 42, CL_PORT_IS_UNIXSOCKET); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4082,28 +3966,28 @@ test_config_parse_port_config__ports__ports_given(void *data) // Test error when encounters an invalid Port specification config_port_invalid = mock_config_line("DNSPort", ""); - ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, NULL, + ret = parse_port_config(NULL, config_port_invalid, "DNS", 0, NULL, 0, 0); tt_int_op(ret, OP_EQ, -1); // Test error when encounters an empty unix domain specification config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("DNSPort", "unix:"); - ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, NULL, + ret = parse_port_config(NULL, config_port_invalid, "DNS", 0, NULL, 0, 0); tt_int_op(ret, OP_EQ, -1); // Test error when encounters a unix domain specification but the listener // doesn't support domain sockets config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar"); - ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", + ret = parse_port_config(NULL, config_port_valid, "DNS", CONN_TYPE_AP_DNS_LISTENER, NULL, 0, 0); tt_int_op(ret, OP_EQ, -1); // Test valid unix domain SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, 0); #ifdef _WIN32 tt_int_op(ret, OP_EQ, -1); @@ -4126,8 +4010,9 @@ test_config_parse_port_config__ports__ports_given(void *data) config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar NoIPv4Traffic " + "NoIPv6Traffic " "NoOnionTraffic"); - ret = parse_port_config(NULL, config_port_invalid, NULL, "SOCKS", + ret = parse_port_config(NULL, config_port_invalid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); tt_int_op(ret, OP_EQ, -1); @@ -4136,7 +4021,7 @@ test_config_parse_port_config__ports__ports_given(void *data) config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("DNSPort", "127.0.0.1:80 NoDNSRequest"); - ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", + ret = parse_port_config(NULL, config_port_invalid, "DNS", CONN_TYPE_AP_DNS_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); tt_int_op(ret, OP_EQ, -1); @@ -4147,8 +4032,9 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "127.0.0.1:80 " + "NoIPv6Traffic " "NoIPv4Traffic NoOnionTraffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", + ret = parse_port_config(slout, config_port_valid, "DNS", CONN_TYPE_AP_DNS_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); tt_int_op(ret, OP_EQ, 0); @@ -4162,8 +4048,9 @@ test_config_parse_port_config__ports__ports_given(void *data) // Test failure if we have DNS but no ipv4 and no ipv6 config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("SOCKSPort", + "NoIPv6Traffic " "unix:/tmp/foo/bar NoIPv4Traffic"); - ret = parse_port_config(NULL, config_port_invalid, NULL, "SOCKS", + ret = parse_port_config(NULL, config_port_invalid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); tt_int_op(ret, OP_EQ, -1); @@ -4174,8 +4061,9 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar " + "NoIPv6Traffic " "NoDNSRequest NoIPv4Traffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); #ifdef _WIN32 @@ -4195,8 +4083,9 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:\"/tmp/foo/ bar\" " + "NoIPv6Traffic " "NoDNSRequest NoIPv4Traffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); #ifdef _WIN32 @@ -4216,8 +4105,9 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:\"/tmp/foo/ bar " + "NoIPv6Traffic " "NoDNSRequest NoIPv4Traffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); tt_int_op(ret, OP_EQ, -1); @@ -4227,8 +4117,9 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:\"\" " + "NoIPv6Traffic " "NoDNSRequest NoIPv4Traffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); tt_int_op(ret, OP_EQ, -1); @@ -4239,7 +4130,7 @@ test_config_parse_port_config__ports__ports_given(void *data) smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar " "OnionTrafficOnly"); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); #ifdef _WIN32 @@ -4260,7 +4151,7 @@ test_config_parse_port_config__ports__ports_given(void *data) smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar " "NoIPv4Traffic IPv6Traffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); #ifdef _WIN32 @@ -4279,7 +4170,7 @@ test_config_parse_port_config__ports__ports_given(void *data) smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar " "IPv4Traffic IPv6Traffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); #ifdef _WIN32 @@ -4295,28 +4186,28 @@ test_config_parse_port_config__ports__ports_given(void *data) // Test failure if we specify world writable for an IP Port config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("DNSPort", "42 WorldWritable"); - ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, + ret = parse_port_config(NULL, config_port_invalid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, -1); // Test failure if we specify group writable for an IP Port config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("DNSPort", "42 GroupWritable"); - ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, + ret = parse_port_config(NULL, config_port_invalid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, -1); // Test failure if we specify group writable for an IP Port config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("DNSPort", "42 RelaxDirModeCheck"); - ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, + ret = parse_port_config(NULL, config_port_invalid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, -1); // Test success with only a port (this will fail without a default address) config_free_lines(config_port_valid); config_port_valid = NULL; config_port_valid = mock_config_line("DNSPort", "42"); - ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(NULL, config_port_valid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, 0); @@ -4325,7 +4216,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 IsolateDestPort"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4338,7 +4229,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 NoIsolateDestPorts"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4351,7 +4242,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 IsolateDestAddr"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4364,7 +4255,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 IsolateSOCKSAuth"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4377,7 +4268,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 IsolateClientProtocol"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4390,7 +4281,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 IsolateClientAddr"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4401,7 +4292,7 @@ test_config_parse_port_config__ports__ports_given(void *data) // Test success with ignored unknown options config_free_lines(config_port_valid); config_port_valid = NULL; config_port_valid = mock_config_line("DNSPort", "42 ThisOptionDoesntExist"); - ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(NULL, config_port_valid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, 0); @@ -4410,7 +4301,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 NoIsolateSOCKSAuth"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.3", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4423,7 +4314,7 @@ test_config_parse_port_config__ports__ports_given(void *data) smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "42 IPv6Traffic PreferIPv6"); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, "127.0.0.42", 0, CL_PORT_TAKES_HOSTNAMES); tt_int_op(ret, OP_EQ, 0); @@ -4436,7 +4327,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 CacheIPv4DNS"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4449,7 +4340,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 CacheIPv6DNS"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4462,7 +4353,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 NoCacheIPv4DNS"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4475,7 +4366,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 CacheDNS"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, CL_PORT_TAKES_HOSTNAMES); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4488,7 +4379,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 UseIPv4Cache"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4501,7 +4392,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 UseIPv6Cache"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4514,7 +4405,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 UseDNSCache"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4527,7 +4418,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 NoPreferIPv6Automap"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4539,7 +4430,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 PreferSOCKSNoAuth"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4554,14 +4445,14 @@ test_config_parse_port_config__ports__ports_given(void *data) config_port_invalid = mock_config_line("DNSPort", "0"); config_port_valid = mock_config_line("DNSPort", "42"); config_port_invalid->next = config_port_valid; - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, "127.0.0.42", 0, 0); tt_int_op(ret, OP_EQ, -1); // Test success with warn non-local control SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - ret = parse_port_config(slout, config_port_valid, NULL, "Control", + ret = parse_port_config(slout, config_port_valid, "Control", CONN_TYPE_CONTROL_LISTENER, "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL); tt_int_op(ret, OP_EQ, 0); @@ -4569,7 +4460,7 @@ test_config_parse_port_config__ports__ports_given(void *data) // Test success with warn non-local listener SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - ret = parse_port_config(slout, config_port_valid, NULL, "ExtOR", + ret = parse_port_config(slout, config_port_valid, "ExtOR", CONN_TYPE_EXT_OR_LISTENER, "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL); tt_int_op(ret, OP_EQ, 0); @@ -4577,12 +4468,12 @@ test_config_parse_port_config__ports__ports_given(void *data) // Test success with warn non-local other SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL); tt_int_op(ret, OP_EQ, 0); // Test success with warn non-local other without out - ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(NULL, config_port_valid, "DNS", 0, "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL); tt_int_op(ret, OP_EQ, 0); @@ -4593,7 +4484,7 @@ test_config_parse_port_config__ports__ports_given(void *data) smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 IPv4Traffic " "IPv6Traffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.44", 0, CL_PORT_TAKES_HOSTNAMES | CL_PORT_NO_STREAM_OPTIONS); @@ -4601,14 +4492,14 @@ test_config_parse_port_config__ports__ports_given(void *data) tt_int_op(smartlist_len(slout), OP_EQ, 1); port_cfg = (port_cfg_t *)smartlist_get(slout, 0); tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1); - tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1); // Test failure for a SessionGroup argument with invalid value config_free_lines(config_port_invalid); config_port_invalid = NULL; SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_invalid = mock_config_line("DNSPort", "42 SessionGroup=invalid"); - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, "127.0.0.44", 0, CL_PORT_NO_STREAM_OPTIONS); tt_int_op(ret, OP_EQ, -1); @@ -4620,7 +4511,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_invalid = mock_config_line("DNSPort", "42 SessionGroup=123"); - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, "127.0.0.44", 0, 0); tt_int_op(ret, OP_EQ, -1); @@ -4630,7 +4521,7 @@ test_config_parse_port_config__ports__ports_given(void *data) smartlist_clear(slout); config_port_invalid = mock_config_line("DNSPort", "42 SessionGroup=123 " "SessionGroup=321"); - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, "127.0.0.44", 0, CL_PORT_NO_STREAM_OPTIONS); tt_int_op(ret, OP_EQ, -1); @@ -4639,7 +4530,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "42 SessionGroup=1111122"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.44", 0, CL_PORT_NO_STREAM_OPTIONS); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4651,7 +4542,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "0"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.45", 0, CL_PORT_IS_UNIXSOCKET); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 0); @@ -4661,7 +4552,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "something"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.45", 0, CL_PORT_IS_UNIXSOCKET); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4674,7 +4565,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "auto"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.46", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4688,7 +4579,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "127.0.0.122:auto"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.46", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4700,8 +4591,10 @@ test_config_parse_port_config__ports__ports_given(void *data) // Test failure when asked to parse an invalid address followed by auto config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("DNSPort", "invalidstuff!!:auto"); - ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); + ret = parse_port_config(NULL, config_port_invalid, "DNS", 0, "127.0.0.46", 0, 0); + UNMOCK(tor_addr_lookup); tt_int_op(ret, OP_EQ, -1); // Test success with parsing both an address and a real port @@ -4709,7 +4602,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "127.0.0.123:656"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, "127.0.0.46", 0, 0); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4723,7 +4616,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_invalid = mock_config_line("DNSPort", "something wrong"); - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, "127.0.0.46", 0, 0); tt_int_op(ret, OP_EQ, -1); @@ -4732,7 +4625,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_invalid = mock_config_line("DNSPort", "127.0.1.0:123:auto"); - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, "127.0.0.46", 0, 0); tt_int_op(ret, OP_EQ, -1); @@ -4742,7 +4635,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/somewhere"); - ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + ret = parse_port_config(slout, config_port_valid, "SOCKS", CONN_TYPE_AP_LISTENER, "127.0.0.46", 0, CL_PORT_DFLT_GROUP_WRITABLE); #ifdef _WIN32 @@ -4777,7 +4670,7 @@ test_config_parse_port_config__ports__server_options(void *data) config_free_lines(config_port_valid); config_port_valid = NULL; config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 NoAdvertise"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4790,7 +4683,7 @@ test_config_parse_port_config__ports__server_options(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 NoListen"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4804,7 +4697,7 @@ test_config_parse_port_config__ports__server_options(void *data) smartlist_clear(slout); config_port_invalid = mock_config_line("DNSPort", "127.0.0.124:656 NoListen " "NoAdvertise"); - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, -1); @@ -4813,7 +4706,7 @@ test_config_parse_port_config__ports__server_options(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 IPv4Only"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4826,7 +4719,7 @@ test_config_parse_port_config__ports__server_options(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "[::1]:656 IPv6Only"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4840,7 +4733,7 @@ test_config_parse_port_config__ports__server_options(void *data) smartlist_clear(slout); config_port_invalid = mock_config_line("DNSPort", "127.0.0.124:656 IPv6Only " "IPv4Only"); - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, -1); @@ -4849,7 +4742,7 @@ test_config_parse_port_config__ports__server_options(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 unknown"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0, + ret = parse_port_config(slout, config_port_valid, "DNS", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, 0); tt_int_op(smartlist_len(slout), OP_EQ, 1); @@ -4860,7 +4753,7 @@ test_config_parse_port_config__ports__server_options(void *data) smartlist_clear(slout); config_port_invalid = mock_config_line("DNSPort", "127.0.0.124:656 IPv6Only"); - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, -1); @@ -4869,7 +4762,7 @@ test_config_parse_port_config__ports__server_options(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_invalid = mock_config_line("DNSPort", "[::1]:656 IPv4Only"); - ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL, + ret = parse_port_config(slout, config_port_invalid, "DNS", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, -1); @@ -4878,7 +4771,7 @@ test_config_parse_port_config__ports__server_options(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_invalid = mock_config_line("ORPort", "unix:\"\""); - ret = parse_port_config(slout, config_port_invalid, NULL, "ORPort", 0, NULL, + ret = parse_port_config(slout, config_port_invalid, "ORPort", 0, NULL, 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, -1); @@ -4890,6 +4783,580 @@ test_config_parse_port_config__ports__server_options(void *data) config_free_lines(config_port_valid); config_port_valid = NULL; } +static void +test_config_parse_log_severity(void *data) +{ + int ret; + const char *severity_log_lines[] = { + "debug file /tmp/debug.log", + "debug\tfile /tmp/debug.log", + "[handshake]debug [~net,~mm]info notice stdout", + "[handshake]debug\t[~net,~mm]info\tnotice\tstdout", + NULL + }; + int i; + log_severity_list_t *severity; + + (void) data; + + severity = tor_malloc(sizeof(log_severity_list_t)); + for (i = 0; severity_log_lines[i]; i++) { + memset(severity, 0, sizeof(log_severity_list_t)); + ret = parse_log_severity_config(&severity_log_lines[i], severity); + tt_int_op(ret, OP_EQ, 0); + } + + done: + tor_free(severity); +} + +static void +test_config_include_limit(void *data) +{ + (void)data; + + config_line_t *result = NULL; + char *dir = tor_strdup(get_fname("test_include_limit")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char torrc_path[PATH_MAX+1]; + tor_snprintf(torrc_path, sizeof(torrc_path), "%s"PATH_SEPARATOR"torrc", dir); + char torrc_contents[1000]; + tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s", + torrc_path); + tt_int_op(write_str_to_file(torrc_path, torrc_contents, 0), OP_EQ, 0); + + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL), + OP_EQ, -1); + + done: + config_free_lines(result); + tor_free(dir); +} + +static void +test_config_include_does_not_exist(void *data) +{ + (void)data; + + config_line_t *result = NULL; + char *dir = tor_strdup(get_fname("test_include_does_not_exist")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char missing_path[PATH_MAX+1]; + tor_snprintf(missing_path, sizeof(missing_path), "%s"PATH_SEPARATOR"missing", + dir); + char torrc_contents[1000]; + tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s", + missing_path); + + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL), + OP_EQ, -1); + + done: + config_free_lines(result); + tor_free(dir); +} + +static void +test_config_include_error_in_included_file(void *data) +{ + (void)data; + config_line_t *result = NULL; + + char *dir = tor_strdup(get_fname("test_error_in_included_file")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char invalid_path[PATH_MAX+1]; + tor_snprintf(invalid_path, sizeof(invalid_path), "%s"PATH_SEPARATOR"invalid", + dir); + tt_int_op(write_str_to_file(invalid_path, "unclosed \"", 0), OP_EQ, 0); + + char torrc_contents[1000]; + tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s", + invalid_path); + + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL), + OP_EQ, -1); + + done: + config_free_lines(result); + tor_free(dir); +} + +static void +test_config_include_empty_file_folder(void *data) +{ + (void)data; + config_line_t *result = NULL; + + char *dir = tor_strdup(get_fname("test_include_empty_file_folder")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char folder_path[PATH_MAX+1]; + tor_snprintf(folder_path, sizeof(folder_path), "%s"PATH_SEPARATOR"empty_dir", + dir); +#ifdef _WIN32 + tt_int_op(mkdir(folder_path), OP_EQ, 0); +#else + tt_int_op(mkdir(folder_path, 0700), OP_EQ, 0); +#endif + char file_path[PATH_MAX+1]; + tor_snprintf(file_path, sizeof(file_path), "%s"PATH_SEPARATOR"empty_file", + dir); + tt_int_op(write_str_to_file(file_path, "", 0), OP_EQ, 0); + + char torrc_contents[1000]; + tor_snprintf(torrc_contents, sizeof(torrc_contents), + "%%include %s\n" + "%%include %s\n", + folder_path, file_path); + + int include_used; + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), + OP_EQ, 0); + tt_ptr_op(result, OP_EQ, NULL); + tt_int_op(include_used, OP_EQ, 1); + + done: + config_free_lines(result); + tor_free(dir); +} + +static void +test_config_include_recursion_before_after(void *data) +{ + (void)data; + + config_line_t *result = NULL; + char *dir = tor_strdup(get_fname("test_include_recursion_before_after")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char torrc_path[PATH_MAX+1]; + tor_snprintf(torrc_path, sizeof(torrc_path), "%s"PATH_SEPARATOR"torrc", dir); + + char file_contents[1000]; + const int limit = MAX_INCLUDE_RECURSION_LEVEL; + int i; + // Loop backwards so file_contents has the contents of the first file by the + // end of the loop + for (i = limit; i > 0; i--) { + if (i < limit) { + tor_snprintf(file_contents, sizeof(file_contents), + "Test %d\n" + "%%include %s%d\n" + "Test %d\n", + i, torrc_path, i + 1, 2 * limit - i); + } else { + tor_snprintf(file_contents, sizeof(file_contents), "Test %d\n", i); + } + + if (i > 1) { + char file_path[PATH_MAX+1]; + tor_snprintf(file_path, sizeof(file_path), "%s%d", torrc_path, i); + tt_int_op(write_str_to_file(file_path, file_contents, 0), OP_EQ, 0); + } + } + + int include_used; + tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used), + OP_EQ, 0); + tt_ptr_op(result, OP_NE, NULL); + tt_int_op(include_used, OP_EQ, 1); + + int len = 0; + config_line_t *next; + for (next = result; next != NULL; next = next->next) { + char expected[10]; + tor_snprintf(expected, sizeof(expected), "%d", len + 1); + tt_str_op(next->key, OP_EQ, "Test"); + tt_str_op(next->value, OP_EQ, expected); + len++; + } + tt_int_op(len, OP_EQ, 2 * limit - 1); + + done: + config_free_lines(result); + tor_free(dir); +} + +static void +test_config_include_recursion_after_only(void *data) +{ + (void)data; + + config_line_t *result = NULL; + char *dir = tor_strdup(get_fname("test_include_recursion_after_only")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char torrc_path[PATH_MAX+1]; + tor_snprintf(torrc_path, sizeof(torrc_path), "%s"PATH_SEPARATOR"torrc", dir); + + char file_contents[1000]; + const int limit = MAX_INCLUDE_RECURSION_LEVEL; + int i; + // Loop backwards so file_contents has the contents of the first file by the + // end of the loop + for (i = limit; i > 0; i--) { + int n = (i - limit - 1) * -1; + if (i < limit) { + tor_snprintf(file_contents, sizeof(file_contents), + "%%include %s%d\n" + "Test %d\n", + torrc_path, i + 1, n); + } else { + tor_snprintf(file_contents, sizeof(file_contents), "Test %d\n", n); + } + + if (i > 1) { + char file_path[PATH_MAX+1]; + tor_snprintf(file_path, sizeof(file_path), "%s%d", torrc_path, i); + tt_int_op(write_str_to_file(file_path, file_contents, 0), OP_EQ, 0); + } + } + + int include_used; + tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used), + OP_EQ, 0); + tt_ptr_op(result, OP_NE, NULL); + tt_int_op(include_used, OP_EQ, 1); + + int len = 0; + config_line_t *next; + for (next = result; next != NULL; next = next->next) { + char expected[10]; + tor_snprintf(expected, sizeof(expected), "%d", len + 1); + tt_str_op(next->key, OP_EQ, "Test"); + tt_str_op(next->value, OP_EQ, expected); + len++; + } + tt_int_op(len, OP_EQ, limit); + + done: + config_free_lines(result); + tor_free(dir); +} + +static void +test_config_include_folder_order(void *data) +{ + (void)data; + + config_line_t *result = NULL; + char *dir = tor_strdup(get_fname("test_include_folder_order")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char torrcd[PATH_MAX+1]; + tor_snprintf(torrcd, sizeof(torrcd), "%s"PATH_SEPARATOR"%s", dir, "torrc.d"); + +#ifdef _WIN32 + tt_int_op(mkdir(torrcd), OP_EQ, 0); +#else + tt_int_op(mkdir(torrcd, 0700), OP_EQ, 0); +#endif + + // test that files in subfolders are ignored + char path[PATH_MAX+1]; + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, + "subfolder"); + +#ifdef _WIN32 + tt_int_op(mkdir(path), OP_EQ, 0); +#else + tt_int_op(mkdir(path, 0700), OP_EQ, 0); +#endif + + char path2[PATH_MAX+1]; + tor_snprintf(path2, sizeof(path2), "%s"PATH_SEPARATOR"%s", path, + "01_ignore"); + tt_int_op(write_str_to_file(path2, "ShouldNotSee 1\n", 0), OP_EQ, 0); + + // test that files starting with . are ignored + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, ".dot"); + tt_int_op(write_str_to_file(path, "ShouldNotSee 2\n", 0), OP_EQ, 0); + + // test file order + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "01_1st"); + tt_int_op(write_str_to_file(path, "Test 1\n", 0), OP_EQ, 0); + + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "02_2nd"); + tt_int_op(write_str_to_file(path, "Test 2\n", 0), OP_EQ, 0); + + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "aa_3rd"); + tt_int_op(write_str_to_file(path, "Test 3\n", 0), OP_EQ, 0); + + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "ab_4th"); + tt_int_op(write_str_to_file(path, "Test 4\n", 0), OP_EQ, 0); + + char torrc_contents[1000]; + tor_snprintf(torrc_contents, sizeof(torrc_contents), + "%%include %s\n", + torrcd); + + int include_used; + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), + OP_EQ, 0); + tt_ptr_op(result, OP_NE, NULL); + tt_int_op(include_used, OP_EQ, 1); + + int len = 0; + config_line_t *next; + for (next = result; next != NULL; next = next->next) { + char expected[10]; + tor_snprintf(expected, sizeof(expected), "%d", len + 1); + tt_str_op(next->key, OP_EQ, "Test"); + tt_str_op(next->value, OP_EQ, expected); + len++; + } + tt_int_op(len, OP_EQ, 4); + + done: + config_free_lines(result); + tor_free(dir); +} + +static void +test_config_include_path_syntax(void *data) +{ + (void)data; + + config_line_t *result = NULL; + char *dir = tor_strdup(get_fname("test_include_path_syntax")); + char *esc_dir = NULL, *dir_with_pathsep = NULL, + *esc_dir_with_pathsep = NULL, *torrc_contents = NULL; + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + esc_dir = esc_for_log(dir); + tor_asprintf(&dir_with_pathsep, "%s%s", dir, PATH_SEPARATOR); + esc_dir_with_pathsep = esc_for_log(dir_with_pathsep); + + tor_asprintf(&torrc_contents, + "%%include %s\n" + "%%include %s%s \n" // space to avoid suppressing newline + "%%include %s\n", + esc_dir, + dir, PATH_SEPARATOR, + esc_dir_with_pathsep); + + int include_used; + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), + OP_EQ, 0); + tt_ptr_op(result, OP_EQ, NULL); + tt_int_op(include_used, OP_EQ, 1); + + done: + config_free_lines(result); + tor_free(dir); + tor_free(torrc_contents); + tor_free(esc_dir); + tor_free(dir_with_pathsep); + tor_free(esc_dir_with_pathsep); +} + +static void +test_config_include_not_processed(void *data) +{ + (void)data; + + char torrc_contents[1000] = "%include does_not_exist\n"; + config_line_t *result = NULL; + tt_int_op(config_get_lines(torrc_contents, &result, 0),OP_EQ, 0); + tt_ptr_op(result, OP_NE, NULL); + + int len = 0; + config_line_t *next; + for (next = result; next != NULL; next = next->next) { + tt_str_op(next->key, OP_EQ, "%include"); + tt_str_op(next->value, OP_EQ, "does_not_exist"); + len++; + } + tt_int_op(len, OP_EQ, 1); + + done: + config_free_lines(result); +} + +static void +test_config_include_has_include(void *data) +{ + (void)data; + + config_line_t *result = NULL; + char *dir = tor_strdup(get_fname("test_include_has_include")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char torrc_contents[1000] = "Test 1\n"; + int include_used; + + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), + OP_EQ, 0); + tt_int_op(include_used, OP_EQ, 0); + config_free_lines(result); + + tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s\n", dir); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), + OP_EQ, 0); + tt_int_op(include_used, OP_EQ, 1); + + done: + config_free_lines(result); + tor_free(dir); +} + +static void +test_config_include_flag_both_without(void *data) +{ + (void)data; + + char *errmsg = NULL; + char conf_empty[1000]; + tor_snprintf(conf_empty, sizeof(conf_empty), + "DataDirectory %s\n", + get_fname(NULL)); + // test with defaults-torrc and torrc without include + int ret = options_init_from_string(conf_empty, conf_empty, CMD_RUN_UNITTESTS, + NULL, &errmsg); + tt_int_op(ret, OP_EQ, 0); + + const or_options_t *options = get_options(); + tt_int_op(options->IncludeUsed, OP_EQ, 0); + + done: + tor_free(errmsg); +} + +static void +test_config_include_flag_torrc_only(void *data) +{ + (void)data; + + char *errmsg = NULL; + char *dir = tor_strdup(get_fname("test_include_flag_torrc_only")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char path[PATH_MAX+1]; + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", dir, "dummy"); + tt_int_op(write_str_to_file(path, "\n", 0), OP_EQ, 0); + + char conf_empty[1000]; + tor_snprintf(conf_empty, sizeof(conf_empty), + "DataDirectory %s\n", + get_fname(NULL)); + char conf_include[1000]; + tor_snprintf(conf_include, sizeof(conf_include), "%%include %s", path); + + // test with defaults-torrc without include and torrc with include + int ret = options_init_from_string(conf_empty, conf_include, + CMD_RUN_UNITTESTS, NULL, &errmsg); + tt_int_op(ret, OP_EQ, 0); + + const or_options_t *options = get_options(); + tt_int_op(options->IncludeUsed, OP_EQ, 1); + + done: + tor_free(errmsg); + tor_free(dir); +} + +static void +test_config_include_flag_defaults_only(void *data) +{ + (void)data; + + char *errmsg = NULL; + char *dir = tor_strdup(get_fname("test_include_flag_defaults_only")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char path[PATH_MAX+1]; + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", dir, "dummy"); + tt_int_op(write_str_to_file(path, "\n", 0), OP_EQ, 0); + + char conf_empty[1000]; + tor_snprintf(conf_empty, sizeof(conf_empty), + "DataDirectory %s\n", + get_fname(NULL)); + char conf_include[1000]; + tor_snprintf(conf_include, sizeof(conf_include), "%%include %s", path); + + // test with defaults-torrc with include and torrc without include + int ret = options_init_from_string(conf_include, conf_empty, + CMD_RUN_UNITTESTS, NULL, &errmsg); + tt_int_op(ret, OP_EQ, 0); + + const or_options_t *options = get_options(); + tt_int_op(options->IncludeUsed, OP_EQ, 0); + + done: + tor_free(errmsg); + tor_free(dir); +} + #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } @@ -4912,10 +5379,23 @@ struct testcase_t config_tests[] = { CONFIG_TEST(fix_my_family, 0), CONFIG_TEST(directory_fetch, 0), CONFIG_TEST(port_cfg_line_extract_addrport, 0), - CONFIG_TEST(parse_port_config__listenaddress, 0), CONFIG_TEST(parse_port_config__ports__no_ports_given, 0), CONFIG_TEST(parse_port_config__ports__server_options, 0), CONFIG_TEST(parse_port_config__ports__ports_given, 0), + CONFIG_TEST(parse_log_severity, 0), + CONFIG_TEST(include_limit, 0), + CONFIG_TEST(include_does_not_exist, 0), + CONFIG_TEST(include_error_in_included_file, 0), + CONFIG_TEST(include_empty_file_folder, 0), + CONFIG_TEST(include_recursion_before_after, 0), + CONFIG_TEST(include_recursion_after_only, 0), + CONFIG_TEST(include_folder_order, 0), + CONFIG_TEST(include_path_syntax, 0), + CONFIG_TEST(include_not_processed, 0), + CONFIG_TEST(include_has_include, 0), + CONFIG_TEST(include_flag_both_without, TT_FORK), + CONFIG_TEST(include_flag_torrc_only, TT_FORK), + CONFIG_TEST(include_flag_defaults_only, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_connection.c b/src/test/test_connection.c index d394fc9852..7e5193b203 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -10,6 +10,7 @@ #include "test.h" #include "connection.h" +#include "hs_common.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -264,14 +265,10 @@ test_conn_get_rend_setup(const struct testcase_t *tc) rend_cache_init(); - /* TODO: use directory_initiate_command_rend() to do this - maybe? */ - conn->rend_data = tor_malloc_zero(sizeof(rend_data_t)); + /* TODO: use directory_initiate_request() to do this - maybe? */ tor_assert(strlen(TEST_CONN_REND_ADDR) == REND_SERVICE_ID_LEN_BASE32); - memcpy(conn->rend_data->onion_address, - TEST_CONN_REND_ADDR, - REND_SERVICE_ID_LEN_BASE32+1); - conn->rend_data->hsdirs_fp = smartlist_new(); - + conn->rend_data = rend_data_client_create(TEST_CONN_REND_ADDR, NULL, NULL, + REND_NO_AUTH); assert_connection_ok(&conn->base_, time(NULL)); return conn; @@ -551,7 +548,8 @@ test_conn_get_rend(void *arg) tt_assert(connection_get_by_type_state_rendquery( conn->base_.type, conn->base_.state, - conn->rend_data->onion_address) + rend_data_get_address( + conn->rend_data)) == TO_CONN(conn)); tt_assert(connection_get_by_type_state_rendquery( TEST_CONN_TYPE, diff --git a/src/test/test_conscache.c b/src/test/test_conscache.c new file mode 100644 index 0000000000..aee1ba8a06 --- /dev/null +++ b/src/test/test_conscache.c @@ -0,0 +1,340 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "config.h" +#include "conscache.h" +#include "test.h" + +#ifdef HAVE_UTIME_H +#include <utime.h> +#endif + +static void +test_conscache_open_failure(void *arg) +{ + (void) arg; + /* Try opening a directory that doesn't exist and which we shouldn't be + * able to create. */ + consensus_cache_t *cache = consensus_cache_open("a/b/c/d/e/f/g", 128); + tt_ptr_op(cache, OP_EQ, NULL); + + done: + ; +} + +static void +test_conscache_simple_usage(void *arg) +{ + (void)arg; + consensus_cache_entry_t *ent = NULL, *ent2 = NULL; + + /* Make a temporary datadir for these tests */ + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + check_private_dir(ddir_fname, CPD_CREATE, NULL); + consensus_cache_t *cache = consensus_cache_open("cons", 128); + + tt_assert(cache); + + /* Create object; make sure it exists. */ + config_line_t *labels = NULL; + config_line_append(&labels, "Hello", "world"); + config_line_append(&labels, "Adios", "planetas"); + ent = consensus_cache_add(cache, + labels, (const uint8_t *)"A\0B\0C", 5); + config_free_lines(labels); + labels = NULL; + tt_assert(ent); + + /* Make a second object */ + config_line_append(&labels, "Hello", "mundo"); + config_line_append(&labels, "Adios", "planets"); + ent2 = consensus_cache_add(cache, + labels, (const uint8_t *)"xyzzy", 5); + config_free_lines(labels); + labels = NULL; + tt_assert(ent2); + tt_assert(! consensus_cache_entry_is_mapped(ent2)); + consensus_cache_entry_decref(ent2); + ent2 = NULL; + + /* Check get_value */ + tt_ptr_op(NULL, OP_EQ, consensus_cache_entry_get_value(ent, "hebbo")); + tt_str_op("world", OP_EQ, consensus_cache_entry_get_value(ent, "Hello")); + + /* Check find_first */ + ent2 = consensus_cache_find_first(cache, "Hello", "world!"); + tt_ptr_op(ent2, OP_EQ, NULL); + ent2 = consensus_cache_find_first(cache, "Hello", "world"); + tt_ptr_op(ent2, OP_EQ, ent); + ent2 = consensus_cache_find_first(cache, "Hello", "mundo"); + tt_ptr_op(ent2, OP_NE, ent); + + tt_assert(! consensus_cache_entry_is_mapped(ent)); + + /* Check get_body */ + const uint8_t *bp = NULL; + size_t sz = 0; + int r = consensus_cache_entry_get_body(ent, &bp, &sz); + tt_int_op(r, OP_EQ, 0); + tt_u64_op(sz, OP_EQ, 5); + tt_mem_op(bp, OP_EQ, "A\0B\0C", 5); + tt_assert(consensus_cache_entry_is_mapped(ent)); + + /* Free and re-create the cache, to rescan the directory. */ + consensus_cache_free(cache); + consensus_cache_entry_decref(ent); + cache = consensus_cache_open("cons", 128); + + /* Make sure the entry is still there */ + ent = consensus_cache_find_first(cache, "Hello", "mundo"); + tt_assert(ent); + ent2 = consensus_cache_find_first(cache, "Adios", "planets"); + tt_ptr_op(ent, OP_EQ, ent2); + consensus_cache_entry_incref(ent); + tt_assert(! consensus_cache_entry_is_mapped(ent)); + r = consensus_cache_entry_get_body(ent, &bp, &sz); + tt_int_op(r, OP_EQ, 0); + tt_u64_op(sz, OP_EQ, 5); + tt_mem_op(bp, OP_EQ, "xyzzy", 5); + tt_assert(consensus_cache_entry_is_mapped(ent)); + + /* There should be two entries total. */ + smartlist_t *entries = smartlist_new(); + consensus_cache_find_all(entries, cache, NULL, NULL); + int n = smartlist_len(entries); + smartlist_free(entries); + tt_int_op(n, OP_EQ, 2); + + done: + consensus_cache_entry_decref(ent); + tor_free(ddir_fname); + consensus_cache_free(cache); +} + +static void +test_conscache_cleanup(void *arg) +{ + (void)arg; + const int N = 20; + consensus_cache_entry_t **ents = + tor_calloc(N, sizeof(consensus_cache_entry_t*)); + + /* Make a temporary datadir for these tests */ + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + check_private_dir(ddir_fname, CPD_CREATE, NULL); + consensus_cache_t *cache = consensus_cache_open("cons", 128); + + tt_assert(cache); + + /* Create a bunch of entries. */ + int i; + for (i = 0; i < N; ++i) { + config_line_t *labels = NULL; + char num[8]; + tor_snprintf(num, sizeof(num), "%d", i); + config_line_append(&labels, "test-id", "cleanup"); + config_line_append(&labels, "index", num); + size_t bodylen = i * 3; + uint8_t *body = tor_malloc(bodylen); + memset(body, i, bodylen); + ents[i] = consensus_cache_add(cache, labels, body, bodylen); + tor_free(body); + config_free_lines(labels); + tt_assert(ents[i]); + /* We're still holding a reference to each entry at this point. */ + } + + /* Page all of the entries into RAM */ + for (i = 0; i < N; ++i) { + const uint8_t *bp; + size_t sz; + tt_assert(! consensus_cache_entry_is_mapped(ents[i])); + consensus_cache_entry_get_body(ents[i], &bp, &sz); + tt_assert(consensus_cache_entry_is_mapped(ents[i])); + } + + /* Mark some of the entries as deletable. */ + for (i = 7; i < N; i += 7) { + consensus_cache_entry_mark_for_removal(ents[i]); + tt_assert(consensus_cache_entry_is_mapped(ents[i])); + } + + /* Mark some of the entries as aggressively unpaged. */ + for (i = 3; i < N; i += 3) { + consensus_cache_entry_mark_for_aggressive_release(ents[i]); + tt_assert(consensus_cache_entry_is_mapped(ents[i])); + } + + /* Incref some of the entries again */ + for (i = 0; i < N; i += 2) { + consensus_cache_entry_incref(ents[i]); + } + + /* Now we're going to decref everything. We do so at a specific time. I'm + * picking the moment when I was writing this test, at 2017-04-05 12:16:48 + * UTC. */ + const time_t example_time = 1491394608; + update_approx_time(example_time); + for (i = 0; i < N; ++i) { + consensus_cache_entry_decref(ents[i]); + if (i % 2) { + ents[i] = NULL; /* We're no longer holding any reference here. */ + } + } + + /* At this point, the aggressively-released items with refcount 1 should + * be unmapped. Nothing should be deleted. */ + consensus_cache_entry_t *e_tmp; + e_tmp = consensus_cache_find_first(cache, "index", "3"); + tt_assert(e_tmp); + tt_assert(! consensus_cache_entry_is_mapped(e_tmp)); + e_tmp = consensus_cache_find_first(cache, "index", "5"); + tt_assert(e_tmp); + tt_assert(consensus_cache_entry_is_mapped(e_tmp)); + e_tmp = consensus_cache_find_first(cache, "index", "6"); + tt_assert(e_tmp); + tt_assert(consensus_cache_entry_is_mapped(e_tmp)); + e_tmp = consensus_cache_find_first(cache, "index", "7"); + tt_assert(e_tmp == NULL); // not found because pending deletion. + + /* Delete the pending-deletion items. */ + consensus_cache_delete_pending(cache, 0); + { + smartlist_t *entries = smartlist_new(); + consensus_cache_find_all(entries, cache, NULL, NULL); + int n = smartlist_len(entries); + smartlist_free(entries); + tt_int_op(n, OP_EQ, 20 - 2); /* 1 entry was deleted; 1 is not-found. */ + } + e_tmp = consensus_cache_find_first(cache, "index", "7"); // refcnt == 1... + tt_assert(e_tmp == NULL); // so deleted. + e_tmp = consensus_cache_find_first(cache, "index", "14"); // refcnt == 2 + tt_assert(e_tmp == NULL); // not deleted; but not found. + + /* Now do lazy unmapping. */ + // should do nothing. + consensus_cache_unmap_lazy(cache, example_time - 10); + e_tmp = consensus_cache_find_first(cache, "index", "11"); + tt_assert(e_tmp); + tt_assert(consensus_cache_entry_is_mapped(e_tmp)); + // should actually unmap + consensus_cache_unmap_lazy(cache, example_time + 10); + e_tmp = consensus_cache_find_first(cache, "index", "11"); + tt_assert(e_tmp); + tt_assert(! consensus_cache_entry_is_mapped(e_tmp)); + // This one will still be mapped, since it has a reference. + e_tmp = consensus_cache_find_first(cache, "index", "16"); + tt_assert(e_tmp); + tt_assert(consensus_cache_entry_is_mapped(e_tmp)); + + for (i = 0; i < N; ++i) { + consensus_cache_entry_decref(ents[i]); + ents[i] = NULL; + } + + /* Free and re-create the cache, to rescan the directory. Make sure the + * deleted thing is still deleted, along with the other deleted thing. */ + consensus_cache_free(cache); + cache = consensus_cache_open("cons", 128); + { + smartlist_t *entries = smartlist_new(); + consensus_cache_find_all(entries, cache, NULL, NULL); + int n = smartlist_len(entries); + smartlist_free(entries); + tt_int_op(n, OP_EQ, 18); + } + + done: + for (i = 0; i < N; ++i) { + consensus_cache_entry_decref(ents[i]); + } + tor_free(ents); + tor_free(ddir_fname); + consensus_cache_free(cache); +} + +static void +test_conscache_filter(void *arg) +{ + (void)arg; + const int N = 30; + smartlist_t *lst = NULL; + + /* Make a temporary datadir for these tests */ + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + check_private_dir(ddir_fname, CPD_CREATE, NULL); + consensus_cache_t *cache = consensus_cache_open("cons", 128); + + tt_assert(cache); + + /* Create a bunch of entries with different labels */ + int i; + for (i = 0; i < N; ++i) { + config_line_t *labels = NULL; + char num[8]; + tor_snprintf(num, sizeof(num), "%d", i); + config_line_append(&labels, "test-id", "filter"); + config_line_append(&labels, "index", num); + tor_snprintf(num, sizeof(num), "%d", i % 3); + config_line_append(&labels, "mod3", num); + tor_snprintf(num, sizeof(num), "%d", i % 5); + config_line_append(&labels, "mod5", num); + + size_t bodylen = i * 3; + uint8_t *body = tor_malloc(bodylen); + memset(body, i, bodylen); + consensus_cache_entry_t *ent = + consensus_cache_add(cache, labels, body, bodylen); + tor_free(body); + config_free_lines(labels); + tt_assert(ent); + consensus_cache_entry_decref(ent); + } + + lst = smartlist_new(); + /* Find nothing. */ + consensus_cache_find_all(lst, cache, "mod5", "5"); + tt_int_op(smartlist_len(lst), OP_EQ, 0); + /* Find everything. */ + consensus_cache_find_all(lst, cache, "test-id", "filter"); + tt_int_op(smartlist_len(lst), OP_EQ, N); + + /* Now filter to find the entries that have i%3 == 1 */ + consensus_cache_filter_list(lst, "mod3", "1"); + tt_int_op(smartlist_len(lst), OP_EQ, 10); + /* Now filter to find the entries that also have i%5 == 3 */ + consensus_cache_filter_list(lst, "mod5", "3"); + tt_int_op(smartlist_len(lst), OP_EQ, 2); + /* So now we have those entries for which i%15 == 13. */ + + consensus_cache_entry_t *ent1 = smartlist_get(lst, 0); + consensus_cache_entry_t *ent2 = smartlist_get(lst, 1); + const char *idx1 = consensus_cache_entry_get_value(ent1, "index"); + const char *idx2 = consensus_cache_entry_get_value(ent2, "index"); + tt_assert( (!strcmp(idx1, "28") && !strcmp(idx2, "13")) || + (!strcmp(idx1, "13") && !strcmp(idx2, "28")) ); + + done: + tor_free(ddir_fname); + consensus_cache_free(cache); + smartlist_free(lst); +} + +#define ENT(name) \ + { #name, test_conscache_ ## name, TT_FORK, NULL, NULL } + +struct testcase_t conscache_tests[] = { + ENT(open_failure), + ENT(simple_usage), + ENT(cleanup), + ENT(filter), + END_OF_TESTCASES +}; + diff --git a/src/test/test_consdiff.c b/src/test/test_consdiff.c new file mode 100644 index 0000000000..7cf8d6ba2b --- /dev/null +++ b/src/test/test_consdiff.c @@ -0,0 +1,1184 @@ +/* Copyright (c) 2014, Daniel Martà + * Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONSDIFF_PRIVATE + +#include "or.h" +#include "test.h" + +#include "consdiff.h" +#include "memarea.h" +#include "log_test_helpers.h" + +#define tt_str_eq_line(a,b) \ + tt_assert(line_str_eq((b),(a))) + +static void +test_consdiff_smartlist_slice(void *arg) +{ + smartlist_t *sl = smartlist_new(); + smartlist_slice_t *sls; + + /* Create a regular smartlist. */ + (void)arg; + smartlist_add(sl, (void*)1); + smartlist_add(sl, (void*)2); + smartlist_add(sl, (void*)3); + smartlist_add(sl, (void*)4); + smartlist_add(sl, (void*)5); + + /* See if the slice was done correctly. */ + sls = smartlist_slice(sl, 2, 5); + tt_ptr_op(sl, OP_EQ, sls->list); + tt_ptr_op((void*)3, OP_EQ, smartlist_get(sls->list, sls->offset)); + tt_ptr_op((void*)5, OP_EQ, + smartlist_get(sls->list, sls->offset + (sls->len-1))); + tor_free(sls); + + /* See that using -1 as the end does get to the last element. */ + sls = smartlist_slice(sl, 2, -1); + tt_ptr_op(sl, OP_EQ, sls->list); + tt_ptr_op((void*)3, OP_EQ, smartlist_get(sls->list, sls->offset)); + tt_ptr_op((void*)5, OP_EQ, + smartlist_get(sls->list, sls->offset + (sls->len-1))); + + done: + tor_free(sls); + smartlist_free(sl); +} + +static void +test_consdiff_smartlist_slice_string_pos(void *arg) +{ + smartlist_t *sl = smartlist_new(); + smartlist_slice_t *sls; + memarea_t *area = memarea_new(); + + /* Create a regular smartlist. */ + (void)arg; + consensus_split_lines(sl, "a\nd\nc\na\nb\n", area); + + /* See that smartlist_slice_string_pos respects the bounds of the slice. */ + sls = smartlist_slice(sl, 2, 5); + cdline_t a_line = { "a", 1 }; + tt_int_op(3, OP_EQ, smartlist_slice_string_pos(sls, &a_line)); + cdline_t d_line = { "d", 1 }; + tt_int_op(-1, OP_EQ, smartlist_slice_string_pos(sls, &d_line)); + + done: + tor_free(sls); + smartlist_free(sl); + memarea_drop_all(area); +} + +static void +test_consdiff_lcs_lengths(void *arg) +{ + smartlist_t *sl1 = smartlist_new(); + smartlist_t *sl2 = smartlist_new(); + smartlist_slice_t *sls1, *sls2; + int *lengths1, *lengths2; + memarea_t *area = memarea_new(); + + /* Expected lcs lengths in regular and reverse order. */ + int e_lengths1[] = { 0, 1, 2, 3, 3, 4 }; + int e_lengths2[] = { 0, 1, 1, 2, 3, 4 }; + + (void)arg; + consensus_split_lines(sl1, "a\nb\nc\nd\ne\n", area); + consensus_split_lines(sl2, "a\nc\nd\ni\ne\n", area); + + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + + lengths1 = lcs_lengths(sls1, sls2, 1); + lengths2 = lcs_lengths(sls1, sls2, -1); + tt_mem_op(e_lengths1, OP_EQ, lengths1, sizeof(int) * 6); + tt_mem_op(e_lengths2, OP_EQ, lengths2, sizeof(int) * 6); + + done: + tor_free(lengths1); + tor_free(lengths2); + tor_free(sls1); + tor_free(sls2); + smartlist_free(sl1); + smartlist_free(sl2); + memarea_drop_all(area); +} + +static void +test_consdiff_trim_slices(void *arg) +{ + smartlist_t *sl1 = smartlist_new(); + smartlist_t *sl2 = smartlist_new(); + smartlist_t *sl3 = smartlist_new(); + smartlist_t *sl4 = smartlist_new(); + smartlist_slice_t *sls1, *sls2, *sls3, *sls4; + memarea_t *area = memarea_new(); + + (void)arg; + consensus_split_lines(sl1, "a\nb\nb\nb\nd\n", area); + consensus_split_lines(sl2, "a\nc\nc\nc\nd\n", area); + consensus_split_lines(sl3, "a\nb\nb\nb\na\n", area); + consensus_split_lines(sl4, "c\nb\nb\nb\nc\n", area); + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + sls3 = smartlist_slice(sl3, 0, -1); + sls4 = smartlist_slice(sl4, 0, -1); + + /* They should be trimmed by one line at each end. */ + tt_int_op(5, OP_EQ, sls1->len); + tt_int_op(5, OP_EQ, sls2->len); + trim_slices(sls1, sls2); + tt_int_op(3, OP_EQ, sls1->len); + tt_int_op(3, OP_EQ, sls2->len); + + /* They should not be trimmed at all. */ + tt_int_op(5, OP_EQ, sls3->len); + tt_int_op(5, OP_EQ, sls4->len); + trim_slices(sls3, sls4); + tt_int_op(5, OP_EQ, sls3->len); + tt_int_op(5, OP_EQ, sls4->len); + + done: + tor_free(sls1); + tor_free(sls2); + tor_free(sls3); + tor_free(sls4); + smartlist_free(sl1); + smartlist_free(sl2); + smartlist_free(sl3); + smartlist_free(sl4); + memarea_drop_all(area); +} + +static void +test_consdiff_set_changed(void *arg) +{ + smartlist_t *sl1 = smartlist_new(); + smartlist_t *sl2 = smartlist_new(); + bitarray_t *changed1 = bitarray_init_zero(4); + bitarray_t *changed2 = bitarray_init_zero(4); + smartlist_slice_t *sls1, *sls2; + memarea_t *area = memarea_new(); + + (void)arg; + consensus_split_lines(sl1, "a\nb\na\na\n", area); + consensus_split_lines(sl2, "a\na\na\na\n", area); + + /* Length of sls1 is 0. */ + sls1 = smartlist_slice(sl1, 0, 0); + sls2 = smartlist_slice(sl2, 1, 3); + set_changed(changed1, changed2, sls1, sls2); + + /* The former is not changed, the latter changes all of its elements. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(!bitarray_is_set(changed1, 1)); + tt_assert(!bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(bitarray_is_set(changed2, 1)); + tt_assert(bitarray_is_set(changed2, 2)); + tt_assert(!bitarray_is_set(changed2, 3)); + bitarray_clear(changed2, 1); + bitarray_clear(changed2, 2); + + /* Length of sls1 is 1 and its element is in sls2. */ + tor_free(sls1); + sls1 = smartlist_slice(sl1, 0, 1); + set_changed(changed1, changed2, sls1, sls2); + + /* The latter changes all elements but the (first) common one. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(!bitarray_is_set(changed1, 1)); + tt_assert(!bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(!bitarray_is_set(changed2, 1)); + tt_assert(bitarray_is_set(changed2, 2)); + tt_assert(!bitarray_is_set(changed2, 3)); + bitarray_clear(changed2, 2); + + /* Length of sls1 is 1 and its element is not in sls2. */ + tor_free(sls1); + sls1 = smartlist_slice(sl1, 1, 2); + set_changed(changed1, changed2, sls1, sls2); + + /* The former changes its element, the latter changes all elements. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(bitarray_is_set(changed1, 1)); + tt_assert(!bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(bitarray_is_set(changed2, 1)); + tt_assert(bitarray_is_set(changed2, 2)); + tt_assert(!bitarray_is_set(changed2, 3)); + + done: + bitarray_free(changed1); + bitarray_free(changed2); + smartlist_free(sl1); + smartlist_free(sl2); + tor_free(sls1); + tor_free(sls2); + memarea_drop_all(area); +} + +static void +test_consdiff_calc_changes(void *arg) +{ + smartlist_t *sl1 = smartlist_new(); + smartlist_t *sl2 = smartlist_new(); + smartlist_slice_t *sls1, *sls2; + bitarray_t *changed1 = bitarray_init_zero(4); + bitarray_t *changed2 = bitarray_init_zero(4); + memarea_t *area = memarea_new(); + + (void)arg; + consensus_split_lines(sl1, "a\na\na\na\n", area); + consensus_split_lines(sl2, "a\na\na\na\n", area); + + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + calc_changes(sls1, sls2, changed1, changed2); + + /* Nothing should be set to changed. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(!bitarray_is_set(changed1, 1)); + tt_assert(!bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(!bitarray_is_set(changed2, 1)); + tt_assert(!bitarray_is_set(changed2, 2)); + tt_assert(!bitarray_is_set(changed2, 3)); + + smartlist_clear(sl2); + consensus_split_lines(sl2, "a\nb\na\nb\n", area); + tor_free(sls1); + tor_free(sls2); + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + calc_changes(sls1, sls2, changed1, changed2); + + /* Two elements are changed. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(bitarray_is_set(changed1, 1)); + tt_assert(bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + bitarray_clear(changed1, 1); + bitarray_clear(changed1, 2); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(bitarray_is_set(changed2, 1)); + tt_assert(!bitarray_is_set(changed2, 2)); + tt_assert(bitarray_is_set(changed2, 3)); + bitarray_clear(changed1, 1); + bitarray_clear(changed1, 3); + + smartlist_clear(sl2); + consensus_split_lines(sl2, "b\nb\nb\nb\n", area); + tor_free(sls1); + tor_free(sls2); + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + calc_changes(sls1, sls2, changed1, changed2); + + /* All elements are changed. */ + tt_assert(bitarray_is_set(changed1, 0)); + tt_assert(bitarray_is_set(changed1, 1)); + tt_assert(bitarray_is_set(changed1, 2)); + tt_assert(bitarray_is_set(changed1, 3)); + + tt_assert(bitarray_is_set(changed2, 0)); + tt_assert(bitarray_is_set(changed2, 1)); + tt_assert(bitarray_is_set(changed2, 2)); + tt_assert(bitarray_is_set(changed2, 3)); + + done: + bitarray_free(changed1); + bitarray_free(changed2); + smartlist_free(sl1); + smartlist_free(sl2); + tor_free(sls1); + tor_free(sls2); + memarea_drop_all(area); +} + +static void +test_consdiff_get_id_hash(void *arg) +{ + (void)arg; + + cdline_t line1 = { "r name", 6 }; + cdline_t line2 = { "r name _hash_isnt_base64 etc", 28 }; + cdline_t line3 = { "r name hash+valid+base64 etc", 28 }; + cdline_t tmp; + + /* No hash. */ + tt_int_op(-1, OP_EQ, get_id_hash(&line1, &tmp)); + /* The hash contains characters that are not base64. */ + tt_int_op(-1, OP_EQ, get_id_hash(&line2, &tmp)); + + /* valid hash. */ + tt_int_op(0, OP_EQ, get_id_hash(&line3, &tmp)); + tt_ptr_op(tmp.s, OP_EQ, line3.s + 7); + tt_uint_op(tmp.len, OP_EQ, line3.len - 11); + + done: + ; +} + +static void +test_consdiff_is_valid_router_entry(void *arg) +{ + /* Doesn't start with "r ". */ + (void)arg; + cdline_t line0 = { "foo", 3 }; + tt_int_op(0, OP_EQ, is_valid_router_entry(&line0)); + + /* These are already tested with get_id_hash, but make sure it's run + * properly. */ + + cdline_t line1 = { "r name", 6 }; + cdline_t line2 = { "r name _hash_isnt_base64 etc", 28 }; + cdline_t line3 = { "r name hash+valid+base64 etc", 28 }; + tt_int_op(0, OP_EQ, is_valid_router_entry(&line1)); + tt_int_op(0, OP_EQ, is_valid_router_entry(&line2)); + tt_int_op(1, OP_EQ, is_valid_router_entry(&line3)); + + done: + ; +} + +static void +test_consdiff_next_router(void *arg) +{ + smartlist_t *sl = smartlist_new(); + memarea_t *area = memarea_new(); + (void)arg; + smartlist_add_linecpy(sl, area, "foo"); + smartlist_add_linecpy(sl, area, + "r name hash+longer+than+27+chars+and+valid+base64 etc"); + smartlist_add_linecpy(sl, area, "foo"); + smartlist_add_linecpy(sl, area, "foo"); + smartlist_add_linecpy(sl, area, + "r name hash+longer+than+27+chars+and+valid+base64 etc"); + smartlist_add_linecpy(sl, area, "foo"); + + /* Not currently on a router entry line, finding the next one. */ + tt_int_op(1, OP_EQ, next_router(sl, 0)); + tt_int_op(4, OP_EQ, next_router(sl, 2)); + + /* Already at the beginning of a router entry line, ignore it. */ + tt_int_op(4, OP_EQ, next_router(sl, 1)); + + /* There are no more router entries, so return the line after the last. */ + tt_int_op(6, OP_EQ, next_router(sl, 4)); + tt_int_op(6, OP_EQ, next_router(sl, 5)); + + done: + smartlist_free(sl); + memarea_drop_all(area); +} + +static int +base64cmp_wrapper(const char *a, const char *b) +{ + cdline_t aa = { a, a ? (uint32_t) strlen(a) : 0 }; + cdline_t bb = { b, b ? (uint32_t) strlen(b) : 0 }; + return base64cmp(&aa, &bb); +} + +static void +test_consdiff_base64cmp(void *arg) +{ + /* NULL arguments. */ + (void)arg; + tt_int_op(0, OP_EQ, base64cmp_wrapper(NULL, NULL)); + tt_int_op(-1, OP_EQ, base64cmp_wrapper(NULL, "foo")); + tt_int_op(1, OP_EQ, base64cmp_wrapper("bar", NULL)); + + /* Nil base64 values. */ + tt_int_op(0, OP_EQ, base64cmp_wrapper("", "")); + tt_int_op(0, OP_EQ, base64cmp_wrapper("_", "&")); + + /* Exact same valid strings. */ + tt_int_op(0, OP_EQ, base64cmp_wrapper("abcABC/+", "abcABC/+")); + /* Both end with an invalid base64 char other than '\0'. */ + tt_int_op(0, OP_EQ, base64cmp_wrapper("abcABC/+ ", "abcABC/+ ")); + /* Only one ends with an invalid base64 char other than '\0'. */ + tt_int_op(-1, OP_EQ, base64cmp_wrapper("abcABC/+ ", "abcABC/+a")); + + /* Comparisons that would return differently with strcmp(). */ + tt_int_op(strcmp("/foo", "Afoo"), OP_LT, 0); + tt_int_op(base64cmp_wrapper("/foo", "Afoo"), OP_GT, 0); + tt_int_op(strcmp("Afoo", "0foo"), OP_GT, 0); + tt_int_op(base64cmp_wrapper("Afoo", "0foo"), OP_LT, 0); + + /* Comparisons that would return the same as with strcmp(). */ + tt_int_op(strcmp("afoo", "Afoo"), OP_GT, 0); + tt_int_op(base64cmp_wrapper("afoo", "Afoo"), OP_GT, 0); + + /* Different lengths */ + tt_int_op(base64cmp_wrapper("afoo", "afooo"), OP_LT, 0); + tt_int_op(base64cmp_wrapper("afooo", "afoo"), OP_GT, 0); + + done: + ; +} + +static void +test_consdiff_gen_ed_diff(void *arg) +{ + smartlist_t *cons1=NULL, *cons2=NULL, *diff=NULL; + int i; + memarea_t *area = memarea_new(); + setup_capture_of_logs(LOG_WARN); + + (void)arg; + cons1 = smartlist_new(); + cons2 = smartlist_new(); + + /* Identity hashes are not sorted properly, return NULL. */ + smartlist_add_linecpy(cons1, area, "r name bbbbbbbbbbbbbbbbbbbbbbbbbbb etc"); + smartlist_add_linecpy(cons1, area, "foo"); + smartlist_add_linecpy(cons1, area, "r name aaaaaaaaaaaaaaaaaaaaaaaaaaa etc"); + smartlist_add_linecpy(cons1, area, "bar"); + + smartlist_add_linecpy(cons2, area, "r name aaaaaaaaaaaaaaaaaaaaaaaaaaa etc"); + smartlist_add_linecpy(cons2, area, "foo"); + smartlist_add_linecpy(cons2, area, "r name ccccccccccccccccccccccccccc etc"); + smartlist_add_linecpy(cons2, area, "bar"); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the base consensus doesn't have its router entries sorted " + "properly."); + + /* Same, but now with the second consensus. */ + mock_clean_saved_logs(); + diff = gen_ed_diff(cons2, cons1, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the target consensus doesn't have its router entries sorted " + "properly."); + + /* Same as the two above, but with the reversed thing immediately after a + match. (The code handles this differently) */ + smartlist_del(cons1, 0); + smartlist_add_linecpy(cons1, area, "r name aaaaaaaaaaaaaaaaaaaaaaaaaaa etc"); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the base consensus doesn't have its router entries sorted " + "properly."); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons2, cons1, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the target consensus doesn't have its router entries sorted " + "properly."); + + /* Identity hashes are repeated, return NULL. */ + smartlist_clear(cons1); + + smartlist_add_linecpy(cons1, area, "r name bbbbbbbbbbbbbbbbbbbbbbbbbbb etc"); + smartlist_add_linecpy(cons1, area, "foo"); + smartlist_add_linecpy(cons1, area, "r name bbbbbbbbbbbbbbbbbbbbbbbbbbb etc"); + smartlist_add_linecpy(cons1, area, "bar"); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the base consensus doesn't have its router entries sorted " + "properly."); + + /* We have to add a line that is just a dot, return NULL. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + + smartlist_add_linecpy(cons1, area, "foo1"); + smartlist_add_linecpy(cons1, area, "foo2"); + + smartlist_add_linecpy(cons2, area, "foo1"); + smartlist_add_linecpy(cons2, area, "."); + smartlist_add_linecpy(cons2, area, "foo2"); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Cannot generate consensus diff " + "because one of the lines to be added is \".\"."); + +#define MAX_LINE_COUNT (10000) + /* Too many lines to be fed to the quadratic-time function. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + + for (i=0; i < MAX_LINE_COUNT; ++i) smartlist_add_linecpy(cons1, area, "a"); + for (i=0; i < MAX_LINE_COUNT; ++i) smartlist_add_linecpy(cons1, area, "b"); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons1, cons2, area); + + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because we found too few common router ids."); + + /* We have dot lines, but they don't interfere with the script format. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + + smartlist_add_linecpy(cons1, area, "foo1"); + smartlist_add_linecpy(cons1, area, "."); + smartlist_add_linecpy(cons1, area, "."); + smartlist_add_linecpy(cons1, area, "foo2"); + + smartlist_add_linecpy(cons2, area, "foo1"); + smartlist_add_linecpy(cons2, area, "."); + smartlist_add_linecpy(cons2, area, "foo2"); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + smartlist_free(diff); + + /* Empty diff tests. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(0, OP_EQ, smartlist_len(diff)); + smartlist_free(diff); + + smartlist_add_linecpy(cons1, area, "foo"); + smartlist_add_linecpy(cons1, area, "bar"); + + smartlist_add_linecpy(cons2, area, "foo"); + smartlist_add_linecpy(cons2, area, "bar"); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(0, OP_EQ, smartlist_len(diff)); + smartlist_free(diff); + + /* Everything is deleted. */ + smartlist_clear(cons2); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(1, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("1,2d", smartlist_get(diff, 0)); + + smartlist_free(diff); + + /* Everything is added. */ + diff = gen_ed_diff(cons2, cons1, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(4, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("0a", smartlist_get(diff, 0)); + tt_str_eq_line("foo", smartlist_get(diff, 1)); + tt_str_eq_line("bar", smartlist_get(diff, 2)); + tt_str_eq_line(".", smartlist_get(diff, 3)); + + smartlist_free(diff); + + /* Everything is changed. */ + smartlist_add_linecpy(cons2, area, "foo2"); + smartlist_add_linecpy(cons2, area, "bar2"); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(4, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("1,2c", smartlist_get(diff, 0)); + tt_str_eq_line("foo2", smartlist_get(diff, 1)); + tt_str_eq_line("bar2", smartlist_get(diff, 2)); + tt_str_eq_line(".", smartlist_get(diff, 3)); + + smartlist_free(diff); + + /* Test 'a', 'c' and 'd' together. See that it is done in reverse order. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + consensus_split_lines(cons1, "A\nB\nC\nD\nE\n", area); + consensus_split_lines(cons2, "A\nC\nO\nE\nU\n", area); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(7, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("5a", smartlist_get(diff, 0)); + tt_str_eq_line("U", smartlist_get(diff, 1)); + tt_str_eq_line(".", smartlist_get(diff, 2)); + tt_str_eq_line("4c", smartlist_get(diff, 3)); + tt_str_eq_line("O", smartlist_get(diff, 4)); + tt_str_eq_line(".", smartlist_get(diff, 5)); + tt_str_eq_line("2d", smartlist_get(diff, 6)); + + smartlist_free(diff); + + smartlist_clear(cons1); + smartlist_clear(cons2); + consensus_split_lines(cons1, "B\n", area); + consensus_split_lines(cons2, "A\nB\n", area); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(3, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("0a", smartlist_get(diff, 0)); + tt_str_eq_line("A", smartlist_get(diff, 1)); + tt_str_eq_line(".", smartlist_get(diff, 2)); + + /* TODO: small real use-cases, i.e. consensuses. */ + + done: + teardown_capture_of_logs(); + smartlist_free(cons1); + smartlist_free(cons2); + smartlist_free(diff); + memarea_drop_all(area); +} + +static void +test_consdiff_apply_ed_diff(void *arg) +{ + smartlist_t *cons1=NULL, *cons2=NULL, *diff=NULL; + memarea_t *area = memarea_new(); + (void)arg; + cons1 = smartlist_new(); + diff = smartlist_new(); + setup_capture_of_logs(LOG_WARN); + + consensus_split_lines(cons1, "A\nB\nC\nD\nE\n", area); + + /* Command without range. */ + smartlist_add_linecpy(diff, area, "a"); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + smartlist_clear(diff); + expect_single_log_msg_containing("an ed command was missing a line number"); + + /* Range without command. */ + smartlist_add_linecpy(diff, area, "1"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("a line with no ed command was found"); + + smartlist_clear(diff); + + /* Range without end. */ + smartlist_add_linecpy(diff, area, "1,"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command was missing a range " + "end line number."); + + smartlist_clear(diff); + + /* Incoherent ranges. */ + smartlist_add_linecpy(diff, area, "1,1"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an invalid range was found"); + + smartlist_clear(diff); + + smartlist_add_linecpy(diff, area, "3,2"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an invalid range was found"); + + smartlist_clear(diff); + + /* Unexpected range for add command. */ + smartlist_add_linecpy(diff, area, "1,2a"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("add lines after a range"); + + smartlist_clear(diff); + + /* $ for a non-delete command. */ + smartlist_add_linecpy(diff, area, "1,$c"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("it wanted to use $ with a command " + "other than delete"); + + smartlist_clear(diff); + + /* Script is not in reverse order. */ + smartlist_add_linecpy(diff, area, "1d"); + smartlist_add_linecpy(diff, area, "3d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("its commands are not properly sorted"); + + smartlist_clear(diff); + + /* Script contains unrecognised commands longer than one char. */ + smartlist_add_linecpy(diff, area, "1foo"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command longer than one char was " + "found"); + + smartlist_clear(diff); + + /* Script contains unrecognised commands. */ + smartlist_add_linecpy(diff, area, "1e"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an unrecognised ed command was found"); + + smartlist_clear(diff); + + /* Command that should be followed by at least one line and a ".", but + * isn't. */ + smartlist_add_linecpy(diff, area, "0a"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("it has an ed command that tries to " + "insert zero lines."); + + /* Now it is followed by a ".", but it inserts zero lines. */ + smartlist_add_linecpy(diff, area, "."); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("it has an ed command that tries to " + "insert zero lines."); + + smartlist_clear(diff); + + /* Now it it inserts something, but has no terminator. */ + smartlist_add_linecpy(diff, area, "0a"); + smartlist_add_linecpy(diff, area, "hello"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("lines to be inserted that don't end with " + "a \".\"."); + + smartlist_clear(diff); + + /* Ranges must be numeric only and cannot contain spaces. */ + smartlist_add_linecpy(diff, area, "0, 4d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command was missing a range " + "end line number."); + + smartlist_clear(diff); + + /* '+' is not a number. */ + smartlist_add_linecpy(diff, area, "+0,4d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command was missing a line number"); + + smartlist_clear(diff); + + /* range duplication */ + smartlist_add_linecpy(diff, area, "0,4d,5d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command longer than one char was " + "found"); + + smartlist_clear(diff); + + /* space before command */ + smartlist_add_linecpy(diff, area, "0,4 d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command longer than one char was " + "found"); + + smartlist_clear(diff); + + /* space inside number */ + smartlist_add_linecpy(diff, area, "0,4 5d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command longer than one char was " + "found"); + + smartlist_clear(diff); + + /* Test appending text, 'a'. */ + consensus_split_lines(diff, "3a\nU\nO\n.\n0a\nV\n.\n", area); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_NE, cons2); + tt_int_op(8, OP_EQ, smartlist_len(cons2)); + tt_str_eq_line("V", smartlist_get(cons2, 0)); + tt_str_eq_line("A", smartlist_get(cons2, 1)); + tt_str_eq_line("B", smartlist_get(cons2, 2)); + tt_str_eq_line("C", smartlist_get(cons2, 3)); + tt_str_eq_line("U", smartlist_get(cons2, 4)); + tt_str_eq_line("O", smartlist_get(cons2, 5)); + tt_str_eq_line("D", smartlist_get(cons2, 6)); + tt_str_eq_line("E", smartlist_get(cons2, 7)); + + smartlist_clear(diff); + smartlist_free(cons2); + + /* Test deleting text, 'd'. */ + consensus_split_lines(diff, "4d\n1,2d\n", area); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_NE, cons2); + tt_int_op(2, OP_EQ, smartlist_len(cons2)); + tt_str_eq_line("C", smartlist_get(cons2, 0)); + tt_str_eq_line("E", smartlist_get(cons2, 1)); + + smartlist_clear(diff); + smartlist_free(cons2); + + /* Test changing text, 'c'. */ + consensus_split_lines(diff, "4c\nT\nX\n.\n1,2c\nM\n.\n", area); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_NE, cons2); + tt_int_op(5, OP_EQ, smartlist_len(cons2)); + tt_str_eq_line("M", smartlist_get(cons2, 0)); + tt_str_eq_line("C", smartlist_get(cons2, 1)); + tt_str_eq_line("T", smartlist_get(cons2, 2)); + tt_str_eq_line("X", smartlist_get(cons2, 3)); + tt_str_eq_line("E", smartlist_get(cons2, 4)); + + smartlist_clear(diff); + smartlist_free(cons2); + + /* Test 'a', 'd' and 'c' together. */ + consensus_split_lines(diff, "4c\nT\nX\n.\n2d\n0a\nM\n.\n", area); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_NE, cons2); + tt_int_op(6, OP_EQ, smartlist_len(cons2)); + tt_str_eq_line("M", smartlist_get(cons2, 0)); + tt_str_eq_line("A", smartlist_get(cons2, 1)); + tt_str_eq_line("C", smartlist_get(cons2, 2)); + tt_str_eq_line("T", smartlist_get(cons2, 3)); + tt_str_eq_line("X", smartlist_get(cons2, 4)); + tt_str_eq_line("E", smartlist_get(cons2, 5)); + + done: + teardown_capture_of_logs(); + smartlist_free(cons1); + smartlist_free(cons2); + smartlist_free(diff); + memarea_drop_all(area); +} + +static void +test_consdiff_gen_diff(void *arg) +{ + char *cons1_str=NULL, *cons2_str=NULL; + smartlist_t *cons1=NULL, *cons2=NULL, *diff=NULL; + consensus_digest_t digests1, digests2; + memarea_t *area = memarea_new(); + (void)arg; + cons1 = smartlist_new(); + cons2 = smartlist_new(); + + /* Identity hashes are not sorted properly, return NULL. + * Already tested in gen_ed_diff, but see that a NULL ed diff also makes + * gen_diff return NULL. */ + cons1_str = tor_strdup( + "network-status-version foo\n" + "r name bbbbbbbbbbbbbbbbb etc\nfoo\n" + "r name aaaaaaaaaaaaaaaaa etc\nbar\n" + "directory-signature foo bar\nbar\n" + ); + cons2_str = tor_strdup( + "network-status-version foo\n" + "r name aaaaaaaaaaaaaaaaa etc\nfoo\n" + "r name ccccccccccccccccc etc\nbar\n" + "directory-signature foo bar\nbar\n" + ); + + tt_int_op(0, OP_EQ, + consensus_compute_digest_as_signed(cons1_str, &digests1)); + tt_int_op(0, OP_EQ, + consensus_compute_digest(cons2_str, &digests2)); + + consensus_split_lines(cons1, cons1_str, area); + consensus_split_lines(cons2, cons2_str, area); + + diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area); + tt_ptr_op(NULL, OP_EQ, diff); + + /* Check that the headers are done properly. */ + tor_free(cons1_str); + cons1_str = tor_strdup( + "network-status-version foo\n" + "r name ccccccccccccccccc etc\nfoo\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "directory-signature foo bar\nbar\n" + ); + tt_int_op(0, OP_EQ, + consensus_compute_digest_as_signed(cons1_str, &digests1)); + smartlist_clear(cons1); + consensus_split_lines(cons1, cons1_str, area); + diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(11, OP_EQ, smartlist_len(diff)); + tt_assert(line_str_eq(smartlist_get(diff, 0), + "network-status-diff-version 1")); + tt_assert(line_str_eq(smartlist_get(diff, 1), "hash " + "95D70F5A3CC65F920AA8B44C4563D7781A082674329661884E19E94B79D539C2 " + "7AFECEFA4599BA33D603653E3D2368F648DF4AC4723929B0F7CF39281596B0C1")); + tt_assert(line_str_eq(smartlist_get(diff, 2), "6,$d")); + tt_assert(line_str_eq(smartlist_get(diff, 3), "3,4c")); + tt_assert(line_str_eq(smartlist_get(diff, 4), "bar")); + tt_assert(line_str_eq(smartlist_get(diff, 5), + "directory-signature foo bar")); + tt_assert(line_str_eq(smartlist_get(diff, 6), + ".")); + tt_assert(line_str_eq(smartlist_get(diff, 7), "1a")); + tt_assert(line_str_eq(smartlist_get(diff, 8), + "r name aaaaaaaaaaaaaaaaa etc")); + tt_assert(line_str_eq(smartlist_get(diff, 9), "foo")); + tt_assert(line_str_eq(smartlist_get(diff, 10), ".")); + + /* TODO: small real use-cases, i.e. consensuses. */ + + done: + tor_free(cons1_str); + tor_free(cons2_str); + smartlist_free(cons1); + smartlist_free(cons2); + smartlist_free(diff); + memarea_drop_all(area); +} + +static void +test_consdiff_apply_diff(void *arg) +{ + smartlist_t *cons1=NULL, *diff=NULL; + char *cons1_str=NULL, *cons2 = NULL; + consensus_digest_t digests1; + (void)arg; + memarea_t *area = memarea_new(); + cons1 = smartlist_new(); + diff = smartlist_new(); + setup_capture_of_logs(LOG_INFO); + + cons1_str = tor_strdup( + "network-status-version foo\n" + "r name ccccccccccccccccc etc\nfoo\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "directory-signature foo bar\nbar\n" + ); + tt_int_op(0, OP_EQ, + consensus_compute_digest(cons1_str, &digests1)); + consensus_split_lines(cons1, cons1_str, area); + + /* diff doesn't have enough lines. */ + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("too short") + + /* first line doesn't match format-version string. */ + smartlist_add_linecpy(diff, area, "foo-bar"); + smartlist_add_linecpy(diff, area, "header-line"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("format is not known") + + /* The first word of the second header line is not "hash". */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "word a b"); + smartlist_add_linecpy(diff, area, "x"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("does not include the necessary digests") + + /* Wrong number of words after "hash". */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash a b c"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("does not include the necessary digests") + + /* base16 digests do not have the expected length. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash aaa bbb"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("includes base16-encoded digests of " + "incorrect size") + + /* base16 digests contain non-base16 characters. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + " ????????????????????????????????????????????????????????????????" + " ----------------------------------------------------------------"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("includes malformed digests") + + /* Invalid ed diff. + * As tested in apply_ed_diff, but check that apply_diff does return NULL if + * the ed diff can't be applied. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4" + /* sha256 of cons2. */ + " 635D34593020C08E5ECD865F9986E29D50028EFA62843766A8197AD228A7F6AA"); + smartlist_add_linecpy(diff, area, "foobar"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("because an ed command was missing a line " + "number") + + /* Base consensus doesn't match its digest as found in the diff. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* bogus sha256. */ + " 3333333333333333333333333333333333333333333333333333333333333333" + /* sha256 of cons2. */ + " 635D34593020C08E5ECD865F9986E29D50028EFA62843766A8197AD228A7F6AA"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_log_msg_containing("base consensus doesn't match the digest " + "as found"); + + /* Resulting consensus doesn't match its digest as found in the diff. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4" + /* bogus sha3. */ + " 3333333333333333333333333333333333333333333333333333333333333333"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_log_msg_containing("resulting consensus doesn't match the " + "digest as found"); + +#if 0 + /* XXXX No longer possible, since we aren't using the other algorithm. */ + /* Resulting consensus digest cannot be computed */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4" + /* bogus sha3. */ + " 3333333333333333333333333333333333333333333333333333333333333333"); + smartlist_add_linecpy(diff, area, "1,2d"); // remove starting line + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_log_msg_containing("Could not compute digests of the consensus " + "resulting from applying a consensus diff."); +#endif + + /* Very simple test, only to see that nothing errors. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4" + /* sha3 of cons2. */ + " 90A418881B2FCAB3D9E60EE02E4D666D56CFA38F8A3B7AA3E0ADBA530DDA9353"); + smartlist_add_linecpy(diff, area, "3c"); + smartlist_add_linecpy(diff, area, "sample"); + smartlist_add_linecpy(diff, area, "."); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_NE, cons2); + tt_str_op( + "network-status-version foo\n" + "r name ccccccccccccccccc etc\nsample\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "directory-signature foo bar\nbar\n", OP_EQ, + cons2); + tor_free(cons2); + + /* Check that lowercase letters in base16-encoded digests work too. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646d6cf563a41869d3b02e73254372ae3140046c5e7d83c9f71e54976af9b4" + /* sha3 of cons2. */ + " 90a418881b2fcab3d9e60ee02e4d666d56cfa38f8a3b7aa3e0adba530dda9353"); + smartlist_add_linecpy(diff, area, "3c"); + smartlist_add_linecpy(diff, area, "sample"); + smartlist_add_linecpy(diff, area, "."); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_NE, cons2); + tt_str_op( + "network-status-version foo\n" + "r name ccccccccccccccccc etc\nsample\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "directory-signature foo bar\nbar\n", OP_EQ, + cons2); + tor_free(cons2); + + smartlist_clear(diff); + + done: + teardown_capture_of_logs(); + tor_free(cons1_str); + smartlist_free(cons1); + smartlist_free(diff); + memarea_drop_all(area); +} + +#define CONSDIFF_LEGACY(name) \ + { #name, test_consdiff_ ## name , 0, NULL, NULL } + +struct testcase_t consdiff_tests[] = { + CONSDIFF_LEGACY(smartlist_slice), + CONSDIFF_LEGACY(smartlist_slice_string_pos), + CONSDIFF_LEGACY(lcs_lengths), + CONSDIFF_LEGACY(trim_slices), + CONSDIFF_LEGACY(set_changed), + CONSDIFF_LEGACY(calc_changes), + CONSDIFF_LEGACY(get_id_hash), + CONSDIFF_LEGACY(is_valid_router_entry), + CONSDIFF_LEGACY(next_router), + CONSDIFF_LEGACY(base64cmp), + CONSDIFF_LEGACY(gen_ed_diff), + CONSDIFF_LEGACY(apply_ed_diff), + CONSDIFF_LEGACY(gen_diff), + CONSDIFF_LEGACY(apply_diff), + END_OF_TESTCASES +}; + diff --git a/src/test/test_consdiffmgr.c b/src/test/test_consdiffmgr.c new file mode 100644 index 0000000000..963a6e427a --- /dev/null +++ b/src/test/test_consdiffmgr.c @@ -0,0 +1,896 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONSDIFFMGR_PRIVATE + +#include "or.h" +#include "config.h" +#include "conscache.h" +#include "consdiff.h" +#include "consdiffmgr.h" +#include "cpuworker.h" +#include "networkstatus.h" +#include "routerparse.h" +#include "workqueue.h" + +#include "test.h" +#include "log_test_helpers.h" + +// ============================== Setup/teardown the consdiffmgr +// These functions get run before/after each test in this module + +static void * +consdiffmgr_test_setup(const struct testcase_t *arg) +{ + (void)arg; + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer. + check_private_dir(ddir_fname, CPD_CREATE, NULL); + + consdiff_cfg_t consdiff_cfg = { 300 }; + consdiffmgr_configure(&consdiff_cfg); + return (void *)1; // must return something non-null. +} +static int +consdiffmgr_test_teardown(const struct testcase_t *arg, void *ignore) +{ + (void)arg; + (void)ignore; + consdiffmgr_free_all(); + return 1; +} +static struct testcase_setup_t setup_diffmgr = { + consdiffmgr_test_setup, + consdiffmgr_test_teardown +}; + +// ============================== NS faking functions +// These functions are for making quick fake consensus objects and +// strings that are just good enough for consdiff and consdiffmgr. + +static networkstatus_t * +fake_ns_new(consensus_flavor_t flav, time_t valid_after) +{ + networkstatus_t *ns = tor_malloc_zero(sizeof(networkstatus_t)); + ns->type = NS_TYPE_CONSENSUS; + ns->flavor = flav; + ns->valid_after = valid_after; + return ns; +} + +static char * +fake_ns_body_new(consensus_flavor_t flav, time_t valid_after) +{ + const char *flavor_string = flav == FLAV_NS ? "" : " microdesc"; + char valid_after_string[ISO_TIME_LEN+1]; + + format_iso_time(valid_after_string, valid_after); + char *random_stuff = crypto_random_hostname(3, 25, "junk ", ""); + char *random_stuff2 = crypto_random_hostname(3, 10, "", ""); + + char *consensus; + tor_asprintf(&consensus, + "network-status-version 3%s\n" + "vote-status consensus\n" + "valid-after %s\n" + "r name ccccccccccccccccc etc\nsample\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "%s\n" + "directory-signature hello-there\n" + "directory-signature %s\n", + flavor_string, + valid_after_string, + random_stuff, + random_stuff2); + tor_free(random_stuff); + tor_free(random_stuff2); + return consensus; +} + +// ============================== Cpuworker mocking code +// These mocking functions and types capture the cpuworker calls +// so we can inspect them and run them in the main thread. +static smartlist_t *fake_cpuworker_queue = NULL; +typedef struct fake_work_queue_ent_t { + enum workqueue_reply_t (*fn)(void *, void *); + void (*reply_fn)(void *); + void *arg; +} fake_work_queue_ent_t; +static struct workqueue_entry_s * +mock_cpuworker_queue_work(workqueue_priority_t prio, + enum workqueue_reply_t (*fn)(void *, void *), + void (*reply_fn)(void *), + void *arg) +{ + (void) prio; + + if (! fake_cpuworker_queue) + fake_cpuworker_queue = smartlist_new(); + + fake_work_queue_ent_t *ent = tor_malloc_zero(sizeof(*ent)); + ent->fn = fn; + ent->reply_fn = reply_fn; + ent->arg = arg; + smartlist_add(fake_cpuworker_queue, ent); + return (struct workqueue_entry_s *)ent; +} +static int +mock_cpuworker_run_work(void) +{ + if (! fake_cpuworker_queue) + return 0; + SMARTLIST_FOREACH(fake_cpuworker_queue, fake_work_queue_ent_t *, ent, { + enum workqueue_reply_t r = ent->fn(NULL, ent->arg); + if (r != WQ_RPL_REPLY) + return -1; + }); + return 0; +} +static void +mock_cpuworker_handle_replies(void) +{ + if (! fake_cpuworker_queue) + return; + SMARTLIST_FOREACH(fake_cpuworker_queue, fake_work_queue_ent_t *, ent, { + ent->reply_fn(ent->arg); + tor_free(ent); + }); + smartlist_free(fake_cpuworker_queue); + fake_cpuworker_queue = NULL; +} + +// ============================== Other helpers + +static consdiff_status_t +lookup_diff_from(consensus_cache_entry_t **out, + consensus_flavor_t flav, + const char *str1) +{ + uint8_t digest[DIGEST256_LEN]; + if (router_get_networkstatus_v3_sha3_as_signed(digest, str1)<0) { + TT_FAIL(("Unable to compute sha3-as-signed")); + return CONSDIFF_NOT_FOUND; + } + return consdiffmgr_find_diff_from(out, flav, + DIGEST_SHA3_256, digest, sizeof(digest), + NO_METHOD); +} + +static int +lookup_apply_and_verify_diff(consensus_flavor_t flav, + const char *str1, + const char *str2) +{ + consensus_cache_entry_t *ent = NULL; + consdiff_status_t status = lookup_diff_from(&ent, flav, str1); + if (ent == NULL || status != CONSDIFF_AVAILABLE) { + return -1; + } + + consensus_cache_entry_incref(ent); + size_t size; + char *diff_string = NULL; + int r = uncompress_or_copy(&diff_string, &size, ent); + consensus_cache_entry_decref(ent); + if (diff_string == NULL || r < 0) + return -1; + + char *applied = consensus_diff_apply(str1, diff_string); + tor_free(diff_string); + if (applied == NULL) + return -1; + + int match = !strcmp(applied, str2); + tor_free(applied); + return match ? 0 : -1; +} + +static void +cdm_reload(void) +{ + consdiffmgr_free_all(); + cdm_cache_get(); + consdiffmgr_rescan(); +} + +// ============================== Beginning of tests + +#if 0 +static int got_failure = 0; +static void +got_assertion_failure(void) +{ + ++got_failure; +} + +/* XXXX This test won't work, because there is currently no way to actually + * XXXX capture a real assertion failure. */ +static void +test_consdiffmgr_init_failure(void *arg) +{ + (void)arg; + // Capture assertions and bugs. + + /* As in ...test_setup, but do not create the datadir. The missing directory + * will cause a failure. */ + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer. + + consdiff_cfg_t consdiff_cfg = { 7200, 300 }; + + tor_set_failed_assertion_callback(got_assertion_failure); + tor_capture_bugs_(1); + consdiffmgr_configure(&consdiff_cfg); // This should fail. + tt_int_op(got_failure, OP_EQ, 1); + const smartlist_t *bugs = tor_get_captured_bug_log_(); + tt_int_op(smartlist_len(bugs), OP_EQ, 1); + + done: + tor_end_capture_bugs_(); +} +#endif + +static void +test_consdiffmgr_sha3_helper(void *arg) +{ + (void) arg; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + config_line_t *lines = NULL; + char *mem_op_hex_tmp = NULL; + config_line_prepend(&lines, "good-sha", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00D"); + config_line_prepend(&lines, "short-sha", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF0"); + config_line_prepend(&lines, "long-sha", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00DF00D"); + config_line_prepend(&lines, "not-sha", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DXXXX"); + consensus_cache_entry_t *ent = + consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + + uint8_t buf[DIGEST256_LEN]; + tt_int_op(-1, OP_EQ, cdm_entry_get_sha3_value(buf, NULL, "good-sha")); + tt_int_op(0, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "good-sha")); + test_memeq_hex(buf, "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00D"); + + tt_int_op(-1, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "missing-sha")); + tt_int_op(-2, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "short-sha")); + tt_int_op(-2, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "long-sha")); + tt_int_op(-2, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "not-sha")); + + done: + consensus_cache_entry_decref(ent); + config_free_lines(lines); + tor_free(mem_op_hex_tmp); +} + +static void +test_consdiffmgr_add(void *arg) +{ + (void) arg; + time_t now = approx_time(); + + char *body = NULL; + + consensus_cache_entry_t *ent = NULL; + networkstatus_t *ns_tmp = fake_ns_new(FLAV_NS, now); + const char *dummy = "foo"; + int r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, 0); + + /* If we add it again, it won't work */ + setup_capture_of_logs(LOG_INFO); + dummy = "bar"; + r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, -1); + expect_single_log_msg_containing("We already have a copy of that " + "consensus"); + mock_clean_saved_logs(); + + /* But it will work fine if the flavor is different */ + dummy = "baz"; + ns_tmp->flavor = FLAV_MICRODESC; + r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, 0); + + /* And it will work fine if the time is different */ + dummy = "quux"; + ns_tmp->flavor = FLAV_NS; + ns_tmp->valid_after = now - 60; + r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, 0); + + /* If we add one a long long time ago, it will fail. */ + dummy = "xyzzy"; + ns_tmp->valid_after = 86400 * 100; /* A few months into 1970 */ + r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, -1); + expect_log_msg_containing("it's too old."); + + /* Try looking up a consensuses. */ + ent = cdm_cache_lookup_consensus(FLAV_NS, now-60); + tt_assert(ent); + consensus_cache_entry_incref(ent); + size_t s; + r = uncompress_or_copy(&body, &s, ent); + tt_int_op(r, OP_EQ, 0); + tt_int_op(s, OP_EQ, 4); + tt_mem_op(body, OP_EQ, "quux", 4); + + /* Try looking up another entry, but fail */ + tt_assert(NULL == cdm_cache_lookup_consensus(FLAV_MICRODESC, now-60)); + tt_assert(NULL == cdm_cache_lookup_consensus(FLAV_NS, now-61)); + + done: + networkstatus_vote_free(ns_tmp); + teardown_capture_of_logs(); + consensus_cache_entry_decref(ent); + tor_free(body); +} + +static void +test_consdiffmgr_make_diffs(void *arg) +{ + (void)arg; + networkstatus_t *ns = NULL; + char *ns_body = NULL, *md_ns_body = NULL, *md_ns_body_2 = NULL; + char *applied = NULL, *diff_text = NULL; + time_t now = approx_time(); + int r; + consensus_cache_entry_t *diff = NULL; + uint8_t md_ns_sha3[DIGEST256_LEN]; + consdiff_status_t diff_status; + + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + // Try rescan with no consensuses: shouldn't crash or queue work. + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + // Make two consensuses, 1 hour sec ago. + ns = fake_ns_new(FLAV_NS, now-3600); + ns_body = fake_ns_body_new(FLAV_NS, now-3600); + r = consdiffmgr_add_consensus(ns_body, ns); + networkstatus_vote_free(ns); + tor_free(ns_body); + tt_int_op(r, OP_EQ, 0); + + ns = fake_ns_new(FLAV_MICRODESC, now-3600); + md_ns_body = fake_ns_body_new(FLAV_MICRODESC, now-3600); + r = consdiffmgr_add_consensus(md_ns_body, ns); + router_get_networkstatus_v3_sha3_as_signed(md_ns_sha3, md_ns_body); + networkstatus_vote_free(ns); + tt_int_op(r, OP_EQ, 0); + + // No diffs will be generated. + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + // Add a MD consensus from 45 minutes ago. This should cause one diff + // worth of work to get queued. + ns = fake_ns_new(FLAV_MICRODESC, now-45*60); + md_ns_body_2 = fake_ns_body_new(FLAV_MICRODESC, now-45*60); + r = consdiffmgr_add_consensus(md_ns_body_2, ns); + networkstatus_vote_free(ns); + tt_int_op(r, OP_EQ, 0); + + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(1, OP_EQ, smartlist_len(fake_cpuworker_queue)); + diff_status = consdiffmgr_find_diff_from(&diff, FLAV_MICRODESC, + DIGEST_SHA3_256, + md_ns_sha3, DIGEST256_LEN, + NO_METHOD); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, diff_status); + + // Now run that process and get the diff. + r = mock_cpuworker_run_work(); + tt_int_op(r, OP_EQ, 0); + mock_cpuworker_handle_replies(); + + // At this point we should be able to get that diff. + diff_status = consdiffmgr_find_diff_from(&diff, FLAV_MICRODESC, + DIGEST_SHA3_256, + md_ns_sha3, DIGEST256_LEN, + NO_METHOD); + tt_int_op(CONSDIFF_AVAILABLE, OP_EQ, diff_status); + tt_assert(diff); + + /* Make sure applying the diff actually works */ + const uint8_t *diff_body; + size_t diff_size; + r = consensus_cache_entry_get_body(diff, &diff_body, &diff_size); + tt_int_op(r, OP_EQ, 0); + diff_text = tor_memdup_nulterm(diff_body, diff_size); + applied = consensus_diff_apply(md_ns_body, diff_text); + tt_assert(applied); + tt_str_op(applied, OP_EQ, md_ns_body_2); + + /* Rescan again: no more work to do. */ + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + done: + tor_free(md_ns_body); + tor_free(md_ns_body_2); + tor_free(diff_text); + tor_free(applied); +} + +static void +test_consdiffmgr_diff_rules(void *arg) +{ + (void)arg; +#define N 6 + char *md_body[N], *ns_body[N]; + networkstatus_t *md_ns[N], *ns_ns[N]; + int i; + + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + /* Create a bunch of consensus things at 15-second intervals. */ + time_t start = approx_time() - 120; + for (i = 0; i < N; ++i) { + time_t when = start + i * 15; + md_body[i] = fake_ns_body_new(FLAV_MICRODESC, when); + ns_body[i] = fake_ns_body_new(FLAV_NS, when); + md_ns[i] = fake_ns_new(FLAV_MICRODESC, when); + ns_ns[i] = fake_ns_new(FLAV_NS, when); + } + + /* For the MD consensuses: add 4 of them, and make sure that + * diffs are created to one consensus (the most recent) only. */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[1], md_ns[1])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[2], md_ns[2])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[3], md_ns[3])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[4], md_ns[4])); + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(3, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + /* For the NS consensuses: add 3, generate, and add one older one and + * make sure that older one is the only one whose diff is generated */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(ns_body[0], ns_ns[0])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(ns_body[1], ns_ns[1])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(ns_body[5], ns_ns[5])); + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(2, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + + /* At this point, we should actually have working diffs! */ + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[0], ns_body[5])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[1], ns_body[5])); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[4])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[2], md_body[4])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[3], md_body[4])); + + /* Self-to-self diff won't be present */ + consensus_cache_entry_t *ent; + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_NS, ns_body[5])); + /* No diff from 2 has been added yet */ + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_NS, ns_body[2])); + /* No diff arriving at old things. */ + tt_int_op(-1, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[2])); + /* No backwards diff */ + tt_int_op(-1, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[4], md_body[3])); + + /* Now, an update: add number 2 and make sure it's the only one whose diff + * is regenerated. */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(ns_body[2], ns_ns[2])); + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(1, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[2], ns_body[5])); + + /* Finally: reload, and make sure that the information is still indexed */ + cdm_reload(); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[0], ns_body[5])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[2], ns_body[5])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[1], ns_body[5])); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[4])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[2], md_body[4])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[3], md_body[4])); + + done: + for (i = 0; i < N; ++i) { + tor_free(md_body[i]); + tor_free(ns_body[i]); + networkstatus_vote_free(md_ns[i]); + networkstatus_vote_free(ns_ns[i]); + } + UNMOCK(cpuworker_queue_work); +#undef N +} + +static void +test_consdiffmgr_diff_failure(void *arg) +{ + (void)arg; + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + /* We're going to make sure that if we have a bogus request where + * we can't actually compute a diff, the world must not end. */ + networkstatus_t *ns1 = NULL; + networkstatus_t *ns2 = NULL; + int r; + + ns1 = fake_ns_new(FLAV_NS, approx_time()-100); + ns2 = fake_ns_new(FLAV_NS, approx_time()-50); + r = consdiffmgr_add_consensus("foo bar baz\n", ns1); + tt_int_op(r, OP_EQ, 0); + // We refuse to compute a diff to or from a line holding only a single dot. + // We can add it here, though. + r = consdiffmgr_add_consensus("foo bar baz\n.\n.\n", ns2); + tt_int_op(r, OP_EQ, 0); + + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + setup_capture_of_logs(LOG_WARN); + tt_int_op(1, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + expect_single_log_msg_containing("one of the lines to be added is \".\"."); + mock_clean_saved_logs(); + mock_cpuworker_handle_replies(); + expect_single_log_msg_containing("Worker was unable to compute consensus " + "diff from "); + + /* Make sure the diff is not present */ + consensus_cache_entry_t *ent; + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_NS, "foo bar baz\n")); + + done: + teardown_capture_of_logs(); + UNMOCK(cpuworker_queue_work); + networkstatus_vote_free(ns1); + networkstatus_vote_free(ns2); +} + +static void +test_consdiffmgr_diff_pending(void *arg) +{ +#define N 3 + (void)arg; + char *md_body[N]; + networkstatus_t *md_ns[N]; + time_t start = approx_time() - 120; + int i; + for (i = 0; i < N; ++i) { + time_t when = start + i * 30; + md_body[i] = fake_ns_body_new(FLAV_MICRODESC, when); + md_ns[i] = fake_ns_new(FLAV_MICRODESC, when); + } + + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[1], md_ns[1])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[2], md_ns[2])); + /* Make a diff */ + consdiffmgr_rescan(); + tt_int_op(1, OP_EQ, smartlist_len(fake_cpuworker_queue)); + + /* Look it up. Is it pending? */ + consensus_cache_entry_t *ent = NULL; + consdiff_status_t diff_status; + diff_status = lookup_diff_from(&ent, FLAV_MICRODESC, md_body[1]); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, diff_status); + tt_ptr_op(ent, OP_EQ, NULL); + + /* Add another old consensus. only one new diff should launch! */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[0], md_ns[0])); + consdiffmgr_rescan(); + tt_int_op(2, OP_EQ, smartlist_len(fake_cpuworker_queue)); + + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[0], md_body[2])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[2])); + + done: + UNMOCK(cpuworker_queue_work); + for (i = 0; i < N; ++i) { + tor_free(md_body[i]); + networkstatus_vote_free(md_ns[i]); + } +#undef N +} + +static void +test_consdiffmgr_cleanup_old(void *arg) +{ + (void)arg; + config_line_t *labels = NULL; + consensus_cache_entry_t *ent = NULL; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + + /* This item will be will be cleanable because it has a valid-after + * time far in the past. */ + config_line_prepend(&labels, "document-type", "confribble-blarg"); + config_line_prepend(&labels, "consensus-valid-after", + "1980-10-10T10:10:10"); + ent = consensus_cache_add(cache, labels, (const uint8_t*)"Foo", 3); + tt_assert(ent); + consensus_cache_entry_decref(ent); + + setup_capture_of_logs(LOG_DEBUG); + tt_int_op(1, OP_EQ, consdiffmgr_cleanup()); + expect_log_msg_containing("Deleting entry because its consensus-valid-" + "after value (1980-10-10T10:10:10) was too old"); + + done: + teardown_capture_of_logs(); + config_free_lines(labels); +} + +static void +test_consdiffmgr_cleanup_bad_valid_after(void *arg) +{ + /* This will seem cleanable, but isn't, because its valid-after time is + * misformed. */ + + (void)arg; + config_line_t *labels = NULL; + consensus_cache_entry_t *ent = NULL; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + + config_line_prepend(&labels, "document-type", "consensus"); + config_line_prepend(&labels, "consensus-valid-after", + "whan that aprille with his shoures soote"); // (~1385?) + ent = consensus_cache_add(cache, labels, (const uint8_t*)"Foo", 3); + tt_assert(ent); + consensus_cache_entry_decref(ent); + + setup_capture_of_logs(LOG_DEBUG); + tt_int_op(0, OP_EQ, consdiffmgr_cleanup()); + expect_log_msg_containing("Ignoring entry because its consensus-valid-" + "after value (\"whan that aprille with his " + "shoures soote\") was unparseable"); + + done: + teardown_capture_of_logs(); + config_free_lines(labels); +} + +static void +test_consdiffmgr_cleanup_no_valid_after(void *arg) +{ + (void)arg; + config_line_t *labels = NULL; + consensus_cache_entry_t *ent = NULL; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + + /* This item will be will be uncleanable because it has no recognized + * valid-after. */ + config_line_prepend(&labels, "document-type", "consensus"); + config_line_prepend(&labels, "confrooble-voolid-oofter", + "2010-10-10T09:08:07"); + ent = consensus_cache_add(cache, labels, (const uint8_t*)"Foo", 3); + tt_assert(ent); + consensus_cache_entry_decref(ent); + + setup_capture_of_logs(LOG_DEBUG); + tt_int_op(0, OP_EQ, consdiffmgr_cleanup()); + expect_log_msg_containing("Ignoring entry because it had no consensus-" + "valid-after label"); + + done: + teardown_capture_of_logs(); + config_free_lines(labels); +} + +static void +test_consdiffmgr_cleanup_old_diffs(void *arg) +{ + (void)arg; +#define N 4 + char *md_body[N]; + networkstatus_t *md_ns[N]; + int i; + consensus_cache_entry_t *hold_ent = NULL, *ent; + + /* Make sure that the cleanup function removes diffs to the not-most-recent + * consensus. */ + + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + /* Create a bunch of consensus things at 15-second intervals. */ + time_t start = approx_time() - 120; + for (i = 0; i < N; ++i) { + time_t when = start + i * 15; + md_body[i] = fake_ns_body_new(FLAV_MICRODESC, when); + md_ns[i] = fake_ns_new(FLAV_MICRODESC, when); + } + + /* add the first 3. */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[0], md_ns[0])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[1], md_ns[1])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[2], md_ns[2])); + /* Make diffs. */ + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(2, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + /* Nothing is deletable now */ + tt_int_op(0, OP_EQ, consdiffmgr_cleanup()); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[0], md_body[2])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[2])); + + tt_int_op(CONSDIFF_AVAILABLE, OP_EQ, + lookup_diff_from(&hold_ent, FLAV_MICRODESC, md_body[1])); + consensus_cache_entry_incref(hold_ent); // incref, so it is preserved. + + /* Now add an even-more-recent consensus; this should make all previous + * diffs deletable, and make delete */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[3], md_ns[3])); + tt_int_op(2 * n_diff_compression_methods() + + (n_consensus_compression_methods() - 1) , OP_EQ, + consdiffmgr_cleanup()); + + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[0])); + /* This one is marked deletable but still in the hashtable */ + tt_int_op(CONSDIFF_AVAILABLE, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[1])); + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[2])); + + /* Everything should be valid at this point */ + tt_int_op(0, OP_EQ, consdiffmgr_validate()); + + /* And if we recan NOW, we'll purge the hashtable of the entries, + * and launch attempts to generate new ones */ + consdiffmgr_rescan(); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[0])); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[1])); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[2])); + + /* We're still holding on to this, though, so we can still map it! */ + const uint8_t *t1 = NULL; + size_t s; + int r = consensus_cache_entry_get_body(hold_ent, &t1, &s); + tt_int_op(r, OP_EQ, 0); + tt_assert(t1); + + done: + for (i = 0; i < N; ++i) { + tor_free(md_body[i]); + networkstatus_vote_free(md_ns[i]); + } + consensus_cache_entry_decref(hold_ent); + UNMOCK(cpuworker_queue_work); +#undef N +} + +static void +test_consdiffmgr_validate(void *arg) +{ + (void)arg; + config_line_t *lines = NULL; + consensus_cache_entry_t *ent = NULL; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + smartlist_t *vals = smartlist_new(); + + /* Put these: objects in the cache: one with a good sha3, one with bad sha3, + * one with a wrong sha3, and one with no sha3. */ + config_line_prepend(&lines, "id", "wrong sha3"); + config_line_prepend(&lines, "sha3-digest", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00D"); + ent = consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + consensus_cache_entry_decref(ent); + config_free_lines(lines); + lines = NULL; + + config_line_prepend(&lines, "id", "bad sha3"); + config_line_prepend(&lines, "sha3-digest", + "now is the winter of our dicotheque"); + ent = consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + consensus_cache_entry_decref(ent); + config_free_lines(lines); + lines = NULL; + + config_line_prepend(&lines, "id", "no sha3"); + ent = consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + consensus_cache_entry_decref(ent); + config_free_lines(lines); + lines = NULL; + + config_line_prepend(&lines, "id", "good sha3"); + config_line_prepend(&lines, "sha3-digest", + "8d8b1998616cd6b4c4055da8d38728dc" + "93c758d4131a53c7d81aa6337dee1c05"); + ent = consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + consensus_cache_entry_decref(ent); + config_free_lines(lines); + lines = NULL; + + cdm_reload(); + cache = cdm_cache_get(); + tt_int_op(1, OP_EQ, consdiffmgr_validate()); + + consensus_cache_find_all(vals, cache, "id", "good sha3"); + tt_int_op(smartlist_len(vals), OP_EQ, 1); + smartlist_clear(vals); + + consensus_cache_find_all(vals, cache, "id", "no sha3"); + tt_int_op(smartlist_len(vals), OP_EQ, 1); + smartlist_clear(vals); + + consensus_cache_find_all(vals, cache, "id", "wrong sha3"); + tt_int_op(smartlist_len(vals), OP_EQ, 0); + consensus_cache_find_all(vals, cache, "id", "bad sha3"); + tt_int_op(smartlist_len(vals), OP_EQ, 0); + + done: + smartlist_free(vals); +} + +#define TEST(name) \ + { #name, test_consdiffmgr_ ## name , TT_FORK, &setup_diffmgr, NULL } + +struct testcase_t consdiffmgr_tests[] = { +#if 0 + { "init_failure", test_consdiffmgr_init_failure, TT_FORK, NULL, NULL }, +#endif + TEST(sha3_helper), + TEST(add), + TEST(make_diffs), + TEST(diff_rules), + TEST(diff_failure), + TEST(diff_pending), + TEST(cleanup_old), + TEST(cleanup_bad_valid_after), + TEST(cleanup_no_valid_after), + TEST(cleanup_old_diffs), + TEST(validate), + + // XXXX Test: non-cacheing cases of replyfn(). + + END_OF_TESTCASES +}; + diff --git a/src/test/test_containers.c b/src/test/test_containers.c index d8b82e0661..54484a2a91 100644 --- a/src/test/test_containers.c +++ b/src/test/test_containers.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -501,13 +501,13 @@ test_container_smartlist_pos(void *arg) (void) arg; smartlist_t *sl = smartlist_new(); - smartlist_add(sl, tor_strdup("This")); - smartlist_add(sl, tor_strdup("is")); - smartlist_add(sl, tor_strdup("a")); - smartlist_add(sl, tor_strdup("test")); - smartlist_add(sl, tor_strdup("for")); - smartlist_add(sl, tor_strdup("a")); - smartlist_add(sl, tor_strdup("function")); + smartlist_add_strdup(sl, "This"); + smartlist_add_strdup(sl, "is"); + smartlist_add_strdup(sl, "a"); + smartlist_add_strdup(sl, "test"); + smartlist_add_strdup(sl, "for"); + smartlist_add_strdup(sl, "a"); + smartlist_add_strdup(sl, "function"); /* Test string_pos */ tt_int_op(smartlist_string_pos(NULL, "Fred"), ==, -1); @@ -830,7 +830,7 @@ test_container_strmap(void *arg) found_keys = smartlist_new(); while (!strmap_iter_done(iter)) { strmap_iter_get(iter,&k,&v); - smartlist_add(found_keys, tor_strdup(k)); + smartlist_add_strdup(found_keys, k); tt_ptr_op(v,OP_EQ, strmap_get(map, k)); if (!strcmp(k, "K2")) { @@ -882,6 +882,46 @@ test_container_strmap(void *arg) tor_free(v105); } +static void +test_container_smartlist_remove(void *arg) +{ + (void) arg; + int array[5]; + smartlist_t *sl = smartlist_new(); + int i,j; + + for (j=0; j < 2; ++j) + for (i=0; i < 5; ++i) + smartlist_add(sl, &array[i]); + + smartlist_remove(sl, &array[0]); + smartlist_remove(sl, &array[3]); + smartlist_remove(sl, &array[4]); + tt_assert(! smartlist_contains(sl, &array[0])); + tt_assert(smartlist_contains(sl, &array[1])); + tt_assert(smartlist_contains(sl, &array[2])); + tt_assert(! smartlist_contains(sl, &array[3])); + tt_assert(! smartlist_contains(sl, &array[4])); + tt_int_op(smartlist_len(sl), OP_EQ, 4); + + smartlist_clear(sl); + for (j=0; j < 2; ++j) + for (i=0; i < 5; ++i) + smartlist_add(sl, &array[i]); + + smartlist_remove_keeporder(sl, &array[0]); + smartlist_remove_keeporder(sl, &array[3]); + smartlist_remove_keeporder(sl, &array[4]); + tt_int_op(smartlist_len(sl), OP_EQ, 4); + tt_ptr_op(smartlist_get(sl, 0), OP_EQ, &array[1]); + tt_ptr_op(smartlist_get(sl, 1), OP_EQ, &array[2]); + tt_ptr_op(smartlist_get(sl, 2), OP_EQ, &array[1]); + tt_ptr_op(smartlist_get(sl, 3), OP_EQ, &array[2]); + + done: + smartlist_free(sl); +} + /** Run unit tests for getting the median of a list. */ static void test_container_order_functions(void *arg) @@ -1239,6 +1279,7 @@ struct testcase_t container_tests[] = { CONTAINER_LEGACY(smartlist_digests), CONTAINER_LEGACY(smartlist_join), CONTAINER_LEGACY(smartlist_pos), + CONTAINER(smartlist_remove, 0), CONTAINER(smartlist_ints_eq, 0), CONTAINER_LEGACY(bitarray), CONTAINER_LEGACY(digestset), diff --git a/src/test/test_controller.c b/src/test/test_controller.c index f19c846144..592f91a988 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -1,14 +1,16 @@ -/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONTROL_PRIVATE #include "or.h" +#include "bridges.h" #include "control.h" #include "entrynodes.h" #include "networkstatus.h" #include "rendservice.h" #include "routerlist.h" #include "test.h" +#include "test_helpers.h" static void test_add_onion_helper_keyarg(void *arg) @@ -107,6 +109,45 @@ test_add_onion_helper_keyarg(void *arg) } static void +test_getinfo_helper_onion(void *arg) +{ + (void)arg; + control_connection_t dummy; + /* Get results out */ + char *answer = NULL; + const char *errmsg = NULL; + char *service_id = NULL; + int rt = 0; + + dummy.ephemeral_onion_services = NULL; + + /* successfully get an empty answer */ + rt = getinfo_helper_onions(&dummy, "onions/current", &answer, &errmsg); + tt_assert(rt == 0); + tt_str_op(answer, OP_EQ, ""); + tor_free(answer); + + /* successfully get an empty answer */ + rt = getinfo_helper_onions(&dummy, "onions/detached", &answer, &errmsg); + tt_assert(rt == 0); + tt_str_op(answer, OP_EQ, ""); + tor_free(answer); + + /* get an answer for one onion service */ + service_id = tor_strdup("dummy_onion_id"); + dummy.ephemeral_onion_services = smartlist_new(); + smartlist_add(dummy.ephemeral_onion_services, service_id); + rt = getinfo_helper_onions(&dummy, "onions/current", &answer, &errmsg); + tt_assert(rt == 0); + tt_str_op(answer, OP_EQ, "dummy_onion_id"); + + done: + tor_free(answer); + tor_free(service_id); + smartlist_free(dummy.ephemeral_onion_services); +} + +static void test_rend_service_parse_port_config(void *arg) { const char *sep = ","; @@ -185,8 +226,10 @@ test_rend_service_parse_port_config(void *arg) tor_free(err_msg); /* bogus IP address */ - cfg = rend_service_parse_port_config("100 1.2.3.4.5:9000", + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); + cfg = rend_service_parse_port_config("100 foo!!.example.com:9000", " ", &err_msg); + UNMOCK(tor_addr_lookup); tt_assert(!cfg); tt_str_op(err_msg, OP_EQ, "Unparseable address in hidden service port " "configuration."); @@ -1328,6 +1371,7 @@ test_download_status_bridge(void *arg) struct testcase_t controller_tests[] = { { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL }, + { "getinfo_helper_onion", test_getinfo_helper_onion, 0, NULL, NULL }, { "rend_service_parse_port_config", test_rend_service_parse_port_config, 0, NULL, NULL }, { "add_onion_helper_clientauth", test_add_onion_helper_clientauth, 0, NULL, diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index 11e1e3dc8f..901ad7ab3d 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONNECTION_PRIVATE diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 8fd9ca7671..75cd30ebb5 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -15,9 +15,6 @@ #include "crypto_ed25519.h" #include "ed25519_vectors.inc" -#include <openssl/evp.h> -#include <openssl/rand.h> - /** Run unit tests for Diffie-Hellman functionality. */ static void test_crypto_dh(void *arg) @@ -331,38 +328,6 @@ test_crypto_rng_strongest(void *arg) #undef N } -/* Test for rectifying openssl RAND engine. */ -static void -test_crypto_rng_engine(void *arg) -{ - (void)arg; - RAND_METHOD dummy_method; - memset(&dummy_method, 0, sizeof(dummy_method)); - - /* We should be a no-op if we're already on RAND_OpenSSL */ - tt_int_op(0, ==, crypto_force_rand_ssleay()); - tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); - - /* We should correct the method if it's a dummy. */ - RAND_set_rand_method(&dummy_method); -#ifdef LIBRESSL_VERSION_NUMBER - /* On libressl, you can't override the RNG. */ - tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); - tt_int_op(0, ==, crypto_force_rand_ssleay()); -#else - tt_assert(RAND_get_rand_method() == &dummy_method); - tt_int_op(1, ==, crypto_force_rand_ssleay()); -#endif - tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); - - /* Make sure we aren't calling dummy_method */ - crypto_rand((void *) &dummy_method, sizeof(dummy_method)); - crypto_rand((void *) &dummy_method, sizeof(dummy_method)); - - done: - ; -} - /** Run unit tests for our AES128 functionality */ static void test_crypto_aes128(void *arg) @@ -1135,6 +1100,54 @@ test_crypto_sha3_xof(void *arg) tor_free(mem_op_hex_tmp); } +/* Test our MAC-SHA3 function. There are not actually any MAC-SHA3 test + * vectors out there for our H(len(k) || k || m) construction. Hence what we + * are gonna do is test our crypto_mac_sha3_256() function against manually + * doing H(len(k) || k||m). If in the future the Keccak group decides to + * standarize an MAC construction and make test vectors, we should + * incorporate them here. */ +static void +test_crypto_mac_sha3(void *arg) +{ + const char msg[] = "i am in a library somewhere using my computer"; + const char key[] = "i'm from the past talking to the future."; + + uint8_t hmac_test[DIGEST256_LEN]; + char hmac_manual[DIGEST256_LEN]; + + (void) arg; + + /* First let's use our nice HMAC-SHA3 function */ + crypto_mac_sha3_256(hmac_test, sizeof(hmac_test), + (uint8_t *) key, strlen(key), + (uint8_t *) msg, strlen(msg)); + + /* Now let's try a manual H(len(k) || k || m) construction */ + { + char *key_msg_concat = NULL, *all = NULL; + int result; + const uint64_t key_len_netorder = tor_htonll(strlen(key)); + size_t all_len; + + tor_asprintf(&key_msg_concat, "%s%s", key, msg); + all_len = sizeof(key_len_netorder) + strlen(key_msg_concat); + all = tor_malloc_zero(all_len); + memcpy(all, &key_len_netorder, sizeof(key_len_netorder)); + memcpy(all + sizeof(key_len_netorder), key_msg_concat, + strlen(key_msg_concat)); + + result = crypto_digest256(hmac_manual, all, all_len, DIGEST_SHA3_256); + tor_free(key_msg_concat); + tor_free(all); + tt_int_op(result, ==, 0); + } + + /* Now compare the two results */ + tt_mem_op(hmac_test, OP_EQ, hmac_manual, DIGEST256_LEN); + + done: ; +} + /** Run unit tests for our public key crypto functions */ static void test_crypto_pk(void *arg) @@ -1469,28 +1482,6 @@ test_crypto_digest_names(void *arg) ; } -#ifndef OPENSSL_1_1_API -#define EVP_ENCODE_CTX_new() tor_malloc_zero(sizeof(EVP_ENCODE_CTX)) -#define EVP_ENCODE_CTX_free(ctx) tor_free(ctx) -#endif - -/** Encode src into dest with OpenSSL's EVP Encode interface, returning the - * length of the encoded data in bytes. - */ -static int -base64_encode_evp(char *dest, char *src, size_t srclen) -{ - const unsigned char *s = (unsigned char*)src; - EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new(); - int len, ret; - - EVP_EncodeInit(ctx); - EVP_EncodeUpdate(ctx, (unsigned char *)dest, &len, s, (int)srclen); - EVP_EncodeFinal(ctx, (unsigned char *)(dest + len), &ret); - EVP_ENCODE_CTX_free(ctx); - return ret+ len; -} - /** Run unit tests for misc crypto formatting functionality (base64, base32, * fingerprints, etc) */ static void @@ -1519,7 +1510,7 @@ test_crypto_formats(void *arg) tt_int_op(i, OP_GE, 0); tt_int_op(i, OP_EQ, strlen(data2)); tt_assert(! strchr(data2, '=')); - j = base64_decode_nopad((uint8_t*)data3, 1024, data2, i); + j = base64_decode(data3, 1024, data2, i); tt_int_op(j, OP_EQ, idx); tt_mem_op(data3,OP_EQ, data1, idx); } @@ -1546,20 +1537,6 @@ test_crypto_formats(void *arg) tt_assert(digest_from_base64(data3, "###") < 0); - for (i = 0; i < 256; i++) { - /* Test the multiline format Base64 encoder with 0 .. 256 bytes of - * output against OpenSSL. - */ - const size_t enclen = base64_encode_size(i, BASE64_ENCODE_MULTILINE); - data1[i] = i; - j = base64_encode(data2, 1024, data1, i, BASE64_ENCODE_MULTILINE); - tt_int_op(j, OP_EQ, enclen); - j = base64_encode_evp(data3, data1, i); - tt_int_op(j, OP_EQ, enclen); - tt_mem_op(data2, OP_EQ, data3, enclen); - tt_int_op(j, OP_EQ, strlen(data2)); - } - /* Encoding SHA256 */ crypto_rand(data2, DIGEST256_LEN); memset(data2, 100, 1024); @@ -2933,7 +2910,6 @@ struct testcase_t crypto_tests[] = { CRYPTO_LEGACY(formats), CRYPTO_LEGACY(rng), { "rng_range", test_crypto_rng_range, 0, NULL, NULL }, - { "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL }, { "rng_strongest", test_crypto_rng_strongest, TT_FORK, NULL, NULL }, { "rng_strongest_nosyscall", test_crypto_rng_strongest, TT_FORK, &passthrough_setup, (void*)"nosyscall" }, @@ -2959,6 +2935,7 @@ struct testcase_t crypto_tests[] = { { "digest_names", test_crypto_digest_names, 0, NULL, NULL }, { "sha3", test_crypto_sha3, TT_FORK, NULL, NULL}, { "sha3_xof", test_crypto_sha3_xof, TT_FORK, NULL, NULL}, + { "mac_sha3", test_crypto_mac_sha3, TT_FORK, NULL, NULL}, CRYPTO_LEGACY(dh), { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup, (void*)"aes" }, diff --git a/src/test/test_crypto_openssl.c b/src/test/test_crypto_openssl.c new file mode 100644 index 0000000000..3d7d2b4639 --- /dev/null +++ b/src/test/test_crypto_openssl.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define CRYPTO_PRIVATE + +#include "crypto.h" +#include "util.h" +#include "util_format.h" +#include "compat.h" +#include "test.h" + +#include <openssl/evp.h> +#include <openssl/rand.h> +#include "compat_openssl.h" + +/* Test for rectifying openssl RAND engine. */ +static void +test_crypto_rng_engine(void *arg) +{ + (void)arg; + RAND_METHOD dummy_method; + memset(&dummy_method, 0, sizeof(dummy_method)); + + /* We should be a no-op if we're already on RAND_OpenSSL */ + tt_int_op(0, ==, crypto_force_rand_ssleay()); + tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); + + /* We should correct the method if it's a dummy. */ + RAND_set_rand_method(&dummy_method); +#ifdef LIBRESSL_VERSION_NUMBER + /* On libressl, you can't override the RNG. */ + tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); + tt_int_op(0, ==, crypto_force_rand_ssleay()); +#else + tt_assert(RAND_get_rand_method() == &dummy_method); + tt_int_op(1, ==, crypto_force_rand_ssleay()); +#endif + tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); + + /* Make sure we aren't calling dummy_method */ + crypto_rand((void *) &dummy_method, sizeof(dummy_method)); + crypto_rand((void *) &dummy_method, sizeof(dummy_method)); + + done: + ; +} + +#ifndef OPENSSL_1_1_API +#define EVP_ENCODE_CTX_new() tor_malloc_zero(sizeof(EVP_ENCODE_CTX)) +#define EVP_ENCODE_CTX_free(ctx) tor_free(ctx) +#endif + +/** Encode src into dest with OpenSSL's EVP Encode interface, returning the + * length of the encoded data in bytes. + */ +static int +base64_encode_evp(char *dest, char *src, size_t srclen) +{ + const unsigned char *s = (unsigned char*)src; + EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new(); + int len, ret; + + EVP_EncodeInit(ctx); + EVP_EncodeUpdate(ctx, (unsigned char *)dest, &len, s, (int)srclen); + EVP_EncodeFinal(ctx, (unsigned char *)(dest + len), &ret); + EVP_ENCODE_CTX_free(ctx); + return ret+ len; +} + +static void +test_crypto_base64_encode_matches(void *arg) +{ + (void)arg; + int i, j; + char data1[1024]; + char data2[1024]; + char data3[1024]; + + for (i = 0; i < 256; i++) { + /* Test the multiline format Base64 encoder with 0 .. 256 bytes of + * output against OpenSSL. + */ + const size_t enclen = base64_encode_size(i, BASE64_ENCODE_MULTILINE); + data1[i] = i; + j = base64_encode(data2, 1024, data1, i, BASE64_ENCODE_MULTILINE); + tt_int_op(j, OP_EQ, enclen); + j = base64_encode_evp(data3, data1, i); + tt_int_op(j, OP_EQ, enclen); + tt_mem_op(data2, OP_EQ, data3, enclen); + tt_int_op(j, OP_EQ, strlen(data2)); + } + + done: + ; +} + +struct testcase_t crypto_openssl_tests[] = { + { "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL }, + { "base64_encode_match", test_crypto_base64_encode_matches, + TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c index 0be58c9389..75c6ba9aaa 100644 --- a/src/test/test_crypto_slow.c +++ b/src/test/test_crypto_slow.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_data.c b/src/test/test_data.c index 788489a097..ce6c3394f6 100644 --- a/src/test/test_data.c +++ b/src/test/test_data.c @@ -1,6 +1,6 @@ /* Copyright 2001-2004 Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "test.h" diff --git a/src/test/test_dir.c b/src/test/test_dir.c index ad5f086434..1ead6d78c2 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -1,12 +1,13 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" #include <math.h> #define CONFIG_PRIVATE +#define CONTROL_PRIVATE #define DIRSERV_PRIVATE #define DIRVOTE_PRIVATE #define ROUTER_PRIVATE @@ -19,10 +20,12 @@ #include "or.h" #include "confparse.h" #include "config.h" +#include "control.h" #include "crypto_ed25519.h" #include "directory.h" #include "dirserv.h" #include "dirvote.h" +#include "entrynodes.h" #include "hibernate.h" #include "memarea.h" #include "networkstatus.h" @@ -328,7 +331,7 @@ test_dir_formats(void *arg) ntor_cc = make_ntor_onion_key_crosscert(&r2_onion_keypair, &kp1.pubkey, r2->cache_info.published_on, - MIN_ONION_KEY_LIFETIME, + get_onion_key_lifetime(), &ntor_cc_sign); tt_assert(ntor_cc); base64_encode(cert_buf, sizeof(cert_buf), @@ -678,16 +681,16 @@ test_dir_parse_router_list(void *arg) routerinfo_t *ri = NULL; char d[DIGEST_LEN]; - smartlist_add(chunks, tor_strdup(EX_RI_MINIMAL)); // ri 0 - smartlist_add(chunks, tor_strdup(EX_RI_BAD_PORTS)); // bad ri 0 - smartlist_add(chunks, tor_strdup(EX_EI_MAXIMAL)); // ei 0 - smartlist_add(chunks, tor_strdup(EX_EI_BAD_SIG2)); // bad ei -- - smartlist_add(chunks, tor_strdup(EX_EI_BAD_NICKNAME));// bad ei 0 - smartlist_add(chunks, tor_strdup(EX_RI_BAD_SIG1)); // bad ri -- - smartlist_add(chunks, tor_strdup(EX_EI_BAD_PUBLISHED)); // bad ei 1 - smartlist_add(chunks, tor_strdup(EX_RI_MAXIMAL)); // ri 1 - smartlist_add(chunks, tor_strdup(EX_RI_BAD_FAMILY)); // bad ri 1 - smartlist_add(chunks, tor_strdup(EX_EI_MINIMAL)); // ei 1 + smartlist_add_strdup(chunks, EX_RI_MINIMAL); // ri 0 + smartlist_add_strdup(chunks, EX_RI_BAD_PORTS); // bad ri 0 + smartlist_add_strdup(chunks, EX_EI_MAXIMAL); // ei 0 + smartlist_add_strdup(chunks, EX_EI_BAD_SIG2); // bad ei -- + smartlist_add_strdup(chunks, EX_EI_BAD_NICKNAME);// bad ei 0 + smartlist_add_strdup(chunks, EX_RI_BAD_SIG1); // bad ri -- + smartlist_add_strdup(chunks, EX_EI_BAD_PUBLISHED); // bad ei 1 + smartlist_add_strdup(chunks, EX_RI_MAXIMAL); // ri 1 + smartlist_add_strdup(chunks, EX_RI_BAD_FAMILY); // bad ri 1 + smartlist_add_strdup(chunks, EX_EI_MINIMAL); // ei 1 list = smartlist_join_strings(chunks, "", 0, NULL); @@ -812,19 +815,19 @@ test_dir_load_routers(void *arg) #define ADD(str) \ do { \ tt_int_op(0,OP_EQ,router_get_router_hash(str, strlen(str), buf)); \ - smartlist_add(wanted, tor_strdup(hex_str(buf, DIGEST_LEN))); \ + smartlist_add_strdup(wanted, hex_str(buf, DIGEST_LEN)); \ } while (0) MOCK(router_get_dl_status_by_descriptor_digest, mock_router_get_dl_status); update_approx_time(1412510400); - smartlist_add(chunks, tor_strdup(EX_RI_MINIMAL)); - smartlist_add(chunks, tor_strdup(EX_RI_BAD_FINGERPRINT)); - smartlist_add(chunks, tor_strdup(EX_RI_BAD_SIG2)); - smartlist_add(chunks, tor_strdup(EX_RI_MAXIMAL)); - smartlist_add(chunks, tor_strdup(EX_RI_BAD_PORTS)); - smartlist_add(chunks, tor_strdup(EX_RI_BAD_TOKENS)); + smartlist_add_strdup(chunks, EX_RI_MINIMAL); + smartlist_add_strdup(chunks, EX_RI_BAD_FINGERPRINT); + smartlist_add_strdup(chunks, EX_RI_BAD_SIG2); + smartlist_add_strdup(chunks, EX_RI_MAXIMAL); + smartlist_add_strdup(chunks, EX_RI_BAD_PORTS); + smartlist_add_strdup(chunks, EX_RI_BAD_TOKENS); /* not ADDing MINIMIAL */ ADD(EX_RI_MAXIMAL); @@ -909,6 +912,23 @@ mock_get_by_ei_desc_digest(const char *d) } } +static signed_descriptor_t * +mock_ei_get_by_ei_digest(const char *d) +{ + char hex[HEX_DIGEST_LEN+1]; + base16_encode(hex, sizeof(hex), d, DIGEST_LEN); + signed_descriptor_t *sd = &sd_ei_minimal; + + if (!strcmp(hex, "11E0EDF526950739F7769810FCACAB8C882FAEEE")) { + sd->signed_descriptor_body = (char *)EX_EI_MINIMAL; + sd->signed_descriptor_len = sizeof(EX_EI_MINIMAL); + sd->annotations_len = 0; + sd->saved_location = SAVED_NOWHERE; + return sd; + } + return NULL; +} + static smartlist_t *mock_ei_insert_list = NULL; static was_router_added_t mock_ei_insert(routerlist_t *rl, extrainfo_t *ei, int warn_if_incompatible) @@ -932,18 +952,18 @@ test_dir_load_extrainfo(void *arg) #define ADD(str) \ do { \ tt_int_op(0,OP_EQ,router_get_extrainfo_hash(str, strlen(str), buf)); \ - smartlist_add(wanted, tor_strdup(hex_str(buf, DIGEST_LEN))); \ + smartlist_add_strdup(wanted, hex_str(buf, DIGEST_LEN)); \ } while (0) mock_ei_insert_list = smartlist_new(); MOCK(router_get_by_extrainfo_digest, mock_get_by_ei_desc_digest); MOCK(extrainfo_insert, mock_ei_insert); - smartlist_add(chunks, tor_strdup(EX_EI_MINIMAL)); - smartlist_add(chunks, tor_strdup(EX_EI_BAD_NICKNAME)); - smartlist_add(chunks, tor_strdup(EX_EI_MAXIMAL)); - smartlist_add(chunks, tor_strdup(EX_EI_BAD_PUBLISHED)); - smartlist_add(chunks, tor_strdup(EX_EI_BAD_TOKENS)); + smartlist_add_strdup(chunks, EX_EI_MINIMAL); + smartlist_add_strdup(chunks, EX_EI_BAD_NICKNAME); + smartlist_add_strdup(chunks, EX_EI_MAXIMAL); + smartlist_add_strdup(chunks, EX_EI_BAD_PUBLISHED); + smartlist_add_strdup(chunks, EX_EI_BAD_TOKENS); /* not ADDing MINIMIAL */ ADD(EX_EI_MAXIMAL); @@ -998,6 +1018,37 @@ test_dir_load_extrainfo(void *arg) } static void +test_dir_getinfo_extra(void *arg) +{ + int r; + char *answer = NULL; + const char *errmsg = NULL; + + (void)arg; + MOCK(extrainfo_get_by_descriptor_digest, mock_ei_get_by_ei_digest); + r = getinfo_helper_dir(NULL, "extra-info/digest/" + "11E0EDF526950739F7769810FCACAB8C882FAEEE", &answer, + &errmsg); + tt_int_op(0, OP_EQ, r); + tt_ptr_op(NULL, OP_EQ, errmsg); + tt_str_op(answer, OP_EQ, EX_EI_MINIMAL); + tor_free(answer); + + answer = NULL; + r = getinfo_helper_dir(NULL, "extra-info/digest/" + "NOTAVALIDHEXSTRINGNOTAVALIDHEXSTRINGNOTA", &answer, + &errmsg); + tt_int_op(0, OP_EQ, r); + /* getinfo_helper_dir() should maybe return an error here but doesn't */ + tt_ptr_op(NULL, OP_EQ, errmsg); + /* In any case, there should be no answer for an invalid hex string. */ + tt_ptr_op(NULL, OP_EQ, answer); + + done: + UNMOCK(extrainfo_get_by_descriptor_digest); +} + +static void test_dir_versions(void *arg) { tor_version_t ver1; @@ -1064,6 +1115,7 @@ test_dir_versions(void *arg) tt_int_op(0, OP_EQ, ver1.patchlevel); tt_int_op(VER_RELEASE, OP_EQ, ver1.status); tt_str_op("alpha", OP_EQ, ver1.status_tag); + /* Go through the full set of status tags */ tt_int_op(0, OP_EQ, tor_version_parse("2.1.700-alpha", &ver1)); tt_int_op(2, OP_EQ, ver1.major); tt_int_op(1, OP_EQ, ver1.minor); @@ -1078,6 +1130,60 @@ test_dir_versions(void *arg) tt_int_op(0, OP_EQ, ver1.patchlevel); tt_int_op(VER_RELEASE, OP_EQ, ver1.status); tt_str_op("alpha-dev", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.5-rc", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(5, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("rc", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.6-rc-dev", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(6, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("rc-dev", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.8", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(8, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.9-dev", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(9, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("dev", OP_EQ, ver1.status_tag); + /* In #21450, we fixed an inconsistency in parsing versions > INT32_MAX + * between i386 and x86_64, as we used tor_parse_long, and then cast to int + */ + tt_int_op(0, OP_EQ, tor_version_parse("0.2147483647.0", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2147483647, OP_EQ, ver1.minor); + tt_int_op(0, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(-1, OP_EQ, tor_version_parse("0.2147483648.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.4294967295.0", &ver1)); + /* In #21278, we reject negative version components */ + tt_int_op(-1, OP_EQ, tor_version_parse("0.-1.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.-2147483648.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.-4294967295.0", &ver1)); + /* In #21507, we reject version components with non-numeric prefixes */ + tt_int_op(-1, OP_EQ, tor_version_parse("0.-0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("+1.0.0", &ver1)); + /* use the list in isspace() */ + tt_int_op(-1, OP_EQ, tor_version_parse("0.\t0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\n0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\v0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\f0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\r0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0. 0.0", &ver1)); #define tt_versionstatus_op(vs1, op, vs2) \ tt_assert_test_type(vs1,vs2,#vs1" "#op" "#vs2,version_status_t, \ @@ -1097,6 +1203,7 @@ test_dir_versions(void *arg) 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"); + test_v_i_o(VS_NEW, "0.2.9.9-dev", "0.2.9.9"); /* 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"); @@ -1135,6 +1242,70 @@ test_dir_versions(void *arg) "Tor 0.2.1.0-dev (r99)")); tt_int_op(1,OP_EQ, tor_version_as_new_as("Tor 0.2.1.1", "Tor 0.2.1.0-dev (r99)")); + /* And git revisions */ + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-56788a2489127072)", + "Tor 0.2.9.9 (git-56788a2489127072)")); + /* a git revision is newer than no git revision */ + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-56788a2489127072)", + "Tor 0.2.9.9")); + /* a longer git revision is newer than a shorter git revision + * this should be true if they prefix-match, but if they don't, they are + * incomparable, because hashes aren't ordered (but we compare their bytes + * anyway) */ + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-56788a2489127072d513cf4baf35a8ff475f3c7b)", + "Tor 0.2.9.9 (git-56788a2489127072)")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-0102)", + "Tor 0.2.9.9 (git-03)")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-0102)", + "Tor 0.2.9.9 (git-00)")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-01)", + "Tor 0.2.9.9 (git-00)")); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-00)", + "Tor 0.2.9.9 (git-01)")); + /* In #21278, we comapre without integer overflows. + * But since #21450 limits version components to [0, INT32_MAX], it is no + * longer possible to cause an integer overflow in tor_version_compare() */ + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.0.0.0", + "Tor 2147483647.0.0.0")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 2147483647.0.0.0", + "Tor 0.0.0.0")); + /* These versions used to cause an overflow, now they don't parse + * (and authorities reject their descriptors), and log a BUG message */ + setup_full_capture_of_logs(LOG_WARN); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.0.0.0", + "Tor 0.-2147483648.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.2147483647.0.0", + "Tor 0.-1.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.2147483647.0.0", + "Tor 0.-2147483648.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 4294967295.0.0.0", + "Tor 0.0.0.0")); + expect_no_log_entry(); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.4294967295.0.0", + "Tor 0.-4294967295.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + teardown_capture_of_logs(); /* Now try git revisions */ tt_int_op(0,OP_EQ, tor_version_parse("0.5.6.7 (git-ff00ff)", &ver1)); @@ -1144,11 +1315,24 @@ test_dir_versions(void *arg) tt_int_op(7,OP_EQ, ver1.patchlevel); tt_int_op(3,OP_EQ, ver1.git_tag_len); tt_mem_op(ver1.git_tag,OP_EQ, "\xff\x00\xff", 3); + /* reject bad hex digits */ tt_int_op(-1,OP_EQ, tor_version_parse("0.5.6.7 (git-ff00xx)", &ver1)); + /* reject odd hex digit count */ tt_int_op(-1,OP_EQ, tor_version_parse("0.5.6.7 (git-ff00fff)", &ver1)); + /* ignore "git " */ tt_int_op(0,OP_EQ, tor_version_parse("0.5.6.7 (git ff00fff)", &ver1)); + /* standard length is 16 hex digits */ + tt_int_op(0,OP_EQ, tor_version_parse("0.5.6.7 (git-0010203040506070)", + &ver1)); + /* length limit is 40 hex digits */ + tt_int_op(0,OP_EQ, tor_version_parse( + "0.5.6.7 (git-000102030405060708090a0b0c0d0e0f10111213)", + &ver1)); + tt_int_op(-1,OP_EQ, tor_version_parse( + "0.5.6.7 (git-000102030405060708090a0b0c0d0e0f1011121314)", + &ver1)); done: - ; + teardown_capture_of_logs(); } /** Run unit tests for directory fp_pair functions. */ @@ -1512,6 +1696,15 @@ test_dir_param_voting(void *arg) tt_int_op(-8,OP_EQ, networkstatus_get_param(&vote4, "ab", -12, -100, -8)); tt_int_op(0,OP_EQ, networkstatus_get_param(&vote4, "foobar", 0, -100, 8)); + tt_int_op(100,OP_EQ, networkstatus_get_overridable_param( + &vote4, -1, "x-yz", 50, 0, 300)); + tt_int_op(30,OP_EQ, networkstatus_get_overridable_param( + &vote4, 30, "x-yz", 50, 0, 300)); + tt_int_op(0,OP_EQ, networkstatus_get_overridable_param( + &vote4, -101, "foobar", 0, -100, 8)); + tt_int_op(-99,OP_EQ, networkstatus_get_overridable_param( + &vote4, -99, "foobar", 0, -100, 8)); + smartlist_add(votes, &vote1); /* Do the first tests without adding all the other votes, for @@ -1893,6 +2086,249 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now) return; } +static void +test_dir_networkstatus_compute_bw_weights_v10(void *arg) +{ + (void) arg; + smartlist_t *chunks = smartlist_new(); + int64_t G, M, E, D, T, weight_scale; + int ret; + weight_scale = 10000; + + /* no case. one or more of the values is 0 */ + G = M = E = D = 0; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(smartlist_len(chunks), OP_EQ, 0); + + /* case 1 */ + /* XXX dir-spec not followed? See #20272. If it isn't closed, then this is + * testing current behavior, not spec. */ + G = E = 10; + M = D = 1; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_int_op(smartlist_len(chunks), OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=3333 " + "Wbe=3000 Wbg=3000 Wbm=10000 Wdb=10000 Web=10000 Wed=3333 Wee=7000 " + "Weg=3333 Wem=7000 Wgb=10000 Wgd=3333 Wgg=7000 Wgm=7000 Wmb=10000 " + "Wmd=3333 Wme=3000 Wmg=3000 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2a E scarce */ + M = 100; + G = 20; + E = D = 5; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=10000 Wee=10000 Weg=10000 " + "Wem=10000 Wgb=10000 Wgd=0 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=0 Wme=0 " + "Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2a G scarce */ + M = 100; + E = 20; + G = D = 5; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=0 Wee=10000 Weg=0 Wem=10000 " + "Wgb=10000 Wgd=10000 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=0 Wme=0 Wmg=0 " + "Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2b1 (Wgg=1, Wmd=Wgd) */ + M = 10; + E = 30; + G = 10; + D = 100; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=4000 " + "Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=2000 Wee=10000 Weg=2000 " + "Wem=10000 Wgb=10000 Wgd=4000 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=4000 " + "Wme=0 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2b2 */ + M = 60; + E = 30; + G = 10; + D = 100; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=666 Wbe=0 " + "Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=3666 Wee=10000 Weg=3666 " + "Wem=10000 Wgb=10000 Wgd=5668 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=666 " + "Wme=0 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2b3 */ + /* XXX I can't get a combination of values that hits this case without error, + * so this just tests that it fails. See #20285. Also see #20284 as 2b3 does + * not follow dir-spec. */ + /* (E < T/3 && G < T/3) && (E+D>=G || G+D>=E) && (M > T/3) */ + M = 80; + E = 30; + G = 30; + D = 30; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 0); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 3a G scarce */ + M = 10; + E = 30; + G = 10; + D = 5; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 " + "Wbe=3333 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=0 Wee=6667 Weg=0 " + "Wem=6667 Wgb=10000 Wgd=10000 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=0 " + "Wme=3333 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 3a E scarce */ + M = 10; + E = 10; + G = 30; + D = 5; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=3333 Wbm=10000 Wdb=10000 Web=10000 Wed=10000 Wee=10000 Weg=10000 " + "Wem=10000 Wgb=10000 Wgd=0 Wgg=6667 Wgm=6667 Wmb=10000 Wmd=0 Wme=0 " + "Wmg=3333 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 3bg */ + M = 10; + E = 30; + G = 10; + D = 10; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 " + "Wbe=3334 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=0 Wee=6666 Weg=0 " + "Wem=6666 Wgb=10000 Wgd=10000 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=0 " + "Wme=3334 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 3be */ + M = 10; + E = 10; + G = 30; + D = 10; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=3334 Wbm=10000 Wdb=10000 Web=10000 Wed=10000 Wee=10000 Weg=10000 " + "Wem=10000 Wgb=10000 Wgd=0 Wgg=6666 Wgm=6666 Wmb=10000 Wmd=0 Wme=0 " + "Wmg=3334 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case from 21 Jul 2013 (3be) */ + G = 5483409; + M = 1455379; + E = 980834; + D = 3385803; + T = 11305425; + tt_i64_op(G+M+E+D, OP_EQ, T); + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=883 Wbe=0 " + "Wbg=3673 Wbm=10000 Wdb=10000 Web=10000 Wed=8233 Wee=10000 Weg=8233 " + "Wem=10000 Wgb=10000 Wgd=883 Wgg=6327 Wgm=6327 Wmb=10000 Wmd=883 Wme=0 " + "Wmg=3673 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case from 04 Oct 2016 (3a E scarce) */ + G=29322240; + M=4721546; + E=1522058; + D=9273571; + T=44839415; + tt_i64_op(G+M+E+D, OP_EQ, T); + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=4194 Wbm=10000 Wdb=10000 Web=10000 Wed=10000 Wee=10000 Weg=10000 " + "Wem=10000 Wgb=10000 Wgd=0 Wgg=5806 Wgm=5806 Wmb=10000 Wmd=0 Wme=0 " + "Wmg=4194 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case from 04 Sep 2013 (2b1) */ + G=3091352; + M=1838837; + E=2109300; + D=2469369; + T=9508858; + tt_i64_op(G+M+E+D, OP_EQ, T); + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=317 " + "Wbe=5938 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=9366 Wee=4061 " + "Weg=9366 Wem=4061 Wgb=10000 Wgd=317 Wgg=10000 Wgm=10000 Wmb=10000 " + "Wmd=317 Wme=5938 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* explicitly test initializing weights to 1*/ + G=1; + M=1; + E=1; + D=1; + T=4; + tt_i64_op(G+M+E+D, OP_EQ, T); + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=3333 " + "Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=3333 Wee=10000 Weg=3333 " + "Wem=10000 Wgb=10000 Wgd=3333 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=3333 " + "Wme=0 Wmg=0 Wmm=10000\n"); + + done: + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_free(chunks); +} + static authority_cert_t *mock_cert; static authority_cert_t * @@ -3272,17 +3708,88 @@ test_dir_http_handling(void *args) } static void -test_dir_purpose_needs_anonymity(void *arg) +test_dir_purpose_needs_anonymity_returns_true_by_default(void *arg) +{ + (void)arg; + + tor_capture_bugs_(1); + setup_full_capture_of_logs(LOG_WARN); + tt_int_op(1, ==, purpose_needs_anonymity(0, 0, NULL)); + tt_int_op(1, ==, smartlist_len(tor_get_captured_bug_log_())); + expect_single_log_msg_containing("Called with dir_purpose=0"); + + tor_end_capture_bugs_(); + done: + tor_end_capture_bugs_(); + teardown_capture_of_logs(); +} + +static void +test_dir_purpose_needs_anonymity_returns_true_for_bridges(void *arg) +{ + (void)arg; + + tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE, NULL)); + tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE, + "foobar")); + tt_int_op(1, ==, purpose_needs_anonymity(DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2, + ROUTER_PURPOSE_BRIDGE, NULL)); + done: ; +} + +static void +test_dir_purpose_needs_anonymity_returns_false_for_own_bridge_desc(void *arg) +{ + (void)arg; + tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_FETCH_SERVERDESC, + ROUTER_PURPOSE_BRIDGE, + "authority.z")); + done: ; +} + +static void +test_dir_purpose_needs_anonymity_returns_true_for_sensitive_purpose(void *arg) { (void)arg; - tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE)); - tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_GENERAL)); - tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_FETCH_MICRODESC, - ROUTER_PURPOSE_GENERAL)); + + tt_int_op(1, ==, purpose_needs_anonymity( + DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2, + ROUTER_PURPOSE_GENERAL, NULL)); + tt_int_op(1, ==, purpose_needs_anonymity( + DIR_PURPOSE_UPLOAD_RENDDESC_V2, 0, NULL)); + tt_int_op(1, ==, purpose_needs_anonymity( + DIR_PURPOSE_FETCH_RENDDESC_V2, 0, NULL)); done: ; } static void +test_dir_purpose_needs_anonymity_ret_false_for_non_sensitive_conn(void *arg) +{ + (void)arg; + + tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_DIR, + ROUTER_PURPOSE_GENERAL, NULL)); + tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_VOTE, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_SIGNATURES, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL)); + tt_int_op(0, ==, purpose_needs_anonymity( + DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_CONSENSUS, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_SERVERDESC, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_EXTRAINFO, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_MICRODESC, 0, NULL)); + done: ; +} + +static void test_dir_fetch_type(void *arg) { (void)arg; @@ -4054,7 +4561,6 @@ test_dir_should_use_directory_guards(void *data) tt_int_op(should_use_directory_guards(options), OP_EQ, 0); tt_int_op(CALLED(public_server_mode), OP_EQ, 1); - options->UseEntryGuardsAsDirGuards = 1; options->UseEntryGuards = 1; options->DownloadExtraInfo = 0; options->FetchDirInfoEarly = 0; @@ -4068,29 +4574,24 @@ test_dir_should_use_directory_guards(void *data) tt_int_op(CALLED(public_server_mode), OP_EQ, 3); options->UseEntryGuards = 1; - options->UseEntryGuardsAsDirGuards = 0; - tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 4); - options->UseEntryGuardsAsDirGuards = 1; - options->DownloadExtraInfo = 1; tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 5); + tt_int_op(CALLED(public_server_mode), OP_EQ, 4); options->DownloadExtraInfo = 0; options->FetchDirInfoEarly = 1; tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 6); + tt_int_op(CALLED(public_server_mode), OP_EQ, 5); options->FetchDirInfoEarly = 0; options->FetchDirInfoExtraEarly = 1; tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 7); + tt_int_op(CALLED(public_server_mode), OP_EQ, 6); options->FetchDirInfoExtraEarly = 0; options->FetchUselessDescriptors = 1; tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 8); + tt_int_op(CALLED(public_server_mode), OP_EQ, 7); options->FetchUselessDescriptors = 0; done: @@ -4100,14 +4601,7 @@ test_dir_should_use_directory_guards(void *data) } NS_DECL(void, -directory_initiate_command_routerstatus, (const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since)); +directory_initiate_request, (directory_request_t *req)); static void test_dir_should_not_init_request_to_ourselves(void *data) @@ -4117,7 +4611,7 @@ test_dir_should_not_init_request_to_ourselves(void *data) crypto_pk_t *key = pk_generate(2); (void) data; - NS_MOCK(directory_initiate_command_routerstatus); + NS_MOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); @@ -4132,15 +4626,15 @@ test_dir_should_not_init_request_to_ourselves(void *data) dir_server_add(ourself); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 0); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 0); done: - NS_UNMOCK(directory_initiate_command_routerstatus); + NS_UNMOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); crypto_pk_free(key); @@ -4154,7 +4648,7 @@ test_dir_should_not_init_request_to_dir_auths_without_v3_info(void *data) | MICRODESC_DIRINFO; (void) data; - NS_MOCK(directory_initiate_command_routerstatus); + NS_MOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); @@ -4165,14 +4659,14 @@ test_dir_should_not_init_request_to_dir_auths_without_v3_info(void *data) dir_server_add(ds); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 0); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 0); done: - NS_UNMOCK(directory_initiate_command_routerstatus); + NS_UNMOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); } @@ -4183,7 +4677,7 @@ test_dir_should_init_request_to_dir_auths(void *data) dir_server_t *ds = NULL; (void) data; - NS_MOCK(directory_initiate_command_routerstatus); + NS_MOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); @@ -4194,37 +4688,23 @@ test_dir_should_init_request_to_dir_auths(void *data) dir_server_add(ds); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 1); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 1); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 2); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 2); done: - NS_UNMOCK(directory_initiate_command_routerstatus); + NS_UNMOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); } void -NS(directory_initiate_command_routerstatus)(const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since) +NS(directory_initiate_request)(directory_request_t *req) { - (void)status; - (void)dir_purpose; - (void)router_purpose; - (void)indirection; - (void)resource; - (void)payload; - (void)payload_len; - (void)if_modified_since; - CALLED(directory_initiate_command_routerstatus)++; + (void)req; + CALLED(directory_initiate_request)++; } static void @@ -5167,9 +5647,9 @@ listdir_mock(const char *dname) (void)dname; l = smartlist_new(); - smartlist_add(l, tor_strdup("foo")); - smartlist_add(l, tor_strdup("bar")); - smartlist_add(l, tor_strdup("baz")); + smartlist_add_strdup(l, "foo"); + smartlist_add_strdup(l, "bar"); + smartlist_add_strdup(l, "baz"); return l; } @@ -5456,6 +5936,67 @@ test_dir_assumed_flags(void *arg) routerstatus_free(rs); } +static void +test_dir_post_parsing(void *arg) +{ + (void) arg; + + /* Test the version parsing from an HS descriptor publish request. */ + { + const char *end; + const char *prefix = "/tor/hs/"; + int version = parse_hs_version_from_post("/tor/hs//publish", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/a/publish", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/3/publish", prefix, &end); + tt_int_op(version, OP_EQ, 3); + tt_str_op(end, OP_EQ, "/publish"); + version = parse_hs_version_from_post("/tor/hs/42/publish", prefix, &end); + tt_int_op(version, OP_EQ, 42); + tt_str_op(end, OP_EQ, "/publish"); + version = parse_hs_version_from_post("/tor/hs/18163/publish",prefix, &end); + tt_int_op(version, OP_EQ, 18163); + tt_str_op(end, OP_EQ, "/publish"); + version = parse_hs_version_from_post("JUNKJUNKJUNK", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/3/publish", "blah", &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + /* Missing the '/' at the end of the prefix. */ + version = parse_hs_version_from_post("/tor/hs/3/publish", "/tor/hs", &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/random/blah/tor/hs/3/publish", + prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/3/publish/random/junk", + prefix, &end); + tt_int_op(version, OP_EQ, 3); + tt_str_op(end, OP_EQ, "/publish/random/junk"); + version = parse_hs_version_from_post("/tor/hs/-1/publish", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + /* INT_MAX */ + version = parse_hs_version_from_post("/tor/hs/2147483647/publish", + prefix, &end); + tt_int_op(version, OP_EQ, INT_MAX); + tt_str_op(end, OP_EQ, "/publish"); + /* INT_MAX + 1*/ + version = parse_hs_version_from_post("/tor/hs/2147483648/publish", + prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + } + + done: + ; +} + #define DIR_LEGACY(name) \ { #name, test_dir_ ## name , TT_FORK, NULL, NULL } @@ -5474,6 +6015,7 @@ struct testcase_t dir_tests[] = { DIR(parse_router_list, TT_FORK), DIR(load_routers, TT_FORK), DIR(load_extrainfo, TT_FORK), + DIR(getinfo_extra, 0), DIR_LEGACY(versions), DIR_LEGACY(fp_pairs), DIR(split_fps, 0), @@ -5490,7 +6032,12 @@ struct testcase_t dir_tests[] = { DIR(fmt_control_ns, 0), DIR(dirserv_set_routerstatus_testing, 0), DIR(http_handling, 0), - DIR(purpose_needs_anonymity, 0), + DIR(purpose_needs_anonymity_returns_true_for_bridges, 0), + DIR(purpose_needs_anonymity_returns_false_for_own_bridge_desc, 0), + DIR(purpose_needs_anonymity_returns_true_by_default, 0), + DIR(purpose_needs_anonymity_returns_true_for_sensitive_purpose, 0), + DIR(purpose_needs_anonymity_ret_false_for_non_sensitive_conn, 0), + DIR(post_parsing, 0), DIR(fetch_type, 0), DIR(packages, 0), DIR(download_status_schedule, 0), @@ -5511,6 +6058,7 @@ struct testcase_t dir_tests[] = { DIR_ARG(find_dl_schedule, TT_FORK, "cf"), DIR_ARG(find_dl_schedule, TT_FORK, "ca"), DIR(assumed_flags, 0), + DIR(networkstatus_compute_bw_weights_v10, 0), END_OF_TESTCASES }; diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c index ca43dd4c04..fca70249bd 100644 --- a/src/test/test_dir_common.c +++ b/src/test/test_dir_common.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_dir_common.h b/src/test/test_dir_common.h index 9682b0db49..65b9cf6436 100644 --- a/src/test/test_dir_common.h +++ b/src/test/test_dir_common.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index a0f22f1f0c..75fe6249ad 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define RENDCOMMON_PRIVATE @@ -12,8 +12,10 @@ #include "or.h" #include "config.h" #include "connection.h" +#include "consdiffmgr.h" #include "directory.h" #include "test.h" +#include "compress.h" #include "connection.h" #include "rendcommon.h" #include "rendcache.h" @@ -28,8 +30,8 @@ #include "networkstatus.h" #include "geoip.h" #include "dirserv.h" -#include "torgzip.h" #include "dirvote.h" +#include "log_test_helpers.h" #ifdef _WIN32 /* For mkdir() */ @@ -50,22 +52,10 @@ ENABLE_GCC_WARNING(overlength-strings) #define NS_MODULE dir_handle_get -static void -connection_write_to_buf_mock(const char *string, size_t len, - connection_t *conn, int zlib) -{ - (void) zlib; - - tor_assert(string); - tor_assert(conn); - - write_to_buf(string, len, conn->outbuf); -} - -#define GET(path) "GET " path " HTTP/1.0\r\n\r\n" #define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n" #define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n" #define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again later\r\n\r\n" +#define TOO_OLD "HTTP/1.0 404 Consensus is too old\r\n\r\n" #define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \ "Consensus not signed by sufficient number of requested authorities\r\n\r\n" @@ -74,6 +64,7 @@ new_dir_conn(void) { dir_connection_t *conn = dir_connection_new(AF_INET); tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001); + TO_CONN(conn)->address = tor_strdup("127.0.0.1"); return conn; } @@ -476,6 +467,8 @@ init_mock_options(void) mock_options = tor_malloc(sizeof(or_options_t)); memset(mock_options, 0, sizeof(or_options_t)); mock_options->TestingTorNetwork = 1; + mock_options->DataDirectory = tor_strdup(get_fname_rnd("datadir_tmp")); + check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL); } static const or_options_t * @@ -512,14 +505,6 @@ test_dir_handle_get_micro_d(void *data) /* SETUP */ init_mock_options(); - const char *fn = get_fname("dir_handle_datadir_test1"); - mock_options->DataDirectory = tor_strdup(fn); - -#ifdef _WIN32 - tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory)); -#else - tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700)); -#endif /* Add microdesc to cache */ crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256); @@ -579,14 +564,6 @@ test_dir_handle_get_micro_d_server_busy(void *data) /* SETUP */ init_mock_options(); - const char *fn = get_fname("dir_handle_datadir_test2"); - mock_options->DataDirectory = tor_strdup(fn); - -#ifdef _WIN32 - tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory)); -#else - tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700)); -#endif /* Add microdesc to cache */ crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256); @@ -754,7 +731,7 @@ test_dir_handle_get_server_descriptors_not_found(void* data) NULL, NULL, 1, 0); tt_str_op(NOT_FOUND, OP_EQ, header); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_SERVER_BY_FP); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: UNMOCK(connection_write_to_buf_impl_); @@ -784,6 +761,7 @@ test_dir_handle_get_server_descriptors_all(void* data) tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1); mock_routerinfo = smartlist_get(our_routerlist->routers, 0); set_server_identity_key(mock_routerinfo->identity_pkey); + mock_routerinfo->cache_info.published_on = time(NULL); /* Treat "all" requests as if they were unencrypted */ mock_routerinfo->cache_info.send_unencrypted = 1; @@ -798,7 +776,7 @@ test_dir_handle_get_server_descriptors_all(void* data) //which is smaller than that by annotation_len bytes fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, &body, &body_used, - mock_routerinfo->cache_info.signed_descriptor_len+1, 0); + 1024*1024, 0); tt_assert(header); tt_assert(body); @@ -814,7 +792,7 @@ test_dir_handle_get_server_descriptors_all(void* data) tt_str_op(body, OP_EQ, mock_routerinfo->cache_info.signed_descriptor_body + mock_routerinfo->cache_info.annotations_len); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: NS_UNMOCK(router_get_my_routerinfo); @@ -893,6 +871,7 @@ test_dir_handle_get_server_descriptors_authority(void* data) mock_routerinfo->cache_info.signed_descriptor_len = strlen(TEST_DESCRIPTOR) - annotation_len;; mock_routerinfo->cache_info.annotations_len = annotation_len; + mock_routerinfo->cache_info.published_on = time(NULL); conn = new_dir_conn(); @@ -915,7 +894,7 @@ test_dir_handle_get_server_descriptors_authority(void* data) tt_int_op(body_used, OP_EQ, strlen(body)); tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: NS_UNMOCK(router_get_my_routerinfo); @@ -957,6 +936,7 @@ test_dir_handle_get_server_descriptors_fp(void* data) mock_routerinfo->cache_info.signed_descriptor_len = strlen(TEST_DESCRIPTOR) - annotation_len; mock_routerinfo->cache_info.annotations_len = annotation_len; + mock_routerinfo->cache_info.published_on = time(NULL); conn = new_dir_conn(); @@ -986,7 +966,7 @@ test_dir_handle_get_server_descriptors_fp(void* data) tt_int_op(body_used, OP_EQ, strlen(body)); tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: NS_UNMOCK(router_get_my_routerinfo); @@ -1052,7 +1032,7 @@ test_dir_handle_get_server_descriptors_d(void* data) tt_str_op(body, OP_EQ, router->cache_info.signed_descriptor_body + router->cache_info.annotations_len); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: UNMOCK(connection_write_to_buf_impl_); @@ -1107,7 +1087,7 @@ test_dir_handle_get_server_descriptors_busy(void* data) tt_assert(header); tt_str_op(SERVER_BUSY, OP_EQ, header); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: UNMOCK(get_options); @@ -1629,7 +1609,13 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d) /* init mock */ mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); mock_ns_val->flavor = FLAV_NS; + mock_ns_val->type = NS_TYPE_CONSENSUS; mock_ns_val->voters = smartlist_new(); + mock_ns_val->valid_after = time(NULL) - 1800; + mock_ns_val->valid_until = time(NULL) - 60; + + #define NETWORK_STATUS "some network status string" + consdiffmgr_add_consensus(NETWORK_STATUS, mock_ns_val); /* init mock */ init_mock_options(); @@ -1709,6 +1695,80 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data) or_options_free(mock_options); mock_options = NULL; } +static void +test_dir_handle_get_status_vote_current_consensus_too_old(void *data) +{ + dir_connection_t *conn = NULL; + char *header = NULL; + (void)data; + + mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); + mock_ns_val->type = NS_TYPE_CONSENSUS; + mock_ns_val->flavor = FLAV_MICRODESC; + mock_ns_val->valid_after = time(NULL) - (24 * 60 * 60 + 1800); + mock_ns_val->fresh_until = time(NULL) - (24 * 60 * 60 + 900); + mock_ns_val->valid_until = time(NULL) - (24 * 60 * 60 + 20); + + #define NETWORK_STATUS "some network status string" + consdiffmgr_add_consensus(NETWORK_STATUS, mock_ns_val); + + init_mock_options(); + + MOCK(get_options, mock_get_options); + MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); + MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor); + + conn = new_dir_conn(); + + setup_capture_of_logs(LOG_WARN); + + tt_int_op(0, OP_EQ, directory_handle_command_get(conn, + GET("/tor/status-vote/current/consensus-microdesc"), NULL, 0)); + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, + NULL, NULL, 1, 0); + tt_assert(header); + tt_str_op(TOO_OLD, OP_EQ, header); + + expect_log_msg_containing("too old"); + + tor_free(header); + teardown_capture_of_logs(); + tor_free(mock_ns_val); + + mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); + mock_ns_val->type = NS_TYPE_CONSENSUS; + mock_ns_val->flavor = FLAV_NS; + mock_ns_val->valid_after = time(NULL) - (24 * 60 * 60 + 1800); + mock_ns_val->fresh_until = time(NULL) - (24 * 60 * 60 + 900); + mock_ns_val->valid_until = time(NULL) - (24 * 60 * 60 + 20); + + #define NETWORK_STATUS "some network status string" + consdiffmgr_add_consensus(NETWORK_STATUS, mock_ns_val); + + setup_capture_of_logs(LOG_WARN); + + tt_int_op(0, OP_EQ, directory_handle_command_get(conn, + GET("/tor/status-vote/current/consensus"), NULL, 0)); + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, + NULL, NULL, 1, 0); + tt_assert(header); + tt_str_op(TOO_OLD, OP_EQ, header); + + expect_no_log_entry(); + + done: + teardown_capture_of_logs(); + UNMOCK(networkstatus_get_latest_consensus_by_flavor); + UNMOCK(connection_write_to_buf_impl_); + UNMOCK(get_options); + connection_free_(TO_CONN(conn)); + tor_free(header); + tor_free(mock_ns_val); + or_options_free(mock_options); mock_options = NULL; +} + NS_DECL(int, geoip_get_country_by_addr, (const tor_addr_t *addr)); int @@ -1723,12 +1783,26 @@ static void status_vote_current_consensus_ns_test(char **header, char **body, size_t *body_len) { - common_digests_t digests; dir_connection_t *conn = NULL; #define NETWORK_STATUS "some network status string" +#if 0 + common_digests_t digests; + uint8_t sha3[DIGEST256_LEN]; + memset(&digests, 0x60, sizeof(digests)); + memset(sha3, 0x06, sizeof(sha3)); dirserv_set_cached_consensus_networkstatus(NETWORK_STATUS, "ns", &digests, + sha3, time(NULL)); +#endif + networkstatus_t *ns = tor_malloc_zero(sizeof(networkstatus_t)); + ns->type = NS_TYPE_CONSENSUS; + ns->flavor = FLAV_NS; + ns->valid_after = time(NULL) - 1800; + ns->fresh_until = time(NULL) - 900; + ns->valid_until = time(NULL) - 60; + consdiffmgr_add_consensus(NETWORK_STATUS, ns); + networkstatus_vote_free(ns); MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); @@ -1741,7 +1815,6 @@ status_vote_current_consensus_ns_test(char **header, char **body, tt_str_op("ab", OP_EQ, geoip_get_country_name(1)); conn = new_dir_conn(); - TO_CONN(conn)->address = tor_strdup("127.0.0.1"); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/current/consensus-ns"), NULL, 0)); @@ -1783,8 +1856,8 @@ test_dir_handle_get_status_vote_current_consensus_ns(void* data) comp_body_used); tt_int_op(ZLIB_METHOD, OP_EQ, compression); - tor_gzip_uncompress(&body, &body_used, comp_body, comp_body_used, - compression, 0, LOG_PROTOCOL_WARN); + tor_uncompress(&body, &body_used, comp_body, comp_body_used, + compression, 0, LOG_PROTOCOL_WARN); tt_str_op(NETWORK_STATUS, OP_EQ, body); tt_int_op(strlen(NETWORK_STATUS), OP_EQ, body_used); @@ -2448,6 +2521,53 @@ test_dir_handle_get_status_vote_current_authority(void* data) dirvote_free_all(); } +static void +test_dir_handle_get_parse_accept_encoding(void *arg) +{ + (void)arg; + const unsigned B_NONE = 1u << NO_METHOD; + const unsigned B_ZLIB = 1u << ZLIB_METHOD; + const unsigned B_GZIP = 1u << GZIP_METHOD; + const unsigned B_LZMA = 1u << LZMA_METHOD; + const unsigned B_ZSTD = 1u << ZSTD_METHOD; + + unsigned encodings; + + encodings = parse_accept_encoding_header(""); + tt_uint_op(B_NONE, OP_EQ, encodings); + + encodings = parse_accept_encoding_header(" "); + tt_uint_op(B_NONE, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("dewey, cheatham, and howe "); + tt_uint_op(B_NONE, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("dewey, cheatham, and gzip"); + tt_uint_op(B_NONE, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("dewey, cheatham, and, gzip"); + tt_uint_op(B_NONE|B_GZIP, OP_EQ, encodings); + + encodings = parse_accept_encoding_header(" gzip"); + tt_uint_op(B_NONE|B_GZIP, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("gzip"); + tt_uint_op(B_NONE|B_GZIP, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("x-zstd, deflate, x-tor-lzma"); + tt_uint_op(B_NONE|B_ZLIB|B_ZSTD|B_LZMA, OP_EQ, encodings); + + encodings = parse_accept_encoding_header( + "x-zstd, deflate, x-tor-lzma, gzip"); + tt_uint_op(B_NONE|B_ZLIB|B_ZSTD|B_LZMA|B_GZIP, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("x-zstd,deflate,x-tor-lzma,gzip"); + tt_uint_op(B_NONE|B_ZLIB|B_ZSTD|B_LZMA|B_GZIP, OP_EQ, encodings); + + done: + ; +} + #define DIR_HANDLE_CMD(name,flags) \ { #name, test_dir_handle_get_##name, (flags), NULL, NULL } @@ -2492,10 +2612,11 @@ struct testcase_t dir_handle_get_tests[] = { DIR_HANDLE_CMD(status_vote_current_authority, 0), DIR_HANDLE_CMD(status_vote_next_authority_not_found, 0), DIR_HANDLE_CMD(status_vote_next_authority, 0), - DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, 0), - DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, 0), - DIR_HANDLE_CMD(status_vote_current_consensus_ns_busy, 0), - DIR_HANDLE_CMD(status_vote_current_consensus_ns, 0), + DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, TT_FORK), + DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, TT_FORK), + DIR_HANDLE_CMD(status_vote_current_consensus_too_old, TT_FORK), + DIR_HANDLE_CMD(status_vote_current_consensus_ns_busy, TT_FORK), + DIR_HANDLE_CMD(status_vote_current_consensus_ns, TT_FORK), DIR_HANDLE_CMD(status_vote_current_d_not_found, 0), DIR_HANDLE_CMD(status_vote_next_d_not_found, 0), DIR_HANDLE_CMD(status_vote_d, 0), @@ -2505,6 +2626,7 @@ struct testcase_t dir_handle_get_tests[] = { DIR_HANDLE_CMD(status_vote_next_consensus_signatures_not_found, 0), DIR_HANDLE_CMD(status_vote_next_consensus_signatures_busy, 0), DIR_HANDLE_CMD(status_vote_next_consensus_signatures, 0), + DIR_HANDLE_CMD(parse_accept_encoding, 0), END_OF_TESTCASES }; diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c index 9580a1fd3f..12a631630b 100644 --- a/src/test/test_entryconn.c +++ b/src/test/test_entryconn.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -100,7 +100,7 @@ test_entryconn_rewrite_automap_ipv4(void *arg) ec3 = entry_connection_new(CONN_TYPE_AP, AF_INET); get_options_mutable()->AutomapHostsOnResolve = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, tor_strdup(".")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, "."); parse_virtual_addr_network("127.202.0.0/16", AF_INET, 0, &msg); /* Automap this on resolve. */ @@ -173,7 +173,7 @@ test_entryconn_rewrite_automap_ipv6(void *arg) ec3 = entry_connection_new(CONN_TYPE_AP, AF_INET6); get_options_mutable()->AutomapHostsOnResolve = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, tor_strdup(".")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, "."); parse_virtual_addr_network("FE80::/32", AF_INET6, 0, &msg); /* Automap this on resolve. */ @@ -489,8 +489,8 @@ test_entryconn_rewrite_automap_exit(void *arg) get_options_mutable()->AutomapHostsOnResolve = 1; get_options_mutable()->AllowDotExit = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, - tor_strdup(".EXIT")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, + ".EXIT"); parse_virtual_addr_network("127.1.0.0/16", AF_INET, 0, &msg); /* Automap this on resolve. */ @@ -574,8 +574,8 @@ test_entryconn_rewrite_mapaddress_automap_onion(void *arg) get_options_mutable()->AutomapHostsOnResolve = 1; get_options_mutable()->AllowDotExit = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, - tor_strdup(".onion")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, + ".onion"); parse_virtual_addr_network("192.168.0.0/16", AF_INET, 0, &msg); config_line_append(&get_options_mutable()->AddressMap, "MapAddress", "foo.onion abcdefghijklmnop.onion"); @@ -709,8 +709,8 @@ test_entryconn_rewrite_mapaddress_automap_onion2(void *arg) { char *msg = NULL; get_options_mutable()->AutomapHostsOnResolve = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, - tor_strdup(".onion")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, + ".onion"); parse_virtual_addr_network("192.168.0.0/16", AF_INET, 0, &msg); config_line_append(&get_options_mutable()->AddressMap, "MapAddress", "irc.example.com abcdefghijklmnop.onion"); @@ -736,8 +736,8 @@ test_entryconn_rewrite_mapaddress_automap_onion4(void *arg) { char *msg = NULL; get_options_mutable()->AutomapHostsOnResolve = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, - tor_strdup(".onion")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, + ".onion"); parse_virtual_addr_network("192.168.0.0/16", AF_INET, 0, &msg); test_entryconn_rewrite_mapaddress_automap_onion_common(arg, 0, 1); diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index b1c3accfab..1f008d93b3 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -1,8 +1,9 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" +#define CIRCUITLIST_PRIVATE #define STATEFILE_PRIVATE #define ENTRYNODES_PRIVATE #define ROUTERLIST_PRIVATE @@ -10,9 +11,13 @@ #include "or.h" #include "test.h" +#include "bridges.h" +#include "circuitlist.h" #include "config.h" +#include "confparse.h" #include "entrynodes.h" #include "nodelist.h" +#include "networkstatus.h" #include "policies.h" #include "routerlist.h" #include "routerparse.h" @@ -21,6 +26,7 @@ #include "util.h" #include "test_helpers.h" +#include "log_test_helpers.h" /* TODO: * choose_random_entry() test with state set. @@ -39,37 +45,120 @@ get_or_state_replacement(void) return dummy_state; } +static networkstatus_t *dummy_consensus = NULL; + +static smartlist_t *big_fake_net_nodes = NULL; + +static smartlist_t * +bfn_mock_nodelist_get_list(void) +{ + return big_fake_net_nodes; +} + +static networkstatus_t * +bfn_mock_networkstatus_get_live_consensus(time_t now) +{ + (void)now; + return dummy_consensus; +} + +static const node_t * +bfn_mock_node_get_by_id(const char *id) +{ + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, + if (fast_memeq(n->identity, id, 20)) + return n); + + return NULL; +} + /* Unittest cleanup function: Cleanup the fake network. */ static int -fake_network_cleanup(const struct testcase_t *testcase, void *ptr) +big_fake_network_cleanup(const struct testcase_t *testcase, void *ptr) { (void) testcase; (void) ptr; - routerlist_free_all(); - nodelist_free_all(); - entry_guards_free_all(); + if (big_fake_net_nodes) { + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, { + tor_free(n->rs); + tor_free(n->md); + tor_free(n); + }); + smartlist_free(big_fake_net_nodes); + } + + UNMOCK(nodelist_get_list); + UNMOCK(node_get_by_id); + UNMOCK(get_or_state); + UNMOCK(networkstatus_get_live_consensus); or_state_free(dummy_state); + dummy_state = NULL; + tor_free(dummy_consensus); return 1; /* NOP */ } /* Unittest setup function: Setup a fake network. */ static void * -fake_network_setup(const struct testcase_t *testcase) +big_fake_network_setup(const struct testcase_t *testcase) { - (void) testcase; + int i; + + /* These are minimal node_t objects that only contain the aspects of node_t + * that we need for entrynodes.c. */ + const int N_NODES = 271; + + big_fake_net_nodes = smartlist_new(); + for (i = 0; i < N_NODES; ++i) { + node_t *n = tor_malloc_zero(sizeof(node_t)); + n->md = tor_malloc_zero(sizeof(microdesc_t)); + + crypto_rand(n->identity, sizeof(n->identity)); + n->rs = tor_malloc_zero(sizeof(routerstatus_t)); + + memcpy(n->rs->identity_digest, n->identity, DIGEST_LEN); + + n->is_running = n->is_valid = n->is_fast = n->is_stable = 1; + + /* Note: all these guards have the same address, so you'll need to + * disable EnforceDistinctSubnets when a restriction is applied. */ + n->rs->addr = 0x04020202; + n->rs->or_port = 1234; + n->rs->is_v2_dir = 1; + n->rs->has_bandwidth = 1; + n->rs->bandwidth_kb = 30; + + /* Call half of the nodes a possible guard. */ + if (i % 2 == 0) { + n->is_possible_guard = 1; + n->rs->guardfraction_percentage = 100; + n->rs->has_guardfraction = 1; + } + + smartlist_add(big_fake_net_nodes, n); + } - /* Setup fake state */ dummy_state = tor_malloc_zero(sizeof(or_state_t)); + dummy_consensus = tor_malloc_zero(sizeof(networkstatus_t)); + dummy_consensus->valid_after = approx_time() - 3600; + dummy_consensus->valid_until = approx_time() + 3600; + + MOCK(nodelist_get_list, bfn_mock_nodelist_get_list); + MOCK(node_get_by_id, bfn_mock_node_get_by_id); MOCK(get_or_state, get_or_state_replacement); - - /* Setup fake routerlist. */ - helper_setup_fake_routerlist(); - + MOCK(networkstatus_get_live_consensus, + bfn_mock_networkstatus_get_live_consensus); /* Return anything but NULL (it's interpreted as test fail) */ - return dummy_state; + return (void*)testcase; +} + +static time_t +mock_randomize_time_no_randomization(time_t a, time_t b) +{ + (void) b; + return a; } static or_options_t mocked_options; @@ -80,796 +169,2563 @@ mock_get_options(void) return &mocked_options; } -/** Test choose_random_entry() with none of our routers being guard nodes. */ +#define TEST_IPV4_ADDR "123.45.67.89" +#define TEST_IPV6_ADDR "[1234:5678:90ab:cdef::]" + static void -test_choose_random_entry_no_guards(void *arg) +test_node_preferred_orport(void *arg) { - const node_t *chosen_entry = NULL; - - (void) arg; - - MOCK(get_options, mock_get_options); + (void)arg; + tor_addr_t ipv4_addr; + const uint16_t ipv4_port = 4444; + tor_addr_t ipv6_addr; + const uint16_t ipv6_port = 6666; + routerinfo_t node_ri; + node_t node; + tor_addr_port_t ap; - /* Check that we get a guard if it passes preferred - * address settings */ + /* Setup options */ memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientPreferIPv6ORPort = 0; - - /* Try to pick an entry even though none of our routers are guards. */ - chosen_entry = choose_random_entry(NULL); - - /* Unintuitively, we actually pick a random node as our entry, - because router_choose_random_node() relaxes its constraints if it - can't find a proper entry guard. */ - tt_assert(chosen_entry); + /* We don't test ClientPreferIPv6ORPort here, because it's used in + * nodelist_set_consensus to setup node.ipv6_preferred, which we set + * directly. */ + MOCK(get_options, mock_get_options); - /* And with the other IP version active */ - mocked_options.ClientUseIPv6 = 1; - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + /* Setup IP addresses */ + tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR); + tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR); - /* And with the preference on auto */ - mocked_options.ClientPreferIPv6ORPort = -1; - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + /* Setup node_ri */ + memset(&node_ri, 0, sizeof(node_ri)); + node_ri.addr = tor_addr_to_ipv4h(&ipv4_addr); + node_ri.or_port = ipv4_port; + tor_addr_copy(&node_ri.ipv6_addr, &ipv6_addr); + node_ri.ipv6_orport = ipv6_port; - /* Check that we don't get a guard if it doesn't pass mandatory address - * settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 0; - mocked_options.ClientPreferIPv6ORPort = 0; + /* Setup node */ + memset(&node, 0, sizeof(node)); + node.ri = &node_ri; - chosen_entry = choose_random_entry(NULL); + /* Check the preferred address is IPv4 if we're only using IPv4, regardless + * of whether we prefer it or not */ + mocked_options.ClientUseIPv4 = 1; + mocked_options.ClientUseIPv6 = 0; + node.ipv6_preferred = 0; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); + tt_assert(ap.port == ipv4_port); - /* If we don't allow IPv4 at all, we don't get a guard*/ - tt_assert(!chosen_entry); + node.ipv6_preferred = 1; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); + tt_assert(ap.port == ipv4_port); - /* Check that we get a guard if it passes allowed but not preferred address - * settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); + /* Check the preferred address is IPv4 if we're using IPv4 and IPv6, but + * don't prefer the IPv6 address */ mocked_options.ClientUseIPv4 = 1; mocked_options.ClientUseIPv6 = 1; - mocked_options.ClientPreferIPv6ORPort = 1; - - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + node.ipv6_preferred = 0; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); + tt_assert(ap.port == ipv4_port); - /* Check that we get a guard if it passes preferred address settings when - * they're auto */ - memset(&mocked_options, 0, sizeof(mocked_options)); + /* Check the preferred address is IPv6 if we prefer it and + * ClientUseIPv6 is 1, regardless of ClientUseIPv4 */ mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientPreferIPv6ORPort = -1; + mocked_options.ClientUseIPv6 = 1; + node.ipv6_preferred = 1; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); + tt_assert(ap.port == ipv6_port); - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + mocked_options.ClientUseIPv4 = 0; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); + tt_assert(ap.port == ipv6_port); - /* And with IPv6 active */ + /* Check the preferred address is IPv6 if we don't prefer it, but + * ClientUseIPv4 is 0 */ + mocked_options.ClientUseIPv4 = 0; mocked_options.ClientUseIPv6 = 1; - - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(&mocked_options); + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); + tt_assert(ap.port == ipv6_port); done: - memset(&mocked_options, 0, sizeof(mocked_options)); UNMOCK(get_options); } -/** Test choose_random_entry() with only one of our routers being a - guard node. */ static void -test_choose_random_entry_one_possible_guard(void *arg) +test_entry_guard_describe(void *arg) { - const node_t *chosen_entry = NULL; - node_t *the_guard = NULL; - smartlist_t *our_nodelist = NULL; + (void)arg; + entry_guard_t g; + memset(&g, 0, sizeof(g)); + strlcpy(g.nickname, "okefenokee", sizeof(g.nickname)); + memcpy(g.identity, "theforestprimeval---", DIGEST_LEN); - (void) arg; + tt_str_op(entry_guard_describe(&g), OP_EQ, + "okefenokee ($746865666F726573747072696D6576616C2D2D2D)"); - MOCK(get_options, mock_get_options); - - /* Set one of the nodes to be a guard. */ - our_nodelist = nodelist_get_list(); - the_guard = smartlist_get(our_nodelist, 4); /* chosen by fair dice roll */ - the_guard->is_possible_guard = 1; + done: + ; +} - /* Check that we get the guard if it passes preferred - * address settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientPreferIPv6ORPort = 0; +static void +test_entry_guard_randomize_time(void *arg) +{ + const time_t now = 1479153573; + const int delay = 86400; + const int N = 1000; + (void)arg; - /* Pick an entry. Make sure we pick the node we marked as guard. */ - chosen_entry = choose_random_entry(NULL); - tt_ptr_op(chosen_entry, OP_EQ, the_guard); + time_t t; + int i; + for (i = 0; i < N; ++i) { + t = randomize_time(now, delay); + tt_int_op(t, OP_LE, now); + tt_int_op(t, OP_GE, now-delay); + } - /* And with the other IP version active */ - mocked_options.ClientUseIPv6 = 1; - chosen_entry = choose_random_entry(NULL); - tt_ptr_op(chosen_entry, OP_EQ, the_guard); + /* now try the corner cases */ + for (i = 0; i < N; ++i) { + t = randomize_time(100, delay); + tt_int_op(t, OP_GE, 1); + tt_int_op(t, OP_LE, 100); - /* And with the preference on auto */ - mocked_options.ClientPreferIPv6ORPort = -1; - chosen_entry = choose_random_entry(NULL); - tt_ptr_op(chosen_entry, OP_EQ, the_guard); + t = randomize_time(0, delay); + tt_int_op(t, OP_EQ, 1); + } - /* Check that we don't get a guard if it doesn't pass mandatory address - * settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 0; - mocked_options.ClientPreferIPv6ORPort = 0; + done: + ; +} - chosen_entry = choose_random_entry(NULL); +static void +test_entry_guard_encode_for_state_minimal(void *arg) +{ + (void) arg; + entry_guard_t *eg = tor_malloc_zero(sizeof(entry_guard_t)); - /* If we don't allow IPv4 at all, we don't get a guard*/ - tt_assert(!chosen_entry); + eg->selection_name = tor_strdup("wubwub"); + memcpy(eg->identity, "plurpyflurpyslurpydo", DIGEST_LEN); + eg->sampled_on_date = 1479081600; + eg->confirmed_idx = -1; - /* Check that we get a node if it passes allowed but not preferred - * address settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 1; - mocked_options.ClientPreferIPv6ORPort = 1; + char *s = NULL; + s = entry_guard_encode_for_state(eg); - chosen_entry = choose_random_entry(NULL); + tt_str_op(s, OP_EQ, + "in=wubwub " + "rsa_id=706C75727079666C75727079736C75727079646F " + "sampled_on=2016-11-14T00:00:00 " + "listed=0"); - /* We disable the guard check and the preferred address check at the same - * time, so we can't be sure we get the guard */ - tt_assert(chosen_entry); + done: + entry_guard_free(eg); + tor_free(s); +} - /* Check that we get a node if it is allowed but not preferred when settings - * are auto */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientPreferIPv6ORPort = -1; +static void +test_entry_guard_encode_for_state_maximal(void *arg) +{ + (void) arg; + entry_guard_t *eg = tor_malloc_zero(sizeof(entry_guard_t)); + + strlcpy(eg->nickname, "Fred", sizeof(eg->nickname)); + eg->selection_name = tor_strdup("default"); + memcpy(eg->identity, "plurpyflurpyslurpydo", DIGEST_LEN); + eg->bridge_addr = tor_malloc_zero(sizeof(tor_addr_port_t)); + tor_addr_from_ipv4h(&eg->bridge_addr->addr, 0x08080404); + eg->bridge_addr->port = 9999; + eg->sampled_on_date = 1479081600; + eg->sampled_by_version = tor_strdup("1.2.3"); + eg->unlisted_since_date = 1479081645; + eg->currently_listed = 1; + eg->confirmed_on_date = 1479081690; + eg->confirmed_idx = 333; + eg->extra_state_fields = tor_strdup("and the green grass grew all around"); + + char *s = NULL; + s = entry_guard_encode_for_state(eg); + + tt_str_op(s, OP_EQ, + "in=default " + "rsa_id=706C75727079666C75727079736C75727079646F " + "bridge_addr=8.8.4.4:9999 " + "nickname=Fred " + "sampled_on=2016-11-14T00:00:00 " + "sampled_by=1.2.3 " + "unlisted_since=2016-11-14T00:00:45 " + "listed=1 " + "confirmed_on=2016-11-14T00:01:30 " + "confirmed_idx=333 " + "and the green grass grew all around"); - chosen_entry = choose_random_entry(NULL); + done: + entry_guard_free(eg); + tor_free(s); +} - /* We disable the guard check and the preferred address check at the same - * time, so we can't be sure we get the guard */ - tt_assert(chosen_entry); +static void +test_entry_guard_parse_from_state_minimal(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp = NULL; + entry_guard_t *eg = NULL; + time_t t = approx_time(); + + eg = entry_guard_parse_from_state( + "in=default_plus " + "rsa_id=596f75206d6179206e656564206120686f626279"); + tt_assert(eg); + + tt_str_op(eg->selection_name, OP_EQ, "default_plus"); + test_mem_op_hex(eg->identity, OP_EQ, + "596f75206d6179206e656564206120686f626279"); + tt_str_op(eg->nickname, OP_EQ, "$596F75206D6179206E656564206120686F626279"); + tt_ptr_op(eg->bridge_addr, OP_EQ, NULL); + tt_i64_op(eg->sampled_on_date, OP_GE, t); + tt_i64_op(eg->sampled_on_date, OP_LE, t+86400); + tt_i64_op(eg->unlisted_since_date, OP_EQ, 0); + tt_ptr_op(eg->sampled_by_version, OP_EQ, NULL); + tt_int_op(eg->currently_listed, OP_EQ, 0); + tt_i64_op(eg->confirmed_on_date, OP_EQ, 0); + tt_int_op(eg->confirmed_idx, OP_EQ, -1); + + tt_int_op(eg->last_tried_to_connect, OP_EQ, 0); + tt_int_op(eg->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); - /* and with IPv6 active */ - mocked_options.ClientUseIPv6 = 1; + done: + entry_guard_free(eg); + tor_free(mem_op_hex_tmp); +} - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); +static void +test_entry_guard_parse_from_state_maximal(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp = NULL; + entry_guard_t *eg = NULL; + + eg = entry_guard_parse_from_state( + "in=fred " + "rsa_id=706C75727079666C75727079736C75727079646F " + "bridge_addr=[1::3]:9999 " + "nickname=Fred " + "sampled_on=2016-11-14T00:00:00 " + "sampled_by=1.2.3 " + "unlisted_since=2016-11-14T00:00:45 " + "listed=1 " + "confirmed_on=2016-11-14T00:01:30 " + "confirmed_idx=333 " + "and the green grass grew all around " + "rsa_id=all,around"); + tt_assert(eg); + + test_mem_op_hex(eg->identity, OP_EQ, + "706C75727079666C75727079736C75727079646F"); + tt_str_op(fmt_addr(&eg->bridge_addr->addr), OP_EQ, "1::3"); + tt_int_op(eg->bridge_addr->port, OP_EQ, 9999); + tt_str_op(eg->nickname, OP_EQ, "Fred"); + tt_i64_op(eg->sampled_on_date, OP_EQ, 1479081600); + tt_i64_op(eg->unlisted_since_date, OP_EQ, 1479081645); + tt_str_op(eg->sampled_by_version, OP_EQ, "1.2.3"); + tt_int_op(eg->currently_listed, OP_EQ, 1); + tt_i64_op(eg->confirmed_on_date, OP_EQ, 1479081690); + tt_int_op(eg->confirmed_idx, OP_EQ, 333); + tt_str_op(eg->extra_state_fields, OP_EQ, + "and the green grass grew all around rsa_id=all,around"); + + tt_int_op(eg->last_tried_to_connect, OP_EQ, 0); + tt_int_op(eg->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); done: - memset(&mocked_options, 0, sizeof(mocked_options)); - UNMOCK(get_options); + entry_guard_free(eg); + tor_free(mem_op_hex_tmp); } -/** Helper to conduct tests for populate_live_entry_guards(). +static void +test_entry_guard_parse_from_state_failure(void *arg) +{ + (void)arg; + entry_guard_t *eg = NULL; + + /* no selection */ + eg = entry_guard_parse_from_state( + "rsa_id=596f75206d6179206e656564206120686f626270"); + tt_assert(! eg); + + /* no RSA ID. */ + eg = entry_guard_parse_from_state("in=default nickname=Fred"); + tt_assert(! eg); + + /* Bad RSA ID: bad character. */ + eg = entry_guard_parse_from_state( + "in=default " + "rsa_id=596f75206d6179206e656564206120686f62627q"); + tt_assert(! eg); + + /* Bad RSA ID: too long.*/ + eg = entry_guard_parse_from_state( + "in=default " + "rsa_id=596f75206d6179206e656564206120686f6262703"); + tt_assert(! eg); + + /* Bad RSA ID: too short.*/ + eg = entry_guard_parse_from_state( + "in=default " + "rsa_id=596f75206d6179206e65656420612"); + tt_assert(! eg); - This test adds some entry guards to our list, and then tests - populate_live_entry_guards() to mke sure it filters them correctly. + done: + entry_guard_free(eg); +} - <b>num_needed</b> is the number of guard nodes we support. It's - configurable to make sure we function properly with 1 or 3 guard - nodes configured. -*/ static void -populate_live_entry_guards_test_helper(int num_needed) +test_entry_guard_parse_from_state_partial_failure(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp = NULL; + entry_guard_t *eg = NULL; + time_t t = approx_time(); + + eg = entry_guard_parse_from_state( + "in=default " + "rsa_id=706C75727079666C75727079736C75727079646F " + "bridge_addr=1.2.3.3.4:5 " + "nickname=FredIsANodeWithAStrangeNicknameThatIsTooLong " + "sampled_on=2016-11-14T00:00:99 " + "sampled_by=1.2.3 stuff in the middle " + "unlisted_since=2016-xx-14T00:00:45 " + "listed=0 " + "confirmed_on=2016-11-14T00:01:30zz " + "confirmed_idx=idx " + "and the green grass grew all around " + "rsa_id=all,around"); + tt_assert(eg); + + test_mem_op_hex(eg->identity, OP_EQ, + "706C75727079666C75727079736C75727079646F"); + tt_str_op(eg->nickname, OP_EQ, "FredIsANodeWithAStrangeNicknameThatIsTooL"); + tt_ptr_op(eg->bridge_addr, OP_EQ, NULL); + tt_i64_op(eg->sampled_on_date, OP_EQ, t); + tt_i64_op(eg->unlisted_since_date, OP_EQ, 0); + tt_str_op(eg->sampled_by_version, OP_EQ, "1.2.3"); + tt_int_op(eg->currently_listed, OP_EQ, 0); + tt_i64_op(eg->confirmed_on_date, OP_EQ, 0); + tt_int_op(eg->confirmed_idx, OP_EQ, -1); + tt_str_op(eg->extra_state_fields, OP_EQ, + "stuff in the middle and the green grass grew all around " + "rsa_id=all,around"); + + tt_int_op(eg->last_tried_to_connect, OP_EQ, 0); + tt_int_op(eg->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + done: + entry_guard_free(eg); + tor_free(mem_op_hex_tmp); +} + +static int +mock_entry_guard_is_listed(guard_selection_t *gs, const entry_guard_t *guard) { - smartlist_t *our_nodelist = NULL; - smartlist_t *live_entry_guards = smartlist_new(); - const smartlist_t *all_entry_guards = get_entry_guards(); - or_options_t *options = get_options_mutable(); - int retval; + (void)gs; + (void)guard; + return 1; +} - /* Set NumEntryGuards to the provided number. */ - options->NumEntryGuards = num_needed; - tt_int_op(num_needed, OP_EQ, decide_num_guards(options, 0)); +static void +test_entry_guard_parse_from_state_full(void *arg) +{ + (void)arg; + /* Here's a state I made while testing. The identities and locations for + * the bridges are redacted. */ + const char STATE[] = + "Guard in=default rsa_id=214F44BD5B638E8C817D47FF7C97397790BF0345 " + "nickname=TotallyNinja sampled_on=2016-11-12T19:32:49 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1\n" + "Guard in=default rsa_id=052900AB0EA3ED54BAB84AE8A99E74E8693CE2B2 " + "nickname=5OfNovember sampled_on=2016-11-20T04:32:05 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-22T08:13:28 confirmed_idx=0 " + "pb_circ_attempts=4.000000 pb_circ_successes=2.000000 " + "pb_successful_circuits_closed=2.000000\n" + "Guard in=default rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-24T08:45:30 confirmed_idx=4 " + "pb_circ_attempts=5.000000 pb_circ_successes=5.000000 " + "pb_successful_circuits_closed=5.000000\n" + "Guard in=wobblesome rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1\n" + "Guard in=default rsa_id=E9025AD60D86875D5F11548D536CC6AF60F0EF5E " + "nickname=maibrunn sampled_on=2016-11-25T22:36:38 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n" + "Guard in=default rsa_id=DCD30B90BA3A792DA75DC54A327EF353FB84C38E " + "nickname=Unnamed sampled_on=2016-11-25T14:34:00 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n" + "Guard in=bridges rsa_id=8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E " + "bridge_addr=24.1.1.1:443 sampled_on=2016-11-25T06:44:14 " + "sampled_by=0.3.0.0-alpha-dev listed=1 " + "confirmed_on=2016-11-29T10:36:06 confirmed_idx=0 " + "pb_circ_attempts=8.000000 pb_circ_successes=8.000000 " + "pb_successful_circuits_closed=13.000000\n" + "Guard in=bridges rsa_id=5800000000000000000000000000000000000000 " + "bridge_addr=37.218.246.143:28366 " + "sampled_on=2016-11-18T15:07:34 sampled_by=0.3.0.0-alpha-dev listed=1\n"; + + config_line_t *lines = NULL; + or_state_t *state = tor_malloc_zero(sizeof(or_state_t)); + int r = config_get_lines(STATE, &lines, 0); + char *msg = NULL; + smartlist_t *text = smartlist_new(); + char *joined = NULL; - /* The global entry guards smartlist should be empty now. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 0); + // So nodes aren't expired. This is Tue, 13 Dec 2016 09:37:14 GMT + update_approx_time(1481621834); - /* Walk the nodelist and add all nodes as entry guards. */ - our_nodelist = nodelist_get_list(); - tt_int_op(smartlist_len(our_nodelist), OP_EQ, HELPER_NUMBER_OF_DESCRIPTORS); + MOCK(entry_guard_is_listed, mock_entry_guard_is_listed); - SMARTLIST_FOREACH_BEGIN(our_nodelist, const node_t *, node) { - const node_t *node_tmp; - node_tmp = add_an_entry_guard(node, 0, 1, 0, 0); - tt_assert(node_tmp); - } SMARTLIST_FOREACH_END(node); + dummy_state = state; + MOCK(get_or_state, + get_or_state_replacement); - /* Make sure the nodes were added as entry guards. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, - HELPER_NUMBER_OF_DESCRIPTORS); + tt_assert(r == 0); + tt_assert(lines); + + state->Guard = lines; + + /* Try it first without setting the result. */ + r = entry_guards_parse_state(state, 0, &msg); + tt_assert(r == 0); + guard_selection_t *gs_br = + get_guard_selection_by_name("bridges", GS_TYPE_BRIDGE, 0); + tt_assert(!gs_br); + + r = entry_guards_parse_state(state, 1, &msg); + tt_assert(r == 0); + gs_br = get_guard_selection_by_name("bridges", GS_TYPE_BRIDGE, 0); + guard_selection_t *gs_df = + get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + guard_selection_t *gs_wb = + get_guard_selection_by_name("wobblesome", GS_TYPE_NORMAL, 0); + + tt_assert(gs_br); + tt_assert(gs_df); + tt_assert(gs_wb); + + tt_int_op(smartlist_len(gs_df->sampled_entry_guards), OP_EQ, 5); + tt_int_op(smartlist_len(gs_br->sampled_entry_guards), OP_EQ, 2); + tt_int_op(smartlist_len(gs_wb->sampled_entry_guards), OP_EQ, 1); + + /* Try again; make sure it doesn't double-add the guards. */ + r = entry_guards_parse_state(state, 1, &msg); + tt_assert(r == 0); + gs_br = get_guard_selection_by_name("bridges", GS_TYPE_BRIDGE, 0); + gs_df = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + tt_assert(gs_br); + tt_assert(gs_df); + tt_int_op(smartlist_len(gs_df->sampled_entry_guards), OP_EQ, 5); + tt_int_op(smartlist_len(gs_br->sampled_entry_guards), OP_EQ, 2); + + /* Re-encode; it should be the same... almost. */ + { + /* (Make a guard nonpersistent first) */ + entry_guard_t *g = smartlist_get(gs_df->sampled_entry_guards, 0); + g->is_persistent = 0; + } + config_free_lines(lines); + lines = state->Guard = NULL; // to prevent double-free. + entry_guards_update_state(state); + tt_assert(state->Guard); + lines = state->Guard; + + config_line_t *ln; + for (ln = lines; ln; ln = ln->next) { + smartlist_add_asprintf(text, "%s %s\n",ln->key, ln->value); + } + joined = smartlist_join_strings(text, "", 0, NULL); + tt_str_op(joined, OP_EQ, + "Guard in=default rsa_id=052900AB0EA3ED54BAB84AE8A99E74E8693CE2B2 " + "nickname=5OfNovember sampled_on=2016-11-20T04:32:05 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-22T08:13:28 confirmed_idx=0 " + "pb_circ_attempts=4.000000 pb_circ_successes=2.000000 " + "pb_successful_circuits_closed=2.000000\n" + "Guard in=default rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-24T08:45:30 confirmed_idx=1 " + "pb_circ_attempts=5.000000 pb_circ_successes=5.000000 " + "pb_successful_circuits_closed=5.000000\n" + "Guard in=default rsa_id=E9025AD60D86875D5F11548D536CC6AF60F0EF5E " + "nickname=maibrunn sampled_on=2016-11-25T22:36:38 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n" + "Guard in=default rsa_id=DCD30B90BA3A792DA75DC54A327EF353FB84C38E " + "nickname=Unnamed sampled_on=2016-11-25T14:34:00 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n" + "Guard in=wobblesome rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1\n" + "Guard in=bridges rsa_id=8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E " + "bridge_addr=24.1.1.1:443 sampled_on=2016-11-25T06:44:14 " + "sampled_by=0.3.0.0-alpha-dev listed=1 " + "confirmed_on=2016-11-29T10:36:06 confirmed_idx=0 " + "pb_circ_attempts=8.000000 pb_circ_successes=8.000000 " + "pb_successful_circuits_closed=13.000000\n" + "Guard in=bridges rsa_id=5800000000000000000000000000000000000000 " + "bridge_addr=37.218.246.143:28366 " + "sampled_on=2016-11-18T15:07:34 sampled_by=0.3.0.0-alpha-dev listed=1\n"); - /* Ensure that all the possible entry guards are enough to satisfy us. */ - tt_int_op(smartlist_len(all_entry_guards), OP_GE, num_needed); + done: + config_free_lines(lines); + tor_free(state); + tor_free(msg); + UNMOCK(get_or_state); + UNMOCK(entry_guard_is_listed); + SMARTLIST_FOREACH(text, char *, cp, tor_free(cp)); + smartlist_free(text); + tor_free(joined); +} - /* Walk the entry guard list for some sanity checking */ - SMARTLIST_FOREACH_BEGIN(all_entry_guards, const entry_guard_t *, entry) { - /* Since we called add_an_entry_guard() with 'for_discovery' being - False, all guards should have made_contact enabled. */ - tt_int_op(entry->made_contact, OP_EQ, 1); +static void +test_entry_guard_parse_from_state_broken(void *arg) +{ + (void)arg; + /* Here's a variation on the previous state. Every line but the first is + * busted somehow. */ + const char STATE[] = + /* Okay. */ + "Guard in=default rsa_id=214F44BD5B638E8C817D47FF7C97397790BF0345 " + "nickname=TotallyNinja sampled_on=2016-11-12T19:32:49 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1\n" + /* No selection listed. */ + "Guard rsa_id=052900AB0EA3ED54BAB84AE8A99E74E8693CE2B2 " + "nickname=5OfNovember sampled_on=2016-11-20T04:32:05 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-22T08:13:28 confirmed_idx=0 " + "pb_circ_attempts=4.000000 pb_circ_successes=2.000000 " + "pb_successful_circuits_closed=2.000000\n" + /* Selection is "legacy"!! */ + "Guard in=legacy rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-24T08:45:30 confirmed_idx=4 " + "pb_circ_attempts=5.000000 pb_circ_successes=5.000000 " + "pb_successful_circuits_closed=5.000000\n"; + + config_line_t *lines = NULL; + or_state_t *state = tor_malloc_zero(sizeof(or_state_t)); + int r = config_get_lines(STATE, &lines, 0); + char *msg = NULL; - } SMARTLIST_FOREACH_END(entry); + dummy_state = state; + MOCK(get_or_state, + get_or_state_replacement); - /* First, try to get some fast guards. This should fail. */ - retval = populate_live_entry_guards(live_entry_guards, - all_entry_guards, - NULL, - NO_DIRINFO, /* Don't care about DIRINFO*/ - 0, 0, - 1); /* We want fast guard! */ - tt_int_op(retval, OP_EQ, 0); - tt_int_op(smartlist_len(live_entry_guards), OP_EQ, 0); + tt_assert(r == 0); + tt_assert(lines); - /* Now try to get some stable guards. This should fail too. */ - retval = populate_live_entry_guards(live_entry_guards, - all_entry_guards, - NULL, - NO_DIRINFO, - 0, - 1, /* We want stable guard! */ - 0); - tt_int_op(retval, OP_EQ, 0); - tt_int_op(smartlist_len(live_entry_guards), OP_EQ, 0); + state->Guard = lines; - /* Now try to get any guard we can find. This should succeed. */ - retval = populate_live_entry_guards(live_entry_guards, - all_entry_guards, - NULL, - NO_DIRINFO, - 0, 0, 0); /* No restrictions! */ + /* First, no-set case. we should get an error. */ + r = entry_guards_parse_state(state, 0, &msg); + tt_int_op(r, OP_LT, 0); + tt_ptr_op(msg, OP_NE, NULL); + /* And we shouldn't have made anything. */ + guard_selection_t *gs_df = + get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + tt_assert(gs_df == NULL); + tor_free(msg); - /* Since we had more than enough guards in 'all_entry_guards', we - should have added 'num_needed' of them to live_entry_guards. - 'retval' should be 1 since we now have enough live entry guards - to pick one. */ - tt_int_op(retval, OP_EQ, 1); - tt_int_op(smartlist_len(live_entry_guards), OP_EQ, num_needed); + /* Now see about the set case (which shouldn't happen IRL) */ + r = entry_guards_parse_state(state, 1, &msg); + tt_int_op(r, OP_LT, 0); + tt_ptr_op(msg, OP_NE, NULL); + gs_df = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + tt_assert(gs_df != NULL); + tt_int_op(smartlist_len(gs_df->sampled_entry_guards), OP_EQ, 1); done: - smartlist_free(live_entry_guards); + config_free_lines(lines); + tor_free(state); + tor_free(msg); + UNMOCK(get_or_state); } -/* Test populate_live_entry_guards() for 1 guard node. */ static void -test_populate_live_entry_guards_1guard(void *arg) +test_entry_guard_get_guard_selection_by_name(void *arg) { - (void) arg; + (void)arg; + guard_selection_t *gs1, *gs2, *gs3; + + gs1 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 0); + tt_assert(gs1 == NULL); + gs1 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 1); + tt_assert(gs1 != NULL); + gs2 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 1); + tt_assert(gs2 == gs1); + gs2 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 0); + tt_assert(gs2 == gs1); + + gs2 = get_guard_selection_by_name("implausible", GS_TYPE_NORMAL, 0); + tt_assert(gs2 == NULL); + gs2 = get_guard_selection_by_name("implausible", GS_TYPE_NORMAL, 1); + tt_assert(gs2 != NULL); + tt_assert(gs2 != gs1); + gs3 = get_guard_selection_by_name("implausible", GS_TYPE_NORMAL, 0); + tt_assert(gs3 == gs2); + + gs3 = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + tt_assert(gs3 == NULL); + gs3 = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 1); + tt_assert(gs3 != NULL); + tt_assert(gs3 != gs2); + tt_assert(gs3 != gs1); + tt_assert(gs3 == get_guard_selection_info()); - populate_live_entry_guards_test_helper(1); + done: + entry_guards_free_all(); } -/* Test populate_live_entry_guards() for 3 guard nodes. */ static void -test_populate_live_entry_guards_3guards(void *arg) +test_entry_guard_choose_selection_initial(void *arg) { - (void) arg; + /* Tests for picking our initial guard selection (based on having had + * no previous selection */ + (void)arg; + guard_selection_type_t type = GS_TYPE_INFER; + const char *name = choose_guard_selection(get_options(), + dummy_consensus, NULL, &type); + tt_str_op(name, OP_EQ, "default"); + tt_int_op(type, OP_EQ, GS_TYPE_NORMAL); + + /* If we're using bridges, we get the bridge selection. */ + get_options_mutable()->UseBridges = 1; + name = choose_guard_selection(get_options(), + dummy_consensus, NULL, &type); + tt_str_op(name, OP_EQ, "bridges"); + tt_int_op(type, OP_EQ, GS_TYPE_BRIDGE); + get_options_mutable()->UseBridges = 0; + + /* If we discard >99% of our guards, though, we should be in the restricted + * set. */ + tt_assert(get_options_mutable()->EntryNodes == NULL); + get_options_mutable()->EntryNodes = routerset_new(); + routerset_parse(get_options_mutable()->EntryNodes, "1.0.0.0/8", "foo"); + name = choose_guard_selection(get_options(), + dummy_consensus, NULL, &type); + tt_str_op(name, OP_EQ, "restricted"); + tt_int_op(type, OP_EQ, GS_TYPE_RESTRICTED); - populate_live_entry_guards_test_helper(3); + done: + ; } -/** Append some EntryGuard lines to the Tor state at <b>state</b>. - - <b>entry_guard_lines</b> is a smartlist containing 2-tuple - smartlists that carry the key and values of the statefile. - As an example: - entry_guard_lines = - (("EntryGuard", "name 67E72FF33D7D41BF11C569646A0A7B4B188340DF DirCache"), - ("EntryGuardDownSince", "2014-06-07 16:02:46 2014-06-07 16:02:46")) -*/ static void -state_insert_entry_guard_helper(or_state_t *state, - smartlist_t *entry_guard_lines) +test_entry_guard_add_single_guard(void *arg) { - config_line_t **next, *line; - - next = &state->EntryGuards; - *next = NULL; - - /* Loop over all the state lines in the smartlist */ - SMARTLIST_FOREACH_BEGIN(entry_guard_lines, const smartlist_t *,state_lines) { - /* Get key and value for each line */ - const char *state_key = smartlist_get(state_lines, 0); - const char *state_value = smartlist_get(state_lines, 1); + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + /* 1: Add a single guard to the sample. */ + node_t *n1 = smartlist_get(big_fake_net_nodes, 0); + time_t now = approx_time(); + tt_assert(n1->is_possible_guard == 1); + entry_guard_t *g1 = entry_guard_add_to_sample(gs, n1); + tt_assert(g1); + + /* Make sure its fields look right. */ + tt_mem_op(n1->identity, OP_EQ, g1->identity, DIGEST_LEN); + tt_i64_op(g1->sampled_on_date, OP_GE, now - 12*86400); + tt_i64_op(g1->sampled_on_date, OP_LE, now); + tt_str_op(g1->sampled_by_version, OP_EQ, VERSION); + tt_assert(g1->currently_listed == 1); + tt_i64_op(g1->confirmed_on_date, OP_EQ, 0); + tt_int_op(g1->confirmed_idx, OP_EQ, -1); + tt_int_op(g1->last_tried_to_connect, OP_EQ, 0); + tt_uint_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + tt_i64_op(g1->failing_since, OP_EQ, 0); + tt_assert(g1->is_filtered_guard == 1); + tt_assert(g1->is_usable_filtered_guard == 1); + tt_assert(g1->is_primary == 0); + tt_assert(g1->extra_state_fields == NULL); + + /* Make sure it got added. */ + tt_int_op(1, OP_EQ, smartlist_len(gs->sampled_entry_guards)); + tt_ptr_op(g1, OP_EQ, smartlist_get(gs->sampled_entry_guards, 0)); + tt_ptr_op(g1, OP_EQ, get_sampled_guard_with_id(gs, (uint8_t*)n1->identity)); + const uint8_t bad_id[20] = {0}; + tt_ptr_op(NULL, OP_EQ, get_sampled_guard_with_id(gs, bad_id)); - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup(state_key); - tor_asprintf(&line->value, "%s", state_value); - next = &(line->next); - } SMARTLIST_FOREACH_END(state_lines); + done: + guard_selection_free(gs); } -/** Free memory occupied by <b>entry_guard_lines</b>. */ static void -state_lines_free(smartlist_t *entry_guard_lines) +test_entry_guard_node_filter(void *arg) { - SMARTLIST_FOREACH_BEGIN(entry_guard_lines, smartlist_t *, state_lines) { - char *state_key = smartlist_get(state_lines, 0); - char *state_value = smartlist_get(state_lines, 1); + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + bridge_line_t *bl = NULL; + + /* Initialize a bunch of node objects that are all guards. */ +#define NUM 7 + node_t *n[NUM]; + entry_guard_t *g[NUM]; + int i; + for (i=0; i < NUM; ++i) { + n[i] = smartlist_get(big_fake_net_nodes, i*2); // even ones are guards. + g[i] = entry_guard_add_to_sample(gs, n[i]); + + // everything starts out filtered-in + tt_assert(g[i]->is_filtered_guard == 1); + tt_assert(g[i]->is_usable_filtered_guard == 1); + } + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, NUM); - tor_free(state_key); - tor_free(state_value); - smartlist_free(state_lines); - } SMARTLIST_FOREACH_END(state_lines); + /* Make sure refiltering doesn't hurt */ + entry_guards_update_filtered_sets(gs); + for (i = 0; i < NUM; ++i) { + tt_assert(g[i]->is_filtered_guard == 1); + tt_assert(g[i]->is_usable_filtered_guard == 1); + } + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, NUM); - smartlist_free(entry_guard_lines); -} + /* Now start doing things to make the guards get filtered out, 1 by 1. */ -/* Tests entry_guards_parse_state(). It creates a fake Tor state with - a saved entry guard and makes sure that Tor can parse it and - creates the right entry node out of it. -*/ -static void -test_entry_guards_parse_state_simple(void *arg) -{ - or_state_t *state = or_state_new(); - const smartlist_t *all_entry_guards = get_entry_guards(); - smartlist_t *entry_state_lines = smartlist_new(); - char *msg = NULL; - int retval; + /* 0: Not listed. */ + g[0]->currently_listed = 0; - /* Details of our fake guard node */ - const char *nickname = "hagbard"; - const char *fpr = "B29D536DD1752D542E1FBB3C9CE4449D51298212"; - const char *tor_version = "0.2.5.3-alpha-dev"; - const char *added_at = get_yesterday_date_str(); - const char *unlisted_since = "2014-06-08 16:16:50"; + /* 1: path bias says this guard is maybe eeeevil. */ + g[1]->pb.path_bias_disabled = 1; - (void) arg; + /* 2: Unreachable address. */ + n[2]->rs->addr = 0; - /* The global entry guards smartlist should be empty now. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 0); + /* 3: ExcludeNodes */ + n[3]->rs->addr = 0x90902020; + routerset_free(get_options_mutable()->ExcludeNodes); + get_options_mutable()->ExcludeNodes = routerset_new(); + routerset_parse(get_options_mutable()->ExcludeNodes, "144.144.0.0/16", ""); - { /* Prepare the state entry */ + /* 4: Bridge. */ + sweep_bridge_list(); + bl = tor_malloc_zero(sizeof(bridge_line_t)); + tor_addr_from_ipv4h(&bl->addr, n[4]->rs->addr); + bl->port = n[4]->rs->or_port; + memcpy(bl->digest, n[4]->identity, 20); + bridge_add_from_config(bl); + bl = NULL; // prevent free. - /* Prepare the smartlist to hold the key/value of each line */ - smartlist_t *state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuard"); - smartlist_add_asprintf(state_line, "%s %s %s", nickname, fpr, "DirCache"); - smartlist_add(entry_state_lines, state_line); + /* 5: Unreachable. This stays in the filter, but isn't in usable-filtered */ + g[5]->last_tried_to_connect = approx_time(); // prevent retry. + g[5]->is_reachable = GUARD_REACHABLE_NO; - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardAddedBy"); - smartlist_add_asprintf(state_line, "%s %s %s", fpr, tor_version, added_at); - smartlist_add(entry_state_lines, state_line); + /* 6: no change. */ - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardUnlistedSince"); - smartlist_add_asprintf(state_line, "%s", unlisted_since); - smartlist_add(entry_state_lines, state_line); + /* Now refilter and inspect. */ + entry_guards_update_filtered_sets(gs); + for (i = 0; i < NUM; ++i) { + tt_assert(g[i]->is_filtered_guard == (i == 5 || i == 6)); + tt_assert(g[i]->is_usable_filtered_guard == (i == 6)); } + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 1); + + /* Now make sure we have no live consensus, and no nodes. Nothing should + * pass the filter any more. */ + tor_free(dummy_consensus); + dummy_consensus = NULL; + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, node, { + memset(node->identity, 0xff, 20); + }); + entry_guards_update_filtered_sets(gs); + for (i = 0; i < NUM; ++i) { + tt_assert(g[i]->is_filtered_guard == 0); + tt_assert(g[i]->is_usable_filtered_guard == 0); + } + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 0); - /* Inject our lines in the state */ - state_insert_entry_guard_helper(state, entry_state_lines); + done: + guard_selection_free(gs); + tor_free(bl); +#undef NUM +} - /* Parse state */ - retval = entry_guards_parse_state(state, 1, &msg); - tt_int_op(retval, OP_GE, 0); +static void +test_entry_guard_expand_sample(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + digestmap_t *node_by_id = digestmap_new(); + + entry_guard_t *guard = entry_guards_expand_sample(gs); + tt_assert(guard); // the last guard returned. + + // Every sampled guard here should be filtered and reachable for now. + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + /* Make sure we got the right number. */ + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + // Make sure everything we got was from our fake node list, and everything + // was unique. + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, g) { + const node_t *n = bfn_mock_node_get_by_id(g->identity); + tt_assert(n); + tt_ptr_op(NULL, OP_EQ, digestmap_get(node_by_id, g->identity)); + digestmap_set(node_by_id, g->identity, (void*) n); + int idx = smartlist_pos(big_fake_net_nodes, n); + // The even ones are the guards; make sure we got guards. + tt_int_op(idx & 1, OP_EQ, 0); + } SMARTLIST_FOREACH_END(g); + + // Nothing became unusable/unfiltered, so a subsequent expand should + // make no changes. + guard = entry_guards_expand_sample(gs); + tt_assert(! guard); // no guard was added. + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + // Make a few guards unreachable. + guard = smartlist_get(gs->sampled_entry_guards, 0); + guard->is_usable_filtered_guard = 0; + guard = smartlist_get(gs->sampled_entry_guards, 1); + guard->is_usable_filtered_guard = 0; + guard = smartlist_get(gs->sampled_entry_guards, 2); + guard->is_usable_filtered_guard = 0; + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE - 3, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + // This time, expanding the sample will add some more guards. + guard = entry_guards_expand_sample(gs); + tt_assert(guard); // no guard was added. + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, + num_reachable_filtered_guards(gs, NULL)+3); + + // Still idempotent. + guard = entry_guards_expand_sample(gs); + tt_assert(! guard); // no guard was added. + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + // Now, do a nasty trick: tell the filter to exclude 31/32 of the guards. + // This will cause the sample size to get reeeeally huge, while the + // filtered sample size grows only slowly. + routerset_free(get_options_mutable()->ExcludeNodes); + get_options_mutable()->ExcludeNodes = routerset_new(); + routerset_parse(get_options_mutable()->ExcludeNodes, "144.144.0.0/16", ""); + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, { + if (n_sl_idx % 64 != 0) { + n->rs->addr = 0x90903030; + } + }); + entry_guards_update_filtered_sets(gs); + + // Surely (p ~ 1-2**-60), one of our guards has been excluded. + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_LT, + DFLT_MIN_FILTERED_SAMPLE_SIZE); + + // Try to regenerate the guards. + guard = entry_guards_expand_sample(gs); + tt_assert(guard); // no guard was added. + + /* this time, it's possible that we didn't add enough sampled guards. */ + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_LE, + DFLT_MIN_FILTERED_SAMPLE_SIZE); + /* but we definitely didn't exceed the sample maximum. */ + const int n_guards = 271 / 2; + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_LE, + (int)(n_guards * .3)); - /* Test that the guard was registered. - We need to re-get the entry guard list since its pointer was - overwritten in entry_guards_parse_state(). */ - all_entry_guards = get_entry_guards(); - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 1); + done: + guard_selection_free(gs); + digestmap_free(node_by_id, NULL); +} + +static void +test_entry_guard_expand_sample_small_net(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + /* Fun corner case: not enough guards to make up our whole sample size. */ + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, { + if (n_sl_idx >= 15) { + tor_free(n->rs); + tor_free(n->md); + tor_free(n); + SMARTLIST_DEL_CURRENT(big_fake_net_nodes, n); + } else { + n->rs->addr = 0; // make the filter reject this. + } + }); + + entry_guard_t *guard = entry_guards_expand_sample(gs); + tt_assert(guard); // the last guard returned -- some guard was added. + // half the nodes are guards, so we have 8 guards left. The set + // is small, so we sampled everything. + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, 8); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 0); + done: + guard_selection_free(gs); +} - { /* Test the entry guard structure */ - char hex_digest[1024]; - char str_time[1024]; +static void +test_entry_guard_update_from_consensus_status(void *arg) +{ + /* Here we're going to have some nodes become un-guardy, and say we got a + * new consensus. This should cause those nodes to get detected as + * unreachable. */ - const entry_guard_t *e = smartlist_get(all_entry_guards, 0); - tt_str_op(e->nickname, OP_EQ, nickname); /* Verify nickname */ + (void)arg; + int i; + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + networkstatus_t *ns_tmp = NULL; + + /* Don't randomly backdate stuff; it will make correctness harder to check.*/ + MOCK(randomize_time, mock_randomize_time_no_randomization); + + /* First, sample some guards. */ + entry_guards_expand_sample(gs); + int n_sampled_pre = smartlist_len(gs->sampled_entry_guards); + int n_filtered_pre = num_reachable_filtered_guards(gs, NULL); + tt_i64_op(n_sampled_pre, OP_EQ, n_filtered_pre); + tt_i64_op(n_sampled_pre, OP_GT, 10); + + /* At this point, it should be a no-op to do this: */ + sampled_guards_update_from_consensus(gs); + + /* Now let's make some of our guards become unlisted. The easiest way to + * do that would be to take away their guard flag. */ + for (i = 0; i < 5; ++i) { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 0; + } - base16_encode(hex_digest, sizeof(hex_digest), - e->identity, DIGEST_LEN); - tt_str_op(hex_digest, OP_EQ, fpr); /* Verify fingerprint */ + update_approx_time(start + 30); + { + /* try this with no live networkstatus. Nothing should happen! */ + ns_tmp = dummy_consensus; + dummy_consensus = NULL; + sampled_guards_update_from_consensus(gs); + tt_i64_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); + tt_i64_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_filtered_pre); + /* put the networkstatus back. */ + dummy_consensus = ns_tmp; + ns_tmp = NULL; + } - tt_assert(e->is_dir_cache); /* Verify dirness */ + /* Now those guards should become unlisted, and drop off the filter, but + * stay in the sample. */ + update_approx_time(start + 60); + sampled_guards_update_from_consensus(gs); + + tt_i64_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); + tt_i64_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_filtered_pre-5); + for (i = 0; i < 5; ++i) { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + tt_assert(! g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, start+60); + } + for (i = 5; i < n_sampled_pre; ++i) { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + tt_assert(g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, 0); + } - tt_str_op(e->chosen_by_version, OP_EQ, tor_version); /* Verify version */ + /* Now re-list one, and remove one completely. */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 0); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 1; + } + { + /* try removing the node, to make sure we don't crash on an absent node + */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 5); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + smartlist_remove(big_fake_net_nodes, n); + tor_free(n->rs); + tor_free(n->md); + tor_free(n); + } + update_approx_time(start + 300); + sampled_guards_update_from_consensus(gs); + + /* guards 1..5 are now unlisted; 0,6,7.. are listed. */ + tt_i64_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); + for (i = 1; i < 6; ++i) { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + tt_assert(! g->currently_listed); + if (i == 5) + tt_i64_op(g->unlisted_since_date, OP_EQ, start+300); + else + tt_i64_op(g->unlisted_since_date, OP_EQ, start+60); + } + for (i = 0; i < n_sampled_pre; i = (!i) ? 6 : i+1) { /* 0,6,7,8, ... */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + tt_assert(g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, 0); + } - tt_assert(e->made_contact); /* All saved guards have been contacted */ + done: + tor_free(ns_tmp); /* in case we couldn't put it back */ + guard_selection_free(gs); + UNMOCK(randomize_time); +} - tt_assert(e->bad_since); /* Verify bad_since timestamp */ - format_iso_time(str_time, e->bad_since); - tt_str_op(str_time, OP_EQ, unlisted_since); +static void +test_entry_guard_update_from_consensus_repair(void *arg) +{ + /* Here we'll make sure that our code to repair the unlisted-since + * times is correct. */ - /* The rest should be unset */ - tt_assert(!e->unreachable_since); - tt_assert(!e->can_retry); - tt_assert(!e->path_bias_noticed); - tt_assert(!e->path_bias_warned); - tt_assert(!e->path_bias_extreme); - tt_assert(!e->path_bias_disabled); - tt_assert(!e->path_bias_use_noticed); - tt_assert(!e->path_bias_use_extreme); - tt_assert(!e->last_attempted); + (void)arg; + int i; + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + /* Don't randomly backdate stuff; it will make correctness harder to check.*/ + MOCK(randomize_time, mock_randomize_time_no_randomization); + + /* First, sample some guards. */ + entry_guards_expand_sample(gs); + int n_sampled_pre = smartlist_len(gs->sampled_entry_guards); + int n_filtered_pre = num_reachable_filtered_guards(gs, NULL); + tt_i64_op(n_sampled_pre, OP_EQ, n_filtered_pre); + tt_i64_op(n_sampled_pre, OP_GT, 10); + + /* Now corrupt the list a bit. Call some unlisted-since-never, and some + * listed-and-unlisted-since-a-time. */ + update_approx_time(start + 300); + for (i = 0; i < 3; ++i) { + /* these will get a date. */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 0; + g->currently_listed = 0; + } + for (i = 3; i < 6; ++i) { + /* these will become listed. */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + g->unlisted_since_date = start+100; + } + setup_full_capture_of_logs(LOG_WARN); + sampled_guards_update_from_consensus(gs); + expect_log_msg_containing( + "was listed, but with unlisted_since_date set"); + expect_log_msg_containing( + "was unlisted, but with unlisted_since_date unset"); + teardown_capture_of_logs(); + + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_filtered_pre-3); + for (i = 3; i < n_sampled_pre; ++i) { + /* these will become listed. */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + if (i < 3) { + tt_assert(! g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, start+300); + } else { + tt_assert(g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, 0); + } } done: - state_lines_free(entry_state_lines); - or_state_free(state); - tor_free(msg); + teardown_capture_of_logs(); + guard_selection_free(gs); + UNMOCK(randomize_time); } -/** Similar to test_entry_guards_parse_state_simple() but aims to test - the PathBias-related details of the entry guard. */ static void -test_entry_guards_parse_state_pathbias(void *arg) +test_entry_guard_update_from_consensus_remove(void *arg) { - or_state_t *state = or_state_new(); - const smartlist_t *all_entry_guards = get_entry_guards(); - char *msg = NULL; - int retval; - smartlist_t *entry_state_lines = smartlist_new(); + /* Now let's check the logic responsible for removing guards from the + * sample entirely. */ - /* Path bias details of the fake guard */ - const double circ_attempts = 9; - const double circ_successes = 8; - const double successful_closed = 4; - const double collapsed = 2; - const double unusable = 0; - const double timeouts = 1; + (void)arg; + //int i; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + smartlist_t *keep_ids = smartlist_new(); + smartlist_t *remove_ids = smartlist_new(); + + /* Don't randomly backdate stuff; it will make correctness harder to check.*/ + MOCK(randomize_time, mock_randomize_time_no_randomization); + + /* First, sample some guards. */ + entry_guards_expand_sample(gs); + int n_sampled_pre = smartlist_len(gs->sampled_entry_guards); + int n_filtered_pre = num_reachable_filtered_guards(gs, NULL); + tt_i64_op(n_sampled_pre, OP_EQ, n_filtered_pre); + tt_i64_op(n_sampled_pre, OP_GT, 10); + + const time_t one_day_ago = approx_time() - 1*24*60*60; + const time_t one_year_ago = approx_time() - 365*24*60*60; + const time_t two_years_ago = approx_time() - 2*365*24*60*60; + /* 0: unlisted for a day. (keep this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 0); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 0; + g->currently_listed = 0; + g->unlisted_since_date = one_day_ago; + smartlist_add(keep_ids, tor_memdup(g->identity, 20)); + } + /* 1: unlisted for a year. (remove this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 1); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 0; + g->currently_listed = 0; + g->unlisted_since_date = one_year_ago; + smartlist_add(remove_ids, tor_memdup(g->identity, 20)); + } + /* 2: added a day ago, never confirmed. (keep this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 2); + g->sampled_on_date = one_day_ago; + smartlist_add(keep_ids, tor_memdup(g->identity, 20)); + } + /* 3: added a year ago, never confirmed. (remove this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 3); + g->sampled_on_date = one_year_ago; + smartlist_add(remove_ids, tor_memdup(g->identity, 20)); + } + /* 4: added two year ago, confirmed yesterday, primary. (keep this.) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 4); + g->sampled_on_date = one_year_ago; + g->confirmed_on_date = one_day_ago; + g->confirmed_idx = 0; + g->is_primary = 1; + smartlist_add(gs->confirmed_entry_guards, g); + smartlist_add(gs->primary_entry_guards, g); + smartlist_add(keep_ids, tor_memdup(g->identity, 20)); + } + /* 5: added two years ago, confirmed a year ago, primary. (remove this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 5); + g->sampled_on_date = two_years_ago; + g->confirmed_on_date = one_year_ago; + g->confirmed_idx = 1; + g->is_primary = 1; + smartlist_add(gs->confirmed_entry_guards, g); + smartlist_add(gs->primary_entry_guards, g); + smartlist_add(remove_ids, tor_memdup(g->identity, 20)); + } - (void) arg; + sampled_guards_update_from_consensus(gs); - /* The global entry guards smartlist should be empty now. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 0); + /* Did we remove the right ones? */ + SMARTLIST_FOREACH(keep_ids, uint8_t *, id, { + tt_assert(get_sampled_guard_with_id(gs, id) != NULL); + }); + SMARTLIST_FOREACH(remove_ids, uint8_t *, id, { + tt_want(get_sampled_guard_with_id(gs, id) == NULL); + }); - { /* Prepare the state entry */ + /* Did we remove the right number? */ + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre - 3); - /* Prepare the smartlist to hold the key/value of each line */ - smartlist_t *state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuard"); - smartlist_add_asprintf(state_line, - "givethanks B29D536DD1752D542E1FBB3C9CE4449D51298212 NoDirCache"); - smartlist_add(entry_state_lines, state_line); + done: + guard_selection_free(gs); + UNMOCK(randomize_time); + SMARTLIST_FOREACH(keep_ids, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(remove_ids, char *, cp, tor_free(cp)); + smartlist_free(keep_ids); + smartlist_free(remove_ids); +} - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardAddedBy"); - smartlist_add_asprintf(state_line, - "B29D536DD1752D542E1FBB3C9CE4449D51298212 0.2.5.3-alpha-dev " - "%s", get_yesterday_date_str()); - smartlist_add(entry_state_lines, state_line); +static void +test_entry_guard_confirming_guards(void *arg) +{ + (void)arg; + /* Now let's check the logic responsible for manipulating the list + * of confirmed guards */ + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + MOCK(randomize_time, mock_randomize_time_no_randomization); + + /* Create the sample. */ + entry_guards_expand_sample(gs); + + /* Confirm a few guards. */ + time_t start = approx_time(); + entry_guard_t *g1 = smartlist_get(gs->sampled_entry_guards, 0); + entry_guard_t *g2 = smartlist_get(gs->sampled_entry_guards, 1); + entry_guard_t *g3 = smartlist_get(gs->sampled_entry_guards, 8); + make_guard_confirmed(gs, g2); + update_approx_time(start + 10); + make_guard_confirmed(gs, g1); + make_guard_confirmed(gs, g3); + + /* Were the correct dates and indices fed in? */ + tt_int_op(g1->confirmed_idx, OP_EQ, 1); + tt_int_op(g2->confirmed_idx, OP_EQ, 0); + tt_int_op(g3->confirmed_idx, OP_EQ, 2); + tt_i64_op(g1->confirmed_on_date, OP_EQ, start+10); + tt_i64_op(g2->confirmed_on_date, OP_EQ, start); + tt_i64_op(g3->confirmed_on_date, OP_EQ, start+10); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 0), OP_EQ, g2); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 1), OP_EQ, g1); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 2), OP_EQ, g3); + + /* Now make sure we can regenerate the confirmed_entry_guards list. */ + smartlist_clear(gs->confirmed_entry_guards); + g2->confirmed_idx = 0; + g1->confirmed_idx = 10; + g3->confirmed_idx = 100; + entry_guards_update_confirmed(gs); + tt_int_op(g1->confirmed_idx, OP_EQ, 1); + tt_int_op(g2->confirmed_idx, OP_EQ, 0); + tt_int_op(g3->confirmed_idx, OP_EQ, 2); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 0), OP_EQ, g2); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 1), OP_EQ, g1); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 2), OP_EQ, g3); + + /* Now make sure we can regenerate the confirmed_entry_guards list if + * the indices are messed up. */ + g1->confirmed_idx = g2->confirmed_idx = g3->confirmed_idx = 999; + smartlist_clear(gs->confirmed_entry_guards); + entry_guards_update_confirmed(gs); + tt_int_op(g1->confirmed_idx, OP_GE, 0); + tt_int_op(g2->confirmed_idx, OP_GE, 0); + tt_int_op(g3->confirmed_idx, OP_GE, 0); + tt_int_op(g1->confirmed_idx, OP_LE, 2); + tt_int_op(g2->confirmed_idx, OP_LE, 2); + tt_int_op(g3->confirmed_idx, OP_LE, 2); + g1 = smartlist_get(gs->confirmed_entry_guards, 0); + g2 = smartlist_get(gs->confirmed_entry_guards, 1); + g3 = smartlist_get(gs->confirmed_entry_guards, 2); + tt_int_op(g1->confirmed_idx, OP_EQ, 0); + tt_int_op(g2->confirmed_idx, OP_EQ, 1); + tt_int_op(g3->confirmed_idx, OP_EQ, 2); + tt_assert(g1 != g2); + tt_assert(g1 != g3); + tt_assert(g2 != g3); - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardUnlistedSince"); - smartlist_add_asprintf(state_line, "2014-06-08 16:16:50"); - smartlist_add(entry_state_lines, state_line); + done: + UNMOCK(randomize_time); + guard_selection_free(gs); +} - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardPathBias"); - smartlist_add_asprintf(state_line, "%f %f %f %f %f %f", - circ_attempts, circ_successes, successful_closed, - collapsed, unusable, timeouts); - smartlist_add(entry_state_lines, state_line); +static void +test_entry_guard_sample_reachable_filtered(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + entry_guards_expand_sample(gs); + const int N = 10000; + bitarray_t *selected = NULL; + int i, j; + + /* We've got a sampled list now; let's make one non-usable-filtered; some + * confirmed, some primary, some pending. + */ + int n_guards = smartlist_len(gs->sampled_entry_guards); + tt_int_op(n_guards, OP_GT, 10); + entry_guard_t *g; + g = smartlist_get(gs->sampled_entry_guards, 0); + g->is_pending = 1; + g = smartlist_get(gs->sampled_entry_guards, 1); + make_guard_confirmed(gs, g); + g = smartlist_get(gs->sampled_entry_guards, 2); + g->is_primary = 1; + g = smartlist_get(gs->sampled_entry_guards, 3); + g->pb.path_bias_disabled = 1; + + entry_guards_update_filtered_sets(gs); + gs->primary_guards_up_to_date = 1; + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_guards - 1); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_guards); + + // +1 since the one we made disabled will make another one get added. + ++n_guards; + + /* Try a bunch of selections. */ + const struct { + int flag; int idx; + } tests[] = { + { 0, -1 }, + { SAMPLE_EXCLUDE_CONFIRMED, 1 }, + { SAMPLE_EXCLUDE_PRIMARY|SAMPLE_NO_UPDATE_PRIMARY, 2 }, + { SAMPLE_EXCLUDE_PENDING, 0 }, + { -1, -1}, + }; + + for (j = 0; tests[j].flag >= 0; ++j) { + selected = bitarray_init_zero(n_guards); + const int excluded_flags = tests[j].flag; + const int excluded_idx = tests[j].idx; + for (i = 0; i < N; ++i) { + g = sample_reachable_filtered_entry_guards(gs, NULL, excluded_flags); + tor_assert(g); + int pos = smartlist_pos(gs->sampled_entry_guards, g); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_guards); + tt_int_op(pos, OP_GE, 0); + tt_int_op(pos, OP_LT, n_guards); + bitarray_set(selected, pos); + } + for (i = 0; i < n_guards; ++i) { + const int should_be_set = (i != excluded_idx && + i != 3); // filtered out. + tt_int_op(!!bitarray_is_set(selected, i), OP_EQ, should_be_set); + } + bitarray_free(selected); + selected = NULL; } - /* Inject our lines in the state */ - state_insert_entry_guard_helper(state, entry_state_lines); + done: + guard_selection_free(gs); + bitarray_free(selected); +} + +static void +test_entry_guard_sample_reachable_filtered_empty(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + /* What if we try to sample from a set of 0? */ + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, + n->is_possible_guard = 0); - /* Parse state */ - retval = entry_guards_parse_state(state, 1, &msg); - tt_int_op(retval, OP_GE, 0); + entry_guard_t *g = sample_reachable_filtered_entry_guards(gs, NULL, 0); + tt_ptr_op(g, OP_EQ, NULL); - /* Test that the guard was registered */ - all_entry_guards = get_entry_guards(); - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 1); + done: + guard_selection_free(gs); +} - { /* Test the path bias of this guard */ - const entry_guard_t *e = smartlist_get(all_entry_guards, 0); +static void +test_entry_guard_retry_unreachable(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + entry_guards_expand_sample(gs); + /* Let's say that we have two guards, and they're down. + */ + time_t start = approx_time();; + entry_guard_t *g1 = smartlist_get(gs->sampled_entry_guards, 0); + entry_guard_t *g2 = smartlist_get(gs->sampled_entry_guards, 1); + entry_guard_t *g3 = smartlist_get(gs->sampled_entry_guards, 2); + g1->is_reachable = GUARD_REACHABLE_NO; + g2->is_reachable = GUARD_REACHABLE_NO; + g1->is_primary = 1; + g1->failing_since = g2->failing_since = start; + g1->last_tried_to_connect = g2->last_tried_to_connect = start; + + /* Wait 5 minutes. Nothing will get retried. */ + update_approx_time(start + 5 * 60); + entry_guard_consider_retry(g1); + entry_guard_consider_retry(g2); + entry_guard_consider_retry(g3); // just to make sure this doesn't crash. + tt_int_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + tt_int_op(g3->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + /* After 30 min, the primary one gets retried */ + update_approx_time(start + 35 * 60); + entry_guard_consider_retry(g1); + entry_guard_consider_retry(g2); + tt_int_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + + g1->is_reachable = GUARD_REACHABLE_NO; + g1->last_tried_to_connect = start + 55*60; + + /* After 1 hour, we'll retry the nonprimary one. */ + update_approx_time(start + 61 * 60); + entry_guard_consider_retry(g1); + entry_guard_consider_retry(g2); + tt_int_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + g2->is_reachable = GUARD_REACHABLE_NO; + g2->last_tried_to_connect = start + 61*60; + + /* And then the primary one again. */ + update_approx_time(start + 66 * 60); + entry_guard_consider_retry(g1); + entry_guard_consider_retry(g2); + tt_int_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_NO); - tt_assert(!e->is_dir_cache); - tt_assert(!e->can_retry); + done: + guard_selection_free(gs); +} - /* XXX tt_double_op doesn't support equality. Cast to int for now. */ - tt_int_op((int)e->circ_attempts, OP_EQ, (int)circ_attempts); - tt_int_op((int)e->circ_successes, OP_EQ, (int)circ_successes); - tt_int_op((int)e->successful_circuits_closed, OP_EQ, - (int)successful_closed); - tt_int_op((int)e->timeouts, OP_EQ, (int)timeouts); - tt_int_op((int)e->collapsed_circuits, OP_EQ, (int)collapsed); - tt_int_op((int)e->unusable_circuits, OP_EQ, (int)unusable); +static void +test_entry_guard_manage_primary(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + smartlist_t *prev_guards = smartlist_new(); + + /* If no guards are confirmed, we should pick a few reachable guards and + * call them all primary. But not confirmed.*/ + entry_guards_update_primary(gs); + int n_primary = smartlist_len(gs->primary_entry_guards); + tt_int_op(n_primary, OP_GE, 1); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, g, { + tt_assert(g->is_primary); + tt_assert(g->confirmed_idx == -1); + }); + + /* Calling it a second time should leave the guards unchanged. */ + smartlist_add_all(prev_guards, gs->primary_entry_guards); + entry_guards_update_primary(gs); + tt_int_op(smartlist_len(gs->primary_entry_guards), OP_EQ, n_primary); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, g, { + tt_ptr_op(g, OP_EQ, smartlist_get(prev_guards, g_sl_idx)); + }); + + /* If we have one confirmed guard, that guards becomes the first primary + * guard, and the other primary guards get kept. */ + + /* find a non-primary guard... */ + entry_guard_t *confirmed = NULL; + SMARTLIST_FOREACH(gs->sampled_entry_guards, entry_guard_t *, g, { + if (! g->is_primary) { + confirmed = g; + break; + } + }); + tt_assert(confirmed); + /* make it confirmed. */ + make_guard_confirmed(gs, confirmed); + /* update the list... */ + smartlist_clear(prev_guards); + smartlist_add_all(prev_guards, gs->primary_entry_guards); + entry_guards_update_primary(gs); + + /* and see what's primary now! */ + tt_int_op(smartlist_len(gs->primary_entry_guards), OP_EQ, n_primary); + tt_ptr_op(smartlist_get(gs->primary_entry_guards, 0), OP_EQ, confirmed); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, g, { + tt_assert(g->is_primary); + if (g_sl_idx == 0) + continue; + tt_ptr_op(g, OP_EQ, smartlist_get(prev_guards, g_sl_idx - 1)); + }); + { + entry_guard_t *prev_last_guard = smartlist_get(prev_guards, n_primary-1); + tt_assert(! prev_last_guard->is_primary); } + /* Calling it a fourth time should leave the guards unchanged. */ + smartlist_clear(prev_guards); + smartlist_add_all(prev_guards, gs->primary_entry_guards); + entry_guards_update_primary(gs); + tt_int_op(smartlist_len(gs->primary_entry_guards), OP_EQ, n_primary); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, g, { + tt_ptr_op(g, OP_EQ, smartlist_get(prev_guards, g_sl_idx)); + }); + done: - or_state_free(state); - state_lines_free(entry_state_lines); - tor_free(msg); + guard_selection_free(gs); + smartlist_free(prev_guards); } -/* Simple test of entry_guards_set_from_config() by specifying a - particular EntryNode and making sure it gets picked. */ static void -test_entry_guards_set_from_config(void *arg) +test_entry_guard_guard_preferred(void *arg) { - or_options_t *options = get_options_mutable(); - const smartlist_t *all_entry_guards = get_entry_guards(); - const char *entrynodes_str = "test003r"; - const node_t *chosen_entry = NULL; - int retval; - (void) arg; + entry_guard_t *g1 = tor_malloc_zero(sizeof(entry_guard_t)); + entry_guard_t *g2 = tor_malloc_zero(sizeof(entry_guard_t)); + + g1->confirmed_idx = g2->confirmed_idx = -1; + g1->last_tried_to_connect = approx_time(); + g2->last_tried_to_connect = approx_time(); + + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g1)); - /* Prase EntryNodes as a routerset. */ - options->EntryNodes = routerset_new(); - retval = routerset_parse(options->EntryNodes, - entrynodes_str, - "test_entrynodes"); - tt_int_op(retval, OP_GE, 0); + /* Neither is pending; priorities equal. */ + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g2)); - /* Read nodes from EntryNodes */ - entry_guards_set_from_config(options); + /* If one is pending, the pending one has higher priority */ + g1->is_pending = 1; + tt_int_op(1, OP_EQ, entry_guard_has_higher_priority(g1, g2)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g2, g1)); - /* Test that only one guard was added. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 1); + /* If both are pending, and last_tried_to_connect is equal: + priorities equal */ + g2->is_pending = 1; + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g2)); - /* Make sure it was the guard we specified. */ - chosen_entry = choose_random_entry(NULL); - tt_str_op(chosen_entry->ri->nickname, OP_EQ, entrynodes_str); + /* One had a connection that startied earlier: it has higher priority. */ + g2->last_tried_to_connect -= 10; + tt_int_op(1, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g2)); + + /* Now, say that g1 is confirmed. It will get higher priority. */ + g1->confirmed_idx = 5; + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(1, OP_EQ, entry_guard_has_higher_priority(g1, g2)); + + /* But if g2 was confirmed first, it will get priority */ + g2->confirmed_idx = 2; + tt_int_op(1, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g2)); done: - routerset_free(options->EntryNodes); + tor_free(g1); + tor_free(g2); } static void -test_entry_is_time_to_retry(void *arg) +test_entry_guard_select_for_circuit_no_confirmed(void *arg) { - entry_guard_t *test_guard; - time_t now; - int retval; + /* Simpler cases: no gaurds are confirmed yet. */ (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + /* simple starting configuration */ + entry_guards_update_primary(gs); + unsigned state = 9999; + + entry_guard_t *g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, + NULL, &state); + + tt_assert(g); + tt_assert(g->is_primary); + tt_int_op(g->confirmed_idx, OP_EQ, -1); + tt_assert(g->is_pending == 0); // primary implies non-pending. + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(g->last_tried_to_connect, OP_EQ, approx_time()); + + // If we do that again, we should get the same guard. + entry_guard_t *g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, + NULL, &state); + tt_ptr_op(g2, OP_EQ, g); + + // if we mark that guard down, we should get a different primary guard. + // auto-retry it. + g->is_reachable = GUARD_REACHABLE_NO; + g->failing_since = approx_time() - 10; + g->last_tried_to_connect = approx_time() - 10; + state = 9999; + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_ptr_op(g2, OP_NE, g); + tt_assert(g2); + tt_assert(g2->is_primary); + tt_int_op(g2->confirmed_idx, OP_EQ, -1); + tt_assert(g2->is_pending == 0); // primary implies non-pending. + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time()); + + // If we say that the first primary guard was last tried a long time ago, we + // should get an automatic retry on it. + g->failing_since = approx_time() - 72*60*60; + g->last_tried_to_connect = approx_time() - 72*60*60; + state = 9999; + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_ptr_op(g2, OP_EQ, g); + tt_assert(g2); + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time()); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + // And if we mark ALL the primary guards down, we should get another guard + // at random. + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, guard, { + guard->is_reachable = GUARD_REACHABLE_NO; + guard->last_tried_to_connect = approx_time() - 5; + guard->failing_since = approx_time() - 30; + }); + state = 9999; + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_assert(g2); + tt_assert(!g2->is_primary); + tt_int_op(g2->confirmed_idx, OP_EQ, -1); + tt_assert(g2->is_pending == 1); + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time()); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + // As a bonus, maybe we should be retrying the primary guards. Let's say so. + mark_primary_guards_maybe_reachable(gs); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, guard, { + tt_int_op(guard->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + tt_assert(guard->is_usable_filtered_guard == 1); + // no change to these fields. + tt_i64_op(guard->last_tried_to_connect, OP_EQ, approx_time() - 5); + tt_i64_op(guard->failing_since, OP_EQ, approx_time() - 30); + }); + + /* Let's try again and we should get the first primary guard again */ + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_ptr_op(g, OP_EQ, smartlist_get(gs->primary_entry_guards, 0)); + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_ptr_op(g2, OP_EQ, g); + + /* But if we impose a restriction, we don't get the same guard */ + entry_guard_restriction_t rst; + memset(&rst, 0, sizeof(rst)); + memcpy(rst.exclude_id, g->identity, DIGEST_LEN); + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); + tt_ptr_op(g2, OP_NE, g); - now = time(NULL); + done: + guard_selection_free(gs); +} - test_guard = tor_malloc_zero(sizeof(entry_guard_t)); +static void +test_entry_guard_select_for_circuit_confirmed(void *arg) +{ + /* Case 2: if all the primary guards are down, and there are more confirmed + guards, we use a confirmed guard. */ + (void)arg; + int i; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + const int N_CONFIRMED = 10; + + /* slightly more complicated simple starting configuration */ + entry_guards_update_primary(gs); + for (i = 0; i < N_CONFIRMED; ++i) { + entry_guard_t *guard = smartlist_get(gs->sampled_entry_guards, i); + make_guard_confirmed(gs, guard); + } + entry_guards_update_primary(gs); // rebuild the primary list. + + unsigned state = 9999; + + // As above, this gives us a primary guard. + entry_guard_t *g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, + NULL, &state); + tt_assert(g); + tt_assert(g->is_primary); + tt_int_op(g->confirmed_idx, OP_EQ, 0); + tt_assert(g->is_pending == 0); // primary implies non-pending. + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(g->last_tried_to_connect, OP_EQ, approx_time()); + tt_ptr_op(g, OP_EQ, smartlist_get(gs->primary_entry_guards, 0)); + + // But if we mark all the primary guards down... + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, guard, { + guard->last_tried_to_connect = approx_time(); + entry_guards_note_guard_failure(gs, guard); + }); + + // ... we should get a confirmed guard. + state = 9999; + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_assert(g); + tt_assert(! g->is_primary); + tt_int_op(g->confirmed_idx, OP_EQ, smartlist_len(gs->primary_entry_guards)); + tt_assert(g->is_pending); + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tt_i64_op(g->last_tried_to_connect, OP_EQ, approx_time()); + + // And if we try again, we should get a different confirmed guard, since + // that one is pending. + state = 9999; + entry_guard_t *g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, + NULL, &state); + tt_assert(g2); + tt_assert(! g2->is_primary); + tt_ptr_op(g2, OP_NE, g); + tt_int_op(g2->confirmed_idx, OP_EQ, + smartlist_len(gs->primary_entry_guards)+1); + tt_assert(g2->is_pending); + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time()); + + // If we say that the next confirmed guard in order is excluded, and + // we disable EnforceDistinctSubnets, we get the guard AFTER the + // one we excluded. + get_options_mutable()->EnforceDistinctSubnets = 0; + g = smartlist_get(gs->confirmed_entry_guards, + smartlist_len(gs->primary_entry_guards)+2); + entry_guard_restriction_t rst; + memset(&rst, 0, sizeof(rst)); + memcpy(rst.exclude_id, g->identity, DIGEST_LEN); + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); + tt_ptr_op(g2, OP_NE, NULL); + tt_ptr_op(g2, OP_NE, g); + tt_int_op(g2->confirmed_idx, OP_EQ, + smartlist_len(gs->primary_entry_guards)+3); + + // If we make every confirmed guard become pending then we start poking + // other guards. + const int n_remaining_confirmed = + N_CONFIRMED - 3 - smartlist_len(gs->primary_entry_guards); + for (i = 0; i < n_remaining_confirmed; ++i) { + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_int_op(g->confirmed_idx, OP_GE, 0); + tt_assert(g); + } + state = 9999; + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_assert(g); + tt_assert(g->is_pending); + tt_int_op(g->confirmed_idx, OP_EQ, -1); + + // If we EnforceDistinctSubnets and apply a restriction, we get + // nothing, since we put all of the nodes in the same /16. + // Regression test for bug 22753/TROVE-2017-006. + get_options_mutable()->EnforceDistinctSubnets = 1; + g = smartlist_get(gs->confirmed_entry_guards, 0); + memset(&rst, 0, sizeof(rst)); + memcpy(rst.exclude_id, g->identity, DIGEST_LEN); + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); + tt_ptr_op(g2, OP_EQ, NULL); - test_guard->last_attempted = now - 10; - test_guard->unreachable_since = now - 1; + done: + guard_selection_free(gs); +} - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); +static void +test_entry_guard_select_for_circuit_highlevel_primary(void *arg) +{ + /* Play around with selecting primary guards for circuits and markign + * them up and down */ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + time_t start = approx_time(); + + const node_t *node = NULL; + circuit_guard_state_t *guard = NULL; + entry_guard_t *g; + guard_usable_t u; + /* + * Make sure that the pick-for-circuit API basically works. We'll get + * a primary guard, so it'll be usable on completion. + */ + int r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + + tt_assert(r == 0); + tt_assert(node); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_mem_op(g->identity, OP_EQ, node->identity, DIGEST_LEN); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_i64_op(g->last_tried_to_connect, OP_EQ, start); + tt_int_op(g->confirmed_idx, OP_EQ, -1); + + /* Call that circuit successful. */ + update_approx_time(start+15); + u = entry_guard_succeeded(&guard); + tt_int_op(u, OP_EQ, GUARD_USABLE_NOW); /* We can use it now. */ + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_YES); + tt_int_op(g->confirmed_idx, OP_EQ, 0); + + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + g = NULL; + + /* Try again. We'll also get a primary guard this time. (The same one, + in fact.) But this time, we'll say the connection has failed. */ + update_approx_time(start+35); + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(r == 0); + tt_assert(node); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(guard->state_set_at, OP_EQ, start+35); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_mem_op(g->identity, OP_EQ, node->identity, DIGEST_LEN); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_i64_op(g->last_tried_to_connect, OP_EQ, start+35); + tt_int_op(g->confirmed_idx, OP_EQ, 0); // same one. + + /* It's failed! What will happen to our poor guard? */ + update_approx_time(start+45); + entry_guard_failed(&guard); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_DEAD); + tt_i64_op(guard->state_set_at, OP_EQ, start+45); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + tt_i64_op(g->failing_since, OP_EQ, start+45); + tt_int_op(g->confirmed_idx, OP_EQ, 0); // still confirmed. + + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + entry_guard_t *g_prev = g; + g = NULL; + + /* Now try a third time. Since the other one is down, we'll get a different + * (still primary) guard. + */ + update_approx_time(start+60); + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(r == 0); + tt_assert(node); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_ptr_op(g, OP_NE, g_prev); + tt_mem_op(g->identity, OP_EQ, node->identity, DIGEST_LEN); + tt_mem_op(g->identity, OP_NE, g_prev->identity, DIGEST_LEN); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_i64_op(g->last_tried_to_connect, OP_EQ, start+60); + tt_int_op(g->confirmed_idx, OP_EQ, -1); // not confirmd now. + + /* Call this one up; watch it get confirmed. */ + update_approx_time(start+90); + u = entry_guard_succeeded(&guard); + tt_int_op(u, OP_EQ, GUARD_USABLE_NOW); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_YES); + tt_int_op(g->confirmed_idx, OP_EQ, 1); - test_guard->unreachable_since = now - (6*60*60 - 1); - test_guard->last_attempted = now - (60*60 + 1); + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); +} - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); +static void +test_entry_guard_select_for_circuit_highlevel_confirm_other(void *arg) +{ + (void) arg; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + + /* At the start, we have no confirmed guards. We'll mark the primary guards + * down, then confirm something else. As soon as we do, it should become + * primary, and we should get it next time. */ + + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + circuit_guard_state_t *guard = NULL; + int i, r; + const node_t *node = NULL; + guard_usable_t u; + + /* Declare that we're on the internet. */ + entry_guards_note_internet_connectivity(gs); + + /* Primary guards are down! */ + for (i = 0; i < N_PRIMARY; ++i) { + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + entry_guard_failed(&guard); + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + } - test_guard->last_attempted = now - (60*60 - 1); + /* Next guard should be non-primary. */ + node = NULL; + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + entry_guard_t *g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tt_int_op(g->confirmed_idx, OP_EQ, -1); + tt_int_op(g->is_primary, OP_EQ, 0); + tt_int_op(g->is_pending, OP_EQ, 1); + (void)start; + + u = entry_guard_succeeded(&guard); + /* We're on the internet (by fiat), so this guard will get called "confirmed" + * and should immediately become primary. + */ + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + tt_assert(u == GUARD_USABLE_NOW); + tt_int_op(g->confirmed_idx, OP_EQ, 0); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_int_op(g->is_pending, OP_EQ, 0); - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,0); + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); +} - test_guard->unreachable_since = now - (6*60*60 + 1); - test_guard->last_attempted = now - (4*60*60 + 1); +static void +test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg) +{ + (void) arg; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + + /* At the start, we have no confirmed guards. We'll mark the primary guards + * down, then confirm something else. As soon as we do, it should become + * primary, and we should get it next time. */ + + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + circuit_guard_state_t *guard = NULL, *guard2 = NULL; + int i, r; + const node_t *node = NULL; + entry_guard_t *g; + guard_usable_t u; + + /* Declare that we're on the internet. */ + entry_guards_note_internet_connectivity(gs); + + /* Make primary guards confirmed (so they won't be superseded by a later + * guard), then mark them down. */ + for (i = 0; i < N_PRIMARY; ++i) { + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + make_guard_confirmed(gs, g); + tt_int_op(g->is_primary, OP_EQ, 1); + entry_guard_failed(&guard); + circuit_guard_state_free(guard); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + guard = NULL; + node = NULL; + } - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + /* Get another guard that we might try. */ + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + g = entry_guard_handle_get(guard->guard); + tt_int_op(g->is_primary, OP_EQ, 0); + + tt_assert(entry_guards_all_primary_guards_are_down(gs)); + + /* And an hour has passed ... */ + update_approx_time(start + 3600); + + /* Say that guard has succeeded! */ + u = entry_guard_succeeded(&guard); + tt_int_op(u, OP_EQ, GUARD_MAYBE_USABLE_LATER); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD); + g = entry_guard_handle_get(guard->guard); + + /* The primary guards should have been marked up! */ + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, pg, { + tt_int_op(pg->is_primary, OP_EQ, 1); + tt_ptr_op(g, OP_NE, pg); + tt_int_op(pg->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + }); + + /* Have a circuit to a primary guard succeed. */ + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard2); + tt_assert(r == 0); + tt_int_op(guard2->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + u = entry_guard_succeeded(&guard2); + tt_assert(u == GUARD_USABLE_NOW); + tt_int_op(guard2->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + + tt_assert(! entry_guards_all_primary_guards_are_down(gs)); - test_guard->unreachable_since = now - (3*24*60*60 - 1); - test_guard->last_attempted = now - (4*60*60 + 1); + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); + circuit_guard_state_free(guard2); +} - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); +static void +test_entry_guard_select_and_cancel(void *arg) +{ + (void) arg; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + int i,r; + const node_t *node = NULL; + circuit_guard_state_t *guard; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + entry_guard_t *g; + + /* Once more, we mark all the primary guards down. */ + entry_guards_note_internet_connectivity(gs); + for (i = 0; i < N_PRIMARY; ++i) { + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_int_op(r, OP_EQ, 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_int_op(g->is_pending, OP_EQ, 0); + make_guard_confirmed(gs, g); + entry_guard_failed(&guard); + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + } - test_guard->unreachable_since = now - (3*24*60*60 + 1); - test_guard->last_attempted = now - (18*60*60 + 1); + tt_assert(entry_guards_all_primary_guards_are_down(gs)); + + /* Now get another guard we could try... */ + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + g = entry_guard_handle_get(guard->guard); + tt_int_op(g->is_primary, OP_EQ, 0); + tt_int_op(g->is_pending, OP_EQ, 1); + + /* Whoops! We should never have asked for this guard. Cancel the request! */ + entry_guard_cancel(&guard); + tt_assert(guard == NULL); + tt_int_op(g->is_primary, OP_EQ, 0); + tt_int_op(g->is_pending, OP_EQ, 0); - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); +} - test_guard->unreachable_since = now - (7*24*60*60 - 1); - test_guard->last_attempted = now - (18*60*60 + 1); +static void +test_entry_guard_drop_guards(void *arg) +{ + (void) arg; + int r; + const node_t *node = NULL; + circuit_guard_state_t *guard; + guard_selection_t *gs = get_guard_selection_info(); + + // Pick a guard, to get things set up. + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_GE, + DFLT_MIN_FILTERED_SAMPLE_SIZE); + tt_ptr_op(gs, OP_EQ, get_guard_selection_info()); + + // Drop all the guards! (This is a bad idea....) + remove_all_entry_guards_for_guard_selection(gs); + gs = get_guard_selection_info(); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, 0); + tt_int_op(smartlist_len(gs->primary_entry_guards), OP_EQ, 0); + tt_int_op(smartlist_len(gs->confirmed_entry_guards), OP_EQ, 0); - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + done: + circuit_guard_state_free(guard); + guard_selection_free(gs); +} - test_guard->last_attempted = now - (18*60*60 - 1); +/* Unit test setup function: Create a fake network, and set everything up + * for testing the upgrade-a-waiting-circuit code. */ +typedef struct { + guard_selection_t *gs; + time_t start; + circuit_guard_state_t *guard1_state; + circuit_guard_state_t *guard2_state; + entry_guard_t *guard1; + entry_guard_t *guard2; + origin_circuit_t *circ1; + origin_circuit_t *circ2; + smartlist_t *all_origin_circuits; +} upgrade_circuits_data_t; +static void * +upgrade_circuits_setup(const struct testcase_t *testcase) +{ + upgrade_circuits_data_t *data = tor_malloc_zero(sizeof(*data)); + guard_selection_t *gs = data->gs = + guard_selection_new("default", GS_TYPE_NORMAL); + circuit_guard_state_t *guard; + const node_t *node; + entry_guard_t *g; + int i; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + const char *argument = testcase->setup_data; + const int make_circ1_succeed = strstr(argument, "c1-done") != NULL; + const int make_circ2_succeed = strstr(argument, "c2-done") != NULL; + + big_fake_network_setup(testcase); + + /* We're going to set things up in a state where a circuit will be ready to + * be upgraded. Each test can make a single change (or not) that should + * block the upgrade. + */ + + /* First, make all the primary guards confirmed, and down. */ + data->start = approx_time(); + entry_guards_note_internet_connectivity(gs); + for (i = 0; i < N_PRIMARY; ++i) { + entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &node, &guard); + g = entry_guard_handle_get(guard->guard); + make_guard_confirmed(gs, g); + entry_guard_failed(&guard); + circuit_guard_state_free(guard); + } - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,0); + /* Grab another couple of guards */ + data->all_origin_circuits = smartlist_new(); + + update_approx_time(data->start + 27); + entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &data->guard1_state); + origin_circuit_t *circ; + data->circ1 = circ = origin_circuit_new(); + circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; + circ->guard_state = data->guard1_state; + smartlist_add(data->all_origin_circuits, circ); + + update_approx_time(data->start + 30); + entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &data->guard2_state); + data->circ2 = circ = origin_circuit_new(); + circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; + circ->guard_state = data->guard2_state; + smartlist_add(data->all_origin_circuits, circ); + + data->guard1 = entry_guard_handle_get(data->guard1_state->guard); + data->guard2 = entry_guard_handle_get(data->guard2_state->guard); + tor_assert(data->guard1 != data->guard2); + tor_assert(data->guard1_state->state == + GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tor_assert(data->guard2_state->state == + GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + + guard_usable_t r; + update_approx_time(data->start + 32); + if (make_circ1_succeed) { + r = entry_guard_succeeded(&data->guard1_state); + tor_assert(r == GUARD_MAYBE_USABLE_LATER); + tor_assert(data->guard1_state->state == + GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD); + } + update_approx_time(data->start + 33); + if (make_circ2_succeed) { + r = entry_guard_succeeded(&data->guard2_state); + tor_assert(r == GUARD_MAYBE_USABLE_LATER); + tor_assert(data->guard2_state->state == + GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD); + } - test_guard->unreachable_since = now - (7*24*60*60 + 1); - test_guard->last_attempted = now - (36*60*60 + 1); + return data; +} +static int +upgrade_circuits_cleanup(const struct testcase_t *testcase, void *ptr) +{ + upgrade_circuits_data_t *data = ptr; + // circuit_guard_state_free(data->guard1_state); // held in circ1 + // circuit_guard_state_free(data->guard2_state); // held in circ2 + guard_selection_free(data->gs); + smartlist_free(data->all_origin_circuits); + circuit_free(TO_CIRCUIT(data->circ1)); + circuit_free(TO_CIRCUIT(data->circ2)); + tor_free(data); + return big_fake_network_cleanup(testcase, NULL); +} + +static void +test_entry_guard_upgrade_a_circuit(void *arg) +{ + upgrade_circuits_data_t *data = arg; - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + /* This is the easy case: we have no COMPLETED circuits, all the + * primary guards are down, we have two WAITING circuits: one will + * get upgraded to COMPLETED! (The one that started first.) + */ - test_guard->unreachable_since = now - (7*24*60*60 + 1); - test_guard->last_attempted = now - (36*60*60 + 1); + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + /* circ1 was started first, so we'll get told to ugrade it... */ + tt_ptr_op(oc, OP_EQ, data->circ1); + + /* And the guard state should be complete */ + tt_ptr_op(data->guard1_state, OP_NE, NULL); + tt_int_op(data->guard1_state->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); done: - tor_free(test_guard); + smartlist_free(result); } -/** XXX Do some tests that entry_is_live() */ static void -test_entry_is_live(void *arg) +test_entry_guard_upgrade_blocked_by_live_primary_guards(void *arg) { - smartlist_t *our_nodelist = NULL; - const smartlist_t *all_entry_guards = get_entry_guards(); - const node_t *test_node = NULL; - const entry_guard_t *test_entry = NULL; - const char *msg; - int which_node; - - (void) arg; + upgrade_circuits_data_t *data = arg; + + /* If any primary guards might be up, we can't upgrade any waiting + * circuits. + */ + mark_primary_guards_maybe_reachable(data->gs); + + smartlist_t *result = smartlist_new(); + int r; + setup_capture_of_logs(LOG_DEBUG); + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(result), OP_EQ, 0); + expect_log_msg_containing("not all primary guards were definitely down."); - /* The global entry guards smartlist should be empty now. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 0); + done: + teardown_capture_of_logs(); + smartlist_free(result); +} - /* Walk the nodelist and add all nodes as entry guards. */ - our_nodelist = nodelist_get_list(); - tt_int_op(smartlist_len(our_nodelist), OP_EQ, HELPER_NUMBER_OF_DESCRIPTORS); +static void +test_entry_guard_upgrade_blocked_by_lack_of_waiting_circuits(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* If no circuits are waiting, we can't upgrade anything. (The test + * setup in this case was told not to make any of the circuits "waiting".) + */ + smartlist_t *result = smartlist_new(); + int r; + setup_capture_of_logs(LOG_DEBUG); + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(result), OP_EQ, 0); + expect_log_msg_containing("Considered upgrading guard-stalled circuits, " + "but didn't find any."); - SMARTLIST_FOREACH_BEGIN(our_nodelist, const node_t *, node) { - const node_t *node_tmp; - node_tmp = add_an_entry_guard(node, 0, 1, 0, 0); - tt_assert(node_tmp); + done: + teardown_capture_of_logs(); + smartlist_free(result); +} - tt_int_op(node->is_stable, OP_EQ, 0); - tt_int_op(node->is_fast, OP_EQ, 0); - } SMARTLIST_FOREACH_END(node); +static void +test_entry_guard_upgrade_blocked_by_better_circ_complete(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* We'll run through the logic of upgrade_a_circuit below... + * and then try again to make sure that circ2 isn't also upgraded. + */ + + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ1); + tt_ptr_op(data->guard1_state, OP_NE, NULL); + tt_int_op(data->guard1_state->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + + /* Now, try again. Make sure that circ2 isn't upgraded. */ + smartlist_clear(result); + setup_capture_of_logs(LOG_DEBUG); + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(result), OP_EQ, 0); + expect_log_msg_containing("At least one complete circuit had higher " + "priority, so not upgrading."); - /* Make sure the nodes were added as entry guards. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, - HELPER_NUMBER_OF_DESCRIPTORS); + done: + teardown_capture_of_logs(); + smartlist_free(result); +} - /* Now get a random test entry that we will use for this unit test. */ - which_node = 3; /* (chosen by fair dice roll) */ - test_entry = smartlist_get(all_entry_guards, which_node); +static void +test_entry_guard_upgrade_not_blocked_by_restricted_circ_complete(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* Once more, let circ1 become complete. But this time, we'll claim + * that circ2 was restricted to not use the same guard as circ1. */ + data->guard2_state->restrictions = + tor_malloc_zero(sizeof(entry_guard_restriction_t)); + memcpy(data->guard2_state->restrictions->exclude_id, + data->guard1->identity, DIGEST_LEN); + + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ1); + tt_ptr_op(data->guard1_state, OP_NE, NULL); + tt_int_op(data->guard1_state->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + + /* Now, we try again. Since circ2 has a restriction that circ1 doesn't obey, + * circ2 _is_ eligible for upgrade. */ + smartlist_clear(result); + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc2 = smartlist_get(result, 0); + tt_ptr_op(oc2, OP_EQ, data->circ2); - /* Let's do some entry_is_live() tests! */ + done: + smartlist_free(result); +} - /* Require the node to be stable, but it's not. Should fail. - Also enable 'assume_reachable' because why not. */ - test_node = entry_is_live(test_entry, - ENTRY_NEED_UPTIME | ENTRY_ASSUME_REACHABLE, - &msg); - tt_assert(!test_node); +static void +test_entry_guard_upgrade_not_blocked_by_worse_circ_complete(void *arg) +{ + upgrade_circuits_data_t *data = arg; + smartlist_t *result = smartlist_new(); + /* here we manually make circ2 COMPLETE, and make sure that circ1 + * gets made complete anyway, since guard1 has higher priority + */ + update_approx_time(data->start + 300); + data->guard2_state->state = GUARD_CIRC_STATE_COMPLETE; + data->guard2_state->state_set_at = approx_time(); + update_approx_time(data->start + 301); + + /* Now, try again. Make sure that circ1 is approved. */ + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ1); - /* Require the node to be fast, but it's not. Should fail. */ - test_node = entry_is_live(test_entry, - ENTRY_NEED_CAPACITY | ENTRY_ASSUME_REACHABLE, - &msg); - tt_assert(!test_node); + done: + smartlist_free(result); +} - /* Don't impose any restrictions on the node. Should succeed. */ - test_node = entry_is_live(test_entry, 0, &msg); - tt_assert(test_node); - tt_ptr_op(test_node, OP_EQ, node_get_by_id(test_entry->identity)); +static void +test_entry_guard_upgrade_blocked_by_better_circ_pending(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* circ2 is done, but circ1 is still pending. Since circ1 is better, + * we won't upgrade circ2. */ + + /* XXXX Prop271 -- this is a kludge. I'm making sure circ1 _is_ better, + * by messing with the guards' confirmed_idx */ + make_guard_confirmed(data->gs, data->guard1); + { + int tmp; + tmp = data->guard1->confirmed_idx; + data->guard1->confirmed_idx = data->guard2->confirmed_idx; + data->guard2->confirmed_idx = tmp; + } - /* Require descriptor for this node. It has one so it should succeed. */ - test_node = entry_is_live(test_entry, ENTRY_NEED_DESCRIPTOR, &msg); - tt_assert(test_node); - tt_ptr_op(test_node, OP_EQ, node_get_by_id(test_entry->identity)); + smartlist_t *result = smartlist_new(); + setup_capture_of_logs(LOG_DEBUG); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(result), OP_EQ, 0); + expect_log_msg_containing("but 1 pending circuit(s) had higher guard " + "priority, so not upgrading."); done: - ; /* XXX */ + teardown_capture_of_logs(); + smartlist_free(result); } -#define TEST_IPV4_ADDR "123.45.67.89" -#define TEST_IPV6_ADDR "[1234:5678:90ab:cdef::]" - static void -test_node_preferred_orport(void *arg) +test_entry_guard_upgrade_not_blocked_by_restricted_circ_pending(void *arg) { - (void)arg; - tor_addr_t ipv4_addr; - const uint16_t ipv4_port = 4444; - tor_addr_t ipv6_addr; - const uint16_t ipv6_port = 6666; - routerinfo_t node_ri; - node_t node; - tor_addr_port_t ap; + upgrade_circuits_data_t *data = arg; + /* circ2 is done, but circ1 is still pending. But when there is a + restriction on circ2 that circ1 can't satisfy, circ1 can't block + circ2. */ + + /* XXXX Prop271 -- this is a kludge. I'm making sure circ1 _is_ better, + * by messing with the guards' confirmed_idx */ + make_guard_confirmed(data->gs, data->guard1); + { + int tmp; + tmp = data->guard1->confirmed_idx; + data->guard1->confirmed_idx = data->guard2->confirmed_idx; + data->guard2->confirmed_idx = tmp; + } - /* Setup options */ - memset(&mocked_options, 0, sizeof(mocked_options)); - /* We don't test ClientPreferIPv6ORPort here, because it's used in - * nodelist_set_consensus to setup node.ipv6_preferred, which we set - * directly. */ - MOCK(get_options, mock_get_options); + data->guard2_state->restrictions = + tor_malloc_zero(sizeof(entry_guard_restriction_t)); + memcpy(data->guard2_state->restrictions->exclude_id, + data->guard1->identity, DIGEST_LEN); + + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ2); - /* Setup IP addresses */ - tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR); - tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR); + done: + smartlist_free(result); +} - /* Setup node_ri */ - memset(&node_ri, 0, sizeof(node_ri)); - node_ri.addr = tor_addr_to_ipv4h(&ipv4_addr); - node_ri.or_port = ipv4_port; - tor_addr_copy(&node_ri.ipv6_addr, &ipv6_addr); - node_ri.ipv6_orport = ipv6_port; +static void +test_entry_guard_upgrade_not_blocked_by_worse_circ_pending(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* circ1 is done, but circ2 is still pending. Since circ1 is better, + * we will upgrade it. */ + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ1); - /* Setup node */ - memset(&node, 0, sizeof(node)); - node.ri = &node_ri; + done: + smartlist_free(result); +} - /* Check the preferred address is IPv4 if we're only using IPv4, regardless - * of whether we prefer it or not */ - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 0; - node.ipv6_preferred = 0; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); - tt_assert(ap.port == ipv4_port); +static void +test_enty_guard_should_expire_waiting(void *arg) +{ + (void)arg; + circuit_guard_state_t *fake_state = tor_malloc_zero(sizeof(*fake_state)); + /* We'll leave "guard" unset -- it won't matter here. */ - node.ipv6_preferred = 1; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); - tt_assert(ap.port == ipv4_port); + /* No state? Can't expire. */ + tt_assert(! entry_guard_state_should_expire(NULL)); - /* Check the preferred address is IPv4 if we're using IPv4 and IPv6, but - * don't prefer the IPv6 address */ - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 1; - node.ipv6_preferred = 0; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); - tt_assert(ap.port == ipv4_port); + /* Let's try one that expires. */ + fake_state->state = GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD; + fake_state->state_set_at = + approx_time() - DFLT_NONPRIMARY_GUARD_IDLE_TIMEOUT - 1; - /* Check the preferred address is IPv6 if we prefer it and - * ClientUseIPv6 is 1, regardless of ClientUseIPv4 */ - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 1; - node.ipv6_preferred = 1; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); - tt_assert(ap.port == ipv6_port); + tt_assert(entry_guard_state_should_expire(fake_state)); - mocked_options.ClientUseIPv4 = 0; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); - tt_assert(ap.port == ipv6_port); + /* But it wouldn't expire if we changed the state. */ + fake_state->state = GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD; + tt_assert(! entry_guard_state_should_expire(fake_state)); - /* Check the preferred address is IPv6 if we don't prefer it, but - * ClientUseIPv4 is 0 */ - mocked_options.ClientUseIPv4 = 0; - mocked_options.ClientUseIPv6 = 1; - node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(&mocked_options); - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); - tt_assert(ap.port == ipv6_port); + /* And it wouldn't have expired a few seconds ago. */ + fake_state->state = GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD; + fake_state->state_set_at = + approx_time() - DFLT_NONPRIMARY_GUARD_IDLE_TIMEOUT + 5; + tt_assert(! entry_guard_state_should_expire(fake_state)); done: - UNMOCK(get_options); + tor_free(fake_state); } -static const struct testcase_setup_t fake_network = { - fake_network_setup, fake_network_cleanup +static const struct testcase_setup_t big_fake_network = { + big_fake_network_setup, big_fake_network_cleanup +}; + +static const struct testcase_setup_t upgrade_circuits = { + upgrade_circuits_setup, upgrade_circuits_cleanup }; +#define BFN_TEST(name) \ + { #name, test_entry_guard_ ## name, TT_FORK, &big_fake_network, NULL } + +#define UPGRADE_TEST(name, arg) \ + { #name, test_entry_guard_ ## name, TT_FORK, &upgrade_circuits, \ + (void*)(arg) } + struct testcase_t entrynodes_tests[] = { - { "entry_is_time_to_retry", test_entry_is_time_to_retry, - TT_FORK, NULL, NULL }, - { "choose_random_entry_no_guards", test_choose_random_entry_no_guards, - TT_FORK, &fake_network, NULL }, - { "choose_random_entry_one_possibleguard", - test_choose_random_entry_one_possible_guard, - TT_FORK, &fake_network, NULL }, - { "populate_live_entry_guards_1guard", - test_populate_live_entry_guards_1guard, - TT_FORK, &fake_network, NULL }, - { "populate_live_entry_guards_3guards", - test_populate_live_entry_guards_3guards, - TT_FORK, &fake_network, NULL }, - { "entry_guards_parse_state_simple", - test_entry_guards_parse_state_simple, - TT_FORK, &fake_network, NULL }, - { "entry_guards_parse_state_pathbias", - test_entry_guards_parse_state_pathbias, - TT_FORK, &fake_network, NULL }, - { "entry_guards_set_from_config", - test_entry_guards_set_from_config, - TT_FORK, &fake_network, NULL }, - { "entry_is_live", - test_entry_is_live, - TT_FORK, &fake_network, NULL }, { "node_preferred_orport", test_node_preferred_orport, 0, NULL, NULL }, + { "entry_guard_describe", test_entry_guard_describe, 0, NULL, NULL }, + { "randomize_time", test_entry_guard_randomize_time, 0, NULL, NULL }, + { "encode_for_state_minimal", + test_entry_guard_encode_for_state_minimal, 0, NULL, NULL }, + { "encode_for_state_maximal", + test_entry_guard_encode_for_state_maximal, 0, NULL, NULL }, + { "parse_from_state_minimal", + test_entry_guard_parse_from_state_minimal, 0, NULL, NULL }, + { "parse_from_state_maximal", + test_entry_guard_parse_from_state_maximal, 0, NULL, NULL }, + { "parse_from_state_failure", + test_entry_guard_parse_from_state_failure, 0, NULL, NULL }, + { "parse_from_state_partial_failure", + test_entry_guard_parse_from_state_partial_failure, 0, NULL, NULL }, + { "parse_from_state_full", + test_entry_guard_parse_from_state_full, TT_FORK, NULL, NULL }, + { "parse_from_state_broken", + test_entry_guard_parse_from_state_broken, TT_FORK, NULL, NULL }, + { "get_guard_selection_by_name", + test_entry_guard_get_guard_selection_by_name, TT_FORK, NULL, NULL }, + BFN_TEST(choose_selection_initial), + BFN_TEST(add_single_guard), + BFN_TEST(node_filter), + BFN_TEST(expand_sample), + BFN_TEST(expand_sample_small_net), + BFN_TEST(update_from_consensus_status), + BFN_TEST(update_from_consensus_repair), + BFN_TEST(update_from_consensus_remove), + BFN_TEST(confirming_guards), + BFN_TEST(sample_reachable_filtered), + BFN_TEST(sample_reachable_filtered_empty), + BFN_TEST(retry_unreachable), + BFN_TEST(manage_primary), + { "guard_preferred", test_entry_guard_guard_preferred, TT_FORK, NULL, NULL }, + BFN_TEST(select_for_circuit_no_confirmed), + BFN_TEST(select_for_circuit_confirmed), + BFN_TEST(select_for_circuit_highlevel_primary), + BFN_TEST(select_for_circuit_highlevel_confirm_other), + BFN_TEST(select_for_circuit_highlevel_primary_retry), + BFN_TEST(select_and_cancel), + BFN_TEST(drop_guards), + + UPGRADE_TEST(upgrade_a_circuit, "c1-done c2-done"), + UPGRADE_TEST(upgrade_blocked_by_live_primary_guards, "c1-done c2-done"), + UPGRADE_TEST(upgrade_blocked_by_lack_of_waiting_circuits, ""), + UPGRADE_TEST(upgrade_blocked_by_better_circ_complete, "c1-done c2-done"), + UPGRADE_TEST(upgrade_not_blocked_by_restricted_circ_complete, + "c1-done c2-done"), + UPGRADE_TEST(upgrade_not_blocked_by_worse_circ_complete, "c1-done c2-done"), + UPGRADE_TEST(upgrade_blocked_by_better_circ_pending, "c2-done"), + UPGRADE_TEST(upgrade_not_blocked_by_restricted_circ_pending, + "c2-done"), + UPGRADE_TEST(upgrade_not_blocked_by_worse_circ_pending, "c1-done"), + { "should_expire_waiting", test_enty_guard_should_expire_waiting, TT_FORK, + NULL, NULL }, + END_OF_TESTCASES }; diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index 1f92780177..fc9f27a5ac 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONNECTION_PRIVATE @@ -72,9 +72,9 @@ test_ext_or_id_map(void *arg) * writes to outbuf. */ static void connection_write_to_buf_impl_replacement(const char *string, size_t len, - connection_t *conn, int zlib) + connection_t *conn, int compressed) { - (void) zlib; + (void) compressed; tor_assert(string); tor_assert(conn); diff --git a/src/test/test_guardfraction.c b/src/test/test_guardfraction.c index 8173e44d47..56006f3cc3 100644 --- a/src/test/test_guardfraction.c +++ b/src/test/test_guardfraction.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define DIRSERV_PRIVATE diff --git a/src/test/test_handles.c b/src/test/test_handles.c index 536a478689..7ddee6e376 100644 --- a/src/test/test_handles.c +++ b/src/test/test_handles.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index ae9fc7a243..9fada5a675 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -10,8 +10,10 @@ #include "orconfig.h" #include "or.h" +#include "relay.h" #include "routerlist.h" #include "nodelist.h" +#include "buffers.h" #include "test.h" #include "test_helpers.h" @@ -22,6 +24,8 @@ DISABLE_GCC_WARNING(overlength-strings) * at large. */ #endif #include "test_descriptors.inc" +#include "or.h" +#include "circuitlist.h" #ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS ENABLE_GCC_WARNING(overlength-strings) #endif @@ -92,3 +96,50 @@ helper_setup_fake_routerlist(void) UNMOCK(router_descriptor_is_older_than); } +void +connection_write_to_buf_mock(const char *string, size_t len, + connection_t *conn, int compressed) +{ + (void) compressed; + + tor_assert(string); + tor_assert(conn); + + write_to_buf(string, len, conn->outbuf); +} + +/* Set up a fake origin circuit with the specified number of cells, + * Return a pointer to the newly-created dummy circuit */ +circuit_t * +dummy_origin_circuit_new(int n_cells) +{ + origin_circuit_t *circ = origin_circuit_new(); + int i; + cell_t cell; + + for (i=0; i < n_cells; ++i) { + crypto_rand((void*)&cell, sizeof(cell)); + cell_queue_append_packed_copy(TO_CIRCUIT(circ), + &TO_CIRCUIT(circ)->n_chan_cells, + 1, &cell, 1, 0); + } + + TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + return TO_CIRCUIT(circ); +} + +/** Mock-replacement. As tor_addr_lookup, but always fails on any + * address containing a !. This is necessary for running the unit tests + * on networks where DNS hijackers think it's helpful to give answers + * for things like 1.2.3.4.5 or "invalidstuff!!" + */ +int +mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, + uint16_t family, tor_addr_t *out) +{ + if (name && strchr(name, '!')) { + return -1; + } + return tor_addr_lookup__real(name, family, out); +} + diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index 684375e1b1..4621631cc1 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_TEST_HELPERS_H @@ -6,11 +6,20 @@ const char *get_yesterday_date_str(void); +circuit_t * dummy_origin_circuit_new(int num_cells); + /* Number of descriptors contained in test_descriptors.txt. */ #define HELPER_NUMBER_OF_DESCRIPTORS 8 void helper_setup_fake_routerlist(void); +#define GET(path) "GET " path " HTTP/1.0\r\n\r\n" +void connection_write_to_buf_mock(const char *string, size_t len, + connection_t *conn, int compressed); + +int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, + uint16_t family, tor_addr_t *out); + extern const char TEST_DESCRIPTORS[]; #endif diff --git a/src/test/test_hs.c b/src/test/test_hs.c index d572dd8d2e..cd71f025a3 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,6 +14,7 @@ #include "test.h" #include "control.h" #include "config.h" +#include "hs_common.h" #include "rendcommon.h" #include "rendservice.h" #include "routerset.h" @@ -136,7 +137,7 @@ test_hs_desc_event(void *arg) #define STR_DESC_ID_BASE32 "hba3gmcgpfivzfhx5rtfqkfdhv65yrj3" int ret; - rend_data_t rend_query; + rend_data_v2_t rend_query; const char *expected_msg; char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; @@ -148,12 +149,13 @@ test_hs_desc_event(void *arg) /* setup rend_query struct */ memset(&rend_query, 0, sizeof(rend_query)); + rend_query.base_.version = 2; strncpy(rend_query.onion_address, STR_HS_ADDR, REND_SERVICE_ID_LEN_BASE32+1); rend_query.auth_type = REND_NO_AUTH; - rend_query.hsdirs_fp = smartlist_new(); - smartlist_add(rend_query.hsdirs_fp, tor_memdup(HSDIR_EXIST_ID, - DIGEST_LEN)); + rend_query.base_.hsdirs_fp = smartlist_new(); + smartlist_add(rend_query.base_.hsdirs_fp, tor_memdup(HSDIR_EXIST_ID, + DIGEST_LEN)); /* Compute descriptor ID for replica 0, should be STR_DESC_ID_BASE32. */ ret = rend_compute_v2_desc_id(rend_query.descriptor_id[0], @@ -167,7 +169,7 @@ test_hs_desc_event(void *arg) sizeof(desc_id_base32)); /* test request event */ - control_event_hs_descriptor_requested(&rend_query, HSDIR_EXIST_ID, + control_event_hs_descriptor_requested(&rend_query.base_, HSDIR_EXIST_ID, STR_DESC_ID_BASE32); expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n"; @@ -178,7 +180,7 @@ test_hs_desc_event(void *arg) /* test received event */ rend_query.auth_type = REND_BASIC_AUTH; control_event_hs_descriptor_received(rend_query.onion_address, - &rend_query, HSDIR_EXIST_ID); + &rend_query.base_, HSDIR_EXIST_ID); expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n"; tt_assert(received_msg); @@ -187,7 +189,7 @@ test_hs_desc_event(void *arg) /* test failed event */ rend_query.auth_type = REND_STEALTH_AUTH; - control_event_hs_descriptor_failed(&rend_query, + control_event_hs_descriptor_failed(&rend_query.base_, HSDIR_NONE_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\ @@ -198,7 +200,7 @@ test_hs_desc_event(void *arg) /* test invalid auth type */ rend_query.auth_type = 999; - control_event_hs_descriptor_failed(&rend_query, + control_event_hs_descriptor_failed(&rend_query.base_, HSDIR_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\ @@ -208,9 +210,30 @@ test_hs_desc_event(void *arg) tt_str_op(received_msg,OP_EQ, expected_msg); tor_free(received_msg); - /* test valid content. */ + /* test no HSDir fingerprint type */ + rend_query.auth_type = REND_NO_AUTH; + control_event_hs_descriptor_failed(&rend_query.base_, NULL, + "QUERY_NO_HSDIR"); + expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \ + "UNKNOWN REASON=QUERY_NO_HSDIR\r\n"; + tt_assert(received_msg); + tt_str_op(received_msg,OP_EQ, expected_msg); + tor_free(received_msg); + + /* Test invalid content with no HSDir fingerprint. */ char *exp_msg; control_event_hs_descriptor_content(rend_query.onion_address, + STR_HS_CONTENT_DESC_ID, NULL, NULL); + tor_asprintf(&exp_msg, "650+HS_DESC_CONTENT " STR_HS_ADDR " "\ + STR_HS_CONTENT_DESC_ID " UNKNOWN" \ + "\r\n\r\n.\r\n650 OK\r\n"); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, exp_msg); + tor_free(received_msg); + tor_free(exp_msg); + + /* test valid content. */ + control_event_hs_descriptor_content(rend_query.onion_address, STR_HS_CONTENT_DESC_ID, HSDIR_EXIST_ID, hs_desc_content); tor_asprintf(&exp_msg, "650+HS_DESC_CONTENT " STR_HS_ADDR " "\ @@ -221,8 +244,8 @@ test_hs_desc_event(void *arg) tt_str_op(received_msg, OP_EQ, exp_msg); tor_free(received_msg); tor_free(exp_msg); - SMARTLIST_FOREACH(rend_query.hsdirs_fp, char *, d, tor_free(d)); - smartlist_free(rend_query.hsdirs_fp); + SMARTLIST_FOREACH(rend_query.base_.hsdirs_fp, char *, d, tor_free(d)); + smartlist_free(rend_query.base_.hsdirs_fp); done: UNMOCK(queue_control_event_string); @@ -322,42 +345,46 @@ test_hs_rend_data(void *arg) client = rend_data_client_create(STR_HS_ADDR, desc_id, client_cookie, REND_NO_AUTH); tt_assert(client); - tt_int_op(client->auth_type, ==, REND_NO_AUTH); - tt_str_op(client->onion_address, OP_EQ, STR_HS_ADDR); - tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); - tt_mem_op(client->descriptor_cookie, OP_EQ, client_cookie, + rend_data_v2_t *client_v2 = TO_REND_DATA_V2(client); + tt_int_op(client_v2->auth_type, ==, REND_NO_AUTH); + tt_str_op(client_v2->onion_address, OP_EQ, STR_HS_ADDR); + tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); + tt_mem_op(client_v2->descriptor_cookie, OP_EQ, client_cookie, sizeof(client_cookie)); tt_assert(client->hsdirs_fp); tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - int ret = rend_compute_v2_desc_id(desc_id, client->onion_address, - client->descriptor_cookie, now, rep); + int ret = rend_compute_v2_desc_id(desc_id, client_v2->onion_address, + client_v2->descriptor_cookie, now, rep); /* That shouldn't never fail. */ tt_int_op(ret, ==, 0); - tt_mem_op(client->descriptor_id[rep], OP_EQ, desc_id, sizeof(desc_id)); + tt_mem_op(client_v2->descriptor_id[rep], OP_EQ, desc_id, + sizeof(desc_id)); } /* The rest should be zeroed because this is a client request. */ - tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1); + tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), ==, 1); tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1); /* Test dup(). */ client_dup = rend_data_dup(client); tt_assert(client_dup); - tt_int_op(client_dup->auth_type, ==, client->auth_type); - tt_str_op(client_dup->onion_address, OP_EQ, client->onion_address); - tt_mem_op(client_dup->desc_id_fetch, OP_EQ, client->desc_id_fetch, - sizeof(client_dup->desc_id_fetch)); - tt_mem_op(client_dup->descriptor_cookie, OP_EQ, client->descriptor_cookie, - sizeof(client_dup->descriptor_cookie)); + rend_data_v2_t *client_dup_v2 = TO_REND_DATA_V2(client_dup); + tt_int_op(client_dup_v2->auth_type, ==, client_v2->auth_type); + tt_str_op(client_dup_v2->onion_address, OP_EQ, client_v2->onion_address); + tt_mem_op(client_dup_v2->desc_id_fetch, OP_EQ, client_v2->desc_id_fetch, + sizeof(client_dup_v2->desc_id_fetch)); + tt_mem_op(client_dup_v2->descriptor_cookie, OP_EQ, + client_v2->descriptor_cookie, + sizeof(client_dup_v2->descriptor_cookie)); tt_assert(client_dup->hsdirs_fp); tt_int_op(smartlist_len(client_dup->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - tt_mem_op(client_dup->descriptor_id[rep], OP_EQ, - client->descriptor_id[rep], DIGEST_LEN); + tt_mem_op(client_dup_v2->descriptor_id[rep], OP_EQ, + client_v2->descriptor_id[rep], DIGEST_LEN); } /* The rest should be zeroed because this is a client request. */ - tt_int_op(tor_digest_is_zero(client_dup->rend_pk_digest), ==, 1); + tt_int_op(tor_digest_is_zero(client_dup_v2->rend_pk_digest), ==, 1); tt_int_op(tor_digest_is_zero(client_dup->rend_cookie), ==, 1); rend_data_free(client); client = NULL; @@ -373,18 +400,19 @@ test_hs_rend_data(void *arg) * zeroed out. */ client = rend_data_client_create(NULL, desc_id, NULL, REND_BASIC_AUTH); tt_assert(client); - tt_int_op(client->auth_type, ==, REND_BASIC_AUTH); - tt_int_op(strlen(client->onion_address), ==, 0); - tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); - tt_int_op(tor_mem_is_zero(client->descriptor_cookie, - sizeof(client->descriptor_cookie)), ==, 1); + client_v2 = TO_REND_DATA_V2(client); + tt_int_op(client_v2->auth_type, ==, REND_BASIC_AUTH); + tt_int_op(strlen(client_v2->onion_address), ==, 0); + tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); + tt_int_op(tor_mem_is_zero(client_v2->descriptor_cookie, + sizeof(client_v2->descriptor_cookie)), ==, 1); tt_assert(client->hsdirs_fp); tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - tt_int_op(tor_digest_is_zero(client->descriptor_id[rep]), ==, 1); + tt_int_op(tor_digest_is_zero(client_v2->descriptor_id[rep]), ==, 1); } /* The rest should be zeroed because this is a client request. */ - tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1); + tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), ==, 1); tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1); rend_data_free(client); client = NULL; @@ -398,37 +426,39 @@ test_hs_rend_data(void *arg) service = rend_data_service_create(STR_HS_ADDR, rend_pk_digest, rend_cookie, REND_NO_AUTH); tt_assert(service); - tt_int_op(service->auth_type, ==, REND_NO_AUTH); - tt_str_op(service->onion_address, OP_EQ, STR_HS_ADDR); - tt_mem_op(service->rend_pk_digest, OP_EQ, rend_pk_digest, + rend_data_v2_t *service_v2 = TO_REND_DATA_V2(service); + tt_int_op(service_v2->auth_type, ==, REND_NO_AUTH); + tt_str_op(service_v2->onion_address, OP_EQ, STR_HS_ADDR); + tt_mem_op(service_v2->rend_pk_digest, OP_EQ, rend_pk_digest, sizeof(rend_pk_digest)); tt_mem_op(service->rend_cookie, OP_EQ, rend_cookie, sizeof(rend_cookie)); tt_assert(service->hsdirs_fp); tt_int_op(smartlist_len(service->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - tt_int_op(tor_digest_is_zero(service->descriptor_id[rep]), ==, 1); + tt_int_op(tor_digest_is_zero(service_v2->descriptor_id[rep]), ==, 1); } /* The rest should be zeroed because this is a service request. */ - tt_int_op(tor_digest_is_zero(service->descriptor_cookie), ==, 1); - tt_int_op(tor_digest_is_zero(service->desc_id_fetch), ==, 1); + tt_int_op(tor_digest_is_zero(service_v2->descriptor_cookie), ==, 1); + tt_int_op(tor_digest_is_zero(service_v2->desc_id_fetch), ==, 1); /* Test dup(). */ service_dup = rend_data_dup(service); + rend_data_v2_t *service_dup_v2 = TO_REND_DATA_V2(service_dup); tt_assert(service_dup); - tt_int_op(service_dup->auth_type, ==, service->auth_type); - tt_str_op(service_dup->onion_address, OP_EQ, service->onion_address); - tt_mem_op(service_dup->rend_pk_digest, OP_EQ, service->rend_pk_digest, - sizeof(service_dup->rend_pk_digest)); + tt_int_op(service_dup_v2->auth_type, ==, service_v2->auth_type); + tt_str_op(service_dup_v2->onion_address, OP_EQ, service_v2->onion_address); + tt_mem_op(service_dup_v2->rend_pk_digest, OP_EQ, service_v2->rend_pk_digest, + sizeof(service_dup_v2->rend_pk_digest)); tt_mem_op(service_dup->rend_cookie, OP_EQ, service->rend_cookie, sizeof(service_dup->rend_cookie)); tt_assert(service_dup->hsdirs_fp); tt_int_op(smartlist_len(service_dup->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - tt_int_op(tor_digest_is_zero(service_dup->descriptor_id[rep]), ==, 1); + tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_id[rep]), ==, 1); } /* The rest should be zeroed because this is a service request. */ - tt_int_op(tor_digest_is_zero(service_dup->descriptor_cookie), ==, 1); - tt_int_op(tor_digest_is_zero(service_dup->desc_id_fetch), ==, 1); + tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_cookie), ==, 1); + tt_int_op(tor_digest_is_zero(service_dup_v2->desc_id_fetch), ==, 1); done: rend_data_free(service); @@ -545,6 +575,7 @@ test_single_onion_poisoning(void *arg) char *dir2 = tor_strdup(get_fname_rnd("test_hs_dir2")); smartlist_t *services = smartlist_new(); char *poison_path = NULL; + char *err_msg = NULL; /* No services, no service to verify, no problem! */ mock_options->HiddenServiceSingleHopMode = 0; @@ -580,7 +611,6 @@ test_single_onion_poisoning(void *arg) /* Add port to service 1 */ service_1->ports = smartlist_new(); service_2->ports = smartlist_new(); - char *err_msg = NULL; rend_service_port_config_t *port1 = rend_service_parse_port_config("80", " ", &err_msg); tt_assert(port1); @@ -779,6 +809,137 @@ test_single_onion_poisoning(void *arg) rend_service_free(service_2); UNMOCK(get_options); tor_free(mock_options->DataDirectory); + tor_free(err_msg); +} + +static rend_service_t * +helper_create_rend_service(const char *path) +{ + rend_service_t *s = tor_malloc_zero(sizeof(rend_service_t)); + s->ports = smartlist_new(); + s->intro_nodes = smartlist_new(); + s->expiring_nodes = smartlist_new(); + if (path) { + s->directory = tor_strdup(path); + } + return s; +} + +static void +test_prune_services_on_reload(void *arg) +{ + smartlist_t *new = smartlist_new(), *old = smartlist_new(); + /* Non ephemeral service. */ + rend_service_t *s1 = helper_create_rend_service("SomePath"); + /* Create a non ephemeral service with the _same_ path as so we can test the + * transfer of introduction point between the same services on reload. */ + rend_service_t *s2 = helper_create_rend_service(s1->directory); + /* Ephemeral service (directory is NULL). */ + rend_service_t *e1 = helper_create_rend_service(NULL); + rend_service_t *e2 = helper_create_rend_service(NULL); + + (void) arg; + + { + /* Add both services to the old list. */ + smartlist_add(old, s1); + smartlist_add(old, e1); + /* Only put the non ephemeral in the new list. */ + smartlist_add(new, s1); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); + /* We expect that the ephemeral one is in the new list but removed from + * the old one. */ + tt_int_op(smartlist_len(old), OP_EQ, 1); + tt_assert(smartlist_get(old, 0) == s1); + tt_int_op(smartlist_len(new), OP_EQ, 2); + tt_assert(smartlist_get(new, 0) == s1); + tt_assert(smartlist_get(new, 1) == e1); + /* Cleanup for next test. */ + smartlist_clear(new); + smartlist_clear(old); + } + + { + /* This test will make sure that only the ephemeral service is kept if the + * new list is empty. The old list should contain only the non ephemeral + * one. */ + smartlist_add(old, s1); + smartlist_add(old, e1); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); + tt_int_op(smartlist_len(old), OP_EQ, 1); + tt_assert(smartlist_get(old, 0) == s1); + tt_int_op(smartlist_len(new), OP_EQ, 1); + tt_assert(smartlist_get(new, 0) == e1); + /* Cleanup for next test. */ + smartlist_clear(new); + smartlist_clear(old); + } + + { + /* This test makes sure that the new list stays the same even from the old + * list being completely different. */ + smartlist_add(new, s1); + smartlist_add(new, e1); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); + tt_int_op(smartlist_len(old), OP_EQ, 0); + tt_int_op(smartlist_len(new), OP_EQ, 2); + tt_assert(smartlist_get(new, 0) == s1); + tt_assert(smartlist_get(new, 1) == e1); + /* Cleanup for next test. */ + smartlist_clear(new); + } + + { + rend_intro_point_t ip1; + /* This IP should be found in the s2 service after pruning. */ + smartlist_add(s1->intro_nodes, &ip1); + /* Setup our list. */ + smartlist_add(old, s1); + smartlist_add(new, s2); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); + tt_int_op(smartlist_len(old), OP_EQ, 1); + /* Intro nodes have been moved to the s2 in theory so it must be empty. */ + tt_int_op(smartlist_len(s1->intro_nodes), OP_EQ, 0); + tt_int_op(smartlist_len(new), OP_EQ, 1); + rend_service_t *elem = smartlist_get(new, 0); + tt_assert(elem); + tt_assert(elem == s2); + tt_int_op(smartlist_len(elem->intro_nodes), OP_EQ, 1); + tt_assert(smartlist_get(elem->intro_nodes, 0) == &ip1); + smartlist_clear(s1->intro_nodes); + smartlist_clear(s2->intro_nodes); + /* Cleanup for next test. */ + smartlist_clear(new); + smartlist_clear(old); + } + + { + /* Test two ephemeral services. */ + smartlist_add(old, e1); + smartlist_add(old, e2); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); + /* Check if they've all been transfered. */ + tt_int_op(smartlist_len(old), OP_EQ, 0); + tt_int_op(smartlist_len(new), OP_EQ, 2); + } + + done: + rend_service_free(s1); + rend_service_free(s2); + rend_service_free(e1); + rend_service_free(e2); + smartlist_free(new); + smartlist_free(old); } struct testcase_t hs_tests[] = { @@ -801,6 +962,9 @@ struct testcase_t hs_tests[] = { TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR2) }, { "single_onion_poisoning_create_dir_both", test_single_onion_poisoning, TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1 | CREATE_HS_DIR2) }, + { "prune_services_on_reload", test_prune_services_on_reload, TT_FORK, + NULL, NULL }, + END_OF_TESTCASES }; diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c new file mode 100644 index 0000000000..40f50b322a --- /dev/null +++ b/src/test/test_hs_cache.c @@ -0,0 +1,443 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_cache.c + * \brief Test hidden service caches. + */ + +#define CONNECTION_PRIVATE +#define HS_CACHE_PRIVATE + +#include "ed25519_cert.h" +#include "hs_cache.h" +#include "rendcache.h" +#include "directory.h" +#include "connection.h" + +#include "hs_test_helpers.h" +#include "test_helpers.h" +#include "test.h" + +/* Static variable used to encoded the HSDir query. */ +static char query_b64[256]; + +/* Build an HSDir query using a ed25519 public key. */ +static const char * +helper_get_hsdir_query(const hs_descriptor_t *desc) +{ + ed25519_public_to_base64(query_b64, &desc->plaintext_data.blinded_pubkey); + return query_b64; +} + +static void +init_test(void) +{ + /* Always needed. Initialize the subsystem. */ + hs_cache_init(); + /* We need the v2 cache since our OOM and cache cleanup does poke at it. */ + rend_cache_init(); +} + +static void +test_directory(void *arg) +{ + int ret; + size_t oom_size; + char *desc1_str = NULL; + const char *desc_out; + ed25519_keypair_t signing_kp1; + hs_descriptor_t *desc1 = NULL; + + (void) arg; + + init_test(); + /* Generate a valid descriptor with normal values. */ + ret = ed25519_keypair_generate(&signing_kp1, 0); + tt_int_op(ret, ==, 0); + desc1 = hs_helper_build_hs_desc_with_ip(&signing_kp1); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str); + tt_int_op(ret, OP_EQ, 0); + + /* Very first basic test, should be able to be stored, survive a + * clean, found with a lookup and then cleaned by our OOM. */ + { + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + /* Re-add, it should fail since we already have it. */ + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, -1); + /* Try to clean now which should be fine, there is at worst few seconds + * between the store and this call. */ + hs_cache_clean_as_dir(time(NULL)); + /* We should find it in our cache. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(desc_out, OP_EQ, desc1_str); + /* Tell our OOM to run and to at least remove a byte which will result in + * removing the descriptor from our cache. */ + oom_size = hs_cache_handle_oom(time(NULL), 1); + tt_int_op(oom_size, >=, 1); + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL); + tt_int_op(ret, OP_EQ, 0); + } + + /* Store two descriptors and remove the expiring one only. */ + { + ed25519_keypair_t signing_kp_zero; + ret = ed25519_keypair_generate(&signing_kp_zero, 0); + tt_int_op(ret, ==, 0); + hs_descriptor_t *desc_zero_lifetime; + desc_zero_lifetime = hs_helper_build_hs_desc_with_ip(&signing_kp_zero); + tt_assert(desc_zero_lifetime); + desc_zero_lifetime->plaintext_data.revision_counter = 1; + desc_zero_lifetime->plaintext_data.lifetime_sec = 0; + char *desc_zero_lifetime_str; + ret = hs_desc_encode_descriptor(desc_zero_lifetime, &signing_kp_zero, + &desc_zero_lifetime_str); + tt_int_op(ret, OP_EQ, 0); + + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + ret = hs_cache_store_as_dir(desc_zero_lifetime_str); + tt_int_op(ret, OP_EQ, 0); + /* This one should clear out our zero lifetime desc. */ + hs_cache_clean_as_dir(time(NULL)); + /* We should find desc1 in our cache. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(desc_out, OP_EQ, desc1_str); + /* We should NOT find our zero lifetime desc in our cache. */ + ret = hs_cache_lookup_as_dir(3, + helper_get_hsdir_query(desc_zero_lifetime), + NULL); + tt_int_op(ret, OP_EQ, 0); + /* Cleanup our entire cache. */ + oom_size = hs_cache_handle_oom(time(NULL), 1); + tt_int_op(oom_size, >=, 1); + hs_descriptor_free(desc_zero_lifetime); + tor_free(desc_zero_lifetime_str); + } + + /* Throw junk at it. */ + { + ret = hs_cache_store_as_dir("blah"); + tt_int_op(ret, OP_EQ, -1); + /* Poor attempt at tricking the decoding. */ + ret = hs_cache_store_as_dir("hs-descriptor 3\nJUNK"); + tt_int_op(ret, OP_EQ, -1); + /* Undecodable base64 query. */ + ret = hs_cache_lookup_as_dir(3, "blah", NULL); + tt_int_op(ret, OP_EQ, -1); + /* Decodable base64 query but wrong ed25519 size. */ + ret = hs_cache_lookup_as_dir(3, "dW5pY29ybg==", NULL); + tt_int_op(ret, OP_EQ, -1); + } + + /* Test descriptor replacement with revision counter. */ + { + char *new_desc_str; + + /* Add a descriptor. */ + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out); + tt_int_op(ret, OP_EQ, 1); + /* Bump revision counter. */ + desc1->plaintext_data.revision_counter++; + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &new_desc_str); + tt_int_op(ret, OP_EQ, 0); + ret = hs_cache_store_as_dir(new_desc_str); + tt_int_op(ret, OP_EQ, 0); + /* Look it up, it should have been replaced. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(desc_out, OP_EQ, new_desc_str); + tor_free(new_desc_str); + } + + done: + hs_descriptor_free(desc1); + tor_free(desc1_str); +} + +static void +test_clean_as_dir(void *arg) +{ + size_t ret; + char *desc1_str = NULL; + time_t now = time(NULL); + hs_descriptor_t *desc1 = NULL; + ed25519_keypair_t signing_kp1; + + (void) arg; + + init_test(); + + /* Generate a valid descriptor with values. */ + ret = ed25519_keypair_generate(&signing_kp1, 0); + tt_int_op(ret, ==, 0); + desc1 = hs_helper_build_hs_desc_with_ip(&signing_kp1); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str); + tt_int_op(ret, OP_EQ, 0); + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + + /* With the lifetime being 3 hours, a cleanup shouldn't remove it. */ + ret = cache_clean_v3_as_dir(now, 0); + tt_int_op(ret, ==, 0); + /* Should be present after clean up. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL); + tt_int_op(ret, OP_EQ, 1); + /* Set a cutoff 100 seconds in the past. It should not remove the entry + * since the entry is still recent enough. */ + ret = cache_clean_v3_as_dir(now, now - 100); + tt_int_op(ret, ==, 0); + /* Should be present after clean up. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL); + tt_int_op(ret, OP_EQ, 1); + /* Set a cutoff of 100 seconds in the future. It should remove the entry + * that we've just added since it's not too old for the cutoff. */ + ret = cache_clean_v3_as_dir(now, now + 100); + tt_int_op(ret, >, 0); + /* Shouldn't be present after clean up. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL); + tt_int_op(ret, OP_EQ, 0); + + done: + hs_descriptor_free(desc1); + tor_free(desc1_str); +} + +/* Test helper: Fetch an HS descriptor from an HSDir (for the hidden service + with <b>blinded_key</b>. Return the received descriptor string. */ +static char * +helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) +{ + int retval; + + char *received_desc = NULL; + char *hsdir_query_str = NULL; + + /* The dir conn we are going to simulate */ + dir_connection_t *conn = NULL; + + /* First extract the blinded public key that we are going to use in our + query, and then build the actual query string. */ + { + char hsdir_cache_key[ED25519_BASE64_LEN+1]; + + retval = ed25519_public_to_base64(hsdir_cache_key, + blinded_key); + tt_int_op(retval, ==, 0); + tor_asprintf(&hsdir_query_str, GET("/tor/hs/3/%s"), hsdir_cache_key); + } + + /* Simulate an HTTP GET request to the HSDir */ + conn = dir_connection_new(AF_INET); + tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001); + TO_CONN(conn)->linked = 1;/* Pretend the conn is encrypted :) */ + retval = directory_handle_command_get(conn, hsdir_query_str, + NULL, 0); + tt_int_op(retval, OP_EQ, 0); + + /* Read the descriptor that the HSDir just served us */ + { + char *headers = NULL; + size_t body_used = 0; + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &headers, MAX_HEADERS_SIZE, + &received_desc, &body_used, HS_DESC_MAX_LEN, 0); + tor_free(headers); + } + + done: + tor_free(hsdir_query_str); + if (conn) + connection_free_(TO_CONN(conn)); + + return received_desc; +} + +/* Publish a descriptor to the HSDir, then fetch it. Check that the received + descriptor matches the published one. */ +static void +test_upload_and_download_hs_desc(void *arg) +{ + int retval; + hs_descriptor_t *published_desc = NULL; + + char *published_desc_str = NULL; + char *received_desc_str = NULL; + + (void) arg; + + /* Initialize HSDir cache subsystem */ + init_test(); + + /* Test a descriptor not found in the directory cache. */ + { + ed25519_public_key_t blinded_key; + memset(&blinded_key.pubkey, 'A', sizeof(blinded_key.pubkey)); + received_desc_str = helper_fetch_desc_from_hsdir(&blinded_key); + tt_int_op(strlen(received_desc_str), OP_EQ, 0); + tor_free(received_desc_str); + } + + /* Generate a valid descriptor with normal values. */ + { + ed25519_keypair_t signing_kp; + retval = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(retval, ==, 0); + published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp); + tt_assert(published_desc); + retval = hs_desc_encode_descriptor(published_desc, &signing_kp, + &published_desc_str); + tt_int_op(retval, OP_EQ, 0); + } + + /* Publish descriptor to the HSDir */ + { + retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str); + tt_int_op(retval, ==, 200); + } + + /* Simulate a fetch of the previously published descriptor */ + { + const ed25519_public_key_t *blinded_key; + blinded_key = &published_desc->plaintext_data.blinded_pubkey; + received_desc_str = helper_fetch_desc_from_hsdir(blinded_key); + } + + /* Verify we received the exact same descriptor we published earlier */ + tt_str_op(received_desc_str, OP_EQ, published_desc_str); + tor_free(received_desc_str); + + /* With a valid descriptor in the directory cache, try again an invalid. */ + { + ed25519_public_key_t blinded_key; + memset(&blinded_key.pubkey, 'A', sizeof(blinded_key.pubkey)); + received_desc_str = helper_fetch_desc_from_hsdir(&blinded_key); + tt_int_op(strlen(received_desc_str), OP_EQ, 0); + } + + done: + tor_free(received_desc_str); + tor_free(published_desc_str); + hs_descriptor_free(published_desc); +} + +/* Test that HSDirs reject outdated descriptors based on their revision + * counter. Also test that HSDirs correctly replace old descriptors with newer + * descriptors. */ +static void +test_hsdir_revision_counter_check(void *arg) +{ + int retval; + + ed25519_keypair_t signing_kp; + + hs_descriptor_t *published_desc = NULL; + char *published_desc_str = NULL; + + char *received_desc_str = NULL; + hs_descriptor_t *received_desc = NULL; + + (void) arg; + + /* Initialize HSDir cache subsystem */ + init_test(); + + /* Generate a valid descriptor with normal values. */ + { + retval = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(retval, ==, 0); + published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp); + tt_assert(published_desc); + retval = hs_desc_encode_descriptor(published_desc, &signing_kp, + &published_desc_str); + tt_int_op(retval, OP_EQ, 0); + } + + /* Publish descriptor to the HSDir */ + { + retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str); + tt_int_op(retval, ==, 200); + } + + /* Try publishing again with the same revision counter: Should fail. */ + { + retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str); + tt_int_op(retval, ==, 400); + } + + /* Fetch the published descriptor and validate the revision counter. */ + { + const ed25519_public_key_t *blinded_key; + + blinded_key = &published_desc->plaintext_data.blinded_pubkey; + received_desc_str = helper_fetch_desc_from_hsdir(blinded_key); + + retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc); + tt_int_op(retval, ==, 0); + tt_assert(received_desc); + + /* Check that the revision counter is correct */ + tt_u64_op(received_desc->plaintext_data.revision_counter, ==, 42); + + hs_descriptor_free(received_desc); + received_desc = NULL; + tor_free(received_desc_str); + } + + /* Increment the revision counter and try again. Should work. */ + { + published_desc->plaintext_data.revision_counter = 1313; + tor_free(published_desc_str); + retval = hs_desc_encode_descriptor(published_desc, &signing_kp, + &published_desc_str); + tt_int_op(retval, OP_EQ, 0); + + retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str); + tt_int_op(retval, ==, 200); + } + + /* Again, fetch the published descriptor and perform the revision counter + validation. The revision counter must have changed. */ + { + const ed25519_public_key_t *blinded_key; + + blinded_key = &published_desc->plaintext_data.blinded_pubkey; + received_desc_str = helper_fetch_desc_from_hsdir(blinded_key); + + retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc); + tt_int_op(retval, ==, 0); + tt_assert(received_desc); + + /* Check that the revision counter is the latest */ + tt_u64_op(received_desc->plaintext_data.revision_counter, ==, 1313); + } + + done: + hs_descriptor_free(published_desc); + hs_descriptor_free(received_desc); + tor_free(received_desc_str); + tor_free(published_desc_str); +} + +struct testcase_t hs_cache[] = { + /* Encoding tests. */ + { "directory", test_directory, TT_FORK, + NULL, NULL }, + { "clean_as_dir", test_clean_as_dir, TT_FORK, + NULL, NULL }, + { "hsdir_revision_counter_check", test_hsdir_revision_counter_check, TT_FORK, + NULL, NULL }, + { "upload_and_download_hs_desc", test_upload_and_download_hs_desc, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c new file mode 100644 index 0000000000..a40616c61e --- /dev/null +++ b/src/test/test_hs_descriptor.c @@ -0,0 +1,889 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_descriptor.c + * \brief Test hidden service descriptor encoding and decoding. + */ + +#define HS_DESCRIPTOR_PRIVATE + +#include "crypto_ed25519.h" +#include "ed25519_cert.h" +#include "or.h" +#include "hs_descriptor.h" +#include "test.h" +#include "torcert.h" + +#include "hs_test_helpers.h" +#include "test_helpers.h" +#include "log_test_helpers.h" + +#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS +DISABLE_GCC_WARNING(overlength-strings) +/* We allow huge string constants in the unit tests, but not in the code + * at large. */ +#endif +#include "test_hs_descriptor.inc" +ENABLE_GCC_WARNING(overlength-strings) + +/* Test certificate encoding put in a descriptor. */ +static void +test_cert_encoding(void *arg) +{ + int ret; + char *encoded = NULL; + time_t now = time(NULL); + ed25519_keypair_t kp; + ed25519_public_key_t signed_key; + ed25519_secret_key_t secret_key; + tor_cert_t *cert = NULL; + + (void) arg; + + ret = ed25519_keypair_generate(&kp, 0); + tt_int_op(ret, == , 0); + ret = ed25519_secret_key_generate(&secret_key, 0); + tt_int_op(ret, == , 0); + ret = ed25519_public_key_generate(&signed_key, &secret_key); + tt_int_op(ret, == , 0); + + cert = tor_cert_create(&kp, CERT_TYPE_SIGNING_AUTH, &signed_key, + now, 3600 * 2, CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(cert); + + /* Test the certificate encoding function. */ + ret = tor_cert_encode_ed22519(cert, &encoded); + tt_int_op(ret, ==, 0); + + /* Validated the certificate string. */ + { + char *end, *pos = encoded; + char *b64_cert, buf[256]; + size_t b64_cert_len; + tor_cert_t *parsed_cert; + + tt_int_op(strcmpstart(pos, "-----BEGIN ED25519 CERT-----\n"), ==, 0); + pos += strlen("-----BEGIN ED25519 CERT-----\n"); + + /* Isolate the base64 encoded certificate and try to decode it. */ + end = strstr(pos, "-----END ED25519 CERT-----"); + tt_assert(end); + b64_cert = pos; + b64_cert_len = end - pos; + ret = base64_decode(buf, sizeof(buf), b64_cert, b64_cert_len); + tt_int_op(ret, >, 0); + /* Parseable? */ + parsed_cert = tor_cert_parse((uint8_t *) buf, ret); + tt_assert(parsed_cert); + /* Signature is valid? */ + ret = tor_cert_checksig(parsed_cert, &kp.pubkey, now + 10); + tt_int_op(ret, ==, 0); + ret = tor_cert_eq(cert, parsed_cert); + tt_int_op(ret, ==, 1); + /* The cert did have the signing key? */ + ret= ed25519_pubkey_eq(&parsed_cert->signing_key, &kp.pubkey); + tt_int_op(ret, ==, 1); + tor_cert_free(parsed_cert); + + /* Get to the end part of the certificate. */ + pos += b64_cert_len; + tt_int_op(strcmpstart(pos, "-----END ED25519 CERT-----"), ==, 0); + pos += strlen("-----END ED25519 CERT-----"); + } + + done: + tor_cert_free(cert); + tor_free(encoded); +} + +/* Test the descriptor padding. */ +static void +test_descriptor_padding(void *arg) +{ + char *plaintext; + size_t plaintext_len, padded_len; + uint8_t *padded_plaintext = NULL; + +/* Example: if l = 129, the ceiled division gives 2 and then multiplied by 128 + * to give 256. With l = 127, ceiled division gives 1 then times 128. */ +#define PADDING_EXPECTED_LEN(l) \ + CEIL_DIV(l, HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE) * \ + HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE + + (void) arg; + + { /* test #1: no padding */ + plaintext_len = HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE; + plaintext = tor_malloc(plaintext_len); + padded_len = build_plaintext_padding(plaintext, plaintext_len, + &padded_plaintext); + tt_assert(padded_plaintext); + tor_free(plaintext); + /* Make sure our padding has been zeroed. */ + tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len, + padded_len - plaintext_len), OP_EQ, 1); + tor_free(padded_plaintext); + /* Never never have a padded length smaller than the plaintext. */ + tt_int_op(padded_len, OP_GE, plaintext_len); + tt_int_op(padded_len, OP_EQ, PADDING_EXPECTED_LEN(plaintext_len)); + } + + { /* test #2: one byte padding? */ + plaintext_len = HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE - 1; + plaintext = tor_malloc(plaintext_len); + padded_plaintext = NULL; + padded_len = build_plaintext_padding(plaintext, plaintext_len, + &padded_plaintext); + tt_assert(padded_plaintext); + tor_free(plaintext); + /* Make sure our padding has been zeroed. */ + tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len, + padded_len - plaintext_len), OP_EQ, 1); + tor_free(padded_plaintext); + /* Never never have a padded length smaller than the plaintext. */ + tt_int_op(padded_len, OP_GE, plaintext_len); + tt_int_op(padded_len, OP_EQ, PADDING_EXPECTED_LEN(plaintext_len)); + } + + { /* test #3: Lots more bytes of padding? */ + plaintext_len = HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE + 1; + plaintext = tor_malloc(plaintext_len); + padded_plaintext = NULL; + padded_len = build_plaintext_padding(plaintext, plaintext_len, + &padded_plaintext); + tt_assert(padded_plaintext); + tor_free(plaintext); + /* Make sure our padding has been zeroed. */ + tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len, + padded_len - plaintext_len), OP_EQ, 1); + tor_free(padded_plaintext); + /* Never never have a padded length smaller than the plaintext. */ + tt_int_op(padded_len, OP_GE, plaintext_len); + tt_int_op(padded_len, OP_EQ, PADDING_EXPECTED_LEN(plaintext_len)); + } + + done: + return; +} + +static void +test_link_specifier(void *arg) +{ + ssize_t ret; + hs_desc_link_specifier_t spec; + smartlist_t *link_specifiers = smartlist_new(); + char buf[256]; + char *b64 = NULL; + link_specifier_t *ls = NULL; + + (void) arg; + + /* Always this port. */ + spec.u.ap.port = 42; + smartlist_add(link_specifiers, &spec); + + /* Test IPv4 for starter. */ + { + uint32_t ipv4; + + spec.type = LS_IPV4; + ret = tor_addr_parse(&spec.u.ap.addr, "1.2.3.4"); + tt_int_op(ret, ==, AF_INET); + b64 = encode_link_specifiers(link_specifiers); + tt_assert(b64); + + /* Decode it and validate the format. */ + ret = base64_decode(buf, sizeof(buf), b64, strlen(b64)); + tt_int_op(ret, >, 0); + /* First byte is the number of link specifier. */ + tt_int_op(get_uint8(buf), ==, 1); + ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1); + tt_int_op(ret, ==, 8); + /* Should be 2 bytes for port and 4 bytes for IPv4. */ + tt_int_op(link_specifier_get_ls_len(ls), ==, 6); + ipv4 = link_specifier_get_un_ipv4_addr(ls); + tt_int_op(tor_addr_to_ipv4h(&spec.u.ap.addr), ==, ipv4); + tt_int_op(link_specifier_get_un_ipv4_port(ls), ==, spec.u.ap.port); + + link_specifier_free(ls); + ls = NULL; + tor_free(b64); + } + + /* Test IPv6. */ + { + uint8_t ipv6[16]; + + spec.type = LS_IPV6; + ret = tor_addr_parse(&spec.u.ap.addr, "[1:2:3:4::]"); + tt_int_op(ret, ==, AF_INET6); + b64 = encode_link_specifiers(link_specifiers); + tt_assert(b64); + + /* Decode it and validate the format. */ + ret = base64_decode(buf, sizeof(buf), b64, strlen(b64)); + tt_int_op(ret, >, 0); + /* First byte is the number of link specifier. */ + tt_int_op(get_uint8(buf), ==, 1); + ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1); + tt_int_op(ret, ==, 20); + /* Should be 2 bytes for port and 16 bytes for IPv6. */ + tt_int_op(link_specifier_get_ls_len(ls), ==, 18); + for (unsigned int i = 0; i < sizeof(ipv6); i++) { + ipv6[i] = link_specifier_get_un_ipv6_addr(ls, i); + } + tt_mem_op(tor_addr_to_in6_addr8(&spec.u.ap.addr), ==, ipv6, sizeof(ipv6)); + tt_int_op(link_specifier_get_un_ipv6_port(ls), ==, spec.u.ap.port); + + link_specifier_free(ls); + ls = NULL; + tor_free(b64); + } + + /* Test legacy. */ + { + uint8_t *id; + + spec.type = LS_LEGACY_ID; + memset(spec.u.legacy_id, 'Y', sizeof(spec.u.legacy_id)); + b64 = encode_link_specifiers(link_specifiers); + tt_assert(b64); + + /* Decode it and validate the format. */ + ret = base64_decode(buf, sizeof(buf), b64, strlen(b64)); + tt_int_op(ret, >, 0); + /* First byte is the number of link specifier. */ + tt_int_op(get_uint8(buf), ==, 1); + ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1); + /* 20 bytes digest + 1 byte type + 1 byte len. */ + tt_int_op(ret, ==, 22); + tt_int_op(link_specifier_getlen_un_legacy_id(ls), OP_EQ, DIGEST_LEN); + /* Digest length is 20 bytes. */ + tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, DIGEST_LEN); + id = link_specifier_getarray_un_legacy_id(ls); + tt_mem_op(spec.u.legacy_id, OP_EQ, id, DIGEST_LEN); + + link_specifier_free(ls); + ls = NULL; + tor_free(b64); + } + + done: + link_specifier_free(ls); + tor_free(b64); + smartlist_free(link_specifiers); +} + +static void +test_encode_descriptor(void *arg) +{ + int ret; + char *encoded = NULL; + ed25519_keypair_t signing_kp; + hs_descriptor_t *desc = NULL; + + (void) arg; + + ret = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(ret, ==, 0); + desc = hs_helper_build_hs_desc_with_ip(&signing_kp); + ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded); + tt_int_op(ret, ==, 0); + tt_assert(encoded); + + done: + hs_descriptor_free(desc); + tor_free(encoded); +} + +static void +test_decode_descriptor(void *arg) +{ + int ret; + char *encoded = NULL; + ed25519_keypair_t signing_kp; + hs_descriptor_t *desc = NULL; + hs_descriptor_t *decoded = NULL; + hs_descriptor_t *desc_no_ip = NULL; + + (void) arg; + + ret = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(ret, ==, 0); + desc = hs_helper_build_hs_desc_with_ip(&signing_kp); + + /* Give some bad stuff to the decoding function. */ + ret = hs_desc_decode_descriptor("hladfjlkjadf", NULL, &decoded); + tt_int_op(ret, OP_EQ, -1); + + ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded); + tt_int_op(ret, ==, 0); + tt_assert(encoded); + + ret = hs_desc_decode_descriptor(encoded, NULL, &decoded); + tt_int_op(ret, ==, 0); + tt_assert(decoded); + + hs_helper_desc_equal(desc, decoded); + + /* Decode a descriptor with _no_ introduction points. */ + { + ed25519_keypair_t signing_kp_no_ip; + ret = ed25519_keypair_generate(&signing_kp_no_ip, 0); + tt_int_op(ret, ==, 0); + desc_no_ip = hs_helper_build_hs_desc_no_ip(&signing_kp_no_ip); + tt_assert(desc_no_ip); + tor_free(encoded); + ret = hs_desc_encode_descriptor(desc_no_ip, &signing_kp_no_ip, &encoded); + tt_int_op(ret, ==, 0); + tt_assert(encoded); + hs_descriptor_free(decoded); + ret = hs_desc_decode_descriptor(encoded, NULL, &decoded); + tt_int_op(ret, ==, 0); + tt_assert(decoded); + } + + done: + hs_descriptor_free(desc); + hs_descriptor_free(desc_no_ip); + hs_descriptor_free(decoded); + tor_free(encoded); +} + +static void +test_supported_version(void *arg) +{ + int ret; + + (void) arg; + + /* Unsupported. */ + ret = hs_desc_is_supported_version(42); + tt_int_op(ret, OP_EQ, 0); + /* To early. */ + ret = hs_desc_is_supported_version(HS_DESC_SUPPORTED_FORMAT_VERSION_MIN - 1); + tt_int_op(ret, OP_EQ, 0); + /* One too new. */ + ret = hs_desc_is_supported_version(HS_DESC_SUPPORTED_FORMAT_VERSION_MAX + 1); + tt_int_op(ret, OP_EQ, 0); + /* Valid version. */ + ret = hs_desc_is_supported_version(3); + tt_int_op(ret, OP_EQ, 1); + + done: + ; +} + +static void +test_encrypted_data_len(void *arg) +{ + int ret; + size_t value; + + (void) arg; + + /* No length, error. */ + ret = encrypted_data_length_is_valid(0); + tt_int_op(ret, OP_EQ, 0); + /* Valid value. */ + value = HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN + 1; + ret = encrypted_data_length_is_valid(value); + tt_int_op(ret, OP_EQ, 1); + + done: + ; +} + +static void +test_decode_invalid_intro_point(void *arg) +{ + int ret; + char *encoded_ip = NULL; + size_t len_out; + hs_desc_intro_point_t *ip = NULL; + ed25519_keypair_t signing_kp; + hs_descriptor_t *desc = NULL; + + (void) arg; + + /* Seperate pieces of a valid encoded introduction point. */ + const char *intro_point = + "introduction-point AQIUMDI5OUYyNjhGQ0E5RDU1Q0QxNTc="; + const char *auth_key = + "auth-key\n" + "-----BEGIN ED25519 CERT-----\n" + "AQkACOhAAQW8ltYZMIWpyrfyE/b4Iyi8CNybCwYs6ADk7XfBaxsFAQAgBAD3/BE4\n" + "XojGE/N2bW/wgnS9r2qlrkydGyuCKIGayYx3haZ39LD4ZTmSMRxwmplMAqzG/XNP\n" + "0Kkpg4p2/VnLFJRdU1SMFo1lgQ4P0bqw7Tgx200fulZ4KUM5z5V7m+a/mgY=\n" + "-----END ED25519 CERT-----"; + const char *enc_key = + "enc-key ntor bpZKLsuhxP6woDQ3yVyjm5gUKSk7RjfAijT2qrzbQk0="; + const char *enc_key_cert = + "enc-key-cert\n" + "-----BEGIN ED25519 CERT-----\n" + "AQsACOhZAUpNvCZ1aJaaR49lS6MCdsVkhVGVrRqoj0Y2T4SzroAtAQAgBABFOcGg\n" + "lbTt1DF5nKTE/gU3Fr8ZtlCIOhu1A+F5LM7fqCUupfesg0KTHwyIZOYQbJuM5/he\n" + "/jDNyLy9woPJdjkxywaY2RPUxGjLYtMQV0E8PUxWyICV+7y52fTCYaKpYQw=\n" + "-----END ED25519 CERT-----"; + + /* Try to decode a junk string. */ + { + hs_descriptor_free(desc); + desc = NULL; + ret = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(ret, ==, 0); + desc = hs_helper_build_hs_desc_with_ip(&signing_kp); + const char *junk = "this is not a descriptor"; + ip = decode_introduction_point(desc, junk); + tt_assert(!ip); + desc_intro_point_free(ip); + ip = NULL; + } + + /* Invalid link specifiers. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = "introduction-point blah"; + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) enc_key); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + desc_intro_point_free(ip); + ip = NULL; + } + + /* Invalid auth key type. */ + { + smartlist_t *lines = smartlist_new(); + /* Try to put a valid object that our tokenize function will be able to + * parse but that has nothing to do with the auth_key. */ + const char *bad_line = + "auth-key\n" + "-----BEGIN UNICORN CERT-----\n" + "MIGJAoGBAO4bATcW8kW4h6RQQAKEgg+aXCpF4JwbcO6vGZtzXTDB+HdPVQzwqkbh\n" + "XzFM6VGArhYw4m31wcP1Z7IwULir7UMnAFd7Zi62aYfU6l+Y1yAoZ1wzu1XBaAMK\n" + "ejpwQinW9nzJn7c2f69fVke3pkhxpNdUZ+vplSA/l9iY+y+v+415AgMBAAE=\n" + "-----END UNICORN CERT-----"; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + /* Invalid enc-key. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = + "enc-key unicorn bpZKLsuhxP6woDQ3yVyjm5gUKSk7RjfAijT2qrzbQk0="; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + /* Invalid enc-key object. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = "enc-key ntor"; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + /* Invalid enc-key base64 curv25519 key. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = "enc-key ntor blah==="; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + /* Invalid enc-key invalid legacy. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = "legacy-key blah==="; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + done: + hs_descriptor_free(desc); + desc_intro_point_free(ip); +} + +/** Make sure we fail gracefully when decoding the bad desc from #23233. */ +static void +test_decode_bad_signature(void *arg) +{ + hs_desc_plaintext_data_t desc_plaintext; + int ret; + + (void) arg; + + /* Update approx time to dodge cert expiration */ + update_approx_time(1502661599); + + setup_full_capture_of_logs(LOG_WARN); + ret = hs_desc_decode_plaintext(HS_DESC_BAD_SIG, &desc_plaintext); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("Malformed signature line. Rejecting."); + teardown_capture_of_logs(); + + done: + desc_plaintext_data_free_contents(&desc_plaintext); +} + +static void +test_decode_plaintext(void *arg) +{ + int ret; + hs_desc_plaintext_data_t desc_plaintext; + const char *bad_value = "unicorn"; + + (void) arg; + +#define template \ + "hs-descriptor %s\n" \ + "descriptor-lifetime %s\n" \ + "descriptor-signing-key-cert\n" \ + "-----BEGIN ED25519 CERT-----\n" \ + "AQgABjvPAQaG3g+dc6oV/oJV4ODAtkvx56uBnPtBT9mYVuHVOhn7AQAgBABUg3mQ\n" \ + "myBr4bu5LCr53wUEbW2EXui01CbUgU7pfo9LvJG3AcXRojj6HlfsUs9BkzYzYdjF\n" \ + "A69Apikgu0ewHYkFFASt7Il+gB3w6J8YstQJZT7dtbtl+doM7ug8B68Qdg8=\n" \ + "-----END ED25519 CERT-----\n" \ + "revision-counter %s\n" \ + "encrypted\n" \ + "-----BEGIN %s-----\n" \ + "UNICORN\n" \ + "-----END MESSAGE-----\n" \ + "signature m20WJH5agqvwhq7QeuEZ1mYyPWQDO+eJOZUjLhAiKu8DbL17DsDfJE6kXbWy" \ + "HimbNj2we0enV3cCOOAsmPOaAw\n" + + /* Invalid version. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, bad_value, "180", "42", "MESSAGE"); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Missing fields. */ + { + const char *plaintext = "hs-descriptor 3\n"; + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Max length. */ + { + size_t big = 64000; + /* Must always be bigger than HS_DESC_MAX_LEN. */ + tt_int_op(HS_DESC_MAX_LEN, <, big); + char *plaintext = tor_malloc_zero(big); + memset(plaintext, 'a', big); + plaintext[big - 1] = '\0'; + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Bad lifetime value. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, "3", bad_value, "42", "MESSAGE"); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Huge lifetime value. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, "3", "7181615", "42", "MESSAGE"); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Invalid encrypted section. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, "3", "180", "42", bad_value); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Invalid revision counter. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, "3", "180", bad_value, "MESSAGE"); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + done: + ; +} + +static void +test_validate_cert(void *arg) +{ + int ret; + time_t now = time(NULL); + ed25519_keypair_t kp; + tor_cert_t *cert = NULL; + + (void) arg; + + ret = ed25519_keypair_generate(&kp, 0); + tt_int_op(ret, ==, 0); + + /* Cert of type CERT_TYPE_AUTH_HS_IP_KEY. */ + cert = tor_cert_create(&kp, CERT_TYPE_AUTH_HS_IP_KEY, + &kp.pubkey, now, 3600, + CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(cert); + /* Test with empty certificate. */ + ret = cert_is_valid(NULL, CERT_TYPE_AUTH_HS_IP_KEY, "unicorn"); + tt_int_op(ret, OP_EQ, 0); + /* Test with a bad type. */ + ret = cert_is_valid(cert, CERT_TYPE_SIGNING_HS_DESC, "unicorn"); + tt_int_op(ret, OP_EQ, 0); + /* Normal validation. */ + ret = cert_is_valid(cert, CERT_TYPE_AUTH_HS_IP_KEY, "unicorn"); + tt_int_op(ret, OP_EQ, 1); + /* Break signing key so signature verification will fails. */ + memset(&cert->signing_key, 0, sizeof(cert->signing_key)); + ret = cert_is_valid(cert, CERT_TYPE_AUTH_HS_IP_KEY, "unicorn"); + tt_int_op(ret, OP_EQ, 0); + tor_cert_free(cert); + + /* Try a cert without including the signing key. */ + cert = tor_cert_create(&kp, CERT_TYPE_AUTH_HS_IP_KEY, &kp.pubkey, now, + 3600, 0); + tt_assert(cert); + /* Test with a bad type. */ + ret = cert_is_valid(cert, CERT_TYPE_AUTH_HS_IP_KEY, "unicorn"); + tt_int_op(ret, OP_EQ, 0); + + done: + tor_cert_free(cert); +} + +static void +test_desc_signature(void *arg) +{ + int ret; + char *data = NULL, *desc = NULL; + char sig_b64[ED25519_SIG_BASE64_LEN + 1]; + ed25519_keypair_t kp; + ed25519_signature_t sig; + + (void) arg; + + ed25519_keypair_generate(&kp, 0); + /* Setup a phoony descriptor but with a valid signature token that is the + * signature is verifiable. */ + tor_asprintf(&data, "This is a signed descriptor\n"); + ret = ed25519_sign_prefixed(&sig, (const uint8_t *) data, strlen(data), + "Tor onion service descriptor sig v3", &kp); + tt_int_op(ret, ==, 0); + ret = ed25519_signature_to_base64(sig_b64, &sig); + tt_int_op(ret, ==, 0); + /* Build the descriptor that should be valid. */ + tor_asprintf(&desc, "%ssignature %s\n", data, sig_b64); + ret = desc_sig_is_valid(sig_b64, &kp.pubkey, desc, strlen(desc)); + tt_int_op(ret, ==, 1); + /* Junk signature. */ + ret = desc_sig_is_valid("JUNK", &kp.pubkey, desc, strlen(desc)); + tt_int_op(ret, ==, 0); + + done: + tor_free(desc); + tor_free(data); +} + +/* bad desc auth type */ +static const char bad_superencrypted_text1[] = "desc-auth-type scoobysnack\n" + "desc-auth-ephemeral-key A/O8DVtnUheb3r1JqoB8uJB7wxXL1XJX3eny4yB+eFA=\n" + "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n" + "encrypted\n" + "-----BEGIN MESSAGE-----\n" + "YmVpbmcgb24gbW91bnRhaW5zLCB0aGlua2luZyBhYm91dCBjb21wdXRlcnMsIGlzIG5vdC" + "BiYWQgYXQgYWxs\n" + "-----END MESSAGE-----\n"; + +/* bad ephemeral key */ +static const char bad_superencrypted_text2[] = "desc-auth-type x25519\n" + "desc-auth-ephemeral-key differentalphabet\n" + "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n" + "encrypted\n" + "-----BEGIN MESSAGE-----\n" + "YmVpbmcgb24gbW91bnRhaW5zLCB0aGlua2luZyBhYm91dCBjb21wdXRlcnMsIGlzIG5vdC" + "BiYWQgYXQgYWxs\n" + "-----END MESSAGE-----\n"; + +/* bad encrypted msg */ +static const char bad_superencrypted_text3[] = "desc-auth-type x25519\n" + "desc-auth-ephemeral-key A/O8DVtnUheb3r1JqoB8uJB7wxXL1XJX3eny4yB+eFA=\n" + "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n" + "encrypted\n" + "-----BEGIN MESSAGE-----\n" + "SO SMALL NOT GOOD\n" + "-----END MESSAGE-----\n"; + +static const char correct_superencrypted_text[] = "desc-auth-type x25519\n" + "desc-auth-ephemeral-key A/O8DVtnUheb3r1JqoB8uJB7wxXL1XJX3eny4yB+eFA=\n" + "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n" + "auth-client Od09Qu636Qo /PKLzqewAdS/+0+vZC+MvQ dpw4NFo13zDnuPz45rxrOg\n" + "auth-client JRr840iGYN0 8s8cxYqF7Lx23+NducC4Qg zAafl4wPLURkuEjJreZq1g\n" + "encrypted\n" + "-----BEGIN MESSAGE-----\n" + "YmVpbmcgb24gbW91bnRhaW5zLCB0aGlua2luZyBhYm91dCBjb21wdXRlcnMsIGlzIG5vdC" + "BiYWQgYXQgYWxs\n" + "-----END MESSAGE-----\n"; + +static const char correct_encrypted_plaintext[] = "being on mountains, " + "thinking about computers, is not bad at all"; + +static void +test_parse_hs_desc_superencrypted(void *arg) +{ + (void) arg; + size_t retval; + uint8_t *encrypted_out = NULL; + + { + setup_full_capture_of_logs(LOG_WARN); + retval = decode_superencrypted(bad_superencrypted_text1, + strlen(bad_superencrypted_text1), + &encrypted_out); + tt_u64_op(retval, ==, 0); + tt_assert(!encrypted_out); + expect_log_msg_containing("Unrecognized desc auth type"); + teardown_capture_of_logs(); + } + + { + setup_full_capture_of_logs(LOG_WARN); + retval = decode_superencrypted(bad_superencrypted_text2, + strlen(bad_superencrypted_text2), + &encrypted_out); + tt_u64_op(retval, ==, 0); + tt_assert(!encrypted_out); + expect_log_msg_containing("Bogus desc auth key in HS desc"); + teardown_capture_of_logs(); + } + + { + setup_full_capture_of_logs(LOG_WARN); + retval = decode_superencrypted(bad_superencrypted_text3, + strlen(bad_superencrypted_text3), + &encrypted_out); + tt_u64_op(retval, ==, 0); + tt_assert(!encrypted_out); + expect_log_msg_containing("Length of descriptor\'s encrypted data " + "is too small."); + teardown_capture_of_logs(); + } + + /* Now finally the good one */ + retval = decode_superencrypted(correct_superencrypted_text, + strlen(correct_superencrypted_text), + &encrypted_out); + + tt_u64_op(retval, ==, strlen(correct_encrypted_plaintext)); + tt_mem_op(encrypted_out, OP_EQ, correct_encrypted_plaintext, + strlen(correct_encrypted_plaintext)); + + done: + tor_free(encrypted_out); +} + +struct testcase_t hs_descriptor[] = { + /* Encoding tests. */ + { "cert_encoding", test_cert_encoding, TT_FORK, + NULL, NULL }, + { "link_specifier", test_link_specifier, TT_FORK, + NULL, NULL }, + { "encode_descriptor", test_encode_descriptor, TT_FORK, + NULL, NULL }, + { "descriptor_padding", test_descriptor_padding, TT_FORK, + NULL, NULL }, + + /* Decoding tests. */ + { "decode_descriptor", test_decode_descriptor, TT_FORK, + NULL, NULL }, + { "encrypted_data_len", test_encrypted_data_len, TT_FORK, + NULL, NULL }, + { "decode_invalid_intro_point", test_decode_invalid_intro_point, TT_FORK, + NULL, NULL }, + { "decode_plaintext", test_decode_plaintext, TT_FORK, + NULL, NULL }, + { "decode_bad_signature", test_decode_bad_signature, TT_FORK, + NULL, NULL }, + + /* Misc. */ + { "version", test_supported_version, TT_FORK, + NULL, NULL }, + { "validate_cert", test_validate_cert, TT_FORK, + NULL, NULL }, + { "desc_signature", test_desc_signature, TT_FORK, + NULL, NULL }, + + { "parse_hs_desc_superencrypted", test_parse_hs_desc_superencrypted, + TT_FORK, NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_descriptor.inc b/src/test/test_hs_descriptor.inc new file mode 100644 index 0000000000..70a2c7c2f7 --- /dev/null +++ b/src/test/test_hs_descriptor.inc @@ -0,0 +1,224 @@ +static const char* HS_DESC_BAD_SIG = +"hs-descriptor 3\n" +"descriptor-lifetime 180\n" +"descriptor-signing-key-cert\n" +"-----BEGIN ED25519 CERT-----\n" +"AQgABl5+AQoPXRnCGEOxIup3AcjQXb8npNiUFm2Qv7A6JKk/K+EuAQAgBAD18iUD\n" +"nbkUblnUvTHzipq4bcr6aPyFVB42Ptobg4xr8s3VjHiJtjs9MDEdr6nXS7UlyhEl\n" +"78vsuFEvLp7cvAgGxYY1xGXdn5RdHMCdi8W9yZLKMQX9OuJckmp1C6q+cA4=\n" +"-----END ED25519 CERT-----\n" +"revision-counter 42\n" +"superencrypted\n" +"-----BEGIN MESSAGE-----\n" +"BxzghAOjM4De6Z6eGTvBrTP2SJDdQOYV/u9qtvlFsa2FRQWk20Adv3zJ/AI10CQO\n" +"mUP4DNXM8FWQYGTvmD7wGz2/cXGjKwBXg1qO7zF5eP/D/My1sXsIfCcb41mkheNt\n" +"xn1I5eKXcnghtd4lw7OkPVjSb/Z+VARUMmf+0qSNgmHLgEVnAoGJsn8W8B4qtIay\n" +"4h4PuV0jPPlqJx6jMFOOEW72uqnfmqeNvClENXXW60xhnaxsf0up62fuW8ktu6Wf\n" +"lnX/lvTstBFZZQ8/XI1+G+BPf8TZf7mxu0WYVg1s/KWYasYMSw46as59nkqdq2Ii\n" +"qJnqHX/R20mWBhgpLse6wO0aNpky/rozEnikaPqyO1DShf6a6jXY8ADBg7spnK2/\n" +"h7sf1+F1xfi2dy2WGxc1EUMP1kTVUmbft7kOo2nA7+3YZwQuSJHaN/66HrzU2x5z\n" +"ayRUJ8+qDtfpEf17xthc/Uh253blFK96IoJJiqBfI6xt3IqOdHJq0OOC9zBbF6Rj\n" +"vKMsaxmc/nc6uOB2WePYSgkZ0qs/dRKBJs6+Ahn1KdGkadyd8mDKL86Oe8lncHdB\n" +"m/6sQjhKqFgngkCDOIlEJyWizqfN84AGqD5Zyxq0rbsN+9KLsHFfEbCRjgqjO5nS\n" +"FYSFtuKgCZl2gaYEslL1pIEYE6BD2Whjn/HWTRyWiULJr6SuavgcbxeNEQDuVCC+\n" +"fm0X7Z+qERaMAMR0vTMJK/NzT4GifrDpgmgbxc+34CtNBF5TriM8aXTNZZlsW00k\n" +"d0XRxFbbbtiT5VOaEHbny7R3MdTVutEc9E/BhLBvjSSrGX7vrryh6Oj++nthIIzm\n" +"F4M7I11S0TyA+UE06qF1C8rKmhcqU9MWy1SiccJ9KOWhJ5xwlsXBIID7wVygUhVl\n" +"ovzfKkDDPfRoBch6NdVkxNJx3gb63CUmC2TzfwOMh973nntMVzqqw9A7jYkro9ln\n" +"217kHUwMk3e83UgFL4nn7NCf3Kj0zhJ4jSfAsuQpV6e7dhzrlNya0lqrUsY2zFXP\n" +"xv8wUtg6Vo1KewgVQas4oElkgFjDN8RJ7uBAwfuE/b9NnYJoQd76G8DHei/1PHbu\n" +"tbtwN9I5RHaTvEOfetsJFnIAkCG6O4CQpzwHu1DdvEP4s6/el10b/4awBJ4VwOVZ\n" +"YHSe4X0DStTV4Cu6aLh5OvrOmGbieRj6HdGQ6syYCaEBTuxbBUUpjIAfVlReAIph\n" +"6aOrY6HNcCmeVmL5qm4dKr2XXOREsnUFuMqmfQuQd9pN3zlmS+RqCgSJuFrguFpd\n" +"mjo6UxZvbjE7yJjtCih38HRe5BaigP5RDRkXmiXjqJ4koLJpyjQh19k3BYGcdxUC\n" +"RCcYXydbGF7qHlnoaX9HnX7y6ZRsyKQpt91PMTGOUsB4fS8NhsqPpl2gdp4poLNs\n" +"+hqjWZJ3uuLotXBcgM39Dtq9tqqu9vM12T80UAfWnVEHrBphmukh49EhEr2sx/la\n" +"kAzRoTbLyTdlGVei8hI7/RtZIaIcOkzlhcFI5zmBlydyrv6/79vzt6WI/w9GVGpM\n" +"OuSM0NS2CDJ7Iw412nz3CV1pEXB551ZBmbme6NHUe4EtEsDbgkP1Z201H4j51yVz\n" +"wNoIksE5Bh5XRKuu4We5f9KZb+AEG9kxKJ5DbJk2YGJEQFTyfv0H68pl9urstPXD\n" +"aMQF806COe2uhGm5gV/skvPVTeEvStE3K8DxZgcWNcTMVk8ZjrUHNfguVVToP8hT\n" +"Fl4Iqo3r+JZEAGXnAbTpxUVC2Xxspf3jsT5xhUfB/NOexZxrXWnQZ+pscsbow0ba\n" +"GATtakD3TF2WBqq5WscmOex+lrJcBCWVIzVWdwi5ngAtm1S7efkJlFUvmi4OuYnN\n" +"RyZfxVIpoer8f2/xPXvxkOWFminDy5sFEvlh2/pnymfKOUV+CKih9ZApt+izlRJn\n" +"+sMIOW6Jhf/WYyjeN6KQpwi6CDpclQJXA1SVoOVVL5A3lotLjs0x7ThIcBoxCZBq\n" +"rFBhBu1gJgJ8guMySAHssIvhHHwXJsYEwzWCVAg/zIUXy4PLwIkgHApl+vGcldGv\n" +"Br5HNCuqQ2pD9z2RvzNneB/LrYB214i+BP2piO5HbmeJBhby93blGXVfQewQT6aF\n" +"dBlK8/jQM0rvb+LkmvQm2ypOttRpX2kyQXooJHYTTusaUr4jVmgngCvGtgqAQVqD\n" +"HULXfHWvugZbAh6dXF7gKnnsyDOWwAgy4OJRi8i0jCaZ8aWSFRUjeGKT26dg/ayB\n" +"U4QfMb8vL8tMdXVBfQLGcBgvrzQYrY69//pV6bX3SbLfUfWXV9eqUVWVPqVyPEwa\n" +"Tz/aGVnGv/dY8h2cVnrgSXJGlOO+mCwSl+k9nk7VcEaKYuNlaOP3ZlKJvVj1LefM\n" +"FODh4qTDBo5NkyfKu5fcZcOqDMBeGWXZzltE7CmvY7fOpDNMsuAoXYWI7q9gK82F\n" +"w+nS0tVFCIWYa9DgGMv9GKTOk4Ia9elkbWypdRE/4oz4QxmHsArEsK4gDI+wmcp7\n" +"/NsAZeuy96r2YDIUam4uASKOiAqrEfCv6B6cYctdYwZbAEXdo4fkGrCIjNRZmZGv\n" +"kcZzHzIymnAmKRTkPt/LQ7Rx27Qd/Vt++B3zt2ORFuopqowOP0ocGZtkm0daK3Fc\n" +"YDXMwIpf6Z8PwvvsG1bQHcSR+cUZi7vK7+hj/LGhMPafHM7HmFUbAxpJYr5CvR6y\n" +"V1pZQYltT8xWayCeMHlLAAg10RgDkqCnY4dHnrY4GdwI2O7Wpxomni7qVHMjn+cN\n" +"UTrd7EeVw+dxAIYosuqG7ua7ee3VGoOs+XMLrscAqHahfGbyYC+j+6Tow4qwWBdU\n" +"/W3NJXnRWaHTXFHllpClnxggPRQx4yPtgTOmBBVl/O0T6i4Bv0ygsJeZAqC3VmAJ\n" +"QodQTzGf2jwqsZf4uHKQa0EKGQvTGjFVgAFNpHmAuzyqh0b1pq5JeXiFERGsKC3j\n" +"xcJilq1XeIx4SL38YNuCxi4pnyJyLnGGHpNjdjeFO5lvgCaKPegsPo4hpNpTvBJ1\n" +"D7+o3E5CqxzjRt9kQmtwBbuH/SQX2T0x8aQ6vhwjj8ftDfw+FbjpMR9zfU0Lf8V7\n" +"UjVGIl2yiVBGScBZu1nSD83PxjFy3XdFtBYoU5OrlXwBEYQs91jwK7UCiGtjI2Ao\n" +"ZGkJaBd4AqP6voyJiGnC3LWFcmeMyzfExgiclQwfhFqqf762TX5JwG6xGqtdcNKS\n" +"k54LlcI/RfvJw3ncSs9YsodZr6Jz5irpRTHX5WwCrX9mLukP96SXo29bIXEZAqEr\n" +"ZxEcF0zlYE+km5bRfRCRcVVrScugCshSNLOdQp6fOAtHCl7rdQ/8Rz7oHuqieLVi\n" +"UldRsAmpk9fIfRLphXj4j24jRP0VtL/LoJwakWTa0xO8K7eBAMVITI+HgFfN4wSO\n" +"Yh1B+bGD5WKxFsWSgBMmW+YLF5ZtxVmmbg7wK2dIpJs4pjg2YO/MTO7SifJ9kjcb\n" +"bCc74Tjs5mLLGGjGCIoXfda6WXbt2it40XhFk2zUAcPPsgjbctftkaWph7JSZpmZ\n" +"fVcPqKdhmA1U0LA2XEOMTxGyCAeseH6pJXZm9LdBozc1CwyWP8XEDHHJf35vfPKY\n" +"JDe2PanFepIOHaoRTgE7ZkGWKzOIKlS0Ucr1ezVfcxiFgQUNM+MYXXbUz51BVVq1\n" +"Dulg4VvX104nt/ULijcfa/TsE+uklEnkyk1mhavH337NQg38XF4cAngNlUF4nSW/\n" +"j0jizbAtaSx1f7q6xqPm3zPRlHrGQizHXLyl+SLzDUVPOXbPwcoeev97YeeyB6h5\n" +"NBbIK9hmekNDmYIwI0bmlrg6IXhC5pyvRe8sQlV+9wBY2liF0M1mq5onW3a55afp\n" +"+ynxXfQucb1HxZLXvRIGMBgWSQ7HfIPASqSE90Vu6qQCfkOW5PDqONr4BM65V4+g\n" +"AYsVEgaosgHw9CF7yKgkvmZpToOtGpCHVcdUeeY2/rrQnAQeSy19gj/baJ+OKl6Q\n" +"i1EGU8Yqo2r0d4XDFp/eKgC4sv57qp1PwkYQ/HKqoelJ09IAZL2sQWc05BGwt1A0\n" +"11qDIEdkZBjzK3qUnY3QlOuoZtALZrnPg56SlF1RGDOPqbcF+3opqsvzBoiikh4V\n" +"WV5OUYjRDMUDLQqf/OkuktdYf5N3RcbYP0XsAvY0ZWG3Gp068b3p8peCpkDzrF9p\n" +"bQ2ZvS304tN7+p0hif3+JyZy5/sxl17RxTeg5I3mo2+J0ptQDYwF/WadONO8r7uU\n" +"YlRltFtQfyMzyVzHON4NHGjZh7dDGtWp0MGeHRBHQsC8bEChhvWme19VXhgZoWpl\n" +"dUIZkSuvRwiURXjhKbZrEdJbVmr9FX6zoyOahv3VnmcEARoR+umxzvo3hGQPbHyH\n" +"jTsQtSBjs75/9fCxcYmBWkh3JHVDVsCbV+z+5KZpk3m50J4Y1hC8hvepC9CaBqOM\n" +"DjfyXh58x1yKiueEbcjSWsRuF7CjcrYnFUBHOs9U1j9WytCI3fhOWPMgR4UZpGuU\n" +"WlcR1BXg1wYxX273xOS/jYn9MLAVlbRpPTUMIH9VRP+sc8+XaxKpJSCl4C+vcwNY\n" +"1YdKD2QiuoBJ3fXGtqMVRtn9eZvatSJuY9CnRKRbf0hWmFD4D5RkiwE0WkdtwoHR\n" +"uEXJ47RlF0/JDU1fY1mXBkq3usvB4Absy78qL06vh45xkk9bHbdf+7Ao1RQKmqiB\n" +"NL5XnjBu+YX535WG7t7Su3mTCJXYHvn72ATxry8yhSLgWqt81STkRwc14HmrOGG8\n" +"Gw7bz7y5vikj/rnPyr7ry+QRgNNDDayAqenAu2vEAzWir0RQC/iZ9rc/r7YQWGgL\n" +"Xrd4TQ6rTZePARhwB3VomnLDDvLvi2oq/jPzLKSYM2a7qj/vBSbJ/NnNaDW5Ccew\n" +"RjMI1lIHeedqYTVAW/CKoSEPcFSAzi/Ija0gcWLgX5xsFDGIYBepAX0KS9426kMu\n" +"0r/V66zmPMusMilqRTx7KW+jZMVxXVc2zClcdmohMmtjsbqLkczprfSbdGswMv9Q\n" +"I1ktHJHIRD0vPeZXnvKZsRKZw3sKb0ltZi33ZxCJFQPeGGtM5aAFthj6awcXy6Tt\n" +"DPUQdCU/vh1zmGRAX17/Xb0irfvN+GhQLEl42pzhigJXc/rCG3a4Na8wT+xAIZVf\n" +"WUI7hMslx5wA+iB4lrAjCq0YIrjINI/lHYpotXUZGmz5wz0jOciTmXMSx9du4cpk\n" +"fIQJfR+fr5tG3fjHMgSP+p+RewHkd/7RUAmHC2k3cuk5pCJvUVJrhUIqsi1fa0LG\n" +"GA0UU6Nr9tpYdNr1WkbKQjxTg0D//AXe61jmUS5XUU4AQf6zQVfN0TMtmuYeacbK\n" +"4r6Z1CSIRbsgcnL1BN8GSd4KddkCqSk941aJUCoX+77ou4t0btVSB9FnLKipigtE\n" +"E/Rpmv+81lA4fLiIag62/pcJ3uppsZ9aaHdR10SMmuCjAVLYHqhJfrHHn32dyqLK\n" +"UI8kEZJ6GQzHLUXcGbbdnk1Qm6JwO8TeF/oQvh9y9py+oAyFy0qzP2UeUMUI2yRQ\n" +"mlWSy+wX1DbVDQ3UHwJjWp65CgyYXuW8eCB0AbyF0kF4KGf7/7Ae7tEGbmYSm5MA\n" +"71z+Azxtv5gRyRb787V2dyo0wcmbRlL7iUBVXNM/czQo31tAZIwLc+lKNp0SPH6g\n" +"gJ2yX/GeDSFNAeEVUZ/f4KZIa7QQsnGWrUr+agSnQFkySmIjWYjwC/abJwah0v0d\n" +"ulwr3tECaaXtoWVdYXa3utEclBz9umBwMJ9MQCm4Kx7dTYUWFT3bMM/ESTkGPcfm\n" +"m+C4FsqFBs80WY0ududu50vTDSdJt1RqZ7Sg6DNH6acBvWyXOpT5mPJKUjnSFwyG\n" +"oVLgv0aDDx7lLZdCkhyz/Ff5LNmBgQsjGllPszJ2gTZxZ5LD68S4kUirQG/qtzlS\n" +"PGfDOC79SMZGgsoAnr4wV3RUTxsTVFlxVHsBMB+EXOFHAr3wHTVxUGBbGzxBlQ9w\n" +"I/jlu8LIIexXAU75HS5KCGGfg0Z7BLqEzqpMKqcBQC7BD7GnCXrDSQ2DCXnl7bLN\n" +"lIrQ/z2Y8AgSdED46R40MqyyN6CPPNiOCjONHZ30fLEXuEgCp4R/+x0WWsWpjGk2\n" +"Ydkc03cx/X6moUYxB5HTqTodBmAQuWMX0rxFDrnR0SWghWjdWth9gjd+dvZ82tt1\n" +"UMUywDPhcYchtUi2lnqnYJm5p00GN9Mk14MC5ZC5qP57IJVqxu0ktOMpks+CLPnz\n" +"qp9OBpI4sIzd0y0aUJC2Gd+E9aAhlREIiicyBDmxLdk1i37QeeCralI3eubLNmE+\n" +"CjDjD8t8FUGPpKglSD3lfLTqbp2TUvyWfvJC6ulFPNsAbeLHTnPnpyPQmWxhMNGt\n" +"h67B9tbYww2TvNwqIgmB4+YIR4/pSs15TpAqvuUvjpmRwGklqgiSmrQrlIxCxux/\n" +"mfsaL3KE97wm8BsaMpMkjUL7ByTIFhFZ/gHPTxaFpbqTZ4G+lABLgp3bIsB9Dl/P\n" +"ovoqX+qL2Mq9T0GrVJGfRBuA5hISw63hx5zdsj2Cj3A3khHPqR+GRN/rVYUuOpLm\n" +"z3v5pU/74vZRmNMAIhyhmweSEPNtyVkgSdgbFErqvhxN0om2Cd/7cWh2g5BXHyUL\n" +"PBr7ZkgfsE9TnuDH7Z0JoBqXJki+MO6nqz73oH2Mm86yxcXp6O/ieKTollrUJ3yQ\n" +"P6hLcEbYPzUV99del7Va5Wi0nn0wbRXCGVQdwY+iWc7pT+VVlncyg0TvLXi0OtOt\n" +"O8xbT2DAzVXxMwOsKV9ZgS/0dtwzwICpnTzBI/47V8GYhHbOUNTBPZ52GaXMeWlX\n" +"cuRGb0+7OkKWuriyOQ5z5xaASCVfqgnOwSZYiAk0gcDoK+JHdr64/sMoJhH87R4i\n" +"2TO90whkScgiGR7A06Ba42bT1nJtI6pxvzdB2b4BDAs2Lr2OdcB3BY1dtzKjFkw/\n" +"qfIw3F55UQwcs84ZEFQDAB/tmfNHajblDFpXR4N5QvU/PdWVWJUub7oNyhIX6ruu\n" +"ln4H7lpTUHJZ7jkr1qpnvkztZtHGlpJ0QdUHgyMYER1xU58Hg77yzIW3EdAa2PyK\n" +"1t4udKbQKChShlShIMzwzj57ss/69QobrpYAHYi6IRMaMUGBfipGBACK3yeXsXz0\n" +"c3Q2J5vI6QbxNsiJ5t7Ry1IqotbJcU7HND/yVUAUbEg5CpEDOSeSOW/ulyLuFxEV\n" +"lRTwIO/68BoIoR7umlP23/1N5OYzaBHhH2nThILBovHeJRXnGXSgeFfwSj7LIYEV\n" +"c1MdDSg/HzoADPXyEPLzqFzHRHeNiqEolmOPnFh0hRzbMZ0W5TQPDGWJdF21g816\n" +"vA0WW4UQjLM+vnX9kKKLA1ut+9JWk1dGKsmWtdWUDfJjUP/L6dS4OYEl6O6+SjM9\n" +"GcyGvHTiC5OpJllYpvELP/NjtTf9or8Bmruuga/axeOuS5ocYLK/sGRlmO6Z96da\n" +"QSlyGWEQAnM2D1cDmdd4CetPslOVIcQ41+coWCi2xg3UjO/bFK1CA4R1rb4ekXfs\n" +"s5U2XChyHhUPgl57y1r0ILXRXWJTJ0/F9hhu4aYQVFeIV/IuzJbmTKKkAcCOH6ys\n" +"qnu2BXz8Pm2tU10JFfRcuZ8rHuUyUErA40ESsLijON98GMwL4Rat9ZSCNS5hlK7y\n" +"yRJdr0ITp8oTbduAoulgWOvtcw1L87QBVojWz3cbhXra+WITirYuGNbzfmZn1WQM\n" +"kukEZUEHSypGOrHr1XiuY4Rw/DBaJSLyZ+VybEOfXqXkDBh5s1ayypBvzrzFZCIn\n" +"PJxIVsvrkhrpEbTJ9d7zLWjhOa9ZWw8lAubllbGm+7qCfdHmGsfBtvJdzx6zhB1Y\n" +"otL/PCis2XVTBEDJeB8pGqKFOZjNz8PC5qP+ymtAfy2ktl/u4HsFlxV7CsEKGYPm\n" +"p3LqnhPUy5M5gin4E4uPPyzzD2kcM3way49FKWUKlblQU0SyWtHRmMB3vcVmyT85\n" +"BRULXF7jgog7XR/EMltwQyJI6GcUCrnWZu+G0BEwXG+CsgCzE7assDavc1NSGLZM\n" +"rmzXiFFyfk7CE6lW2Lm+oWaFwKdvpmNZJFGGX8ZHRE9ZvkFMnfw9MYf2W7xa0jf7\n" +"k3c6X5wMuk9mznVtq5itNFVXh1mT1ujeWOiiqyH5UhQQjj6O+ZXt4gqt/jT6dd1i\n" +"jRuhhxaUGOlhpVBW/ySXhZ+HgOy9aCJ/bgjRGaqGixogk4f4rcgigHruwTpOQuDn\n" +"xDZ3Xns70S40WtHSYN+Gbl9nIh4yl78aNnA4FVtTAuLlVKEKlMJi9OBFuP5TEczG\n" +"+0HTwL/VPSCI+8FUZBhlz3YwecYq6dY5mS46+luPW+5Wl+5jtzb8V9oxVnRx2hQq\n" +"B5HJsM5FOOhHDHMXoCsevj7N/ufK7cU7Wbr0DkgYRwvb0ZJB5WYgcaQ0W7aduhGb\n" +"MQsandhP8Ajb2cmLobi3mHHPbcEkvjT8JP9Sim5xtfF+oCMMB5ByA5bI2aIFybZm\n" +"jX9e/V8wNgtpDKDVKPjB3+9dj5gU1N5JsrjQwQDB0kVRMWdpJCtD4hZ2+T/QE3SI\n" +"f8Rdk8pj8qBzRPbnhW6qsoWZdjMRC8qixZqHw4jol09UF7Ab9hjEF5ZDTfNGXwy8\n" +"/hz8su+mr8hhrlCrOF2vBYUayAA96zhbDWfg3Pdxo9bTn3/DmyAngL4J5Gu679xK\n" +"rWN4j7uQG4bzTa8WJb09/lW49UzWvmrz0c6/yexk3T//xDD067FafdnP5pYs4Cvp\n" +"rCoHpXbKjxx99DJmb5iXW0JRLSpFSCbf1HPHbmzST3minSXap5FCWDJcSgExKIJp\n" +"DXZ9rk0LMnQA74MWC5gjjM+5t0AHKuNRhJbQSwYWTKqeApXho53T/COlfDlSs2tb\n" +"Vz1Ia5z7IOfu1QheE93huNAHT3Ob+mSmUq782SqFPr6uwud/l5uP3HpcuwugdlFm\n" +"Jw8uBBOQ53W4lLbYfQYTVgieClVhmYMu7Ye0xYZ5B2jf714sjZRMa0LCbsyj58xH\n" +"uzs8ddNN1fLMzb0JRBE8JWj5PbxhA/sTwMkD7SnEMBUTtP0obmuQ982aTfyvQCH/\n" +"ve8OUPtYf5XWNv18mpR+h+riMt1Y8Eb6BJzTMFNWagMJAe3JV6A6upHroNFo2FxY\n" +"1XPRM1Rt0zKo7GD+oXnixfpl1aG8yqZhYo1ZC9buaHwH6zvM+xoiGD0iujeDtpVy\n" +"Vp6cAqqaGmrNwcPVBLc7hNKrJnbFKyhjL5/xp9j6jQov1aWQ8HsaNvh0p2ljmlwb\n" +"daTYZcwLgSgPna7HhiqnOSAmXZ7St/qe/b9TqBtIVzwzmtevgMyG98QV0syFP5X6\n" +"2Jc1g9733sTZp7njq4Cu07JhpICpinhLWR3nkODJbjk/mpLcQZgtV6W749AUo8oT\n" +"jRVEJ8MpCo1h0bVDxsRnA3DrMneD88L8/b10aHs+bPm1HKbCmT+kJAFaUQNa8JvJ\n" +"pReN37qTWvZCte7vaPAIP5cboATMu/J4t3izpm+YJoJlWcIegGx3kQ+17P4MbgDl\n" +"S93U4sOLvTk9+MoyPo9yGWU/zHgzcQ6wCFdzWMDRswuh+/4TJ2+yg6maq3iBtj39\n" +"gNLMR+sRgGGvYisqE9bfvNQy5IWrABBKcSBTXeTM1DmW6jv3TI8DoCzCbpjqcIwT\n" +"u2J+7k8wJEHPcAwnBjlyWphVvwNwM0cXqOnlJZ/4z7OGgjiNEem7TMuvxk+YkiXK\n" +"OzftdTjeIpzBwsGRP8/teMBpjS95M7GloKtxO+muBVxXbmsq8GBRC9vtNJ2Ma/xP\n" +"bXvd+7caytD3ob6ZfOzCpi4ZS8uByEfIMxlgZ5Sn3jhgEkcIU+YW9b3teMZOuWdA\n" +"QpDCoMpXaHVyRqwVV59JjmftiBnNBEo1/QzRj2UxRi7fHMfmNxL5LRM4CHSLUSCq\n" +"Y3A3pkxvBHUzemhynSFvtCPa8GHiUpe9so0V/2hlgaENAVELPjMlWytaYufRllgy\n" +"tUnCd32C5PrrmYzMKnxKRPXLcxLgziruJGSks9vIspoPk0pWgkZm+M9fRpJKlWHF\n" +"yT9OOGBW2yynw/yvXssxJmdUDxVcWL4uS2bZc4s0Zc6RSL9uQPjZVX0JLj+cXfx4\n" +"93Gn5bDhMgm+CGM6j3RiAAD7tT5V0sytNFjXd1A4U1u8yj3wzhKqOtZpDmuGUlMn\n" +"EODu7I5KtWxOTPThy7TecI5r+F/6KL+2MOtRhj2PmlT/Xed6PaAmDkQeiXGps08x\n" +"u0JIpuB61axvT4PAsKZNUd4ExbzNxRDAARUMgY8krpmyKZyHVFIQ19uHM2lGl9/i\n" +"h3PKlLHYI8RsHutHElzq+F5tWd5AA99LVRZX4axAVIQNiqRg8IMSoCwUaCCbjUMz\n" +"sJCo2t36GYk5S2BRnfrCqYoZRHw+ENYN0tDEMhXq1OqjvNHW3TzL3DsUhM6EZU5n\n" +"cRR4ynUvPqqWFphLefRW10vCtaW9roJQZyFYf9kd8xgW/BhcDNbTTaQ1U6xCHgX+\n" +"78DKee/NvY1WIEBR8X0iVk5XlSJb14eRtxNawXFyebVdmC/DiMNgnTBncMbePnZi\n" +"KCl1r5xqo7tSIoJ6Z0l6qINd89T9fcg9mujTVwsfQ+5/kdEy0Iw7CQcTOGvMaoPX\n" +"IAJlWSVeZ8eu8kmsD1Z8ewoPufMKiY4cPRAK5bCDgsrK6bAExOlCwPnNNM8Ym1Hz\n" +"aYFeGs5sW468Qww+Nbl5xcNFKtwUKZ6EebRHjwttiyTgCdAhv9wL1u2WFydWWgkG\n" +"rwUbNpSLKls+pijCeJAscvxzbZz96iOaYrY8IyzGBFwfgFAESfnzBc8SQjZzMzoO\n" +"vmYIRon2m/5w5AZA2IjQ4VxXJDK6XExD/ZLsxNXzMnROD++hE+s8DvPlRPmN4egF\n" +"gAzJs/9t7IyE/dDf7gSSBqzEBbwduD8ozzYHwELUc4ERdRzjEdBM0azT61g7Yilr\n" +"iT5Hy+2iw/pNwiqVOYiAbj2lwcoMlFZmdxviD4IMXdsNVWsCAVJL0PqIh1UDDb3z\n" +"Urv3idBJeSBuuFr6AFS6kAgvrwV/pEGoBoHuyii/rZxVugGKeuMynKEvSHuFNuQU\n" +"qIHcNgqQR34v2Ut5pQ1R8s7K3Rae/AhE5GncJa6FJmB9TF8MYMu9PlSZV/eGv8UL\n" +"IDWQ7sY3NdhZini//xtwPqIw29yOeZ0X6Aqsek9tfh21UwKSpHb7T+PwXYmoB+23\n" +"p3FXkP/rv4AGRq1xJqFYzKJvwsXqTFuNFWP74yhTg6rC90w2p5TeH1rJMAnv4u0L\n" +"hGtG/NL+D1Tzdf00TYAjno5Ia5dQJDd/eO+Ygqnhl6hAqGtS6r9JhIEXw1nQD7SC\n" +"lj96ZuKdUWO8rpIiAtvHAsn++xvMVPm/S1SwA8oE049iVwS8/eNNiMKoSlTlYc7o\n" +"pusBZQrVF4We4HHYFjysBbcXlvoXDd8LkZ8Nh63VQPnoIGNKH2U6aXCnQcJ8dZqO\n" +"DNxL4uyM4A578FUUR6vxqt2asnLHQ0Z7pPE4uqtz/WgbiHI/i2oHS8oe1clsifCw\n" +"3ZY33kflqLftkTNka1oiftDb0OqFLjkS7/AUorqHazw53gM3gqJY5EXA3Px9+nhu\n" +"NzxSK/t41JoCfgQJHMkIWb3yUcO4OFZeGCeAxIJY95hv/brt6/WNielXjNaohYvc\n" +"lsSUHEJRHwVxQmWK0LS+g13HAgOI7cNt3MA8sSkzTneHGFgEvmrSyb0wCEmushC9\n" +"mjQThvaxfQk9douA/cR2bHr7axXqv9vjztmxUr0a30a7lvLMBQbJmFtJJylW+tJe\n" +"v/vKNOB+9mK793cttr2JFnMhwUKFKWiFDQJtxw/eLQWY4BJ19Rs2x4BJgmV+u1jB\n" +"zR8uvxuArG/cqVEJsoC6uuSzhAWSwdvumijO6yuyWF6nHY6aAcy8dyFQlDFHAd+/\n" +"J05Lrbzj4N9lcI7hPalh0uMdERGvtUdT8QRm5ebP1zogYEkZk/1GOU29dMawkAt/\n" +"SWhp2yWdjLt8f5HQKu72vUF/yyTfzfdqQqJwfthP7+vp+sHDO85AMF45uU9g3pxW\n" +"IbXSbZ4fFGC1/41db/2GOHFgaheMXj0SIWHqQE1jtihr3BBBO4b3Ccz5QCnrn48J\n" +"8L+QRdh4a/cAx4ty/oHEiXwpSBBSFRl5+y2NijC8GITA5dRjCRWP+Y0zuTrJ7j1a\n" +"h+3kGs1kxqskhaEuhXnXyknGLjXrU+ewRGhHzP23o5betVhX+c1XjVqmJNZ5OPn/\n" +"wrqx/XwoIl/3F5lMmGDG9mPtyg0E227nKl9Sy0Vbwx2tu1unjOlzSCa7lpoD4TIX\n" +"PBJ5+Zb0CE6HEt3V0ec1m4uUe/xObAnzyr4UbzdqLaMy8vTcF/qsncXyPBjwqdjR\n" +"ReDAtt99bAPY4roPKGt8dgKUPE0t/XoY+SlmUp75TkZDXrOIJXpEW0GpLPf53T+W\n" +"Ex3KtfLAnZzrw8+dIageY7IgoQ85h3sYE7uEI8QlcO/o4udqUzTp4Sn4sWvdTLrx\n" +"W7ImvK2rsU5ubVdsEaFKM7+7nxGn2JyMpIWFz0SbP34CkXHhrXxyRD+GhMIDHFxV\n" +"uBnZnjJsw+ooIm1rL4I7/VMWEwmVegreT6w9Gsmb5igw+zu9v2YBgTOhysA9XZd4\n" +"7O3VjqKkhTXcBqdpRWuz8gPQ+4rfwij28Gg2alG04Eh3G3868NOCFJhhaHVmwYR2\n" +"ygRm6N9eDW1bHhYSN75HSEb6aIebk+1AT4S1QtJaPSH0EduIXO++JYAs+jIFKy2c\n" +"jCVFlO/LbXl7iCdXurJHpSbMNmZFNUri6zEolENODLwke836jBOKiVrWzLnEMxHI\n" +"WDDTpLTYhR3C7sEprpEQm9SX2Eik3WxVb4ZTb7SZFU1y1d4tWnjGu3U1D+vO9wVq\n" +"Sss9lDipbkhQ9k4j1/Pqozaxvi8lYLbh3WEjK3Iwpr66Bk6Ai2oRg4b+7vzV4o+6\n" +"L47JPJhajdHac0CIlmupyA4eejECS6OpoLDf5Wr/616k3dxM//3kAWGUnXVw9GSo\n" +"UF5W8AaKlaGZ6EZk09NyGSFRjEs18z+g5ckviGF0EhZI7ZPWQQmlqWUsL9O0S4GO\n" +"ZZ9f0UhNmHEspcugbs7e1yfjwGVyxIBkrmxpkmfHE4Gb47UGlJevg2OvZOPT3wMH\n" +"vOds2BtqdT3tuss9k+7hsISGse7isEOb7TN5MHb6yyzqnCUZhp5m3Iag7TUkiyfU\n" +"jKH5R13tHqKUoJ2rofWoLO2H5xSfp/lqF9sLd4rJ+Pbjhiuvfwz5copYsuTNL4kB\n" +"SPUikHlTxSOgTBYNV77qxpsqOI3+iziCrSqHsxNdlaA1T3fiq6SeZBNdD822AYm9\n" +"L5hbcgpDPEEwT/n5kWNbRNueerJkJwboaOnT1ZX1601Pwj5QDi+YM1NYy5PsdWxb\n" +"bPGpQyZ+uf917q9gV7Ykr5cic10YD11khAghr0n6fYfb8Ijc22uP6m47KItDqQc1\n" +"eFym149F56B0yg5FR85Arg==\n" +"-----END MESSAGE-----\n" +" signature Of+jvQKzH9ot2NV5twlDO2CFbzLSB4absWTwG58TCHb+TWgQi3z6SZIoTnGGY/uicJgEkCN++bZZR49GiyHyCQ\n"; diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c new file mode 100644 index 0000000000..c6197875b5 --- /dev/null +++ b/src/test/test_hs_intropoint.c @@ -0,0 +1,888 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_service.c + * \brief Test hidden service functionality. + */ + +#define HS_SERVICE_PRIVATE +#define HS_INTROPOINT_PRIVATE +#define RENDSERVICE_PRIVATE +#define CIRCUITLIST_PRIVATE + +#include "test.h" +#include "log_test_helpers.h" +#include "crypto.h" +#include "log_test_helpers.h" + +#include "or.h" +#include "ht.h" + +/* Trunnel. */ +#include "hs/cell_establish_intro.h" +#include "hs/cell_introduce1.h" +#include "hs/cell_common.h" +#include "hs_service.h" +#include "hs_common.h" +#include "hs_circuitmap.h" +#include "hs_intropoint.h" + +#include "circuitlist.h" +#include "circuituse.h" +#include "rendservice.h" +#include "relay.h" + +/* Mock function to avoid networking in unittests */ +static int +mock_send_intro_established_cell(or_circuit_t *circ) +{ + (void) circ; + return 0; +} + +static int +mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ, + uint8_t relay_command, const char *payload, + size_t payload_len, + crypt_path_t *cpath_layer, + const char *filename, int lineno) +{ + (void) stream_id; + (void) circ; + (void) relay_command; + (void) payload; + (void) payload_len; + (void) cpath_layer; + (void) filename; + (void) lineno; + return 0; +} + +static or_circuit_t * +helper_create_intro_circuit(void) +{ + or_circuit_t *circ = or_circuit_new(0, NULL); + tt_assert(circ); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + done: + return circ; +} + +static trn_cell_introduce1_t * +helper_create_introduce1_cell(void) +{ + trn_cell_introduce1_t *cell = NULL; + ed25519_keypair_t auth_key_kp; + + /* Generate the auth_key of the cell. */ + if (ed25519_keypair_generate(&auth_key_kp, 0) < 0) { + goto err; + } + + cell = trn_cell_introduce1_new(); + tt_assert(cell); + + /* Set the auth key. */ + { + size_t auth_key_len = sizeof(auth_key_kp.pubkey); + trn_cell_introduce1_set_auth_key_type(cell, + HS_INTRO_AUTH_KEY_TYPE_ED25519); + trn_cell_introduce1_set_auth_key_len(cell, auth_key_len); + trn_cell_introduce1_setlen_auth_key(cell, auth_key_len); + uint8_t *auth_key_ptr = trn_cell_introduce1_getarray_auth_key(cell); + memcpy(auth_key_ptr, auth_key_kp.pubkey.pubkey, auth_key_len); + } + + /* Set the cell extentions to none. */ + { + trn_cell_extension_t *ext = trn_cell_extension_new(); + trn_cell_extension_set_num(ext, 0); + trn_cell_introduce1_set_extensions(cell, ext); + } + + /* Set the encrypted section to some data. */ + { + size_t enc_len = 128; + trn_cell_introduce1_setlen_encrypted(cell, enc_len); + uint8_t *enc_ptr = trn_cell_introduce1_getarray_encrypted(cell); + memset(enc_ptr, 'a', enc_len); + } + + return cell; + err: + done: + trn_cell_introduce1_free(cell); + return NULL; +} + +/* Try sending an ESTABLISH_INTRO cell on a circuit that is already an intro + * point. Should fail. */ +static void +test_establish_intro_wrong_purpose(void *arg) +{ + int retval; + trn_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + memcpy(intro_circ->rend_circ_nonce, circuit_key_material, DIGEST_LEN); + + /* Set a bad circuit purpose!! :) */ + circuit_change_purpose(TO_CIRCUIT(intro_circ), CIRCUIT_PURPOSE_INTRO_POINT); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("Rejecting ESTABLISH_INTRO on non-OR circuit."); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + trn_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Prepare a circuit for accepting an ESTABLISH_INTRO cell */ +static void +helper_prepare_circ_for_intro(or_circuit_t *circ, + uint8_t *circuit_key_material) +{ + /* Prepare the circuit for the incoming ESTABLISH_INTRO */ + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + memcpy(circ->rend_circ_nonce, circuit_key_material, DIGEST_LEN); +} + +/* Send an empty ESTABLISH_INTRO cell. Should fail. */ +static void +test_establish_intro_wrong_keytype(void *arg) +{ + int retval; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, (uint8_t*)"", 0); + expect_log_msg_containing("Empty ESTABLISH_INTRO cell."); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send an ESTABLISH_INTRO cell with an unknown auth key type. Should fail. */ +static void +test_establish_intro_wrong_keytype2(void *arg) +{ + int retval; + trn_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Mutate the auth key type! :) */ + cell_body[0] = 42; + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("Unrecognized AUTH_KEY_TYPE 42."); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + trn_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send a legit ESTABLISH_INTRO cell but with a wrong MAC. Should fail. */ +static void +test_establish_intro_wrong_mac(void *arg) +{ + int retval; + trn_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + /* Mangle one byte of the MAC. */ + uint8_t *handshake_ptr = + trn_cell_establish_intro_getarray_handshake_mac(establish_intro_cell); + handshake_ptr[TRUNNEL_SHA3_256_LEN - 1]++; + /* We need to resign the payload with that change. */ + { + ed25519_signature_t sig; + ed25519_keypair_t key_struct; + /* New keypair for the signature since we don't have access to the private + * key material generated earlier when creating the cell. */ + retval = ed25519_keypair_generate(&key_struct, 0); + tt_int_op(retval, OP_EQ, 0); + uint8_t *auth_key_ptr = + trn_cell_establish_intro_getarray_auth_key(establish_intro_cell); + memcpy(auth_key_ptr, key_struct.pubkey.pubkey, ED25519_PUBKEY_LEN); + /* Encode payload so we can sign it. */ + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + retval = ed25519_sign_prefixed(&sig, cell_body, + cell_len - + (ED25519_SIG_LEN + + sizeof(establish_intro_cell->sig_len)), + ESTABLISH_INTRO_SIG_PREFIX, &key_struct); + tt_int_op(retval, OP_EQ, 0); + /* And write the signature to the cell */ + uint8_t *sig_ptr = + trn_cell_establish_intro_getarray_sig(establish_intro_cell); + memcpy(sig_ptr, sig.sig, establish_intro_cell->sig_len); + /* Re-encode with the new signature. */ + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + } + + /* Receive the cell. Should fail because our MAC is wrong. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("ESTABLISH_INTRO handshake_auth not as expected"); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + trn_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send a legit ESTABLISH_INTRO cell but with a wrong auth key length. Should + * fail. */ +static void +test_establish_intro_wrong_auth_key_len(void *arg) +{ + int retval; + trn_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + size_t bad_auth_key_len = ED25519_PUBKEY_LEN - 1; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + /* Mangle the auth key length. */ + trn_cell_establish_intro_set_auth_key_len(establish_intro_cell, + bad_auth_key_len); + trn_cell_establish_intro_setlen_auth_key(establish_intro_cell, + bad_auth_key_len); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("ESTABLISH_INTRO auth key length is invalid"); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + trn_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send a legit ESTABLISH_INTRO cell but with a wrong sig length. Should + * fail. */ +static void +test_establish_intro_wrong_sig_len(void *arg) +{ + int retval; + trn_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + size_t bad_sig_len = ED25519_SIG_LEN - 1; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + /* Mangle the signature length. */ + trn_cell_establish_intro_set_sig_len(establish_intro_cell, bad_sig_len); + trn_cell_establish_intro_setlen_sig(establish_intro_cell, bad_sig_len); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("ESTABLISH_INTRO sig len is invalid"); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + trn_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send a legit ESTABLISH_INTRO cell but slightly change the signature. Should + * fail. */ +static void +test_establish_intro_wrong_sig(void *arg) +{ + int retval; + trn_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Mutate the last byte (signature)! :) */ + cell_body[cell_len-1]++; + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("Failed to verify ESTABLISH_INTRO cell."); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + trn_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Helper function: Send a well-formed v3 ESTABLISH_INTRO cell to + * <b>intro_circ</b>. Return the cell. */ +static trn_cell_establish_intro_t * +helper_establish_intro_v3(or_circuit_t *intro_circ) +{ + int retval; + trn_cell_establish_intro_t *establish_intro_cell = NULL; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + tt_assert(intro_circ); + + /* Prepare the circuit for the incoming ESTABLISH_INTRO */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Receive the cell */ + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + tt_int_op(retval, ==, 0); + + done: + return establish_intro_cell; +} + +/* Helper function: Send a well-formed v2 ESTABLISH_INTRO cell to + * <b>intro_circ</b>. Return the public key advertised in the cell. */ +static crypto_pk_t * +helper_establish_intro_v2(or_circuit_t *intro_circ) +{ + crypto_pk_t *key1 = NULL; + int retval; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + tt_assert(intro_circ); + + /* Prepare the circuit for the incoming ESTABLISH_INTRO */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Send legacy establish_intro */ + key1 = pk_generate(0); + + /* Use old circuit_key_material why not */ + cell_len = encode_establish_intro_cell_legacy((char*)cell_body, + sizeof(cell_body), + key1, + (char *) circuit_key_material); + tt_int_op(cell_len, >, 0); + + /* Receive legacy establish_intro */ + retval = hs_intro_received_establish_intro(intro_circ, + cell_body, cell_len); + tt_int_op(retval, ==, 0); + + done: + return key1; +} + +/* Helper function: test circuitmap free_all function outside of + * test_intro_point_registration to prevent Coverity from seeing a + * double free if the assertion hypothetically fails. + */ +static void +test_circuitmap_free_all(void) +{ + hs_circuitmap_ht *the_hs_circuitmap = NULL; + + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(the_hs_circuitmap); + hs_circuitmap_free_all(); + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(!the_hs_circuitmap); + done: + ; +} + +/** Successfuly register a v2 intro point and a v3 intro point. Ensure that HS + * circuitmap is maintained properly. */ +static void +test_intro_point_registration(void *arg) +{ + int retval; + hs_circuitmap_ht *the_hs_circuitmap = NULL; + + or_circuit_t *intro_circ = NULL; + trn_cell_establish_intro_t *establish_intro_cell = NULL; + ed25519_public_key_t auth_key; + + crypto_pk_t *legacy_auth_key = NULL; + or_circuit_t *legacy_intro_circ = NULL; + + or_circuit_t *returned_intro_circ = NULL; + + (void) arg; + + MOCK(hs_intro_send_intro_established_cell, mock_send_intro_established_cell); + + hs_circuitmap_init(); + + /* Check that the circuitmap is currently empty */ + { + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(the_hs_circuitmap); + tt_int_op(0, ==, HT_SIZE(the_hs_circuitmap)); + /* Do a circuitmap query in any case */ + returned_intro_circ =hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); + tt_ptr_op(returned_intro_circ, ==, NULL); + } + + /* Create a v3 intro point */ + { + intro_circ = or_circuit_new(0, NULL); + tt_assert(intro_circ); + establish_intro_cell = helper_establish_intro_v3(intro_circ); + + /* Check that the intro point was registered on the HS circuitmap */ + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(the_hs_circuitmap); + tt_int_op(1, ==, HT_SIZE(the_hs_circuitmap)); + get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO, + establish_intro_cell); + returned_intro_circ = + hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); + tt_ptr_op(intro_circ, ==, returned_intro_circ); + } + + /* Create a v2 intro point */ + { + char key_digest[DIGEST_LEN]; + + legacy_intro_circ = or_circuit_new(1, NULL); + tt_assert(legacy_intro_circ); + legacy_auth_key = helper_establish_intro_v2(legacy_intro_circ); + tt_assert(legacy_auth_key); + + /* Check that the circuitmap now has two elements */ + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(the_hs_circuitmap); + tt_int_op(2, ==, HT_SIZE(the_hs_circuitmap)); + + /* Check that the new element is our legacy intro circuit. */ + retval = crypto_pk_get_digest(legacy_auth_key, key_digest); + tt_int_op(retval, ==, 0); + returned_intro_circ = + hs_circuitmap_get_intro_circ_v2_relay_side((uint8_t*)key_digest); + tt_ptr_op(legacy_intro_circ, ==, returned_intro_circ); + } + + /* XXX Continue test and try to register a second v3 intro point with the + * same auth key. Make sure that old intro circuit gets closed. */ + + done: + crypto_pk_free(legacy_auth_key); + circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free(TO_CIRCUIT(legacy_intro_circ)); + trn_cell_establish_intro_free(establish_intro_cell); + test_circuitmap_free_all(); + + UNMOCK(hs_intro_send_intro_established_cell); +} + +static void +test_introduce1_suitable_circuit(void *arg) +{ + int ret; + or_circuit_t *circ = NULL; + + (void) arg; + + /* Valid suitable circuit. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 1); + } + + /* Test if the circuit purpose safeguard works correctly. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + + /* Test the non-edge circuit safeguard works correctly. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + /* Bogus pointer, the check is against NULL on n_chan. */ + circ->base_.n_chan = (channel_t *) circ; + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + + /* Mangle the circuit a bit more so see if our only one INTRODUCE1 cell + * limit works correctly. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + circ->already_received_introduce1 = 1; + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + + done: + ; +} + +static void +test_introduce1_is_legacy(void *arg) +{ + int ret; + uint8_t request[256]; + + (void) arg; + + /* For a cell to be considered legacy, according to the specification, the + * first 20 bytes MUST BE non-zero else it's a v3 cell. */ + memset(request, 'a', DIGEST_LEN); + memset(request + DIGEST_LEN, 0, sizeof(request) - DIGEST_LEN); + ret = introduce1_cell_is_legacy(request); + tt_int_op(ret, OP_EQ, 1); + + /* This is a NON legacy cell. */ + memset(request, 0, DIGEST_LEN); + memset(request + DIGEST_LEN, 'a', sizeof(request) - DIGEST_LEN); + ret = introduce1_cell_is_legacy(request); + tt_int_op(ret, OP_EQ, 0); + + done: + ; +} + +static void +test_introduce1_validation(void *arg) +{ + int ret; + trn_cell_introduce1_t *cell = NULL; + + (void) arg; + + /* Create our decoy cell that we'll modify as we go to test the validation + * function of that parsed cell. */ + cell = helper_create_introduce1_cell(); + + /* It should NOT be a legacy cell which will trigger a BUG(). */ + memset(cell->legacy_key_id, 'a', sizeof(cell->legacy_key_id)); + tor_capture_bugs_(1); + ret = validate_introduce1_parsed_cell(cell); + tor_end_capture_bugs_(); + tt_int_op(ret, OP_EQ, -1); + /* Reset legacy ID and make sure it's correct. */ + memset(cell->legacy_key_id, 0, sizeof(cell->legacy_key_id)); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + /* Non existing auth key type. */ + cell->auth_key_type = 42; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Reset is to correct value and make sure it's correct. */ + cell->auth_key_type = HS_INTRO_AUTH_KEY_TYPE_ED25519; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + /* Really bad key length. */ + cell->auth_key_len = 0; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + cell->auth_key_len = UINT16_MAX; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Correct size, let's try that. */ + cell->auth_key_len = sizeof(ed25519_public_key_t); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + /* Set an invalid size of the auth key buffer. */ + trn_cell_introduce1_setlen_auth_key(cell, 3); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Reset auth key buffer and make sure it works. */ + trn_cell_introduce1_setlen_auth_key(cell, sizeof(ed25519_public_key_t)); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + /* Empty encrypted section. */ + trn_cell_introduce1_setlen_encrypted(cell, 0); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Reset it to some non zero bytes and validate. */ + trn_cell_introduce1_setlen_encrypted(cell, 1); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + done: + trn_cell_introduce1_free(cell); +} + +static void +test_received_introduce1_handling(void *arg) +{ + int ret; + uint8_t *request = NULL, buf[128]; + trn_cell_introduce1_t *cell = NULL; + or_circuit_t *circ = NULL; + + (void) arg; + + MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge); + + hs_circuitmap_init(); + + /* Too small request length. An INTRODUCE1 expect at the very least a + * DIGEST_LEN size. */ + { + circ = helper_create_intro_circuit(); + ret = hs_intro_received_introduce1(circ, buf, DIGEST_LEN - 1); + tt_int_op(ret, OP_EQ, -1); + circuit_free(TO_CIRCUIT(circ)); + } + + /* We have a unit test only for the suitability of a circuit to receive an + * INTRODUCE1 cell so from now on we'll only test the handling of a cell. */ + + /* Bad request. */ + { + circ = helper_create_intro_circuit(); + uint8_t test[2]; /* Too small request. */ + ret = handle_introduce1(circ, test, sizeof(test)); + tor_free(circ->p_chan); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, -1); + } + + /* Valid case. */ + { + cell = helper_create_introduce1_cell(); + ssize_t request_len = trn_cell_introduce1_encoded_len(cell); + tt_int_op((int)request_len, OP_GT, 0); + request = tor_malloc_zero(request_len); + ssize_t encoded_len = + trn_cell_introduce1_encode(request, request_len, cell); + tt_int_op((int)encoded_len, OP_GT, 0); + + circ = helper_create_intro_circuit(); + or_circuit_t *service_circ = helper_create_intro_circuit(); + circuit_change_purpose(TO_CIRCUIT(service_circ), + CIRCUIT_PURPOSE_INTRO_POINT); + /* Register the circuit in the map for the auth key of the cell. */ + ed25519_public_key_t auth_key; + const uint8_t *cell_auth_key = + trn_cell_introduce1_getconstarray_auth_key(cell); + memcpy(auth_key.pubkey, cell_auth_key, ED25519_PUBKEY_LEN); + hs_circuitmap_register_intro_circ_v3_relay_side(service_circ, &auth_key); + ret = hs_intro_received_introduce1(circ, request, request_len); + circuit_free(TO_CIRCUIT(circ)); + circuit_free(TO_CIRCUIT(service_circ)); + tt_int_op(ret, OP_EQ, 0); + } + + /* Valid legacy cell. */ + { + tor_free(request); + trn_cell_introduce1_free(cell); + cell = helper_create_introduce1_cell(); + uint8_t *legacy_key_id = trn_cell_introduce1_getarray_legacy_key_id(cell); + memset(legacy_key_id, 'a', DIGEST_LEN); + /* Add an arbitrary amount of data for the payload of a v2 cell. */ + size_t request_len = trn_cell_introduce1_encoded_len(cell) + 256; + tt_size_op(request_len, OP_GT, 0); + request = tor_malloc_zero(request_len + 256); + ssize_t encoded_len = + trn_cell_introduce1_encode(request, request_len, cell); + tt_int_op((int)encoded_len, OP_GT, 0); + + circ = helper_create_intro_circuit(); + or_circuit_t *service_circ = helper_create_intro_circuit(); + circuit_change_purpose(TO_CIRCUIT(service_circ), + CIRCUIT_PURPOSE_INTRO_POINT); + /* Register the circuit in the map for the auth key of the cell. */ + uint8_t token[REND_TOKEN_LEN]; + memcpy(token, legacy_key_id, sizeof(token)); + hs_circuitmap_register_intro_circ_v2_relay_side(service_circ, token); + ret = hs_intro_received_introduce1(circ, request, request_len); + circuit_free(TO_CIRCUIT(circ)); + circuit_free(TO_CIRCUIT(service_circ)); + tt_int_op(ret, OP_EQ, 0); + } + + done: + trn_cell_introduce1_free(cell); + tor_free(request); + hs_circuitmap_free_all(); + UNMOCK(relay_send_command_from_edge_); +} + +struct testcase_t hs_intropoint_tests[] = { + { "intro_point_registration", + test_intro_point_registration, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_keytype", + test_establish_intro_wrong_keytype, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_keytype2", + test_establish_intro_wrong_keytype2, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_purpose", + test_establish_intro_wrong_purpose, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_sig", + test_establish_intro_wrong_sig, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_sig_len", + test_establish_intro_wrong_sig_len, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_auth_key_len", + test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_mac", + test_establish_intro_wrong_mac, TT_FORK, NULL, NULL }, + + { "introduce1_suitable_circuit", + test_introduce1_suitable_circuit, TT_FORK, NULL, NULL }, + + { "introduce1_is_legacy", + test_introduce1_is_legacy, TT_FORK, NULL, NULL }, + + { "introduce1_validation", + test_introduce1_validation, TT_FORK, NULL, NULL }, + + { "received_introduce1_handling", + test_received_introduce1_handling, TT_FORK, NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_ntor.sh b/src/test/test_hs_ntor.sh new file mode 100755 index 0000000000..8a0003d44a --- /dev/null +++ b/src/test/test_hs_ntor.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Validate Tor's ntor implementation. + +exitcode=0 + +# Run the python integration test sand return the exitcode of the python +# script. The python script might ask the testsuite to skip it if not all +# python dependencies are covered. +"${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/hs_ntor_ref.py" || exitcode=$? + +exit ${exitcode} diff --git a/src/test/test_hs_ntor_cl.c b/src/test/test_hs_ntor_cl.c new file mode 100644 index 0000000000..ed1eda58ea --- /dev/null +++ b/src/test/test_hs_ntor_cl.c @@ -0,0 +1,255 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** This is a wrapper over the little-t-tor HS ntor functions. The wrapper is + * used by src/test/hs_ntor_ref.py to conduct the HS ntor integration + * tests. + * + * The logic of this wrapper is basically copied from src/test/test_ntor_cl.c + */ + +#include "orconfig.h" +#include <stdio.h> +#include <stdlib.h> + +#define ONION_NTOR_PRIVATE +#include "or.h" +#include "util.h" +#include "compat.h" +#include "crypto.h" +#include "crypto_curve25519.h" +#include "hs_ntor.h" +#include "onion_ntor.h" + +#define N_ARGS(n) STMT_BEGIN { \ + if (argc < (n)) { \ + fprintf(stderr, "%s needs %d arguments.\n",argv[1],n); \ + return 1; \ + } \ + } STMT_END +#define BASE16(idx, var, n) STMT_BEGIN { \ + const char *s = argv[(idx)]; \ + if (base16_decode((char*)var, n, s, strlen(s)) < (int)n ) { \ + fprintf(stderr, "couldn't decode argument %d (%s)\n",idx,s); \ + return 1; \ + } \ + } STMT_END +#define INT(idx, var) STMT_BEGIN { \ + var = atoi(argv[(idx)]); \ + if (var <= 0) { \ + fprintf(stderr, "bad integer argument %d (%s)\n",idx,argv[(idx)]); \ + } \ + } STMT_END + +/** The first part of the HS ntor protocol. The client-side computes all + necessary key material and sends the appropriate message to the service. */ +static int +client1(int argc, char **argv) +{ + int retval; + + /* Inputs */ + curve25519_public_key_t intro_enc_pubkey; + ed25519_public_key_t intro_auth_pubkey; + curve25519_keypair_t client_ephemeral_enc_keypair; + uint8_t subcredential[DIGEST256_LEN]; + + /* Output */ + hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys; + + char buf[256]; + + N_ARGS(6); + BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN); + BASE16(3, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN); + BASE16(4, client_ephemeral_enc_keypair.seckey.secret_key, + CURVE25519_SECKEY_LEN); + BASE16(5, subcredential, DIGEST256_LEN); + + /* Generate keypair */ + curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey, + &client_ephemeral_enc_keypair.seckey); + + retval = hs_ntor_client_get_introduce1_keys(&intro_auth_pubkey, + &intro_enc_pubkey, + &client_ephemeral_enc_keypair, + subcredential, + &hs_ntor_intro_cell_keys); + if (retval < 0) { + goto done; + } + + /* Send ENC_KEY */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_intro_cell_keys.enc_key, + sizeof(hs_ntor_intro_cell_keys.enc_key)); + printf("%s\n", buf); + /* Send MAC_KEY */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_intro_cell_keys.mac_key, + sizeof(hs_ntor_intro_cell_keys.mac_key)); + printf("%s\n", buf); + + done: + return retval; +} + +/** The second part of the HS ntor protocol. The service-side computes all + necessary key material and sends the appropriate message to the client */ +static int +server1(int argc, char **argv) +{ + int retval; + + /* Inputs */ + curve25519_keypair_t intro_enc_keypair; + ed25519_public_key_t intro_auth_pubkey; + curve25519_public_key_t client_ephemeral_enc_pubkey; + uint8_t subcredential[DIGEST256_LEN]; + + /* Output */ + hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys; + hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys; + curve25519_keypair_t service_ephemeral_rend_keypair; + + char buf[256]; + + N_ARGS(6); + BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN); + BASE16(3, intro_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN); + BASE16(4, client_ephemeral_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN); + BASE16(5, subcredential, DIGEST256_LEN); + + /* Generate keypair */ + curve25519_public_key_generate(&intro_enc_keypair.pubkey, + &intro_enc_keypair.seckey); + curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0); + + /* Get INTRODUCE1 keys */ + retval = hs_ntor_service_get_introduce1_keys(&intro_auth_pubkey, + &intro_enc_keypair, + &client_ephemeral_enc_pubkey, + subcredential, + &hs_ntor_intro_cell_keys); + if (retval < 0) { + goto done; + } + + /* Get RENDEZVOUS1 keys */ + retval = hs_ntor_service_get_rendezvous1_keys(&intro_auth_pubkey, + &intro_enc_keypair, + &service_ephemeral_rend_keypair, + &client_ephemeral_enc_pubkey, + &hs_ntor_rend_cell_keys); + if (retval < 0) { + goto done; + } + + /* Send ENC_KEY */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_intro_cell_keys.enc_key, + sizeof(hs_ntor_intro_cell_keys.enc_key)); + printf("%s\n", buf); + /* Send MAC_KEY */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_intro_cell_keys.mac_key, + sizeof(hs_ntor_intro_cell_keys.mac_key)); + printf("%s\n", buf); + /* Send AUTH_MAC */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac, + sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac)); + printf("%s\n", buf); + /* Send NTOR_KEY_SEED */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_rend_cell_keys.ntor_key_seed, + sizeof(hs_ntor_rend_cell_keys.ntor_key_seed)); + printf("%s\n", buf); + /* Send service ephemeral pubkey (Y) */ + base16_encode(buf, sizeof(buf), + (const char*)service_ephemeral_rend_keypair.pubkey.public_key, + sizeof(service_ephemeral_rend_keypair.pubkey.public_key)); + printf("%s\n", buf); + + done: + return retval; +} + +/** The final step of the ntor protocol, the client computes and returns the + * rendezvous key material. */ +static int +client2(int argc, char **argv) +{ + int retval; + + /* Inputs */ + curve25519_public_key_t intro_enc_pubkey; + ed25519_public_key_t intro_auth_pubkey; + curve25519_keypair_t client_ephemeral_enc_keypair; + curve25519_public_key_t service_ephemeral_rend_pubkey; + uint8_t subcredential[DIGEST256_LEN]; + + /* Output */ + hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys; + + char buf[256]; + + N_ARGS(7); + BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN); + BASE16(3, client_ephemeral_enc_keypair.seckey.secret_key, + CURVE25519_SECKEY_LEN); + BASE16(4, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN); + BASE16(5, service_ephemeral_rend_pubkey.public_key, CURVE25519_PUBKEY_LEN); + BASE16(6, subcredential, DIGEST256_LEN); + + /* Generate keypair */ + curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey, + &client_ephemeral_enc_keypair.seckey); + + /* Get RENDEZVOUS1 keys */ + retval = hs_ntor_client_get_rendezvous1_keys(&intro_auth_pubkey, + &client_ephemeral_enc_keypair, + &intro_enc_pubkey, + &service_ephemeral_rend_pubkey, + &hs_ntor_rend_cell_keys); + if (retval < 0) { + goto done; + } + + /* Send AUTH_MAC */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac, + sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac)); + printf("%s\n", buf); + /* Send NTOR_KEY_SEED */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_rend_cell_keys.ntor_key_seed, + sizeof(hs_ntor_rend_cell_keys.ntor_key_seed)); + printf("%s\n", buf); + + done: + return 1; +} + +/** Perform a different part of the protocol depdning on the argv used. */ +int +main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "I need arguments. Read source for more info.\n"); + return 1; + } + + curve25519_init(); + if (!strcmp(argv[1], "client1")) { + return client1(argc, argv); + } else if (!strcmp(argv[1], "server1")) { + return server1(argc, argv); + } else if (!strcmp(argv[1], "client2")) { + return client2(argc, argv); + } else { + fprintf(stderr, "What's a %s?\n", argv[1]); + return 1; + } +} + diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c new file mode 100644 index 0000000000..fcfb3b992d --- /dev/null +++ b/src/test/test_hs_service.c @@ -0,0 +1,250 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_service.c + * \brief Test hidden service functionality. + */ + +#define HS_COMMON_PRIVATE +#define HS_SERVICE_PRIVATE +#define HS_INTROPOINT_PRIVATE + +#include "test.h" +#include "log_test_helpers.h" +#include "crypto.h" + +#include "hs/cell_establish_intro.h" +#include "hs_common.h" +#include "hs_service.h" +#include "hs_intropoint.h" + +#include "hs_ntor.h" + +/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we + * parse it from the receiver side. */ +static void +test_gen_establish_intro_cell(void *arg) +{ + (void) arg; + ssize_t retval; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + uint8_t buf[RELAY_PAYLOAD_SIZE]; + trn_cell_establish_intro_t *cell_out = NULL; + trn_cell_establish_intro_t *cell_in = NULL; + + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + { + cell_out = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(cell_out); + + retval = get_establish_intro_payload(buf, sizeof(buf), cell_out); + tt_int_op(retval, >=, 0); + } + + /* Parse it as the receiver */ + { + ssize_t parse_result = trn_cell_establish_intro_parse(&cell_in, + buf, sizeof(buf)); + tt_int_op(parse_result, >=, 0); + + retval = verify_establish_intro_cell(cell_in, + circuit_key_material, + sizeof(circuit_key_material)); + tt_int_op(retval, >=, 0); + } + + done: + trn_cell_establish_intro_free(cell_out); + trn_cell_establish_intro_free(cell_in); +} + +/* Mocked ed25519_sign_prefixed() function that always fails :) */ +static int +mock_ed25519_sign_prefixed(ed25519_signature_t *signature_out, + const uint8_t *msg, size_t msg_len, + const char *prefix_str, + const ed25519_keypair_t *keypair) { + (void) signature_out; + (void) msg; + (void) msg_len; + (void) prefix_str; + (void) keypair; + return -1; +} + +/** We simulate a failure to create an ESTABLISH_INTRO cell */ +static void +test_gen_establish_intro_cell_bad(void *arg) +{ + (void) arg; + trn_cell_establish_intro_t *cell = NULL; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed); + + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + + setup_full_capture_of_logs(LOG_WARN); + /* Easiest way to make that function fail is to mock the + ed25519_sign_prefixed() function and make it fail. */ + cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + expect_log_msg_containing("Unable to gen signature for " + "ESTABLISH_INTRO cell."); + teardown_capture_of_logs(); + tt_assert(!cell); + + done: + trn_cell_establish_intro_free(cell); + UNMOCK(ed25519_sign_prefixed); +} + +/** Test the HS ntor handshake. Simulate the sending of an encrypted INTRODUCE1 + * cell, and verify the proper derivation of decryption keys on the other end. + * Then simulate the sending of an authenticated RENDEZVOUS1 cell and verify + * the proper verification on the other end. */ +static void +test_hs_ntor(void *arg) +{ + int retval; + + uint8_t subcredential[DIGEST256_LEN]; + + ed25519_keypair_t service_intro_auth_keypair; + curve25519_keypair_t service_intro_enc_keypair; + curve25519_keypair_t service_ephemeral_rend_keypair; + + curve25519_keypair_t client_ephemeral_enc_keypair; + + hs_ntor_intro_cell_keys_t client_hs_ntor_intro_cell_keys; + hs_ntor_intro_cell_keys_t service_hs_ntor_intro_cell_keys; + + hs_ntor_rend_cell_keys_t service_hs_ntor_rend_cell_keys; + hs_ntor_rend_cell_keys_t client_hs_ntor_rend_cell_keys; + + (void) arg; + + /* Generate fake data for this unittest */ + { + /* Generate fake subcredential */ + memset(subcredential, 'Z', DIGEST256_LEN); + + /* service */ + curve25519_keypair_generate(&service_intro_enc_keypair, 0); + ed25519_keypair_generate(&service_intro_auth_keypair, 0); + curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0); + /* client */ + curve25519_keypair_generate(&client_ephemeral_enc_keypair, 0); + } + + /* Client: Simulate the sending of an encrypted INTRODUCE1 cell */ + retval = + hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey, + &service_intro_enc_keypair.pubkey, + &client_ephemeral_enc_keypair, + subcredential, + &client_hs_ntor_intro_cell_keys); + tt_int_op(retval, ==, 0); + + /* Service: Simulate the decryption of the received INTRODUCE1 */ + retval = + hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey, + &service_intro_enc_keypair, + &client_ephemeral_enc_keypair.pubkey, + subcredential, + &service_hs_ntor_intro_cell_keys); + tt_int_op(retval, ==, 0); + + /* Test that the INTRODUCE1 encryption/mac keys match! */ + tt_mem_op(client_hs_ntor_intro_cell_keys.enc_key, OP_EQ, + service_hs_ntor_intro_cell_keys.enc_key, + CIPHER256_KEY_LEN); + tt_mem_op(client_hs_ntor_intro_cell_keys.mac_key, OP_EQ, + service_hs_ntor_intro_cell_keys.mac_key, + DIGEST256_LEN); + + /* Service: Simulate creation of RENDEZVOUS1 key material. */ + retval = + hs_ntor_service_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey, + &service_intro_enc_keypair, + &service_ephemeral_rend_keypair, + &client_ephemeral_enc_keypair.pubkey, + &service_hs_ntor_rend_cell_keys); + tt_int_op(retval, ==, 0); + + /* Client: Simulate the verification of a received RENDEZVOUS1 cell */ + retval = + hs_ntor_client_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey, + &client_ephemeral_enc_keypair, + &service_intro_enc_keypair.pubkey, + &service_ephemeral_rend_keypair.pubkey, + &client_hs_ntor_rend_cell_keys); + tt_int_op(retval, ==, 0); + + /* Test that the RENDEZVOUS1 key material match! */ + tt_mem_op(client_hs_ntor_rend_cell_keys.rend_cell_auth_mac, OP_EQ, + service_hs_ntor_rend_cell_keys.rend_cell_auth_mac, + DIGEST256_LEN); + tt_mem_op(client_hs_ntor_rend_cell_keys.ntor_key_seed, OP_EQ, + service_hs_ntor_rend_cell_keys.ntor_key_seed, + DIGEST256_LEN); + + done: + ; +} + +/** Test that our HS time period calculation functions work properly */ +static void +test_time_period(void *arg) +{ + (void) arg; + uint64_t tn; + int retval; + time_t fake_time; + + /* Let's do the example in prop224 section [TIME-PERIODS] */ + retval = parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC", + &fake_time); + tt_int_op(retval, ==, 0); + + /* Check that the time period number is right */ + tn = get_time_period_num(fake_time); + tt_u64_op(tn, ==, 16903); + + /* Increase current time to 11:59:59 UTC and check that the time period + number is still the same */ + fake_time += 3599; + tn = get_time_period_num(fake_time); + tt_u64_op(tn, ==, 16903); + + /* Now take time to 12:00:00 UTC and check that the time period rotated */ + fake_time += 1; + tn = get_time_period_num(fake_time); + tt_u64_op(tn, ==, 16904); + + /* Now also check our hs_get_next_time_period_num() function */ + tn = hs_get_next_time_period_num(fake_time); + tt_u64_op(tn, ==, 16905); + + done: + ; +} + +struct testcase_t hs_service_tests[] = { + { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK, + NULL, NULL }, + { "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK, + NULL, NULL }, + { "hs_ntor", test_hs_ntor, TT_FORK, + NULL, NULL }, + { "time_period", test_time_period, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c index 810b03c93d..cfb8d83b1d 100644 --- a/src/test/test_introduce.c +++ b/src/test/test_introduce.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Tor Project, Inc. */ +/* Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_keypin.c b/src/test/test_keypin.c index 95657349c6..d2ec8e9ca7 100644 --- a/src/test/test_keypin.c +++ b/src/test/test_keypin.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index ddf66f4d34..c5508b0f04 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -6,13 +6,20 @@ #define CHANNELTLS_PRIVATE #define CONNECTION_PRIVATE #define TOR_CHANNEL_INTERNAL_ +#define TORTLS_PRIVATE + +#include "compat.h" + #include "or.h" #include "config.h" #include "connection.h" #include "connection_or.h" #include "channeltls.h" #include "link_handshake.h" +#include "router.h" +#include "routerkeys.h" #include "scheduler.h" +#include "torcert.h" #include "test.h" #include "log_test_helpers.h" @@ -37,6 +44,16 @@ mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert) (void) cert; // XXXX look at this. return 1; } +static tor_tls_t *mock_peer_cert_expect_tortls = NULL; +static tor_x509_cert_t *mock_peer_cert = NULL; +static tor_x509_cert_t * +mock_get_peer_cert(tor_tls_t *tls) +{ + if (mock_peer_cert_expect_tortls && + mock_peer_cert_expect_tortls != tls) + return NULL; + return tor_x509_cert_dup(mock_peer_cert); +} static int mock_send_netinfo_called = 0; static int @@ -57,14 +74,29 @@ mock_close_for_err(or_connection_t *orconn, int flush) } static int mock_send_authenticate_called = 0; +static int mock_send_authenticate_called_with_type = 0; static int mock_send_authenticate(or_connection_t *conn, int type) { (void) conn; - (void) type; + mock_send_authenticate_called_with_type = type; ++mock_send_authenticate_called;// XXX check_this return 0; } +static int +mock_export_key_material(tor_tls_t *tls, uint8_t *secrets_out, + const uint8_t *context, + size_t context_len, + const char *label) +{ + (void) tls; + (void)secrets_out; + (void)context; + (void)context_len; + (void)label; + memcpy(secrets_out, "int getRandomNumber(){return 4;}", 32); + return 0; +} static tor_x509_cert_t *mock_own_cert = NULL; static tor_x509_cert_t * @@ -78,20 +110,23 @@ mock_get_own_cert(tor_tls_t *tls) static void test_link_handshake_certs_ok(void *arg) { - (void) arg; - or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); or_connection_t *c2 = or_connection_new(CONN_TYPE_OR, AF_INET); var_cell_t *cell1 = NULL, *cell2 = NULL; certs_cell_t *cc1 = NULL, *cc2 = NULL; channel_tls_t *chan1 = NULL, *chan2 = NULL; crypto_pk_t *key1 = NULL, *key2 = NULL; + const int with_ed = !strcmp((const char *)arg, "Ed25519"); + + tor_addr_from_ipv4h(&c1->base_.addr, 0x7f000001); + tor_addr_from_ipv4h(&c2->base_.addr, 0x7f000001); scheduler_init(); MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); MOCK(connection_or_send_netinfo, mock_send_netinfo); + MOCK(tor_tls_get_peer_cert, mock_get_peer_cert); MOCK(tor_tls_get_own_cert, mock_get_own_cert); key1 = pk_generate(2); @@ -103,6 +138,15 @@ test_link_handshake_certs_ok(void *arg) tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, key1, key2, 86400), ==, 0); + if (with_ed) { + /* If we're making a CERTS cell for an ed handshake, let's make sure we + * have some Ed25519 certificates and keys. */ + init_mock_ed_keys(key2); + } else { + certs_cell_ed25519_disabled_for_testing = 1; + } + + /* c1 has started_here == 1 */ { const tor_x509_cert_t *link_cert = NULL; tt_assert(!tor_tls_get_my_certs(1, &link_cert, NULL)); @@ -113,6 +157,7 @@ test_link_handshake_certs_ok(void *arg) c1->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0); + /* c2 has started_here == 0 */ c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; c2->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c2, 0), ==, 0); @@ -136,8 +181,13 @@ test_link_handshake_certs_ok(void *arg) tt_int_op(cell2->payload_len, ==, certs_cell_parse(&cc2, cell2->payload, cell2->payload_len)); - tt_int_op(2, ==, cc1->n_certs); - tt_int_op(2, ==, cc2->n_certs); + if (with_ed) { + tt_int_op(5, ==, cc1->n_certs); + tt_int_op(5, ==, cc2->n_certs); + } else { + tt_int_op(2, ==, cc1->n_certs); + tt_int_op(2, ==, cc2->n_certs); + } tt_int_op(certs_cell_get_certs(cc1, 0)->cert_type, ==, CERTTYPE_RSA1024_ID_AUTH); @@ -149,6 +199,22 @@ test_link_handshake_certs_ok(void *arg) tt_int_op(certs_cell_get_certs(cc2, 1)->cert_type, ==, CERTTYPE_RSA1024_ID_ID); + if (with_ed) { + tt_int_op(certs_cell_get_certs(cc1, 2)->cert_type, ==, + CERTTYPE_ED_ID_SIGN); + tt_int_op(certs_cell_get_certs(cc1, 3)->cert_type, ==, + CERTTYPE_ED_SIGN_AUTH); + tt_int_op(certs_cell_get_certs(cc1, 4)->cert_type, ==, + CERTTYPE_RSA1024_ID_EDID); + + tt_int_op(certs_cell_get_certs(cc2, 2)->cert_type, ==, + CERTTYPE_ED_ID_SIGN); + tt_int_op(certs_cell_get_certs(cc2, 3)->cert_type, ==, + CERTTYPE_ED_SIGN_LINK); + tt_int_op(certs_cell_get_certs(cc2, 4)->cert_type, ==, + CERTTYPE_RSA1024_ID_EDID); + } + chan1 = tor_malloc_zero(sizeof(*chan1)); channel_tls_common_init(chan1); c1->chan = chan1; @@ -159,13 +225,39 @@ test_link_handshake_certs_ok(void *arg) c1->base_.conn_array_index = -1; crypto_pk_get_digest(key2, c1->identity_digest); + if (with_ed) { + const tor_x509_cert_t *linkc, *idc; + tor_tls_get_my_certs(1, &linkc, &idc); + mock_peer_cert_expect_tortls = c1->tls; /* We should see this tls... */ + mock_peer_cert = tor_x509_cert_dup(linkc); /* and when we do, the peer's + * cert is this... */ + } channel_tls_process_certs_cell(cell2, chan1); + mock_peer_cert_expect_tortls = NULL; + tor_x509_cert_free(mock_peer_cert); + mock_peer_cert = NULL; + + tor_assert(c1->handshake_state->authenticated); tt_assert(c1->handshake_state->received_certs_cell); - tt_assert(c1->handshake_state->auth_cert == NULL); - tt_assert(c1->handshake_state->id_cert); + tt_assert(c1->handshake_state->certs->auth_cert == NULL); + tt_assert(c1->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(c1->handshake_state->certs->id_cert); + if (with_ed) { + tt_assert(c1->handshake_state->certs->ed_sign_link); + tt_assert(c1->handshake_state->certs->ed_rsa_crosscert); + tt_assert(c1->handshake_state->certs->ed_id_sign); + tt_assert(c1->handshake_state->authenticated_rsa); + tt_assert(c1->handshake_state->authenticated_ed25519); + } else { + tt_assert(c1->handshake_state->certs->ed_sign_link == NULL); + tt_assert(c1->handshake_state->certs->ed_rsa_crosscert == NULL); + tt_assert(c1->handshake_state->certs->ed_id_sign == NULL); + tt_assert(c1->handshake_state->authenticated_rsa); + tt_assert(! c1->handshake_state->authenticated_ed25519); + } tt_assert(! tor_mem_is_zero( - (char*)c1->handshake_state->authenticated_peer_id, 20)); + (char*)c1->handshake_state->authenticated_rsa_peer_id, 20)); chan2 = tor_malloc_zero(sizeof(*chan2)); channel_tls_common_init(chan2); @@ -180,18 +272,34 @@ test_link_handshake_certs_ok(void *arg) channel_tls_process_certs_cell(cell1, chan2); tt_assert(c2->handshake_state->received_certs_cell); - tt_assert(c2->handshake_state->auth_cert); - tt_assert(c2->handshake_state->id_cert); + if (with_ed) { + tt_assert(c2->handshake_state->certs->ed_sign_auth); + tt_assert(c2->handshake_state->certs->ed_rsa_crosscert); + tt_assert(c2->handshake_state->certs->ed_id_sign); + } else { + tt_assert(c2->handshake_state->certs->auth_cert); + tt_assert(c2->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(c2->handshake_state->certs->ed_rsa_crosscert == NULL); + tt_assert(c2->handshake_state->certs->ed_id_sign == NULL); + } + tt_assert(c2->handshake_state->certs->id_cert); tt_assert(tor_mem_is_zero( - (char*)c2->handshake_state->authenticated_peer_id, 20)); + (char*)c2->handshake_state->authenticated_rsa_peer_id, 20)); + /* no authentication has happened yet, since we haen't gotten an AUTH cell. + */ + tt_assert(! c2->handshake_state->authenticated); + tt_assert(! c2->handshake_state->authenticated_rsa); + tt_assert(! c2->handshake_state->authenticated_ed25519); done: UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_write_var_cell_to_buf); UNMOCK(connection_or_send_netinfo); + UNMOCK(tor_tls_get_peer_cert); UNMOCK(tor_tls_get_own_cert); tor_x509_cert_free(mock_own_cert); - mock_own_cert = NULL; + tor_x509_cert_free(mock_peer_cert); + mock_own_cert = mock_peer_cert = NULL; memset(c1->identity_digest, 0, sizeof(c1->identity_digest)); memset(c2->identity_digest, 0, sizeof(c2->identity_digest)); connection_free_(TO_CONN(c1)); @@ -211,6 +319,8 @@ test_link_handshake_certs_ok(void *arg) } typedef struct certs_data_s { + int is_ed; + int is_link_cert; or_connection_t *c; channel_tls_t *chan; certs_cell_t *ccell; @@ -226,11 +336,13 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj) UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_send_netinfo); UNMOCK(connection_or_close_for_error); + UNMOCK(tor_tls_get_peer_cert); + UNMOCK(tor_tls_get_own_cert); if (d) { tor_free(d->cell); certs_cell_free(d->ccell); - connection_or_remove_from_identity_map(d->c); + connection_or_clear_identity(d->c); connection_free_(TO_CONN(d->c)); circuitmux_free(d->chan->base_.cmux); tor_free(d->chan); @@ -238,6 +350,7 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj) crypto_pk_free(d->key2); tor_free(d); } + routerkeys_free_all(); return 1; } @@ -249,11 +362,18 @@ recv_certs_setup(const struct testcase_t *test) certs_cell_cert_t *ccc1 = NULL; certs_cell_cert_t *ccc2 = NULL; ssize_t n; + int is_ed = d->is_ed = !strcmpstart(test->setup_data, "Ed25519"); + int is_rsa = !strcmpstart(test->setup_data, "RSA"); + int is_link = d->is_link_cert = !strcmpend(test->setup_data, "-Link"); + int is_auth = !strcmpend(test->setup_data, "-Auth"); + tor_assert(is_ed != is_rsa); + tor_assert(is_link != is_auth); d->c = or_connection_new(CONN_TYPE_OR, AF_INET); d->chan = tor_malloc_zero(sizeof(*d->chan)); d->c->chan = d->chan; d->c->base_.address = tor_strdup("HaveAnAddress"); + tor_addr_from_ipv4h(&d->c->base_.addr, 0x801f0127); d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; d->chan->conn = d->c; tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0); @@ -264,19 +384,25 @@ recv_certs_setup(const struct testcase_t *test) tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, d->key1, d->key2, 86400), ==, 0); + if (is_ed) { + init_mock_ed_keys(d->key2); + } else { + routerkeys_free_all(); + } + d->ccell = certs_cell_new(); ccc1 = certs_cell_cert_new(); certs_cell_add_certs(d->ccell, ccc1); ccc2 = certs_cell_cert_new(); certs_cell_add_certs(d->ccell, ccc2); d->ccell->n_certs = 2; - ccc1->cert_type = 1; + ccc1->cert_type = is_link ? 1 : 3; ccc2->cert_type = 2; const tor_x509_cert_t *a,*b; const uint8_t *enca, *encb; size_t lena, lenb; - tor_tls_get_my_certs(1, &a, &b); + tor_tls_get_my_certs(is_link ? 1 : 0, &a, &b); tor_x509_cert_get_der(a, &enca, &lena); tor_x509_cert_get_der(b, &encb, &lenb); certs_cell_cert_setlen_body(ccc1, lena); @@ -287,6 +413,41 @@ recv_certs_setup(const struct testcase_t *test) memcpy(certs_cell_cert_getarray_body(ccc1), enca, lena); memcpy(certs_cell_cert_getarray_body(ccc2), encb, lenb); + if (is_ed) { + certs_cell_cert_t *ccc3 = NULL; /* Id->Sign */ + certs_cell_cert_t *ccc4 = NULL; /* Sign->Link or Sign->Auth. */ + certs_cell_cert_t *ccc5 = NULL; /* RSAId->Ed Id. */ + const tor_cert_t *id_sign = get_master_signing_key_cert(); + const tor_cert_t *secondary = + is_link ? get_current_link_cert_cert() : get_current_auth_key_cert(); + const uint8_t *cc = NULL; + size_t cc_sz; + get_master_rsa_crosscert(&cc, &cc_sz); + + ccc3 = certs_cell_cert_new(); + ccc4 = certs_cell_cert_new(); + ccc5 = certs_cell_cert_new(); + certs_cell_add_certs(d->ccell, ccc3); + certs_cell_add_certs(d->ccell, ccc4); + certs_cell_add_certs(d->ccell, ccc5); + ccc3->cert_len = id_sign->encoded_len; + ccc4->cert_len = secondary->encoded_len; + ccc5->cert_len = cc_sz; + certs_cell_cert_setlen_body(ccc3, ccc3->cert_len); + certs_cell_cert_setlen_body(ccc4, ccc4->cert_len); + certs_cell_cert_setlen_body(ccc5, ccc5->cert_len); + memcpy(certs_cell_cert_getarray_body(ccc3), id_sign->encoded, + ccc3->cert_len); + memcpy(certs_cell_cert_getarray_body(ccc4), secondary->encoded, + ccc4->cert_len); + memcpy(certs_cell_cert_getarray_body(ccc5), cc, ccc5->cert_len); + ccc3->cert_type = 4; + ccc4->cert_type = is_link ? 5 : 6; + ccc5->cert_type = 7; + + d->ccell->n_certs = 5; + } + d->cell = var_cell_new(4096); d->cell->command = CELL_CERTS; @@ -297,6 +458,12 @@ recv_certs_setup(const struct testcase_t *test) MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); MOCK(connection_or_send_netinfo, mock_send_netinfo); MOCK(connection_or_close_for_error, mock_close_for_err); + MOCK(tor_tls_get_peer_cert, mock_get_peer_cert); + + if (is_link) { + /* Say that this is the peer's certificate */ + mock_peer_cert = tor_x509_cert_dup(a); + } tt_int_op(0, ==, d->c->handshake_state->received_certs_cell); tt_int_op(0, ==, mock_send_authenticate_called); @@ -320,9 +487,24 @@ test_link_handshake_recv_certs_ok(void *arg) channel_tls_process_certs_cell(d->cell, d->chan); tt_int_op(0, ==, mock_close_called); tt_int_op(d->c->handshake_state->authenticated, ==, 1); + tt_int_op(d->c->handshake_state->authenticated_rsa, ==, 1); tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1); - tt_assert(d->c->handshake_state->id_cert != NULL); - tt_assert(d->c->handshake_state->auth_cert == NULL); + tt_assert(d->c->handshake_state->certs->id_cert != NULL); + tt_assert(d->c->handshake_state->certs->auth_cert == NULL); + + if (d->is_ed) { + tt_assert(d->c->handshake_state->certs->ed_id_sign != NULL); + tt_assert(d->c->handshake_state->certs->ed_sign_link != NULL); + tt_assert(d->c->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(d->c->handshake_state->certs->ed_rsa_crosscert != NULL); + tt_int_op(d->c->handshake_state->authenticated_ed25519, ==, 1); + } else { + tt_assert(d->c->handshake_state->certs->ed_id_sign == NULL); + tt_assert(d->c->handshake_state->certs->ed_sign_link == NULL); + tt_assert(d->c->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(d->c->handshake_state->certs->ed_rsa_crosscert == NULL); + tt_int_op(d->c->handshake_state->authenticated_ed25519, ==, 0); + } done: ; @@ -333,17 +515,20 @@ test_link_handshake_recv_certs_ok_server(void *arg) { certs_data_t *d = arg; d->c->handshake_state->started_here = 0; - certs_cell_get_certs(d->ccell, 0)->cert_type = 3; - certs_cell_get_certs(d->ccell, 1)->cert_type = 2; - ssize_t n = certs_cell_encode(d->cell->payload, 2048, d->ccell); - tt_int_op(n, >, 0); - d->cell->payload_len = n; + d->c->handshake_state->certs->started_here = 0; channel_tls_process_certs_cell(d->cell, d->chan); tt_int_op(0, ==, mock_close_called); tt_int_op(d->c->handshake_state->authenticated, ==, 0); tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1); - tt_assert(d->c->handshake_state->id_cert != NULL); - tt_assert(d->c->handshake_state->auth_cert != NULL); + tt_assert(d->c->handshake_state->certs->id_cert != NULL); + tt_assert(d->c->handshake_state->certs->link_cert == NULL); + if (d->is_ed) { + tt_assert(d->c->handshake_state->certs->ed_sign_auth != NULL); + tt_assert(d->c->handshake_state->certs->auth_cert == NULL); + } else { + tt_assert(d->c->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(d->c->handshake_state->certs->auth_cert != NULL); + } done: ; @@ -361,6 +546,8 @@ test_link_handshake_recv_certs_ok_server(void *arg) tt_int_op(1, ==, mock_close_called); \ tt_int_op(0, ==, mock_send_authenticate_called); \ tt_int_op(0, ==, mock_send_netinfo_called); \ + tt_int_op(0, ==, d->c->handshake_state->authenticated_rsa); \ + tt_int_op(0, ==, d->c->handshake_state->authenticated_ed25519); \ if (require_failure_message) { \ expect_log_msg_containing(require_failure_message); \ } \ @@ -401,12 +588,41 @@ CERTS_FAIL(truncated_3, d->cell->payload_len = 7; memcpy(d->cell->payload, "\x01\x01\x00\x05""abc", 7); }) +CERTS_FAIL(truncated_4, /* ed25519 */ + { + require_failure_message = "It couldn't be parsed"; + d->cell->payload_len -= 10; + }) +CERTS_FAIL(truncated_5, /* ed25519 */ + { + require_failure_message = "It couldn't be parsed"; + d->cell->payload_len -= 100; + }) + #define REENCODE() do { \ + const char *msg = certs_cell_check(d->ccell); \ + if (msg) puts(msg); \ ssize_t n = certs_cell_encode(d->cell->payload, 4096, d->ccell); \ tt_int_op(n, >, 0); \ d->cell->payload_len = n; \ } while (0) +CERTS_FAIL(truncated_6, /* ed25519 */ + { + /* truncate the link certificate */ + require_failure_message = "undecodable Ed certificate"; + certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 3), 7); + certs_cell_get_certs(d->ccell, 3)->cert_len = 7; + REENCODE(); + }) +CERTS_FAIL(truncated_7, /* ed25519 */ + { + /* truncate the crosscert */ + require_failure_message = "Unparseable or overlong crosscert"; + certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 4), 7); + certs_cell_get_certs(d->ccell, 4)->cert_len = 7; + REENCODE(); + }) CERTS_FAIL(not_x509, { require_failure_message = "Received undecodable certificate"; @@ -435,6 +651,201 @@ CERTS_FAIL(both_auth, certs_cell_get_certs(d->ccell, 1)->cert_type = 3; REENCODE(); }) +CERTS_FAIL(duplicate_id, /* ed25519 */ + { + require_failure_message = "Duplicate Ed25519 certificate"; + certs_cell_get_certs(d->ccell, 2)->cert_type = 4; + certs_cell_get_certs(d->ccell, 3)->cert_type = 4; + REENCODE(); + }) +CERTS_FAIL(duplicate_link, /* ed25519 */ + { + require_failure_message = "Duplicate Ed25519 certificate"; + certs_cell_get_certs(d->ccell, 2)->cert_type = 5; + certs_cell_get_certs(d->ccell, 3)->cert_type = 5; + REENCODE(); + }) +CERTS_FAIL(duplicate_crosscert, /* ed25519 */ + { + require_failure_message = "Duplicate RSA->Ed25519 crosscert"; + certs_cell_get_certs(d->ccell, 2)->cert_type = 7; + certs_cell_get_certs(d->ccell, 3)->cert_type = 7; + REENCODE(); + }) +static void +test_link_handshake_recv_certs_missing_id(void *arg) /* ed25519 */ +{ + certs_data_t *d = arg; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_set_certs(d->ccell, 2, certs_cell_get_certs(d->ccell, 4)); + certs_cell_set0_certs(d->ccell, 4, NULL); /* prevent free */ + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + + /* This handshake succeeds, but since we have no ID cert, we will + * just do the RSA handshake. */ + channel_tls_process_certs_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(0, ==, d->c->handshake_state->authenticated_ed25519); + tt_int_op(1, ==, d->c->handshake_state->authenticated_rsa); + done: + ; +} +CERTS_FAIL(missing_signing_key, /* ed25519 */ + { + require_failure_message = "No Ed25519 signing key"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 2); + tt_int_op(cert->cert_type, ==, CERTTYPE_ED_ID_SIGN); + /* replace this with a valid master->signing cert, but with no + * signing key. */ + const ed25519_keypair_t *mk = get_master_identity_keypair(); + const ed25519_keypair_t *sk = get_master_signing_keypair(); + tor_cert_t *bad_cert = tor_cert_create(mk, CERT_TYPE_ID_SIGNING, + &sk->pubkey, time(NULL), 86400, + 0 /* don't include signer */); + certs_cell_cert_setlen_body(cert, bad_cert->encoded_len); + memcpy(certs_cell_cert_getarray_body(cert), + bad_cert->encoded, bad_cert->encoded_len); + cert->cert_len = bad_cert->encoded_len; + tor_cert_free(bad_cert); + REENCODE(); + }) +CERTS_FAIL(missing_link, /* ed25519 */ + { + require_failure_message = "No Ed25519 link key"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_set_certs(d->ccell, 3, certs_cell_get_certs(d->ccell, 4)); + certs_cell_set0_certs(d->ccell, 4, NULL); /* prevent free */ + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + }) +CERTS_FAIL(missing_auth, /* ed25519 */ + { + d->c->handshake_state->started_here = 0; + d->c->handshake_state->certs->started_here = 0; + require_failure_message = "No Ed25519 link authentication key"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_set_certs(d->ccell, 3, certs_cell_get_certs(d->ccell, 4)); + certs_cell_set0_certs(d->ccell, 4, NULL); /* prevent free */ + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + }) +CERTS_FAIL(missing_crosscert, /* ed25519 */ + { + require_failure_message = "Missing RSA->Ed25519 crosscert"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + }) +CERTS_FAIL(missing_rsa_id, /* ed25519 */ + { + require_failure_message = "Missing legacy RSA ID cert"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_set_certs(d->ccell, 1, certs_cell_get_certs(d->ccell, 4)); + certs_cell_set0_certs(d->ccell, 4, NULL); /* prevent free */ + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + }) +CERTS_FAIL(link_mismatch, /* ed25519 */ + { + require_failure_message = "Link certificate does not match " + "TLS certificate"; + const tor_x509_cert_t *idc; + tor_tls_get_my_certs(1, NULL, &idc); + tor_x509_cert_free(mock_peer_cert); + /* Pretend that the peer cert was something else. */ + mock_peer_cert = tor_x509_cert_dup(idc); + /* No reencode needed. */ + }) +CERTS_FAIL(bad_ed_sig, /* ed25519 */ + { + require_failure_message = "At least one Ed25519 certificate was " + "badly signed"; + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 3); + uint8_t *body = certs_cell_cert_getarray_body(cert); + ssize_t body_len = certs_cell_cert_getlen_body(cert); + /* Frob a byte in the signature */ + body[body_len - 13] ^= 7; + REENCODE(); + }) +CERTS_FAIL(bad_crosscert, /*ed25519*/ + { + require_failure_message = "Invalid RSA->Ed25519 crosscert"; + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 4); + uint8_t *body = certs_cell_cert_getarray_body(cert); + ssize_t body_len = certs_cell_cert_getlen_body(cert); + /* Frob a byte in the signature */ + body[body_len - 13] ^= 7; + REENCODE(); + }) +CERTS_FAIL(bad_rsa_id_cert, /*ed25519*/ + { + require_failure_message = "legacy RSA ID certificate was not valid"; + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 1); + uint8_t *body = certs_cell_cert_getarray_body(cert); + ssize_t body_len = certs_cell_cert_getlen_body(cert); + /* Frob a byte in the signature */ + body[body_len - 13] ^= 7; + REENCODE(); + }) +CERTS_FAIL(expired_rsa_id, /* both */ + { + require_failure_message = "Certificate already expired"; + /* we're going to replace the identity cert with an expired one. */ + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 1); + const tor_x509_cert_t *idc; + tor_tls_get_my_certs(1, NULL, &idc); + tor_x509_cert_t *newc; + time_t new_end = time(NULL) - 86400 * 10; + newc = tor_x509_cert_replace_expiration(idc, new_end, d->key2); + certs_cell_cert_setlen_body(cert, newc->encoded_len); + memcpy(certs_cell_cert_getarray_body(cert), + newc->encoded, newc->encoded_len); + REENCODE(); + tor_x509_cert_free(newc); + }) +CERTS_FAIL(expired_ed_id, /* ed25519 */ + { + /* we're going to replace the Ed Id->sign cert with an expired one. */ + require_failure_message = "At least one certificate expired"; + /* We don't need to re-sign, since we check for expiration first. */ + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 2); + uint8_t *body = certs_cell_cert_getarray_body(cert); + /* The expiration field is bytes [2..5]. It is in HOURS since the + * epoch. */ + set_uint32(body+2, htonl(24)); /* Back to jan 2, 1970. */ + REENCODE(); + }) +CERTS_FAIL(expired_ed_link, /* ed25519 */ + { + /* we're going to replace the Ed Sign->link cert with an expired one. */ + require_failure_message = "At least one certificate expired"; + /* We don't need to re-sign, since we check for expiration first. */ + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 3); + uint8_t *body = certs_cell_cert_getarray_body(cert); + /* The expiration field is bytes [2..5]. It is in HOURS since the + * epoch. */ + set_uint32(body+2, htonl(24)); /* Back to jan 2, 1970. */ + REENCODE(); + }) +CERTS_FAIL(expired_crosscert, /* ed25519 */ + { + /* we're going to replace the Ed Sign->link cert with an expired one. */ + require_failure_message = "Crosscert is expired"; + /* We don't need to re-sign, since we check for expiration first. */ + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 4); + uint8_t *body = certs_cell_cert_getarray_body(cert); + /* The expiration field is bytes [32..35]. once again, HOURS. */ + set_uint32(body+32, htonl(24)); /* Back to jan 2, 1970. */ + REENCODE(); + }) + CERTS_FAIL(wrong_labels_1, { require_failure_message = "The link certificate was not valid"; @@ -459,21 +870,26 @@ CERTS_FAIL(wrong_labels_2, }) CERTS_FAIL(wrong_labels_3, { - require_failure_message = "The certs we wanted were missing"; + require_failure_message = + "The certs we wanted (ID, Link) were missing"; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 3; REENCODE(); }) CERTS_FAIL(server_missing_certs, { - require_failure_message = "The certs we wanted were missing"; + require_failure_message = + "The certs we wanted (ID, Auth) were missing"; d->c->handshake_state->started_here = 0; + d->c->handshake_state->certs->started_here = 0; + }) CERTS_FAIL(server_wrong_labels_1, { require_failure_message = "The authentication certificate was not valid"; d->c->handshake_state->started_here = 0; + d->c->handshake_state->certs->started_here = 0; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 3; REENCODE(); @@ -487,6 +903,11 @@ test_link_handshake_send_authchallenge(void *arg) or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); var_cell_t *cell1=NULL, *cell2=NULL; + crypto_pk_t *rsa0 = pk_generate(0), *rsa1 = pk_generate(1); + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + rsa0, rsa1, 86400), ==, 0); + init_mock_ed_keys(rsa0); + MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); tt_int_op(connection_init_or_handshake_state(c1, 0), ==, 0); @@ -496,15 +917,15 @@ test_link_handshake_send_authchallenge(void *arg) cell1 = mock_got_var_cell; tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1)); cell2 = mock_got_var_cell; - tt_int_op(36, ==, cell1->payload_len); - tt_int_op(36, ==, cell2->payload_len); + tt_int_op(38, ==, cell1->payload_len); + tt_int_op(38, ==, cell2->payload_len); tt_int_op(0, ==, cell1->circ_id); tt_int_op(0, ==, cell2->circ_id); tt_int_op(CELL_AUTH_CHALLENGE, ==, cell1->command); tt_int_op(CELL_AUTH_CHALLENGE, ==, cell2->command); - tt_mem_op("\x00\x01\x00\x01", ==, cell1->payload + 32, 4); - tt_mem_op("\x00\x01\x00\x01", ==, cell2->payload + 32, 4); + tt_mem_op("\x00\x02\x00\x01\x00\x03", ==, cell1->payload + 32, 6); + tt_mem_op("\x00\x02\x00\x01\x00\x03", ==, cell2->payload + 32, 6); tt_mem_op(cell1->payload, !=, cell2->payload, 32); done: @@ -512,6 +933,8 @@ test_link_handshake_send_authchallenge(void *arg) connection_free_(TO_CONN(c1)); tor_free(cell1); tor_free(cell2); + crypto_pk_free(rsa0); + crypto_pk_free(rsa1); } typedef struct authchallenge_data_s { @@ -556,9 +979,9 @@ recv_authchallenge_setup(const struct testcase_t *test) d->c->handshake_state->received_certs_cell = 1; d->cell = var_cell_new(128); d->cell->payload_len = 38; - d->cell->payload[33] = 2; - d->cell->payload[35] = 7; - d->cell->payload[37] = 1; + d->cell->payload[33] = 2; /* 2 methods */ + d->cell->payload[35] = 7; /* This one isn't real */ + d->cell->payload[37] = 1; /* This is the old RSA one. */ d->cell->command = CELL_AUTH_CHALLENGE; get_options_mutable()->ORPort_set = 1; @@ -566,7 +989,6 @@ recv_authchallenge_setup(const struct testcase_t *test) MOCK(connection_or_close_for_error, mock_close_for_err); MOCK(connection_or_send_netinfo, mock_send_netinfo); MOCK(connection_or_send_authenticate_cell, mock_send_authenticate); - tt_int_op(0, ==, d->c->handshake_state->received_auth_challenge); tt_int_op(0, ==, mock_send_authenticate_called); tt_int_op(0, ==, mock_send_netinfo_called); @@ -592,6 +1014,26 @@ test_link_handshake_recv_authchallenge_ok(void *arg) tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); tt_int_op(1, ==, mock_send_authenticate_called); tt_int_op(1, ==, mock_send_netinfo_called); + tt_int_op(1, ==, mock_send_authenticate_called_with_type); /* RSA */ + done: + ; +} + +static void +test_link_handshake_recv_authchallenge_ok_ed25519(void *arg) +{ + authchallenge_data_t *d = arg; + + /* Add the ed25519 authentication mechanism here. */ + d->cell->payload[33] = 3; /* 3 types are supported now. */ + d->cell->payload[39] = 3; + d->cell->payload_len += 2; + channel_tls_process_auth_challenge_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(1, ==, mock_send_authenticate_called); + tt_int_op(1, ==, mock_send_netinfo_called); + tt_int_op(3, ==, mock_send_authenticate_called_with_type); /* Ed25519 */ done: ; } @@ -655,7 +1097,8 @@ AUTHCHALLENGE_FAIL(badproto, AUTHCHALLENGE_FAIL(as_server, require_failure_message = "We didn't originate this " "connection"; - d->c->handshake_state->started_here = 0;) + d->c->handshake_state->started_here = 0; + d->c->handshake_state->certs->started_here = 0;) AUTHCHALLENGE_FAIL(duplicate, require_failure_message = "We already received one"; d->c->handshake_state->received_auth_challenge = 1) @@ -673,15 +1116,6 @@ AUTHCHALLENGE_FAIL(nonzero_circid, require_failure_message = "It had a nonzero circuit ID"; d->cell->circ_id = 1337) -static tor_x509_cert_t *mock_peer_cert = NULL; - -static tor_x509_cert_t * -mock_get_peer_cert(tor_tls_t *tls) -{ - (void)tls; - return tor_x509_cert_dup(mock_peer_cert); -} - static int mock_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out) { @@ -701,6 +1135,7 @@ mock_set_circid_type(channel_t *chan, } typedef struct authenticate_data_s { + int is_ed; or_connection_t *c1, *c2; channel_tls_t *chan2; var_cell_t *cell; @@ -717,11 +1152,12 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) UNMOCK(tor_tls_get_tlssecrets); UNMOCK(connection_or_close_for_error); UNMOCK(channel_set_circid_type); + UNMOCK(tor_tls_export_key_material); authenticate_data_t *d = arg; if (d) { tor_free(d->cell); - connection_or_remove_from_identity_map(d->c1); - connection_or_remove_from_identity_map(d->c2); + connection_or_clear_identity(d->c1); + connection_or_clear_identity(d->c2); connection_free_(TO_CONN(d->c1)); connection_free_(TO_CONN(d->c2)); circuitmux_free(d->chan2->base_.cmux); @@ -742,6 +1178,7 @@ static void * authenticate_data_setup(const struct testcase_t *test) { authenticate_data_t *d = tor_malloc_zero(sizeof(*d)); + int is_ed = d->is_ed = (test->setup_data == (void*)3); scheduler_init(); @@ -751,6 +1188,7 @@ authenticate_data_setup(const struct testcase_t *test) MOCK(tor_tls_get_tlssecrets, mock_get_tlssecrets); MOCK(connection_or_close_for_error, mock_close_for_err); MOCK(channel_set_circid_type, mock_set_circid_type); + MOCK(tor_tls_export_key_material, mock_export_key_material); d->c1 = or_connection_new(CONN_TYPE_OR, AF_INET); d->c2 = or_connection_new(CONN_TYPE_OR, AF_INET); tor_addr_from_ipv4h(&d->c1->base_.addr, 0x01020304); @@ -761,6 +1199,8 @@ authenticate_data_setup(const struct testcase_t *test) tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, d->key1, d->key2, 86400), ==, 0); + init_mock_ed_keys(d->key2); + d->c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; d->c1->link_proto = 3; tt_int_op(connection_init_or_handshake_state(d->c1, 1), ==, 0); @@ -791,21 +1231,37 @@ authenticate_data_setup(const struct testcase_t *test) const uint8_t *der; size_t sz; tor_x509_cert_get_der(id_cert, &der, &sz); - d->c1->handshake_state->id_cert = tor_x509_cert_decode(der, sz); - d->c2->handshake_state->id_cert = tor_x509_cert_decode(der, sz); + d->c1->handshake_state->certs->id_cert = tor_x509_cert_decode(der, sz); + d->c2->handshake_state->certs->id_cert = tor_x509_cert_decode(der, sz); + + if (is_ed) { + d->c1->handshake_state->certs->ed_id_sign = + tor_cert_dup(get_master_signing_key_cert()); + d->c2->handshake_state->certs->ed_id_sign = + tor_cert_dup(get_master_signing_key_cert()); + d->c2->handshake_state->certs->ed_sign_auth = + tor_cert_dup(get_current_auth_key_cert()); + } else { + tt_assert(! tor_tls_get_my_certs(0, &auth_cert, &id_cert)); + tor_x509_cert_get_der(auth_cert, &der, &sz); + d->c2->handshake_state->certs->auth_cert = tor_x509_cert_decode(der, sz); + } tor_x509_cert_get_der(link_cert, &der, &sz); mock_peer_cert = tor_x509_cert_decode(der, sz); tt_assert(mock_peer_cert); + mock_own_cert = tor_x509_cert_decode(der, sz); tt_assert(mock_own_cert); - tt_assert(! tor_tls_get_my_certs(0, &auth_cert, &id_cert)); - tor_x509_cert_get_der(auth_cert, &der, &sz); - d->c2->handshake_state->auth_cert = tor_x509_cert_decode(der, sz); /* Make an authenticate cell ... */ - tt_int_op(0, ==, connection_or_send_authenticate_cell(d->c1, - AUTHTYPE_RSA_SHA256_TLSSECRET)); + int authtype; + if (is_ed) + authtype = AUTHTYPE_ED25519_SHA256_RFC5705; + else + authtype = AUTHTYPE_RSA_SHA256_TLSSECRET; + tt_int_op(0, ==, connection_or_send_authenticate_cell(d->c1, authtype)); + tt_assert(mock_got_var_cell); d->cell = mock_got_var_cell; mock_got_var_cell = NULL; @@ -831,42 +1287,64 @@ test_link_handshake_auth_cell(void *arg) /* Is the cell well-formed on the outer layer? */ tt_int_op(d->cell->command, ==, CELL_AUTHENTICATE); tt_int_op(d->cell->payload[0], ==, 0); - tt_int_op(d->cell->payload[1], ==, 1); + if (d->is_ed) + tt_int_op(d->cell->payload[1], ==, 3); + else + tt_int_op(d->cell->payload[1], ==, 1); tt_int_op(ntohs(get_uint16(d->cell->payload + 2)), ==, d->cell->payload_len - 4); /* Check it out for plausibility... */ auth_ctx_t ctx; - ctx.is_ed = 0; + ctx.is_ed = d->is_ed; tt_int_op(d->cell->payload_len-4, ==, auth1_parse(&auth1, d->cell->payload+4, d->cell->payload_len - 4, &ctx)); tt_assert(auth1); - tt_mem_op(auth1->type, ==, "AUTH0001", 8); + if (d->is_ed) { + tt_mem_op(auth1->type, ==, "AUTH0003", 8); + } else { + tt_mem_op(auth1->type, ==, "AUTH0001", 8); + } tt_mem_op(auth1->tlssecrets, ==, "int getRandomNumber(){return 4;}", 32); - tt_int_op(auth1_getlen_sig(auth1), >, 120); /* Is the signature okay? */ - uint8_t sig[128]; - uint8_t digest[32]; - - auth_pubkey = tor_tls_cert_get_key(d->c2->handshake_state->auth_cert); - int n = crypto_pk_public_checksig( + const uint8_t *start = d->cell->payload+4, *end = auth1->end_of_signed; + if (d->is_ed) { + ed25519_signature_t sig; + tt_int_op(auth1_getlen_sig(auth1), ==, ED25519_SIG_LEN); + memcpy(&sig.sig, auth1_getarray_sig(auth1), ED25519_SIG_LEN); + tt_assert(!ed25519_checksig(&sig, start, end-start, + &get_current_auth_keypair()->pubkey)); + } else { + uint8_t sig[128]; + uint8_t digest[32]; + tt_int_op(auth1_getlen_sig(auth1), >, 120); + auth_pubkey = tor_tls_cert_get_key( + d->c2->handshake_state->certs->auth_cert); + int n = crypto_pk_public_checksig( auth_pubkey, (char*)sig, sizeof(sig), (char*)auth1_getarray_sig(auth1), auth1_getlen_sig(auth1)); - tt_int_op(n, ==, 32); - const uint8_t *start = d->cell->payload+4, *end = auth1->end_of_signed; - crypto_digest256((char*)digest, - (const char*)start, end-start, DIGEST_SHA256); - tt_mem_op(sig, ==, digest, 32); + tt_int_op(n, ==, 32); + crypto_digest256((char*)digest, + (const char*)start, end-start, DIGEST_SHA256); + tt_mem_op(sig, ==, digest, 32); + } /* Then feed it to c2. */ tt_int_op(d->c2->handshake_state->authenticated, ==, 0); channel_tls_process_authenticate_cell(d->cell, d->chan2); tt_int_op(mock_close_called, ==, 0); tt_int_op(d->c2->handshake_state->authenticated, ==, 1); + if (d->is_ed) { + tt_int_op(d->c2->handshake_state->authenticated_ed25519, ==, 1); + tt_int_op(d->c2->handshake_state->authenticated_rsa, ==, 1); + } else { + tt_int_op(d->c2->handshake_state->authenticated_ed25519, ==, 0); + tt_int_op(d->c2->handshake_state->authenticated_rsa, ==, 1); + } done: auth1_free(auth1); @@ -900,7 +1378,8 @@ AUTHENTICATE_FAIL(badproto, d->c2->link_proto = 2) AUTHENTICATE_FAIL(atclient, require_failure_message = "We originated this connection"; - d->c2->handshake_state->started_here = 1) + d->c2->handshake_state->started_here = 1; + d->c2->handshake_state->certs->started_here = 1;) AUTHENTICATE_FAIL(duplicate, require_failure_message = "We already got one"; d->c2->handshake_state->received_authenticate = 1) @@ -924,13 +1403,13 @@ AUTHENTICATE_FAIL(nocerts, AUTHENTICATE_FAIL(noidcert, require_failure_message = "We never got an identity " "certificate"; - tor_x509_cert_free(d->c2->handshake_state->id_cert); - d->c2->handshake_state->id_cert = NULL) + tor_x509_cert_free(d->c2->handshake_state->certs->id_cert); + d->c2->handshake_state->certs->id_cert = NULL) AUTHENTICATE_FAIL(noauthcert, - require_failure_message = "We never got an authentication " - "certificate"; - tor_x509_cert_free(d->c2->handshake_state->auth_cert); - d->c2->handshake_state->auth_cert = NULL) + require_failure_message = "We never got an RSA " + "authentication certificate"; + tor_x509_cert_free(d->c2->handshake_state->certs->auth_cert); + d->c2->handshake_state->certs->auth_cert = NULL) AUTHENTICATE_FAIL(tooshort, require_failure_message = "Cell was way too short"; d->cell->payload_len = 3) @@ -954,11 +1433,33 @@ AUTHENTICATE_FAIL(badcontent, "cell body was not as expected"; d->cell->payload[10] ^= 0xff) AUTHENTICATE_FAIL(badsig_1, - require_failure_message = "Signature wasn't valid"; + if (d->is_ed) + require_failure_message = "Ed25519 signature wasn't valid"; + else + require_failure_message = "RSA signature wasn't valid"; d->cell->payload[d->cell->payload_len - 5] ^= 0xff) - -#define TEST(name, flags) \ - { #name , test_link_handshake_ ## name, (flags), NULL, NULL } +AUTHENTICATE_FAIL(missing_ed_id, + { + tor_cert_free(d->c2->handshake_state->certs->ed_id_sign); + d->c2->handshake_state->certs->ed_id_sign = NULL; + require_failure_message = "Ed authenticate without Ed ID " + "cert from peer"; + }) +AUTHENTICATE_FAIL(missing_ed_auth, + { + tor_cert_free(d->c2->handshake_state->certs->ed_sign_auth); + d->c2->handshake_state->certs->ed_sign_auth = NULL; + require_failure_message = "We never got an Ed25519 " + "authentication certificate"; + }) + +#define TEST_RSA(name, flags) \ + { #name , test_link_handshake_ ## name, (flags), \ + &passthrough_setup, (void*)"RSA" } + +#define TEST_ED(name, flags) \ + { #name "_ed25519" , test_link_handshake_ ## name, (flags), \ + &passthrough_setup, (void*)"Ed25519" } #define TEST_RCV_AUTHCHALLENGE(name) \ { "recv_authchallenge/" #name , \ @@ -968,17 +1469,34 @@ AUTHENTICATE_FAIL(badsig_1, #define TEST_RCV_CERTS(name) \ { "recv_certs/" #name , \ test_link_handshake_recv_certs_ ## name, TT_FORK, \ - &setup_recv_certs, NULL } + &setup_recv_certs, (void*)"RSA-Link" } + +#define TEST_RCV_CERTS_RSA(name,type) \ + { "recv_certs/" #name , \ + test_link_handshake_recv_certs_ ## name, TT_FORK, \ + &setup_recv_certs, (void*)type } + +#define TEST_RCV_CERTS_ED(name, type) \ + { "recv_certs/" #name "_ed25519", \ + test_link_handshake_recv_certs_ ## name, TT_FORK, \ + &setup_recv_certs, (void*)type } #define TEST_AUTHENTICATE(name) \ { "authenticate/" #name , test_link_handshake_auth_ ## name, TT_FORK, \ &setup_authenticate, NULL } +#define TEST_AUTHENTICATE_ED(name) \ + { "authenticate/" #name "_ed25519" , test_link_handshake_auth_ ## name, \ + TT_FORK, &setup_authenticate, (void*)3 } + struct testcase_t link_handshake_tests[] = { - TEST(certs_ok, TT_FORK), - //TEST(certs_bad, TT_FORK), + TEST_RSA(certs_ok, TT_FORK), + TEST_ED(certs_ok, TT_FORK), + TEST_RCV_CERTS(ok), - TEST_RCV_CERTS(ok_server), + TEST_RCV_CERTS_ED(ok, "Ed25519-Link"), + TEST_RCV_CERTS_RSA(ok_server, "RSA-Auth"), + TEST_RCV_CERTS_ED(ok_server, "Ed25519-Auth"), TEST_RCV_CERTS(badstate), TEST_RCV_CERTS(badproto), TEST_RCV_CERTS(duplicate), @@ -988,18 +1506,41 @@ struct testcase_t link_handshake_tests[] = { TEST_RCV_CERTS(truncated_1), TEST_RCV_CERTS(truncated_2), TEST_RCV_CERTS(truncated_3), + TEST_RCV_CERTS_ED(truncated_4, "Ed25519-Link"), + TEST_RCV_CERTS_ED(truncated_5, "Ed25519-Link"), + TEST_RCV_CERTS_ED(truncated_6, "Ed25519-Link"), + TEST_RCV_CERTS_ED(truncated_7, "Ed25519-Link"), TEST_RCV_CERTS(not_x509), TEST_RCV_CERTS(both_link), TEST_RCV_CERTS(both_id_rsa), TEST_RCV_CERTS(both_auth), + TEST_RCV_CERTS_ED(duplicate_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(duplicate_link, "Ed25519-Link"), + TEST_RCV_CERTS_ED(duplicate_crosscert, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_crosscert, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_signing_key, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_link, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_auth, "Ed25519-Auth"), + TEST_RCV_CERTS_ED(missing_rsa_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(link_mismatch, "Ed25519-Link"), + TEST_RCV_CERTS_ED(bad_ed_sig, "Ed25519-Link"), + TEST_RCV_CERTS_ED(bad_rsa_id_cert, "Ed25519-Link"), + TEST_RCV_CERTS_ED(bad_crosscert, "Ed25519-Link"), + TEST_RCV_CERTS_RSA(expired_rsa_id, "RSA-Link"), + TEST_RCV_CERTS_ED(expired_rsa_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(expired_ed_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(expired_ed_link, "Ed25519-Link"), + TEST_RCV_CERTS_ED(expired_crosscert, "Ed25519-Link"), TEST_RCV_CERTS(wrong_labels_1), TEST_RCV_CERTS(wrong_labels_2), TEST_RCV_CERTS(wrong_labels_3), TEST_RCV_CERTS(server_missing_certs), TEST_RCV_CERTS(server_wrong_labels_1), - TEST(send_authchallenge, TT_FORK), + TEST_RSA(send_authchallenge, TT_FORK), TEST_RCV_AUTHCHALLENGE(ok), + TEST_RCV_AUTHCHALLENGE(ok_ed25519), TEST_RCV_AUTHCHALLENGE(ok_noserver), TEST_RCV_AUTHCHALLENGE(ok_unrecognized), TEST_RCV_AUTHCHALLENGE(badstate), @@ -1012,6 +1553,7 @@ struct testcase_t link_handshake_tests[] = { TEST_RCV_AUTHCHALLENGE(nonzero_circid), TEST_AUTHENTICATE(cell), + TEST_AUTHENTICATE_ED(cell), TEST_AUTHENTICATE(badstate), TEST_AUTHENTICATE(badproto), TEST_AUTHENTICATE(atclient), @@ -1027,6 +1569,9 @@ struct testcase_t link_handshake_tests[] = { TEST_AUTHENTICATE(tooshort_1), TEST_AUTHENTICATE(badcontent), TEST_AUTHENTICATE(badsig_1), + TEST_AUTHENTICATE_ED(badsig_1), + TEST_AUTHENTICATE_ED(missing_ed_id), + TEST_AUTHENTICATE_ED(missing_ed_auth), //TEST_AUTHENTICATE(), END_OF_TESTCASES diff --git a/src/test/test_logging.c b/src/test/test_logging.c index 15471e46d0..94b3e4ea68 100644 --- a/src/test/test_logging.c +++ b/src/test/test_logging.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index 2ae605b8db..c78fda3b69 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -14,12 +14,6 @@ #include "test.h" -DISABLE_GCC_WARNING(redundant-decls) -#include <openssl/rsa.h> -#include <openssl/bn.h> -#include <openssl/pem.h> -ENABLE_GCC_WARNING(redundant-decls) - #ifdef _WIN32 /* For mkdir() */ #include <direct.h> diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c index d58f8a7fca..256354415c 100644 --- a/src/test/test_nodelist.c +++ b/src/test/test_nodelist.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c index a560e5fc5e..d0eea85d6f 100644 --- a/src/test/test_ntor_cl.c +++ b/src/test/test_ntor_cl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Tor Project, Inc. */ +/* Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_oom.c b/src/test/test_oom.c index 6102af01f5..f03a504d1d 100644 --- a/src/test/test_oom.c +++ b/src/test/test_oom.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Unit tests for OOM handling logic */ @@ -15,6 +15,7 @@ #include "config.h" #include "relay.h" #include "test.h" +#include "test_helpers.h" /* small replacement mock for circuit_mark_for_close_ to avoid doing all * the other bookkeeping that comes with marking circuits. */ @@ -58,24 +59,6 @@ dummy_or_circuit_new(int n_p_cells, int n_n_cells) return TO_CIRCUIT(circ); } -static circuit_t * -dummy_origin_circuit_new(int n_cells) -{ - origin_circuit_t *circ = origin_circuit_new(); - int i; - cell_t cell; - - for (i=0; i < n_cells; ++i) { - crypto_rand((void*)&cell, sizeof(cell)); - cell_queue_append_packed_copy(TO_CIRCUIT(circ), - &TO_CIRCUIT(circ)->n_chan_cells, - 1, &cell, 1, 0); - } - - TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - return TO_CIRCUIT(circ); -} - static void add_bytes_to_buf(buf_t *buf, size_t n_bytes) { diff --git a/src/test/test_oos.c b/src/test/test_oos.c index db06625116..9fd6bce5ae 100644 --- a/src/test/test_oos.c +++ b/src/test/test_oos.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Unit tests for OOS handler */ diff --git a/src/test/test_options.c b/src/test/test_options.c index e85e11805b..ad735b72a6 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONFIG_PRIVATE @@ -18,6 +18,7 @@ #include "sandbox.h" #include "memarea.h" #include "policies.h" +#include "test_helpers.h" #define NS_MODULE test_options @@ -104,11 +105,71 @@ clear_log_messages(void) "EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212:80 7EA6 EAD6 FD83" \ " 083C 538F 4403 8BBF A077 587D D755\n" +static int +test_options_checklog(const char *configuration, int expect_log_severity, + const char *expect_log) +{ + int found = 0, ret = -1; + char *actual_log = NULL; + + if (messages) { + SMARTLIST_FOREACH_BEGIN(messages, logmsg_t *, m) { + if (m->severity == expect_log_severity && + strstr(m->msg, expect_log)) { + found = 1; + break; + } + } SMARTLIST_FOREACH_END(m); + } + if (!found) { + actual_log = dump_logs(); + TT_DIE(("Expected log message [%s] %s from <%s>, but got <%s>.", + log_level_to_string(expect_log_severity), expect_log, + configuration, actual_log)); + } + ret = 0; + + done: + tor_free(actual_log); + return ret; +} + +static int +test_options_checkmsgs(const char *configuration, + const char *expect_errmsg, + int expect_log_severity, + const char *expect_log, + char *msg) +{ + if (expect_errmsg && !msg) { + TT_DIE(("Expected error message <%s> from <%s>, but got none.", + expect_errmsg, configuration)); + } else if (expect_errmsg && !strstr(msg, expect_errmsg)) { + TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.", + expect_errmsg, configuration, msg)); + } else if (!expect_errmsg && msg) { + TT_DIE(("Expected no error message from <%s> but got <%s>.", + configuration, msg)); + } + if (expect_log) { + return test_options_checklog(configuration, expect_log_severity, + expect_log); + } + return 0; + + done: + return -1; +} + +/* Which phases of config parsing/validation to check for messages/logs */ +enum { PH_GETLINES, PH_ASSIGN, PH_VALIDATE }; + static void test_options_validate_impl(const char *configuration, const char *expect_errmsg, int expect_log_severity, - const char *expect_log) + const char *expect_log, + int phase) { or_options_t *opt=NULL; or_options_t *dflt; @@ -119,43 +180,34 @@ test_options_validate_impl(const char *configuration, setup_options(opt, dflt); r = config_get_lines(configuration, &cl, 1); - tt_int_op(r, OP_EQ, 0); + if (phase == PH_GETLINES) { + if (test_options_checkmsgs(configuration, expect_errmsg, + expect_log_severity, + expect_log, msg)) + goto done; + } + if (r) + goto done; r = config_assign(&options_format, opt, cl, 0, &msg); - tt_int_op(r, OP_EQ, 0); - - r = options_validate(NULL, opt, dflt, 0, &msg); - if (expect_errmsg && !msg) { - TT_DIE(("Expected error message <%s> from <%s>, but got none.", - expect_errmsg, configuration)); - } else if (expect_errmsg && !strstr(msg, expect_errmsg)) { - TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.", - expect_errmsg, configuration, msg)); - } else if (!expect_errmsg && msg) { - TT_DIE(("Expected no error message from <%s> but got <%s>.", - configuration, msg)); + if (phase == PH_ASSIGN) { + if (test_options_checkmsgs(configuration, expect_errmsg, + expect_log_severity, + expect_log, msg)) + goto done; } tt_int_op((r == 0), OP_EQ, (msg == NULL)); + if (r) + goto done; - if (expect_log) { - int found = 0; - if (messages) { - SMARTLIST_FOREACH_BEGIN(messages, logmsg_t *, m) { - if (m->severity == expect_log_severity && - strstr(m->msg, expect_log)) { - found = 1; - break; - } - } SMARTLIST_FOREACH_END(m); - } - if (!found) { - tor_free(msg); - msg = dump_logs(); - TT_DIE(("Expected log message [%s] %s from <%s>, but got <%s>.", - log_level_to_string(expect_log_severity), expect_log, - configuration, msg)); - } + r = options_validate(NULL, opt, dflt, 0, &msg); + if (phase == PH_VALIDATE) { + if (test_options_checkmsgs(configuration, expect_errmsg, + expect_log_severity, + expect_log, msg)) + goto done; } + tt_int_op((r == 0), OP_EQ, (msg == NULL)); done: escaped(NULL); @@ -167,14 +219,14 @@ test_options_validate_impl(const char *configuration, clear_log_messages(); } -#define WANT_ERR(config, msg) \ - test_options_validate_impl((config), (msg), 0, NULL) -#define WANT_LOG(config, severity, msg) \ - test_options_validate_impl((config), NULL, (severity), (msg)) -#define WANT_ERR_LOG(config, msg, severity, logmsg) \ - test_options_validate_impl((config), (msg), (severity), (logmsg)) -#define OK(config) \ - test_options_validate_impl((config), NULL, 0, NULL) +#define WANT_ERR(config, msg, ph) \ + test_options_validate_impl((config), (msg), 0, NULL, (ph)) +#define WANT_LOG(config, severity, msg, ph) \ + test_options_validate_impl((config), NULL, (severity), (msg), (ph)) +#define WANT_ERR_LOG(config, msg, severity, logmsg, ph) \ + test_options_validate_impl((config), (msg), (severity), (logmsg), (ph)) +#define OK(config, ph) \ + test_options_validate_impl((config), NULL, 0, NULL, (ph)) static void test_options_validate(void *arg) @@ -183,21 +235,39 @@ test_options_validate(void *arg) setup_log_callback(); sandbox_disable_getaddrinfo_cache(); - WANT_ERR("ExtORPort 500000", "Invalid ExtORPort"); + WANT_ERR("ExtORPort 500000", "Invalid ExtORPort", PH_VALIDATE); WANT_ERR_LOG("ServerTransportOptions trebuchet", "ServerTransportOptions did not parse", - LOG_WARN, "Too few arguments"); - OK("ServerTransportOptions trebuchet sling=snappy"); - OK("ServerTransportOptions trebuchet sling="); + LOG_WARN, "Too few arguments", PH_VALIDATE); + OK("ServerTransportOptions trebuchet sling=snappy", PH_VALIDATE); + OK("ServerTransportOptions trebuchet sling=", PH_VALIDATE); WANT_ERR_LOG("ServerTransportOptions trebuchet slingsnappy", "ServerTransportOptions did not parse", - LOG_WARN, "\"slingsnappy\" is not a k=v"); + LOG_WARN, "\"slingsnappy\" is not a k=v", PH_VALIDATE); WANT_ERR("DirPort 8080\nDirCache 0", - "DirPort configured but DirCache disabled."); + "DirPort configured but DirCache disabled.", PH_VALIDATE); WANT_ERR("BridgeRelay 1\nDirCache 0", - "We're a bridge but DirCache is disabled."); + "We're a bridge but DirCache is disabled.", PH_VALIDATE); + + WANT_ERR_LOG("HeartbeatPeriod 21 snarks", + "Interval 'HeartbeatPeriod 21 snarks' is malformed or" + " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + PH_ASSIGN); + WANT_ERR_LOG("LogTimeGranularity 21 snarks", + "Msec interval 'LogTimeGranularity 21 snarks' is malformed or" + " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + PH_ASSIGN); + OK("HeartbeatPeriod 1 hour", PH_VALIDATE); + OK("LogTimeGranularity 100 milliseconds", PH_VALIDATE); + + WANT_LOG("ControlSocket \"string with trailing garbage\" bogus", LOG_WARN, + "Error while parsing configuration: " + "Excess data after quoted string", PH_GETLINES); + WANT_LOG("ControlSocket \"bogus escape \\@\"", LOG_WARN, + "Error while parsing configuration: " + "Invalid escape sequence in quoted string", PH_GETLINES); close_temp_logs(); clear_log_messages(); @@ -332,7 +402,8 @@ fixed_get_uname(void) "VirtualAddrNetworkIPv4 127.192.0.0/10\n" \ "VirtualAddrNetworkIPv6 [FE80::]/10\n" \ "SchedulerHighWaterMark__ 42\n" \ - "SchedulerLowWaterMark__ 10\n" + "SchedulerLowWaterMark__ 10\n" \ + "UseEntryGuards 1\n" typedef struct { or_options_t *old_opt; @@ -352,6 +423,12 @@ get_options_test_data(const char *conf) result->opt = options_new(); result->old_opt = options_new(); result->def_opt = options_new(); + + // XXX: Really, all of these options should be set to defaults + // with options_init(), but about a dozen tests break when I do that. + // Being kinda lame and just fixing the immedate breakage for now.. + result->opt->ConnectionPadding = -1; // default must be "auto" + rv = config_get_lines(conf, &cl, 1); tt_assert(rv == 0); rv = config_assign(&options_format, result->opt, cl, 0, &msg); @@ -400,7 +477,7 @@ test_options_validate__uname_for_server(void *ignored) (void)ignored; char *msg; options_test_data_t *tdata = get_options_test_data( - "ORListenAddress 127.0.0.1:5555"); + "ORPort 127.0.0.1:5555"); setup_capture_of_logs(LOG_WARN); MOCK(get_uname, fixed_get_uname); @@ -534,7 +611,7 @@ test_options_validate__contactinfo(void *ignored) int ret; char *msg; options_test_data_t *tdata = get_options_test_data( - "ORListenAddress 127.0.0.1:5555\nORPort 955"); + "ORPort 127.0.0.1:5555"); setup_capture_of_logs(LOG_DEBUG); tdata->opt->ContactInfo = NULL; @@ -547,7 +624,7 @@ test_options_validate__contactinfo(void *ignored) tor_free(msg); free_options_test_data(tdata); - tdata = get_options_test_data("ORListenAddress 127.0.0.1:5555\nORPort 955\n" + tdata = get_options_test_data("ORPort 127.0.0.1:5555\n" "ContactInfo hella@example.org"); mock_clean_saved_logs(); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); @@ -650,16 +727,18 @@ test_options_validate__authdir(void *ignored) setup_capture_of_logs(LOG_INFO); options_test_data_t *tdata = get_options_test_data( "AuthoritativeDirectory 1\n" - "Address this.should.not_exist.example.org"); + "Address this.should.not!exist!.example.org"); sandbox_disable_getaddrinfo_cache(); + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + UNMOCK(tor_addr_lookup); tt_int_op(ret, OP_EQ, -1); tt_str_op(msg, OP_EQ, "Failed to resolve/guess local address. See logs for" " details."); expect_log_msg("Could not resolve local Address " - "'this.should.not_exist.example.org'. Failing.\n"); + "'this.should.not!exist!.example.org'. Failing.\n"); tor_free(msg); free_options_test_data(tdata); @@ -953,8 +1032,7 @@ test_options_validate__relay_with_hidden_services(void *ignored) char *msg; setup_capture_of_logs(LOG_DEBUG); options_test_data_t *tdata = get_options_test_data( - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "HiddenServiceDir " "/Library/Tor/var/lib/tor/hidden_service/\n" "HiddenServicePort 80 127.0.0.1:8080\n" @@ -1018,13 +1096,13 @@ test_options_validate__transproxy(void *ignored) ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); -#if !defined(__OpenBSD__) && !defined( DARWIN ) +#if !defined(OpenBSD) && !defined( DARWIN ) tt_str_op(msg, OP_EQ, "pf-divert is a OpenBSD-specific and OS X/Darwin-specific feature."); #else tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_PF_DIVERT); tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without " - "any valid TransPort or TransListenAddress."); + "any valid TransPort."); #endif tor_free(msg); @@ -1039,7 +1117,7 @@ test_options_validate__transproxy(void *ignored) #else tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_TPROXY); tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without any valid " - "TransPort or TransListenAddress."); + "TransPort."); #endif tor_free(msg); @@ -1055,7 +1133,7 @@ test_options_validate__transproxy(void *ignored) #else tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_IPFW); tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without any valid " - "TransPort or TransListenAddress."); + "TransPort."); #endif tor_free(msg); @@ -1087,7 +1165,7 @@ test_options_validate__transproxy(void *ignored) if (msg) { TT_DIE(("Expected NULL but got '%s'", msg)); } -#elif defined(__OpenBSD__) +#elif defined(OpenBSD) tdata = get_options_test_data("TransProxyType pf-divert\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); @@ -1113,8 +1191,7 @@ test_options_validate__transproxy(void *ignored) ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); - tt_str_op(msg, OP_EQ, "TransPort and TransListenAddress are disabled in " - "this build."); + tt_str_op(msg, OP_EQ, "TransPort is disabled in this build."); tor_free(msg); #endif @@ -1309,54 +1386,6 @@ test_options_validate__node_families(void *ignored) } static void -test_options_validate__tlsec(void *ignored) -{ - (void)ignored; - int ret; - char *msg; - setup_capture_of_logs(LOG_DEBUG); - options_test_data_t *tdata = get_options_test_data( - "TLSECGroup ed25519\n" - "SchedulerHighWaterMark__ 42\n" - "SchedulerLowWaterMark__ 10\n"); - - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, -1); - expect_log_msg("Unrecognized TLSECGroup: Falling back to the default.\n"); - tt_assert(!tdata->opt->TLSECGroup); - tor_free(msg); - - free_options_test_data(tdata); - tdata = get_options_test_data("TLSECGroup P224\n" - "SchedulerHighWaterMark__ 42\n" - "SchedulerLowWaterMark__ 10\n"); - mock_clean_saved_logs(); - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, -1); - expect_no_log_msg( - "Unrecognized TLSECGroup: Falling back to the default.\n"); - tt_assert(tdata->opt->TLSECGroup); - tor_free(msg); - - free_options_test_data(tdata); - tdata = get_options_test_data("TLSECGroup P256\n" - "SchedulerHighWaterMark__ 42\n" - "SchedulerLowWaterMark__ 10\n"); - mock_clean_saved_logs(); - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, -1); - expect_no_log_msg( - "Unrecognized TLSECGroup: Falling back to the default.\n"); - tt_assert(tdata->opt->TLSECGroup); - tor_free(msg); - - done: - teardown_capture_of_logs(); - free_options_test_data(tdata); - tor_free(msg); -} - -static void test_options_validate__token_bucket(void *ignored) { (void)ignored; @@ -1738,8 +1767,7 @@ test_options_validate__reachable_addresses(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data("ReachableAddresses *:82\n" - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "MaxClientCircuitsPending 1\n" "ConnLimit 1\n" "SchedulerHighWaterMark__ 42\n" @@ -1752,8 +1780,7 @@ test_options_validate__reachable_addresses(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data("ReachableORAddresses *:82\n" - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "MaxClientCircuitsPending 1\n" "ConnLimit 1\n" "SchedulerHighWaterMark__ 42\n" @@ -1766,8 +1793,7 @@ test_options_validate__reachable_addresses(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data("ReachableDirAddresses *:82\n" - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "MaxClientCircuitsPending 1\n" "ConnLimit 1\n" "SchedulerHighWaterMark__ 42\n" @@ -1780,8 +1806,7 @@ test_options_validate__reachable_addresses(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data("ClientUseIPv4 0\n" - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "MaxClientCircuitsPending 1\n" "ConnLimit 1\n" "SchedulerHighWaterMark__ 42\n" @@ -1794,14 +1819,6 @@ test_options_validate__reachable_addresses(void *ignored) /* Test IPv4-only clients setting IPv6 preferences */ -#define WARN_PLEASE_USE_IPV6_OR_LOG_MSG \ - "ClientPreferIPv6ORPort 1 is ignored unless tor is using IPv6. " \ - "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges.\n" - -#define WARN_PLEASE_USE_IPV6_DIR_LOG_MSG \ - "ClientPreferIPv6DirPort 1 is ignored unless tor is using IPv6. " \ - "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges.\n" - free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES "ClientUseIPv4 1\n" @@ -1811,7 +1828,6 @@ test_options_validate__reachable_addresses(void *ignored) ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, 0); - expect_log_msg(WARN_PLEASE_USE_IPV6_OR_LOG_MSG); tor_free(msg); free_options_test_data(tdata); @@ -1823,7 +1839,6 @@ test_options_validate__reachable_addresses(void *ignored) ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, 0); - expect_log_msg(WARN_PLEASE_USE_IPV6_DIR_LOG_MSG); tor_free(msg); /* Now test an IPv4/IPv6 client setting IPv6 preferences */ @@ -1891,8 +1906,7 @@ test_options_validate__use_bridges(void *ignored) options_test_data_t *tdata = get_options_test_data( "UseBridges 1\n" "ClientUseIPv4 1\n" - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "MaxClientCircuitsPending 1\n" "ConnLimit 1\n" "SchedulerHighWaterMark__ 42\n" @@ -1945,6 +1959,19 @@ test_options_validate__use_bridges(void *ignored) tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES "UseBridges 1\n" "Bridge 10.0.0.1\n" + "UseEntryGuards 0\n" + ); + + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + tt_str_op(msg, OP_EQ, + "Setting UseBridges requires also setting UseEntryGuards."); + tor_free(msg); + + free_options_test_data(tdata); + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "UseBridges 1\n" + "Bridge 10.0.0.1\n" "Bridge !!!\n" ); @@ -2001,56 +2028,6 @@ test_options_validate__entry_nodes(void *ignored) } static void -test_options_validate__invalid_nodes(void *ignored) -{ - (void)ignored; - int ret; - char *msg; - options_test_data_t *tdata = get_options_test_data( - "AllowInvalidNodes something_stupid\n" - "MaxClientCircuitsPending 1\n" - "ConnLimit 1\n" - "SchedulerHighWaterMark__ 42\n" - "SchedulerLowWaterMark__ 10\n"); - - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, -1); - tt_str_op(msg, OP_EQ, - "Unrecognized value 'something_stupid' in AllowInvalidNodes"); - tor_free(msg); - - free_options_test_data(tdata); - tdata = get_options_test_data("AllowInvalidNodes entry, middle, exit\n" - "MaxClientCircuitsPending 1\n" - "ConnLimit 1\n" - "SchedulerHighWaterMark__ 42\n" - "SchedulerLowWaterMark__ 10\n"); - - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, -1); - tt_int_op(tdata->opt->AllowInvalid_, OP_EQ, ALLOW_INVALID_ENTRY | - ALLOW_INVALID_EXIT | ALLOW_INVALID_MIDDLE); - tor_free(msg); - - free_options_test_data(tdata); - tdata = get_options_test_data("AllowInvalidNodes introduction, rendezvous\n" - "MaxClientCircuitsPending 1\n" - "ConnLimit 1\n" - "SchedulerHighWaterMark__ 42\n" - "SchedulerLowWaterMark__ 10\n"); - - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, -1); - tt_int_op(tdata->opt->AllowInvalid_, OP_EQ, ALLOW_INVALID_INTRODUCTION | - ALLOW_INVALID_RENDEZVOUS); - tor_free(msg); - - done: - free_options_test_data(tdata); - tor_free(msg); -} - -static void test_options_validate__safe_logging(void *ignored) { (void)ignored; @@ -2320,30 +2297,6 @@ test_options_validate__hidserv(void *ignored) } static void -test_options_validate__predicted_ports(void *ignored) -{ - (void)ignored; - int ret; - char *msg; - setup_capture_of_logs(LOG_WARN); - - options_test_data_t *tdata = get_options_test_data( - "PredictedPortsRelevanceTime 100000000\n" - TEST_OPTIONS_DEFAULT_VALUES); - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, 0); - expect_log_msg("PredictedPortsRelevanceTime is too " - "large; clipping to 3600s.\n"); - tt_int_op(tdata->opt->PredictedPortsRelevanceTime, OP_EQ, 3600); - - done: - teardown_capture_of_logs(); - policies_free_all(); - free_options_test_data(tdata); - tor_free(msg); -} - -static void test_options_validate__path_bias(void *ignored) { (void)ignored; @@ -2489,8 +2442,7 @@ test_options_validate__bandwidth(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "BandwidthRate 1\n" ); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); @@ -2501,8 +2453,7 @@ test_options_validate__bandwidth(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "BandwidthRate 76800\n" "MaxAdvertisedBandwidth 30000\n" ); @@ -2514,8 +2465,7 @@ test_options_validate__bandwidth(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "BandwidthRate 76800\n" "RelayBandwidthRate 1\n" "MaxAdvertisedBandwidth 38400\n" @@ -2528,8 +2478,7 @@ test_options_validate__bandwidth(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "BandwidthRate 76800\n" "BandwidthBurst 76800\n" "RelayBandwidthRate 76800\n" @@ -2819,8 +2768,8 @@ test_options_validate__single_onion(void *ignored) tt_int_op(ret, OP_EQ, -1); tt_str_op(msg, OP_EQ, "HiddenServiceNonAnonymousMode is incompatible with " "using Tor as an anonymous client. Please set " - "Socks/Trans/NATD/DNSPort to 0, or HiddenServiceNonAnonymousMode " - "to 0, or use the non-anonymous Tor2webMode."); + "Socks/Trans/NATD/DNSPort to 0, or revert " + "HiddenServiceNonAnonymousMode to 0."); tor_free(msg); free_options_test_data(tdata); @@ -2967,8 +2916,7 @@ test_options_validate__accounting(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data( TEST_OPTIONS_DEFAULT_VALUES - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "BandwidthRate 76800\n" "BandwidthBurst 76800\n" "MaxAdvertisedBandwidth 38400\n" @@ -3037,6 +2985,7 @@ test_options_validate__proxy(void *ignored) options_test_data_t *tdata = NULL; sandbox_disable_getaddrinfo_cache(); setup_capture_of_logs(LOG_WARN); + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -3057,6 +3006,7 @@ test_options_validate__proxy(void *ignored) tor_free(msg); free_options_test_data(tdata); + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES "HttpProxy not_so_valid!\n" ); @@ -3357,6 +3307,7 @@ test_options_validate__proxy(void *ignored) policies_free_all(); // sandbox_free_getaddrinfo_cache(); tor_free(msg); + UNMOCK(tor_addr_lookup); } static void @@ -3599,8 +3550,7 @@ test_options_validate__families(void *ignored) tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES "MyFamily home\n" "BridgeRelay 1\n" - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "BandwidthRate 51300\n" "BandwidthBurst 51300\n" "MaxAdvertisedBandwidth 25700\n" @@ -3829,8 +3779,7 @@ test_options_validate__transport(void *ignored) free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES "ServerTransportPlugin foo exec bar\n" - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "BandwidthRate 76900\n" "BandwidthBurst 76900\n" "MaxAdvertisedBandwidth 38500\n" @@ -3872,8 +3821,7 @@ test_options_validate__transport(void *ignored) tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES "ServerTransportListenAddr foo 127.0.0.42:55\n" "ServerTransportPlugin foo exec bar\n" - "ORListenAddress 127.0.0.1:5555\n" - "ORPort 955\n" + "ORPort 127.0.0.1:5555\n" "BandwidthRate 76900\n" "BandwidthBurst 76900\n" "MaxAdvertisedBandwidth 38500\n" @@ -4230,48 +4178,6 @@ test_options_validate__virtual_addr(void *ignored) } static void -test_options_validate__exits(void *ignored) -{ - (void)ignored; - int ret; - char *msg; - options_test_data_t *tdata = NULL; - setup_capture_of_logs(LOG_WARN); - - free_options_test_data(tdata); - tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES - "AllowSingleHopExits 1" - ); - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, 0); - expect_log_msg("You have set AllowSingleHopExits; " - "now your relay will allow others to make one-hop exits. However," - " since by default most clients avoid relays that set this option," - " most clients will ignore you.\n"); - tor_free(msg); - - free_options_test_data(tdata); - tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES - "AllowSingleHopExits 1\n" - VALID_DIR_AUTH - ); - mock_clean_saved_logs(); - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, 0); - expect_no_log_msg("You have set AllowSingleHopExits; " - "now your relay will allow others to make one-hop exits. However," - " since by default most clients avoid relays that set this option," - " most clients will ignore you.\n"); - tor_free(msg); - - done: - policies_free_all(); - teardown_capture_of_logs(); - free_options_test_data(tdata); - tor_free(msg); -} - -static void test_options_validate__testing_options(void *ignored) { (void)ignored; @@ -4509,7 +4415,6 @@ struct testcase_t options_tests[] = { LOCAL_VALIDATE_TEST(exclude_nodes), LOCAL_VALIDATE_TEST(scheduler), LOCAL_VALIDATE_TEST(node_families), - LOCAL_VALIDATE_TEST(tlsec), LOCAL_VALIDATE_TEST(token_bucket), LOCAL_VALIDATE_TEST(recommended_packages), LOCAL_VALIDATE_TEST(fetch_dir), @@ -4520,12 +4425,10 @@ struct testcase_t options_tests[] = { LOCAL_VALIDATE_TEST(reachable_addresses), LOCAL_VALIDATE_TEST(use_bridges), LOCAL_VALIDATE_TEST(entry_nodes), - LOCAL_VALIDATE_TEST(invalid_nodes), LOCAL_VALIDATE_TEST(safe_logging), LOCAL_VALIDATE_TEST(publish_server_descriptor), LOCAL_VALIDATE_TEST(testing), LOCAL_VALIDATE_TEST(hidserv), - LOCAL_VALIDATE_TEST(predicted_ports), LOCAL_VALIDATE_TEST(path_bias), LOCAL_VALIDATE_TEST(bandwidth), LOCAL_VALIDATE_TEST(circuits), @@ -4543,7 +4446,6 @@ struct testcase_t options_tests[] = { LOCAL_VALIDATE_TEST(constrained_sockets), LOCAL_VALIDATE_TEST(v3_auth), LOCAL_VALIDATE_TEST(virtual_addr), - LOCAL_VALIDATE_TEST(exits), LOCAL_VALIDATE_TEST(testing_options), LOCAL_VALIDATE_TEST(accel), END_OF_TESTCASES /* */ diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 1ffdc2cd51..1b2fac4325 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" @@ -1031,10 +1031,10 @@ test_policies_general(void *arg) { char *policy_strng = NULL; smartlist_t *chunks = smartlist_new(); - smartlist_add(chunks, tor_strdup("accept ")); + smartlist_add_strdup(chunks, "accept "); for (i=1; i<10000; ++i) smartlist_add_asprintf(chunks, "%d,", i); - smartlist_add(chunks, tor_strdup("20000")); + smartlist_add_strdup(chunks, "20000"); policy_strng = smartlist_join_strings(chunks, "", 0, NULL); SMARTLIST_FOREACH(chunks, char *, ch, tor_free(ch)); smartlist_free(chunks); @@ -1048,9 +1048,9 @@ test_policies_general(void *arg) for (i=1; i<2000; i+=2) { char buf[POLICY_BUF_LEN]; tor_snprintf(buf, sizeof(buf), "reject *:%d", i); - smartlist_add(sm, tor_strdup(buf)); + smartlist_add_strdup(sm, buf); } - smartlist_add(sm, tor_strdup("accept *:*")); + smartlist_add_strdup(sm, "accept *:*"); policy_str = smartlist_join_strings(sm, ",", 0, NULL); test_policy_summary_helper( policy_str, "accept 2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44," @@ -1587,8 +1587,12 @@ test_policies_getinfo_helper_policies(void *arg) append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*"); mock_options.IPv6Exit = 1; - tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR); - tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR); + tor_addr_from_ipv4h( + &mock_options.OutboundBindAddresses[OUTBOUND_ADDR_EXIT][0], + TEST_IPV4_ADDR); + tor_addr_parse( + &mock_options.OutboundBindAddresses[OUTBOUND_ADDR_EXIT][1], + TEST_IPV6_ADDR); mock_options.ExitPolicyRejectPrivate = 1; mock_options.ExitPolicyRejectLocalInterfaces = 1; diff --git a/src/test/test_procmon.c b/src/test/test_procmon.c index 9e63fc006d..5c52af8693 100644 --- a/src/test/test_procmon.c +++ b/src/test/test_procmon.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define PROCMON_PRIVATE diff --git a/src/test/test_protover.c b/src/test/test_protover.c index 92ead3ca37..c093e69968 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define PROTOVER_PRIVATE diff --git a/src/test/test_pt.c b/src/test/test_pt.c index e5cdc5f3cd..79b03171bc 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -155,9 +155,9 @@ test_pt_get_transport_options(void *arg) opt_str = get_transport_options_for_server_proxy(mp); tt_ptr_op(opt_str, OP_EQ, NULL); - smartlist_add(mp->transports_to_launch, tor_strdup("gruyere")); - smartlist_add(mp->transports_to_launch, tor_strdup("roquefort")); - smartlist_add(mp->transports_to_launch, tor_strdup("stnectaire")); + smartlist_add_strdup(mp->transports_to_launch, "gruyere"); + smartlist_add_strdup(mp->transports_to_launch, "roquefort"); + smartlist_add_strdup(mp->transports_to_launch, "stnectaire"); tt_assert(options); @@ -284,13 +284,13 @@ test_pt_get_extrainfo_string(void *arg) } #ifdef _WIN32 -#define STDIN_HANDLE HANDLE +#define STDIN_HANDLE HANDLE* #else -#define STDIN_HANDLE FILE +#define STDIN_HANDLE int #endif static smartlist_t * -tor_get_lines_from_handle_replacement(STDIN_HANDLE *handle, +tor_get_lines_from_handle_replacement(STDIN_HANDLE handle, enum stream_status *stream_status_out) { static int times_called = 0; @@ -305,7 +305,7 @@ tor_get_lines_from_handle_replacement(STDIN_HANDLE *handle, smartlist_add_asprintf(retval_sl, "SMETHOD mock%d 127.0.0.1:555%d", times_called, times_called); } else { - smartlist_add(retval_sl, tor_strdup("SMETHODS DONE")); + smartlist_add_strdup(retval_sl, "SMETHODS DONE"); } return retval_sl; diff --git a/src/test/test_pubsub.c b/src/test/test_pubsub.c index 547d6c6b32..2f047d9f2c 100644 --- a/src/test/test_pubsub.c +++ b/src/test/test_pubsub.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/test/test_relay.c b/src/test/test_relay.c index 4713c79ea5..238d4c5baf 100644 --- a/src/test/test_relay.c +++ b/src/test/test_relay.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c index fb6748965a..eea1f5dc80 100644 --- a/src/test/test_relaycell.c +++ b/src/test/test_relaycell.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Unit tests for handling different kinds of relay cell */ diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c index a5d3f351f8..feba8f664e 100644 --- a/src/test/test_rendcache.c +++ b/src/test/test_rendcache.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -10,7 +10,7 @@ #include "router.h" #include "routerlist.h" #include "config.h" -#include <openssl/rsa.h> +#include "hs_common.h" #include "rend_test_helpers.h" #include "log_test_helpers.h" @@ -24,15 +24,16 @@ static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 60; static rend_data_t * mock_rend_data(const char *onion_address) { - rend_data_t *rend_query = tor_malloc_zero(sizeof(rend_data_t)); + rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data)); + rend_data_t *rend_query = &v2_data->base_; + rend_query->version = 2; - strlcpy(rend_query->onion_address, onion_address, - sizeof(rend_query->onion_address)); - rend_query->auth_type = REND_NO_AUTH; + strlcpy(v2_data->onion_address, onion_address, + sizeof(v2_data->onion_address)); + v2_data->auth_type = REND_NO_AUTH; rend_query->hsdirs_fp = smartlist_new(); smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa", DIGEST_LEN)); - return rend_query; } @@ -144,7 +145,8 @@ test_rend_cache_store_v2_desc_as_client(void *data) // Test mismatch between service ID and onion address rend_cache_init(); - strncpy(mock_rend_query->onion_address, "abc", REND_SERVICE_ID_LEN_BASE32+1); + strncpy(TO_REND_DATA_V2(mock_rend_query)->onion_address, "abc", + REND_SERVICE_ID_LEN_BASE32+1); ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, mock_rend_query, NULL); @@ -155,12 +157,16 @@ test_rend_cache_store_v2_desc_as_client(void *data) // Test incorrect descriptor ID rend_cache_init(); mock_rend_query = mock_rend_data(service_id); - desc_id_base32[0]++; + char orig = desc_id_base32[0]; + if (desc_id_base32[0] == 'a') + desc_id_base32[0] = 'b'; + else + desc_id_base32[0] = 'a'; ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, mock_rend_query, NULL); tt_int_op(ret, OP_EQ, -1); - desc_id_base32[0]--; + desc_id_base32[0] = orig; rend_cache_free_all(); // Test too old descriptor @@ -230,9 +236,9 @@ test_rend_cache_store_v2_desc_as_client(void *data) generate_desc(RECENT_TIME, &desc_holder, &service_id, 3); mock_rend_query = mock_rend_data(service_id); - mock_rend_query->auth_type = REND_BASIC_AUTH; + TO_REND_DATA_V2(mock_rend_query)->auth_type = REND_BASIC_AUTH; client_cookie[0] = 'A'; - memcpy(mock_rend_query->descriptor_cookie, client_cookie, + memcpy(TO_REND_DATA_V2(mock_rend_query)->descriptor_cookie, client_cookie, REND_DESC_COOKIE_LEN); base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN); @@ -250,7 +256,7 @@ test_rend_cache_store_v2_desc_as_client(void *data) generate_desc(RECENT_TIME, &desc_holder, &service_id, 3); mock_rend_query = mock_rend_data(service_id); - mock_rend_query->auth_type = REND_BASIC_AUTH; + TO_REND_DATA_V2(mock_rend_query)->auth_type = REND_BASIC_AUTH; base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN); ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, @@ -1078,9 +1084,10 @@ static void test_rend_cache_clean_v2_descs_as_dir(void *data) { rend_cache_entry_t *e; - time_t now; + time_t now, cutoff; rend_service_descriptor_t *desc; now = time(NULL); + cutoff = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW); const char key[DIGEST_LEN] = "abcde"; (void)data; @@ -1088,7 +1095,7 @@ test_rend_cache_clean_v2_descs_as_dir(void *data) rend_cache_init(); // Test running with an empty cache - rend_cache_clean_v2_descs_as_dir(now, 0); + rend_cache_clean_v2_descs_as_dir(cutoff); tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0); // Test with only one new entry @@ -1100,38 +1107,15 @@ test_rend_cache_clean_v2_descs_as_dir(void *data) e->parsed = desc; digestmap_set(rend_cache_v2_dir, key, e); - rend_cache_clean_v2_descs_as_dir(now, 0); + /* Set the cutoff to minus 10 seconds. */ + rend_cache_clean_v2_descs_as_dir(cutoff - 10); tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1); // Test with one old entry - desc->timestamp = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000); - rend_cache_clean_v2_descs_as_dir(now, 0); - tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0); - - // Test with one entry that has an old last served - e = tor_malloc_zero(sizeof(rend_cache_entry_t)); - e->last_served = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000); - desc = tor_malloc_zero(sizeof(rend_service_descriptor_t)); - desc->timestamp = now; - desc->pk = pk_generate(0); - e->parsed = desc; - digestmap_set(rend_cache_v2_dir, key, e); - - rend_cache_clean_v2_descs_as_dir(now, 0); + desc->timestamp = cutoff - 1000; + rend_cache_clean_v2_descs_as_dir(cutoff); tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0); - // Test a run through asking for a large force_remove - e = tor_malloc_zero(sizeof(rend_cache_entry_t)); - e->last_served = now; - desc = tor_malloc_zero(sizeof(rend_service_descriptor_t)); - desc->timestamp = now; - desc->pk = pk_generate(0); - e->parsed = desc; - digestmap_set(rend_cache_v2_dir, key, e); - - rend_cache_clean_v2_descs_as_dir(now, 20000); - tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1); - done: rend_cache_free_all(); } diff --git a/src/test/test_replay.c b/src/test/test_replay.c index e882bc6164..80e7203716 100644 --- a/src/test/test_replay.c +++ b/src/test/test_replay.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Tor Project, Inc. */ +/* Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define REPLAYCACHE_PRIVATE diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 24b0da1c46..db6b9b3872 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -450,8 +450,8 @@ test_routerkeys_ed_keys_init_all(void *arg) options->DataDirectory = dir; - tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(1, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_assert(get_master_identity_key()); tt_assert(get_master_identity_key()); tt_assert(get_master_signing_keypair()); @@ -466,7 +466,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Call load_ed_keys again, but nothing has changed. */ tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_mem_op(&auth, ==, get_current_auth_keypair(), sizeof(auth)); @@ -474,8 +474,8 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Force a reload: we make new link/auth keys. */ routerkeys_free_all(); - tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(1, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -489,7 +489,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Force a link/auth-key regeneration by advancing time. */ tt_int_op(0, ==, load_ed_keys(options, now+3*86400)); - tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400)); + tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -502,8 +502,8 @@ test_routerkeys_ed_keys_init_all(void *arg) memcpy(&auth, get_current_auth_keypair(), sizeof(auth)); /* Force a signing-key regeneration by advancing time. */ - tt_int_op(0, ==, load_ed_keys(options, now+100*86400)); - tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400)); + tt_int_op(1, ==, load_ed_keys(options, now+100*86400)); + tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, !=, get_master_signing_keypair(), sizeof(sign)); tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -520,8 +520,8 @@ test_routerkeys_ed_keys_init_all(void *arg) routerkeys_free_all(); unlink(get_fname("test_ed_keys_init_all/keys/" "ed25519_master_id_secret_key")); - tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(1, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -614,6 +614,66 @@ test_routerkeys_cross_certify_tap(void *args) crypto_pk_free(onion_key); } +static void +test_routerkeys_rsa_ed_crosscert(void *arg) +{ + (void)arg; + ed25519_public_key_t ed; + crypto_pk_t *rsa = pk_generate(2); + + uint8_t *cc = NULL; + ssize_t cc_len; + time_t expires_in = 1470846177; + + tt_int_op(0, OP_EQ, ed25519_public_from_base64(&ed, + "ThisStringCanContainAnythingSoNoKeyHereNowX")); + cc_len = tor_make_rsa_ed25519_crosscert(&ed, rsa, expires_in, &cc); + + tt_int_op(cc_len, OP_GT, 0); + tt_int_op(cc_len, OP_GT, 37); /* key, expires, siglen */ + tt_mem_op(cc, OP_EQ, ed.pubkey, 32); + time_t expires_out = 3600 * ntohl(get_uint32(cc+32)); + tt_int_op(expires_out, OP_GE, expires_in); + tt_int_op(expires_out, OP_LE, expires_in + 3600); + + tt_int_op(cc_len, OP_EQ, 37 + get_uint8(cc+36)); + + tt_int_op(0, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_in - 10)); + + /* Now try after it has expired */ + tt_int_op(-4, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_out + 1)); + + /* Truncated object */ + tt_int_op(-2, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len - 2, rsa, &ed, + expires_in - 10)); + + /* Key not as expected */ + cc[0] ^= 3; + tt_int_op(-3, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_in - 10)); + cc[0] ^= 3; + + /* Bad signature */ + cc[40] ^= 3; + tt_int_op(-5, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_in - 10)); + cc[40] ^= 3; + + /* Signature of wrong data */ + cc[0] ^= 3; + ed.pubkey[0] ^= 3; + tt_int_op(-6, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_in - 10)); + cc[0] ^= 3; + ed.pubkey[0] ^= 3; + + done: + crypto_pk_free(rsa); + tor_free(cc); +} + #define TEST(name, flags) \ { #name , test_routerkeys_ ## name, (flags), NULL, NULL } @@ -626,6 +686,7 @@ struct testcase_t routerkeys_tests[] = { TEST(ed_keys_init_all, TT_FORK), TEST(cross_certify_ntor, 0), TEST(cross_certify_tap, 0), + TEST(rsa_ed_crosscert, 0), END_OF_TESTCASES }; diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index 088bd257c3..0b4b6c5c44 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -15,6 +15,7 @@ #include "container.h" #include "directory.h" #include "dirvote.h" +#include "entrynodes.h" #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" @@ -335,30 +336,6 @@ test_router_pick_directory_server_impl(void *arg) node_router1->is_valid = 1; node_router3->is_valid = 1; - flags |= PDS_FOR_GUARD; - node_router1->using_as_guard = 1; - node_router2->using_as_guard = 1; - node_router3->using_as_guard = 1; - rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); - tt_assert(rs == NULL); - node_router1->using_as_guard = 0; - rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); - tt_assert(rs != NULL); - tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN)); - rs = NULL; - node_router2->using_as_guard = 0; - node_router3->using_as_guard = 0; - - /* One not valid, one guard. This should leave one remaining */ - node_router1->is_valid = 0; - node_router2->using_as_guard = 1; - rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); - tt_assert(rs != NULL); - tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN)); - rs = NULL; - node_router1->is_valid = 1; - node_router2->using_as_guard = 0; - /* Manipulate overloaded */ node_router2->rs->last_dir_503_at = now; @@ -420,6 +397,7 @@ test_router_pick_directory_server_impl(void *arg) done: UNMOCK(usable_consensus_flavor); + if (router1_id) tor_free(router1_id); if (router2_id) diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c index 1b526d430b..7efd042ed5 100644 --- a/src/test/test_routerset.c +++ b/src/test/test_routerset.c @@ -623,7 +623,7 @@ NS(test_main)(void *arg) (void)arg; tgt = routerset_new(); - smartlist_add(src->list, tor_strdup("{xx}")); + smartlist_add_strdup(src->list, "{xx}"); routerset_union(tgt, src); tt_int_op(smartlist_len(tgt->list), OP_NE, 0); @@ -745,7 +745,7 @@ NS(test_main)(void *arg) tt_int_op(is_empty, OP_NE, 0); set = routerset_new(); - smartlist_add(set->list, tor_strdup("{xx}")); + smartlist_add_strdup(set->list, "{xx}"); is_empty = routerset_is_empty(set); routerset_free(set); set = NULL; @@ -1616,7 +1616,7 @@ NS(test_main)(void *arg) NS_MOCK(node_get_by_nickname); NS(mock_nickname) = "foo"; - smartlist_add(set->list, tor_strdup(NS(mock_nickname))); + smartlist_add_strdup(set->list, NS(mock_nickname)); routerset_get_all_nodes(out, set, NULL, 0); out_len = smartlist_len(out); @@ -1667,7 +1667,7 @@ NS(test_main)(void *arg) NS(mock_node).is_running = 0; NS(mock_nickname) = "foo"; - smartlist_add(set->list, tor_strdup(NS(mock_nickname))); + smartlist_add_strdup(set->list, NS(mock_nickname)); routerset_get_all_nodes(out, set, NULL, 1); out_len = smartlist_len(out); @@ -1766,7 +1766,7 @@ NS(test_main)(void *arg) NS_MOCK(nodelist_get_list); - smartlist_add(set->country_names, tor_strdup("{xx}")); + smartlist_add_strdup(set->country_names, "{xx}"); NS(mock_smartlist) = smartlist_new(); routerset_get_all_nodes(out, set, NULL, 1); @@ -1813,7 +1813,7 @@ NS(test_main)(void *arg) NS_MOCK(nodelist_get_list); - smartlist_add(set->country_names, tor_strdup("{xx}")); + smartlist_add_strdup(set->country_names, "{xx}"); NS(mock_smartlist) = smartlist_new(); NS(mock_node).is_running = 0; smartlist_add(NS(mock_smartlist), (void *)&NS(mock_node)); @@ -1985,7 +1985,7 @@ NS(test_main)(void *arg) int r; (void)arg; - smartlist_add(b->list, tor_strdup("{xx}")); + smartlist_add_strdup(b->list, "{xx}"); r = routerset_equal(a, b); routerset_free(a); routerset_free(b); @@ -2010,9 +2010,9 @@ NS(test_main)(void *arg) int r; (void)arg; - smartlist_add(a->list, tor_strdup("{aa}")); - smartlist_add(b->list, tor_strdup("{b1}")); - smartlist_add(b->list, tor_strdup("{b2}")); + smartlist_add_strdup(a->list, "{aa}"); + smartlist_add_strdup(b->list, "{b1}"); + smartlist_add_strdup(b->list, "{b2}"); r = routerset_equal(a, b); routerset_free(a); routerset_free(b); @@ -2037,8 +2037,8 @@ NS(test_main)(void *arg) int r; (void)arg; - smartlist_add(a->list, tor_strdup("foo")); - smartlist_add(b->list, tor_strdup("bar")); + smartlist_add_strdup(a->list, "foo"); + smartlist_add_strdup(b->list, "bar"); r = routerset_equal(a, b); routerset_free(a); routerset_free(b); @@ -2063,8 +2063,8 @@ NS(test_main)(void *arg) int r; (void)arg; - smartlist_add(a->list, tor_strdup("foo")); - smartlist_add(b->list, tor_strdup("foo")); + smartlist_add_strdup(a->list, "foo"); + smartlist_add_strdup(b->list, "foo"); r = routerset_equal(a, b); routerset_free(a); routerset_free(b); diff --git a/src/test/test_rust.c b/src/test/test_rust.c new file mode 100644 index 0000000000..6ad57d6fcb --- /dev/null +++ b/src/test/test_rust.c @@ -0,0 +1,31 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "compat_rust.h" +#include "test.h" +#include "util.h" + +static void +test_welcome_string(void *arg) +{ + (void)arg; + rust_str_t s = rust_welcome_string(); + const char *c_str = rust_str_get(s); + tt_assert(c_str); + size_t len = strlen(c_str); +#ifdef HAVE_RUST + tt_assert(len > 0); +#else + tt_assert(len == 0); +#endif + + done: + rust_str_free(s); +} + +struct testcase_t rust_tests[] = { + { "welcome_string", test_welcome_string, 0, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh new file mode 100755 index 0000000000..d559f94ce0 --- /dev/null +++ b/src/test/test_rust.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Test all the Rust crates we're using + +crates=tor_util + +exitcode=0 + +for crate in $crates; do + cd "${abs_top_srcdir:-.}/src/rust/${crate}" + CARGO_TARGET_DIR="${abs_top_builddir}/src/rust/target" CARGO_HOME="${abs_top_builddir}/src/rust" "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} || exitcode=1 +done + +exit $exitcode diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c index 05ea8e86e8..4c536b0905 100644 --- a/src/test/test_scheduler.c +++ b/src/test/test_scheduler.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index 6a8c1abaff..8cb35be6a1 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -48,7 +48,7 @@ init_authority_state(void) mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); tt_assert(mock_cert); options->AuthoritativeDir = 1; - tt_int_op(0, ==, load_ed_keys(options, time(NULL))); + tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0); sr_state_init(0, 0); /* It's possible a commit has been generated in our state depending on * the phase we are currently in which uses "now" as the starting @@ -286,7 +286,7 @@ test_sr_commit(void *arg) tt_assert(auth_cert); options->AuthoritativeDir = 1; - tt_int_op(0, ==, load_ed_keys(options, now)); + tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0); } /* Generate our commit object and validate it has the appropriate field @@ -348,12 +348,12 @@ test_sr_commit(void *arg) /* We'll build a list of values from our commit that our parsing function * takes from a vote line and see if we can parse it correctly. */ { - smartlist_add(args, tor_strdup("1")); - smartlist_add(args, - tor_strdup(crypto_digest_algorithm_get_name(our_commit->alg))); - smartlist_add(args, tor_strdup(sr_commit_get_rsa_fpr(our_commit))); - smartlist_add(args, tor_strdup(our_commit->encoded_commit)); - smartlist_add(args, tor_strdup(our_commit->encoded_reveal)); + smartlist_add_strdup(args, "1"); + smartlist_add_strdup(args, + crypto_digest_algorithm_get_name(our_commit->alg)); + smartlist_add_strdup(args, sr_commit_get_rsa_fpr(our_commit)); + smartlist_add_strdup(args, our_commit->encoded_commit); + smartlist_add_strdup(args, our_commit->encoded_reveal); parsed_commit = sr_parse_commit(args); tt_assert(parsed_commit); /* That parsed commit should be _EXACTLY_ like our original commit (we diff --git a/src/test/test_slow.c b/src/test/test_slow.c index 7c9f0b1cc2..e640702499 100644 --- a/src/test/test_slow.c +++ b/src/test/test_slow.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/test/test_socks.c b/src/test/test_socks.c index 62ff12fe15..bb1be11f2b 100644 --- a/src/test/test_socks.c +++ b/src/test/test_socks.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_storagedir.c b/src/test/test_storagedir.c new file mode 100644 index 0000000000..19e5de4ea3 --- /dev/null +++ b/src/test/test_storagedir.c @@ -0,0 +1,375 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "storagedir.h" +#include "test.h" + +#ifdef HAVE_UTIME_H +#include <utime.h> +#endif + +static void +test_storagedir_empty(void *arg) +{ + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + (void)arg; + + tt_int_op(FN_NOENT, OP_EQ, file_status(dirname)); + + d = storage_dir_new(dirname, 10); + tt_assert(d); + + tt_int_op(FN_DIR, OP_EQ, file_status(dirname)); + + tt_int_op(0, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + storage_dir_free(d); + d = storage_dir_new(dirname, 10); + tt_assert(d); + + tt_int_op(FN_DIR, OP_EQ, file_status(dirname)); + + tt_int_op(0, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + done: + storage_dir_free(d); + tor_free(dirname); +} + +static void +test_storagedir_basic(void *arg) +{ + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + uint8_t *junk = NULL, *bytes = NULL; + const size_t junklen = 1024; + char *fname1 = NULL, *fname2 = NULL; + const char hello_str[] = "then what are we but cold, alone ... ?"; + tor_mmap_t *mapping = NULL; + (void)arg; + + junk = tor_malloc(junklen); + crypto_rand((void*)junk, junklen); + + d = storage_dir_new(dirname, 10); + tt_assert(d); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + int r; + r = storage_dir_save_string_to_file(d, hello_str, 1, &fname1); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(fname1, OP_NE, NULL); + tt_u64_op(strlen(hello_str), OP_EQ, storage_dir_get_usage(d)); + + r = storage_dir_save_bytes_to_file(d, junk, junklen, 1, &fname2); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(fname2, OP_NE, NULL); + + tt_str_op(fname1, OP_NE, fname2); + + tt_int_op(2, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(junklen + strlen(hello_str), OP_EQ, storage_dir_get_usage(d)); + tt_assert(smartlist_contains_string(storage_dir_list(d), fname1)); + tt_assert(smartlist_contains_string(storage_dir_list(d), fname2)); + + storage_dir_free(d); + d = storage_dir_new(dirname, 10); + tt_assert(d); + tt_int_op(2, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(junklen + strlen(hello_str), OP_EQ, storage_dir_get_usage(d)); + tt_assert(smartlist_contains_string(storage_dir_list(d), fname1)); + tt_assert(smartlist_contains_string(storage_dir_list(d), fname2)); + + size_t n; + bytes = storage_dir_read(d, fname2, 1, &n); + tt_assert(bytes); + tt_u64_op(n, OP_EQ, junklen); + tt_mem_op(bytes, OP_EQ, junk, junklen); + + mapping = storage_dir_map(d, fname1); + tt_assert(mapping); + tt_u64_op(mapping->size, OP_EQ, strlen(hello_str)); + tt_mem_op(mapping->data, OP_EQ, hello_str, strlen(hello_str)); + + done: + tor_free(dirname); + tor_free(junk); + tor_free(bytes); + tor_munmap_file(mapping); + storage_dir_free(d); + tor_free(fname1); + tor_free(fname2); +} + +static void +test_storagedir_deletion(void *arg) +{ + (void)arg; + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + char *fn1 = NULL, *fn2 = NULL; + char *bytes = NULL; + int r; + const char str1[] = "There are nine and sixty ways to disguise communiques"; + const char str2[] = "And rather more than one of them is right"; + + // Make sure the directory is there. */ + d = storage_dir_new(dirname, 10); + storage_dir_free(d); + d = NULL; + + tor_asprintf(&fn1, "%s/1007", dirname); + r = write_str_to_file(fn1, str1, 0); + tt_int_op(r, OP_EQ, 0); + + tor_asprintf(&fn2, "%s/1003.tmp", dirname); + r = write_str_to_file(fn2, str2, 0); + tt_int_op(r, OP_EQ, 0); + + // The tempfile should be deleted the next time we list the directory. + d = storage_dir_new(dirname, 10); + tt_int_op(1, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(strlen(str1), OP_EQ, storage_dir_get_usage(d)); + tt_int_op(FN_FILE, OP_EQ, file_status(fn1)); + tt_int_op(FN_NOENT, OP_EQ, file_status(fn2)); + + bytes = (char*) storage_dir_read(d, "1007", 1, NULL); + tt_str_op(bytes, OP_EQ, str1); + + // Should have no effect; file already gone. + storage_dir_remove_file(d, "1003.tmp"); + tt_int_op(1, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(strlen(str1), OP_EQ, storage_dir_get_usage(d)); + + // Actually remove a file. + storage_dir_remove_file(d, "1007"); + tt_int_op(FN_NOENT, OP_EQ, file_status(fn1)); + tt_int_op(0, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + done: + tor_free(dirname); + tor_free(fn1); + tor_free(fn2); + storage_dir_free(d); + tor_free(bytes); +} + +static void +test_storagedir_full(void *arg) +{ + (void)arg; + + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + const char str[] = "enemies of the peephole"; + int r; + + d = storage_dir_new(dirname, 3); + tt_assert(d); + + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, 0); + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, 0); + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, 0); + + // These should fail! + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, -1); + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, -1); + + tt_u64_op(strlen(str) * 3, OP_EQ, storage_dir_get_usage(d)); + + done: + tor_free(dirname); + storage_dir_free(d); +} + +static void +test_storagedir_cleaning(void *arg) +{ + (void)arg; + + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + const char str[] = + "On a mountain halfway between Reno and Rome / " + "We have a machine in a plexiglass dome / " + "Which listens and looks into everyone's home." + " -- Dr. Seuss"; + char *fns[8]; + int r, i; + + memset(fns, 0, sizeof(fns)); + d = storage_dir_new(dirname, 10); + tt_assert(d); + + for (i = 0; i < 8; ++i) { + r = storage_dir_save_string_to_file(d, str+i*2, 1, &fns[i]); + tt_int_op(r, OP_EQ, 0); + } + + /* Now we're going to make sure all the files have distinct mtimes. */ + time_t now = time(NULL); + struct utimbuf ub; + ub.actime = now; + ub.modtime = now - 1000; + for (i = 0; i < 8; ++i) { + char *f = NULL; + tor_asprintf(&f, "%s/%s", dirname, fns[i]); + r = utime(f, &ub); + tor_free(f); + tt_int_op(r, OP_EQ, 0); + ub.modtime += 5; + } + + const uint64_t usage_orig = storage_dir_get_usage(d); + /* No changes needed if we are already under target. */ + storage_dir_shrink(d, 1024*1024, 0); + tt_u64_op(usage_orig, OP_EQ, storage_dir_get_usage(d)); + + /* Get rid of at least one byte. This will delete fns[0]. */ + storage_dir_shrink(d, usage_orig - 1, 0); + tt_u64_op(usage_orig, OP_GT, storage_dir_get_usage(d)); + tt_u64_op(usage_orig - strlen(str), OP_EQ, storage_dir_get_usage(d)); + + /* Get rid of at least two files. This will delete fns[1] and fns[2]. */ + storage_dir_shrink(d, 1024*1024, 2); + tt_u64_op(usage_orig - strlen(str)*3 + 6, OP_EQ, storage_dir_get_usage(d)); + + /* Get rid of everything. */ + storage_dir_remove_all(d); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + done: + tor_free(dirname); + storage_dir_free(d); + for (i = 0; i < 8; ++i) { + tor_free(fns[i]); + } +} + +static void +test_storagedir_save_labeled(void *arg) +{ + (void)arg; + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + uint8_t *inp = tor_malloc_zero(8192); + config_line_t *labels = NULL; + char *fname = NULL; + uint8_t *saved = NULL; + + d = storage_dir_new(dirname, 10); + tt_assert(d); + + crypto_rand((char *)inp, 8192); + + config_line_append(&labels, "Foo", "bar baz"); + config_line_append(&labels, "quux", "quuzXxz"); + const char expected[] = + "Foo bar baz\n" + "quux quuzXxz\n"; + + int r = storage_dir_save_labeled_to_file(d, labels, inp, 8192, &fname); + tt_int_op(r, OP_EQ, 0); + + size_t n; + saved = storage_dir_read(d, fname, 1, &n); + tt_assert(memchr(saved, '\0', n)); + tt_str_op((char*)saved, OP_EQ, expected); /* NUL guarantees strcmp works */ + tt_mem_op(saved+strlen(expected)+1, OP_EQ, inp, 8192); + + done: + storage_dir_free(d); + tor_free(dirname); + tor_free(inp); + tor_free(fname); + config_free_lines(labels); + tor_free(saved); +} + +static void +test_storagedir_read_labeled(void *arg) +{ + (void)arg; + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + uint8_t *inp = tor_malloc_zero(8192); + config_line_t *labels = NULL, *labels2 = NULL; + char *fname = NULL; + tor_mmap_t *map = NULL; + uint8_t *as_read = NULL; + + d = storage_dir_new(dirname, 10); + tt_assert(d); + + tor_snprintf((char*)inp, 8192, + "Hello world\n" + "This is a test\n" + "Yadda yadda.\n"); + size_t bodylen = 8192 - strlen((char*)inp) - 1; + crypto_rand((char *)inp+strlen((char*)inp)+1, bodylen); + + int r = storage_dir_save_bytes_to_file(d, inp, 8192, 1, &fname); + tt_int_op(r, OP_EQ, 0); + + /* Try mapping */ + const uint8_t *datap = NULL; + size_t sz = 0; + map = storage_dir_map_labeled(d, fname, &labels, &datap, &sz); + tt_assert(map); + tt_assert(datap); + tt_u64_op(sz, OP_EQ, bodylen); + tt_mem_op(datap, OP_EQ, inp+strlen((char*)inp)+1, bodylen); + tt_assert(labels); + tt_str_op(labels->key, OP_EQ, "Hello"); + tt_str_op(labels->value, OP_EQ, "world"); + tt_assert(labels->next); + tt_str_op(labels->next->key, OP_EQ, "This"); + tt_str_op(labels->next->value, OP_EQ, "is a test"); + tt_assert(labels->next->next); + tt_str_op(labels->next->next->key, OP_EQ, "Yadda"); + tt_str_op(labels->next->next->value, OP_EQ, "yadda."); + tt_assert(labels->next->next->next == NULL); + + /* Try reading this time. */ + sz = 0; + as_read = storage_dir_read_labeled(d, fname, &labels2, &sz); + tt_assert(as_read); + tt_u64_op(sz, OP_EQ, bodylen); + tt_mem_op(as_read, OP_EQ, inp+strlen((char*)inp)+1, bodylen); + tt_assert(config_lines_eq(labels, labels2)); + + done: + storage_dir_free(d); + tor_free(dirname); + tor_free(inp); + tor_free(fname); + config_free_lines(labels); + config_free_lines(labels2); + tor_munmap_file(map); + tor_free(as_read); +} + +#define ENT(name) \ + { #name, test_storagedir_ ## name, TT_FORK, NULL, NULL } + +struct testcase_t storagedir_tests[] = { + ENT(empty), + ENT(basic), + ENT(deletion), + ENT(full), + ENT(cleaning), + ENT(save_labeled), + ENT(read_labeled), + END_OF_TESTCASES +}; + diff --git a/src/test/test_switch_id.c b/src/test/test_switch_id.c index e12205bb2e..53de793fe8 100644 --- a/src/test/test_switch_id.c +++ b/src/test/test_switch_id.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_threads.c b/src/test/test_threads.c index ebbc95c7ca..18a9407ff7 100644 --- a/src/test/test_threads.c +++ b/src/test/test_threads.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 47455cff83..7aa3051464 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -1,7 +1,8 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TORTLS_PRIVATE +#define TORTLS_OPENSSL_PRIVATE #define LOG_PRIVATE #include "orconfig.h" @@ -1091,13 +1092,13 @@ test_tortls_check_lifetime(void *ignored) time_t now = time(NULL); tls = tor_malloc_zero(sizeof(tor_tls_t)); - ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0); + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); tt_int_op(ret, OP_EQ, -1); tls->ssl = tor_malloc_zero(sizeof(SSL)); tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); tls->ssl->session->peer = validCert; - ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0); + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); tt_int_op(ret, OP_EQ, 0); ASN1_STRING_free(validCert->cert_info->validity->notBefore); @@ -1105,10 +1106,10 @@ test_tortls_check_lifetime(void *ignored) ASN1_STRING_free(validCert->cert_info->validity->notAfter); validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60); - ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, -1000); + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, -1000); tt_int_op(ret, OP_EQ, -1); - ret = tor_tls_check_lifetime(LOG_WARN, tls, -1000, 0); + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), -1000, 0); tt_int_op(ret, OP_EQ, -1); done: @@ -2658,18 +2659,18 @@ test_tortls_cert_is_valid(void *ignored) tor_x509_cert_t *cert = NULL, *scert = NULL; scert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 0); cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 0); tor_free(scert); tor_free(cert); cert = tor_x509_cert_new(read_cert_from(validCertString)); scert = tor_x509_cert_new(read_cert_from(caCertString)); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 1); #ifndef OPENSSL_OPAQUE @@ -2680,7 +2681,7 @@ test_tortls_cert_is_valid(void *ignored) ASN1_TIME_free(cert->cert->cert_info->validity->notAfter); cert->cert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, time(NULL)-1000000); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 0); tor_x509_cert_free(cert); @@ -2689,7 +2690,7 @@ test_tortls_cert_is_valid(void *ignored) scert = tor_x509_cert_new(read_cert_from(caCertString)); X509_PUBKEY_free(cert->cert->cert_info->key); cert->cert->cert_info->key = NULL; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); tt_int_op(ret, OP_EQ, 0); #endif @@ -2700,7 +2701,7 @@ test_tortls_cert_is_valid(void *ignored) scert = tor_x509_cert_new(read_cert_from(caCertString)); /* This doesn't actually change the key in the cert. XXXXXX */ BN_one(EVP_PKEY_get1_RSA(X509_get_pubkey(cert->cert))->n); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); tt_int_op(ret, OP_EQ, 0); tor_x509_cert_free(cert); @@ -2709,7 +2710,7 @@ test_tortls_cert_is_valid(void *ignored) scert = tor_x509_cert_new(read_cert_from(caCertString)); /* This doesn't actually change the key in the cert. XXXXXX */ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); tt_int_op(ret, OP_EQ, 0); tor_x509_cert_free(cert); @@ -2718,7 +2719,7 @@ test_tortls_cert_is_valid(void *ignored) scert = tor_x509_cert_new(read_cert_from(caCertString)); /* This doesn't actually change the key in the cert. XXXXXX */ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 1); tor_x509_cert_free(cert); @@ -2728,7 +2729,7 @@ test_tortls_cert_is_valid(void *ignored) /* This doesn't actually change the key in the cert. XXXXXX */ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; X509_get_pubkey(cert->cert)->ameth = NULL; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 0); #endif diff --git a/src/test/test_util.c b/src/test/test_util.c index 0b707caeeb..fa6ce1dc85 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -9,6 +9,7 @@ #define CONTROL_PRIVATE #define UTIL_PRIVATE #include "or.h" +#include "buffers.h" #include "config.h" #include "control.h" #include "test.h" @@ -1059,6 +1060,23 @@ test_util_time(void *arg) tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04 00:48:22.100", &t_res)); tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04 00:48:22XYZ", &t_res)); + /* but... that _is_ acceptable if we aren't being strict. */ + t_res = 0; + i = parse_iso_time_("2004-08-04 00:48:22XYZ", &t_res, 0, 0); + tt_int_op(0,OP_EQ, i); + tt_int_op(t_res,OP_EQ, (time_t)1091580502UL); + + /* try nospace variant. */ + t_res = 0; + i = parse_iso_time_nospace("2004-08-04T00:48:22", &t_res); + tt_int_op(0,OP_EQ, i); + tt_int_op(t_res,OP_EQ, (time_t)1091580502UL); + + tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04T00:48:22", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time_nospace("2004-08-04 00:48:22", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04x00:48:22", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time_nospace("2004-08-04x00:48:22", &t_res)); + /* Test tor_gettimeofday */ end.tv_sec = 4; @@ -2224,114 +2242,220 @@ test_util_pow2(void *arg) ; } -/** Run unit tests for compression functions */ static void -test_util_gzip(void *arg) +test_util_compress_impl(compress_method_t method) { - char *buf1=NULL, *buf2=NULL, *buf3=NULL, *cp1, *cp2; - const char *ccp2; + char *buf1=NULL, *buf2=NULL, *buf3=NULL; size_t len1, len2; - tor_zlib_state_t *state = NULL; - - (void)arg; - buf1 = tor_strdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ"); - tt_assert(detect_compression_method(buf1, strlen(buf1)) == UNKNOWN_METHOD); - tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1, - GZIP_METHOD)); - tt_assert(buf2); - tt_assert(len1 < strlen(buf1)); - tt_assert(detect_compression_method(buf2, len1) == GZIP_METHOD); + tt_assert(tor_compress_supports_method(method)); - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1, - GZIP_METHOD, 1, LOG_INFO)); - tt_assert(buf3); - tt_int_op(strlen(buf1) + 1,OP_EQ, len2); - tt_str_op(buf1,OP_EQ, buf3); + if (method != NO_METHOD) { + tt_assert(tor_compress_version_str(method) != NULL); + tt_assert(tor_compress_header_version_str(method) != NULL); + } - tor_free(buf2); - tor_free(buf3); + buf1 = tor_strdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ"); + tt_assert(detect_compression_method(buf1, strlen(buf1)) == UNKNOWN_METHOD); - tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1, - ZLIB_METHOD)); - tt_assert(buf2); - tt_assert(detect_compression_method(buf2, len1) == ZLIB_METHOD); + tt_assert(!tor_compress(&buf2, &len1, buf1, strlen(buf1)+1, method)); + tt_assert(buf2 != NULL); + if (method == NO_METHOD) { + // The identity transform doesn't actually compress, and it isn't + // detectable as "the identity transform." + tt_int_op(len1, OP_EQ, strlen(buf1)+1); + tt_int_op(detect_compression_method(buf2, len1), OP_EQ, UNKNOWN_METHOD); + } else { + tt_int_op(len1, OP_LT, strlen(buf1)); + tt_int_op(detect_compression_method(buf2, len1), OP_EQ, method); + } - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1, - ZLIB_METHOD, 1, LOG_INFO)); - tt_assert(buf3); - tt_int_op(strlen(buf1) + 1,OP_EQ, len2); - tt_str_op(buf1,OP_EQ, buf3); + tt_assert(!tor_uncompress(&buf3, &len2, buf2, len1, method, 1, LOG_INFO)); + tt_assert(buf3 != NULL); + tt_int_op(strlen(buf1) + 1, OP_EQ, len2); + tt_str_op(buf1, OP_EQ, buf3); + tt_int_op(buf3[len2], OP_EQ, 0); /* Check whether we can uncompress concatenated, compressed strings. */ tor_free(buf3); buf2 = tor_reallocarray(buf2, len1, 2); memcpy(buf2+len1, buf2, len1); - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1*2, - ZLIB_METHOD, 1, LOG_INFO)); - tt_int_op((strlen(buf1)+1)*2,OP_EQ, len2); - tt_mem_op(buf3,OP_EQ, + tt_assert(!tor_uncompress(&buf3, &len2, buf2, len1*2, method, 1, LOG_INFO)); + tt_int_op((strlen(buf1)+1)*2, OP_EQ, len2); + tt_mem_op(buf3, OP_EQ, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ\0" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ\0", (strlen(buf1)+1)*2); + tt_int_op(buf3[len2], OP_EQ, 0); + + /* Check whether we can uncompress partial strings */ tor_free(buf1); tor_free(buf2); tor_free(buf3); - /* Check whether we can uncompress partial strings. */ - buf1 = - tor_strdup("String with low redundancy that won't be compressed much."); - tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1, - ZLIB_METHOD)); - tt_assert(len1>16); - /* when we allow an incomplete string, we should succeed.*/ - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1-16, - ZLIB_METHOD, 0, LOG_INFO)); - tt_assert(len2 > 5); - buf3[len2]='\0'; - tt_assert(!strcmpstart(buf1, buf3)); - - /* when we demand a complete string, this must fail. */ + size_t b1len = 1<<10; + if (method == ZSTD_METHOD) { + // zstd needs a big input before it starts generating output that it + // can partially decompress. + b1len = 1<<18; + } + buf1 = tor_malloc(b1len); + crypto_rand(buf1, b1len); + tt_assert(!tor_compress(&buf2, &len1, buf1, b1len, method)); + tt_int_op(len1, OP_GT, 16); + /* when we allow an incomplete output we should succeed.*/ + tt_assert(!tor_uncompress(&buf3, &len2, buf2, len1-16, + method, 0, LOG_INFO)); + tt_int_op(len2, OP_GT, 5); + tt_int_op(len2, OP_LE, len1); + tt_assert(fast_memeq(buf1, buf3, len2)); + tt_int_op(buf3[len2], OP_EQ, 0); + + /* when we demand a complete output from a real compression method, this + * must fail. */ tor_free(buf3); - tt_assert(tor_gzip_uncompress(&buf3, &len2, buf2, len1-16, - ZLIB_METHOD, 1, LOG_INFO)); - tt_assert(!buf3); + if (method != NO_METHOD) { + tt_assert(tor_uncompress(&buf3, &len2, buf2, len1-16, + method, 1, LOG_INFO)); + tt_assert(buf3 == NULL); + } - /* Now, try streaming compression. */ + done: tor_free(buf1); tor_free(buf2); tor_free(buf3); - state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); +} + +static void +test_util_compress_stream_impl(compress_method_t method, + compression_level_t level) +{ + char *buf1=NULL, *buf2=NULL, *buf3=NULL, *cp1, *cp2; + const char *ccp2; + size_t len1, len2; + + tor_compress_state_t *state = NULL; + state = tor_compress_new(1, method, level); tt_assert(state); cp1 = buf1 = tor_malloc(1024); len1 = 1024; ccp2 = "ABCDEFGHIJABCDEFGHIJ"; len2 = 21; - tt_assert(tor_zlib_process(state, &cp1, &len1, &ccp2, &len2, 0) - == TOR_ZLIB_OK); - tt_int_op(0,OP_EQ, len2); /* Make sure we compressed it all. */ + tt_int_op(tor_compress_process(state, &cp1, &len1, &ccp2, &len2, 0), + OP_EQ, TOR_COMPRESS_OK); + tt_int_op(0, OP_EQ, len2); /* Make sure we compressed it all. */ tt_assert(cp1 > buf1); len2 = 0; cp2 = cp1; - tt_assert(tor_zlib_process(state, &cp1, &len1, &ccp2, &len2, 1) - == TOR_ZLIB_DONE); - tt_int_op(0,OP_EQ, len2); - tt_assert(cp1 > cp2); /* Make sure we really added something. */ + tt_int_op(tor_compress_process(state, &cp1, &len1, &ccp2, &len2, 1), + OP_EQ, TOR_COMPRESS_DONE); + tt_int_op(0, OP_EQ, len2); + if (method == NO_METHOD) { + tt_ptr_op(cp1, OP_EQ, cp2); + } else { + tt_assert(cp1 > cp2); /* Make sure we really added something. */ + } - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf1, 1024-len1, - ZLIB_METHOD, 1, LOG_WARN)); + tt_int_op(tor_compress_state_size(state), OP_GT, 0); + + tt_assert(!tor_uncompress(&buf3, &len2, buf1, 1024-len1, + method, 1, LOG_WARN)); /* Make sure it compressed right. */ tt_str_op(buf3, OP_EQ, "ABCDEFGHIJABCDEFGHIJ"); - tt_int_op(21,OP_EQ, len2); + tt_int_op(21, OP_EQ, len2); done: if (state) - tor_zlib_free(state); + tor_compress_free(state); + tor_free(buf1); tor_free(buf2); tor_free(buf3); - tor_free(buf1); +} + +/** Run unit tests for compression functions */ +static void +test_util_compress(void *arg) +{ + const char *methodname = arg; + tt_assert(methodname); + + compress_method_t method = compression_method_get_by_name(methodname); + tt_int_op(method, OP_NE, UNKNOWN_METHOD); + + if (! tor_compress_supports_method(method)) { + tt_skip(); + } + + compression_level_t levels[] = { + BEST_COMPRESSION, + HIGH_COMPRESSION, + MEDIUM_COMPRESSION, + LOW_COMPRESSION + }; + + test_util_compress_impl(method); + + for (unsigned l = 0; l < ARRAY_LENGTH(levels); ++l) { + compression_level_t level = levels[l]; + test_util_compress_stream_impl(method, level); + } + done: + ; +} + +static void +test_util_decompress_concatenated_impl(compress_method_t method) +{ + char input[4096]; + char *c1 = NULL, *c2 = NULL, *c3 = NULL; + char *result = NULL; + size_t sz1, sz2, sz3, szr; + int r; + + crypto_rand(input, sizeof(input)); + + /* Compress the input in two chunks. */ + r = tor_compress(&c1, &sz1, input, 2048, method); + tt_int_op(r, OP_EQ, 0); + r = tor_compress(&c2, &sz2, input+2048, 2048, method); + tt_int_op(r, OP_EQ, 0); + + /* concatenate the chunks. */ + sz3 = sz1 + sz2; + c3 = tor_malloc(sz3); + memcpy(c3, c1, sz1); + memcpy(c3+sz1, c2, sz2); + + /* decompress the concatenated result */ + r = tor_uncompress(&result, &szr, c3, sz3, method, 0, LOG_WARN); + tt_int_op(r, OP_EQ, 0); + tt_int_op(szr, OP_EQ, sizeof(input)); + tt_mem_op(result, OP_EQ, input, sizeof(input)); + + done: + tor_free(c1); + tor_free(c2); + tor_free(c3); + tor_free(result); +} + +static void +test_util_decompress_concatenated(void *arg) +{ + const char *methodname = arg; + tt_assert(methodname); + + compress_method_t method = compression_method_get_by_name(methodname); + tt_int_op(method, OP_NE, UNKNOWN_METHOD); + if (! tor_compress_supports_method(method)) { + tt_skip(); + } + + test_util_decompress_concatenated_impl(method); + done: + ; } static void @@ -2346,44 +2470,44 @@ test_util_gzip_compression_bomb(void *arg) char *one_mb = tor_malloc_zero(one_million); char *result = NULL; size_t result_len = 0; - tor_zlib_state_t *state = NULL; + tor_compress_state_t *state = NULL; /* Make sure we can't produce a compression bomb */ setup_full_capture_of_logs(LOG_WARN); - tt_int_op(-1, OP_EQ, tor_gzip_compress(&result, &result_len, - one_mb, one_million, - ZLIB_METHOD)); + tt_int_op(-1, OP_EQ, tor_compress(&result, &result_len, + one_mb, one_million, + ZLIB_METHOD)); expect_single_log_msg_containing( "We compressed something and got an insanely high " "compression factor; other Tors would think this " - "was a zlib bomb."); + "was a compression bomb."); teardown_capture_of_logs(); /* Here's a compression bomb that we made manually. */ const char compression_bomb[1039] = { 0x78, 0xDA, 0xED, 0xC1, 0x31, 0x01, 0x00, 0x00, 0x00, 0xC2, 0xA0, 0xF5, 0x4F, 0x6D, 0x08, 0x5F, 0xA0 /* .... */ }; - tt_int_op(-1, OP_EQ, tor_gzip_uncompress(&result, &result_len, - compression_bomb, 1039, - ZLIB_METHOD, 0, LOG_WARN)); + tt_int_op(-1, OP_EQ, tor_uncompress(&result, &result_len, + compression_bomb, 1039, + ZLIB_METHOD, 0, LOG_WARN)); /* Now try streaming that. */ - state = tor_zlib_new(0, ZLIB_METHOD, HIGH_COMPRESSION); - tor_zlib_output_t r; + state = tor_compress_new(0, ZLIB_METHOD, HIGH_COMPRESSION); + tor_compress_output_t r; const char *inp = compression_bomb; size_t inlen = 1039; do { char *outp = one_mb; size_t outleft = 4096; /* small on purpose */ - r = tor_zlib_process(state, &outp, &outleft, &inp, &inlen, 0); + r = tor_compress_process(state, &outp, &outleft, &inp, &inlen, 0); tt_int_op(inlen, OP_NE, 0); - } while (r == TOR_ZLIB_BUF_FULL); + } while (r == TOR_COMPRESS_BUFFER_FULL); - tt_int_op(r, OP_EQ, TOR_ZLIB_ERR); + tt_int_op(r, OP_EQ, TOR_COMPRESS_ERROR); done: tor_free(one_mb); - tor_zlib_free(state); + tor_compress_free(state); } /** Run unit tests for mmap() wrapper functionality. */ @@ -3333,6 +3457,13 @@ test_util_memarea(void *arg) void *malloced_ptr = NULL; int i; +#ifdef DISABLE_MEMORY_SENTINELS + /* If memory sentinels are disabled, this whole module is just an alias for + malloc(), which is free to lay out memory most any way it wants. */ + if (1) + tt_skip(); +#endif + (void)arg; tt_assert(area); @@ -3926,17 +4057,13 @@ test_util_exit_status(void *ptr) #endif #ifndef _WIN32 -/* Check that fgets with a non-blocking pipe returns partial lines and sets - * EAGAIN, returns full lines and sets no error, and returns NULL on EOF and - * sets no error */ static void -test_util_fgets_eagain(void *ptr) +test_util_string_from_pipe(void *ptr) { int test_pipe[2] = {-1, -1}; - int retval; + int retval = 0; + enum stream_status status = IO_STREAM_TERM; ssize_t retlen; - char *retptr; - FILE *test_stream = NULL; char buf[4] = { 0 }; (void)ptr; @@ -3947,91 +4074,115 @@ test_util_fgets_eagain(void *ptr) retval = pipe(test_pipe); tt_int_op(retval, OP_EQ, 0); - /* Set up the read-end to be non-blocking */ - retval = fcntl(test_pipe[0], F_SETFL, O_NONBLOCK); - tt_int_op(retval, OP_EQ, 0); + /* Send in a string. */ + retlen = write(test_pipe[1], "ABC", 3); + tt_int_op(retlen, OP_EQ, 3); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); + tt_int_op(errno, OP_EQ, 0); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "ABC"); + errno = 0; - /* Open it as a stdio stream */ - test_stream = fdopen(test_pipe[0], "r"); - tt_ptr_op(test_stream, OP_NE, NULL); + /* Send in a string that contains a nul. */ + retlen = write(test_pipe[1], "AB\0", 3); + tt_int_op(retlen, OP_EQ, 3); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); + tt_int_op(errno, OP_EQ, 0); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "AB"); + errno = 0; - /* Send in a partial line */ - retlen = write(test_pipe[1], "A", 1); + /* Send in a string that contains a nul only. */ + retlen = write(test_pipe[1], "\0", 1); tt_int_op(retlen, OP_EQ, 1); - retptr = fgets(buf, sizeof(buf), test_stream); - tt_int_op(errno, OP_EQ, EAGAIN); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "A"); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); + tt_int_op(errno, OP_EQ, 0); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, ""); errno = 0; - /* Send in the rest */ - retlen = write(test_pipe[1], "B\n", 2); - tt_int_op(retlen, OP_EQ, 2); - retptr = fgets(buf, sizeof(buf), test_stream); + /* Send in a string that contains a trailing newline. */ + retlen = write(test_pipe[1], "AB\n", 3); + tt_int_op(retlen, OP_EQ, 3); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); + tt_int_op(errno, OP_EQ, 0); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "AB"); + errno = 0; + + /* Send in a string that contains a newline only. */ + retlen = write(test_pipe[1], "\n", 1); + tt_int_op(retlen, OP_EQ, 1); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "B\n"); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, ""); errno = 0; - /* Send in a full line */ - retlen = write(test_pipe[1], "CD\n", 3); + /* Send in a string and check that we nul terminate return values. */ + retlen = write(test_pipe[1], "AAA", 3); tt_int_op(retlen, OP_EQ, 3); - retptr = fgets(buf, sizeof(buf), test_stream); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "CD\n"); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "AAA"); + tt_mem_op(buf, OP_EQ, "AAA\0", sizeof(buf)); errno = 0; - /* Send in a partial line */ - retlen = write(test_pipe[1], "E", 1); + retlen = write(test_pipe[1], "B", 1); tt_int_op(retlen, OP_EQ, 1); - retptr = fgets(buf, sizeof(buf), test_stream); - tt_int_op(errno, OP_EQ, EAGAIN); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "E"); + + memset(buf, '\xff', sizeof(buf)); + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); + tt_int_op(errno, OP_EQ, 0); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "B"); + tt_mem_op(buf, OP_EQ, "B\0\xff\xff", sizeof(buf)); errno = 0; - /* Send in the rest */ - retlen = write(test_pipe[1], "F\n", 2); - tt_int_op(retlen, OP_EQ, 2); - retptr = fgets(buf, sizeof(buf), test_stream); + /* Send in multiple lines. */ + retlen = write(test_pipe[1], "A\nB", 3); + tt_int_op(retlen, OP_EQ, 3); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "F\n"); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "A\nB"); errno = 0; - /* Send in a full line and close */ - retlen = write(test_pipe[1], "GH", 2); + /* Send in a line and close */ + retlen = write(test_pipe[1], "AB", 2); tt_int_op(retlen, OP_EQ, 2); retval = close(test_pipe[1]); tt_int_op(retval, OP_EQ, 0); test_pipe[1] = -1; - retptr = fgets(buf, sizeof(buf), test_stream); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "GH"); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "AB"); errno = 0; /* Check for EOF */ - retptr = fgets(buf, sizeof(buf), test_stream); + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, NULL); - retval = feof(test_stream); - tt_int_op(retval, OP_NE, 0); + tt_int_op(status, OP_EQ, IO_STREAM_CLOSED); errno = 0; - /* Check that buf is unchanged according to C99 and C11 */ - tt_str_op(buf, OP_EQ, "GH"); - done: - if (test_stream != NULL) - fclose(test_stream); if (test_pipe[0] != -1) close(test_pipe[0]); if (test_pipe[1] != -1) close(test_pipe[1]); } -#endif + +#endif // _WIN32 /** * Test for format_hex_number_sigsafe() @@ -5085,7 +5236,8 @@ test_util_socket(void *arg) fd1 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 0, 0); int err = tor_socket_errno(fd1); - if (fd1 < 0 && err == SOCK_ERRNO(EPROTONOSUPPORT)) { + if (fd1 < 0 && (err == SOCK_ERRNO(EPROTONOSUPPORT) || + err == SOCK_ERRNO(EAFNOSUPPORT))) { /* Assume we're on an IPv4-only or IPv6-only system, and give up now. */ goto done; } @@ -5473,26 +5625,26 @@ test_util_calloc_check(void *arg) { (void) arg; /* Easy cases that are good. */ - tt_assert(size_mul_check__(0,0)); - tt_assert(size_mul_check__(0,100)); - tt_assert(size_mul_check__(100,0)); - tt_assert(size_mul_check__(100,100)); + tt_assert(size_mul_check(0,0)); + tt_assert(size_mul_check(0,100)); + tt_assert(size_mul_check(100,0)); + tt_assert(size_mul_check(100,100)); /* Harder cases that are still good. */ - tt_assert(size_mul_check__(SIZE_MAX, 1)); - tt_assert(size_mul_check__(1, SIZE_MAX)); - tt_assert(size_mul_check__(SIZE_MAX / 10, 9)); - tt_assert(size_mul_check__(11, SIZE_MAX / 12)); + tt_assert(size_mul_check(SIZE_MAX, 1)); + tt_assert(size_mul_check(1, SIZE_MAX)); + tt_assert(size_mul_check(SIZE_MAX / 10, 9)); + tt_assert(size_mul_check(11, SIZE_MAX / 12)); const size_t sqrt_size_max_p1 = ((size_t)1) << (sizeof(size_t) * 4); - tt_assert(size_mul_check__(sqrt_size_max_p1, sqrt_size_max_p1 - 1)); + tt_assert(size_mul_check(sqrt_size_max_p1, sqrt_size_max_p1 - 1)); /* Cases that overflow */ - tt_assert(! size_mul_check__(SIZE_MAX, 2)); - tt_assert(! size_mul_check__(2, SIZE_MAX)); - tt_assert(! size_mul_check__(SIZE_MAX / 10, 11)); - tt_assert(! size_mul_check__(11, SIZE_MAX / 10)); - tt_assert(! size_mul_check__(SIZE_MAX / 8, 9)); - tt_assert(! size_mul_check__(sqrt_size_max_p1, sqrt_size_max_p1)); + tt_assert(! size_mul_check(SIZE_MAX, 2)); + tt_assert(! size_mul_check(2, SIZE_MAX)); + tt_assert(! size_mul_check(SIZE_MAX / 10, 11)); + tt_assert(! size_mul_check(11, SIZE_MAX / 10)); + tt_assert(! size_mul_check(SIZE_MAX / 8, 9)); + tt_assert(! size_mul_check(sqrt_size_max_p1, sqrt_size_max_p1)); done: ; @@ -5623,12 +5775,126 @@ test_util_monotonic_time_ratchet(void *arg) ; } +static void +test_util_htonll(void *arg) +{ + (void)arg; +#ifdef WORDS_BIGENDIAN + const uint64_t res_be = 0x8877665544332211; +#else + const uint64_t res_le = 0x1122334455667788; +#endif + + tt_u64_op(0, OP_EQ, tor_htonll(0)); + tt_u64_op(0, OP_EQ, tor_ntohll(0)); + tt_u64_op(UINT64_MAX, OP_EQ, tor_htonll(UINT64_MAX)); + tt_u64_op(UINT64_MAX, OP_EQ, tor_ntohll(UINT64_MAX)); + +#ifdef WORDS_BIGENDIAN + tt_u64_op(res_be, OP_EQ, tor_htonll(0x8877665544332211)); + tt_u64_op(res_be, OP_EQ, tor_ntohll(0x8877665544332211)); +#else + tt_u64_op(res_le, OP_EQ, tor_htonll(0x8877665544332211)); + tt_u64_op(res_le, OP_EQ, tor_ntohll(0x8877665544332211)); +#endif + + done: + ; +} + +static void +test_util_get_unquoted_path(void *arg) +{ + (void)arg; + + char *r = NULL; + + r = get_unquoted_path("\""); // " + tt_ptr_op(r, OP_EQ, NULL); + tor_free(r); + + r = get_unquoted_path("\"\"\""); // """ + tt_ptr_op(r, OP_EQ, NULL); + tor_free(r); + + r = get_unquoted_path("\\\""); // \" + tt_ptr_op(r, OP_EQ, NULL); + tor_free(r); + + r = get_unquoted_path("\\\"\\\""); // \"\" + tt_ptr_op(r, OP_EQ, NULL); + tor_free(r); + + r = get_unquoted_path("A\\B\\C\""); // A\B\C" + tt_ptr_op(r, OP_EQ, NULL); + tor_free(r); + + r = get_unquoted_path("\"A\\B\\C"); // "A\B\C + tt_ptr_op(r, OP_EQ, NULL); + tor_free(r); + + r = get_unquoted_path("\"A\\B\"C\""); // "A\B"C" + tt_ptr_op(r, OP_EQ, NULL); + tor_free(r); + + r = get_unquoted_path("A\\B\"C"); // A\B"C + tt_ptr_op(r, OP_EQ, NULL); + tor_free(r); + + r = get_unquoted_path(""); + tt_str_op(r, OP_EQ, ""); + tor_free(r); + + r = get_unquoted_path("\"\""); // "" + tt_str_op(r, OP_EQ, ""); + tor_free(r); + + r = get_unquoted_path("A\\B\\C"); // A\B\C + tt_str_op(r, OP_EQ, "A\\B\\C"); // A\B\C + tor_free(r); + + r = get_unquoted_path("\"A\\B\\C\""); // "A\B\C" + tt_str_op(r, OP_EQ, "A\\B\\C"); // A\B\C + tor_free(r); + + r = get_unquoted_path("\"\\\""); // "\" + tt_str_op(r, OP_EQ, "\\"); // \ /* comment to prevent line continuation */ + tor_free(r); + + r = get_unquoted_path("\"\\\"\""); // "\"" + tt_str_op(r, OP_EQ, "\""); // " + tor_free(r); + + r = get_unquoted_path("\"A\\B\\C\\\"\""); // "A\B\C\"" + tt_str_op(r, OP_EQ, "A\\B\\C\""); // A\B\C" + tor_free(r); + + r = get_unquoted_path("A\\B\\\"C"); // A\B\"C + tt_str_op(r, OP_EQ, "A\\B\"C"); // A\B"C + tor_free(r); + + r = get_unquoted_path("\"A\\B\\\"C\""); // "A\B\"C" + tt_str_op(r, OP_EQ, "A\\B\"C"); // A\B"C + + done: + tor_free(r); +} + #define UTIL_LEGACY(name) \ { #name, test_util_ ## name , 0, NULL, NULL } #define UTIL_TEST(name, flags) \ { #name, test_util_ ## name, flags, NULL, NULL } +#define COMPRESS(name, identifier) \ + { "compress/" #name, test_util_compress, 0, &passthrough_setup, \ + (char*)(identifier) } + +#define COMPRESS_CONCAT(name, identifier) \ + { "compress_concat/" #name, test_util_decompress_concatenated, 0, \ + &passthrough_setup, \ + (char*)(identifier) } + #ifdef _WIN32 #define UTIL_TEST_NO_WIN(n, f) { #n, NULL, TT_SKIP, NULL, NULL } #define UTIL_TEST_WIN_ONLY(n, f) UTIL_TEST(n, (f)) @@ -5653,7 +5919,16 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(strmisc), UTIL_TEST(parse_integer, 0), UTIL_LEGACY(pow2), - UTIL_LEGACY(gzip), + COMPRESS(zlib, "deflate"), + COMPRESS(gzip, "gzip"), + COMPRESS(lzma, "x-tor-lzma"), + COMPRESS(zstd, "x-zstd"), + COMPRESS(none, "identity"), + COMPRESS_CONCAT(zlib, "deflate"), + COMPRESS_CONCAT(gzip, "gzip"), + COMPRESS_CONCAT(lzma, "x-tor-lzma"), + COMPRESS_CONCAT(zstd, "x-zstd"), + COMPRESS_CONCAT(none, "identity"), UTIL_TEST(gzip_compression_bomb, TT_FORK), UTIL_LEGACY(datadir), UTIL_LEGACY(memarea), @@ -5677,7 +5952,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(num_cpus, 0), UTIL_TEST_WIN_ONLY(load_win_lib, 0), UTIL_TEST_NO_WIN(exit_status, 0), - UTIL_TEST_NO_WIN(fgets_eagain, 0), + UTIL_TEST_NO_WIN(string_from_pipe, 0), UTIL_TEST(format_hex_number, 0), UTIL_TEST(format_dec_number, 0), UTIL_TEST(join_win_cmdline, 0), @@ -5716,6 +5991,8 @@ struct testcase_t util_tests[] = { UTIL_TEST(calloc_check, 0), UTIL_TEST(monotonic_time, 0), UTIL_TEST(monotonic_time_ratchet, TT_FORK), + UTIL_TEST(htonll, 0), + UTIL_TEST(get_unquoted_path, 0), END_OF_TESTCASES }; diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c index 63a668238c..ea0a86499f 100644 --- a/src/test/test_util_format.c +++ b/src/test/test_util_format.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -11,25 +11,14 @@ #define NS_MODULE util_format -#if !defined(HAVE_HTONLL) && !defined(htonll) -#ifdef WORDS_BIGENDIAN -#define htonll(x) (x) -#else -static uint64_t -htonll(uint64_t a) -{ - return htonl((uint32_t)(a>>32)) | (((uint64_t)htonl((uint32_t)a))<<32); -} -#endif -#endif - static void test_util_format_unaligned_accessors(void *ignored) { (void)ignored; char buf[9] = "onionsoup"; // 6f6e696f6e736f7570 - tt_u64_op(get_uint64(buf+1), OP_EQ, htonll(U64_LITERAL(0x6e696f6e736f7570))); + tt_u64_op(get_uint64(buf+1), OP_EQ, + tor_htonll(U64_LITERAL(0x6e696f6e736f7570))); tt_uint_op(get_uint32(buf+1), OP_EQ, htonl(0x6e696f6e)); tt_uint_op(get_uint16(buf+1), OP_EQ, htons(0x6e69)); tt_uint_op(get_uint8(buf+1), OP_EQ, 0x6e); @@ -43,7 +32,7 @@ test_util_format_unaligned_accessors(void *ignored) set_uint32(buf+1, htonl(0x78696465)); tt_mem_op(buf, OP_EQ, "oxidestop", 9); - set_uint64(buf+1, htonll(U64_LITERAL(0x6266757363617465))); + set_uint64(buf+1, tor_htonll(U64_LITERAL(0x6266757363617465))); tt_mem_op(buf, OP_EQ, "obfuscate", 9); done: ; @@ -144,48 +133,54 @@ test_util_format_base64_encode(void *ignored) } static void -test_util_format_base64_decode_nopad(void *ignored) +test_util_format_base64_decode_oddsize(void *ignored) { (void)ignored; int res; int i; char *src; - uint8_t *dst, *real_dst; - uint8_t expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65}; + char *dst, real_dst[7]; + char expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65}; char real_src[] = "ZXhhbXBsZQ"; + char expected40[] = "testing40characteroddsizebase64encoding!"; + char src40[] = "dGVzdGluZzQwY2hhcmFjdGVyb2Rkc2l6ZWJhc2U2NGVuY29kaW5nIQ"; + char pad40[] = "dGVzdGluZzQwY2hhcmFjdGVyb2Rkc2l6ZWJhc2U2NGVuY29kaW5nIQ=="; src = tor_malloc_zero(256); dst = tor_malloc_zero(1000); - real_dst = tor_malloc_zero(10); for (i=0;i<256;i++) { src[i] = (char)i; } - res = base64_decode_nopad(dst, 1, src, SIZE_T_CEILING); - tt_int_op(res, OP_EQ, -1); - - res = base64_decode_nopad(dst, 1, src, 5); + res = base64_decode(dst, 1, src, 5); tt_int_op(res, OP_EQ, -1); const char *s = "SGVsbG8gd29ybGQ"; - res = base64_decode_nopad(dst, 1000, s, strlen(s)); + res = base64_decode(dst, 1000, s, strlen(s)); tt_int_op(res, OP_EQ, 11); tt_mem_op(dst, OP_EQ, "Hello world", 11); s = "T3BhIG11bmRv"; - res = base64_decode_nopad(dst, 9, s, strlen(s)); + res = base64_decode(dst, 9, s, strlen(s)); tt_int_op(res, OP_EQ, 9); tt_mem_op(dst, OP_EQ, "Opa mundo", 9); - res = base64_decode_nopad(real_dst, 10, real_src, 10); + res = base64_decode(real_dst, sizeof(real_dst), real_src, 10); tt_int_op(res, OP_EQ, 7); tt_mem_op(real_dst, OP_EQ, expected, 7); + res = base64_decode(dst, 40, src40, strlen(src40)); + tt_int_op(res, OP_EQ, 40); + tt_mem_op(dst, OP_EQ, expected40, 40); + + res = base64_decode(dst, 40, pad40, strlen(pad40)); + tt_int_op(res, OP_EQ, 40); + tt_mem_op(dst, OP_EQ, expected40, 40); + done: tor_free(src); tor_free(dst); - tor_free(real_dst); } static void @@ -207,10 +202,10 @@ test_util_format_base64_decode(void *ignored) src[i] = (char)i; } - res = base64_decode(dst, 1, src, SIZE_T_CEILING); + res = base64_decode(dst, 1, src, 100); tt_int_op(res, OP_EQ, -1); - res = base64_decode(dst, SIZE_T_CEILING+1, src, 10); + res = base64_decode(dst, 1, real_src, 10); tt_int_op(res, OP_EQ, -1); const char *s = "T3BhIG11bmRv"; @@ -378,11 +373,39 @@ test_util_format_base32_decode(void *arg) tor_free(dst); } +static void +test_util_format_encoded_size(void *arg) +{ + (void)arg; + uint8_t inbuf[256]; + char outbuf[1024]; + unsigned i; + + crypto_rand((char *)inbuf, sizeof(inbuf)); + for (i = 0; i <= sizeof(inbuf); ++i) { + /* XXXX (Once the return values are consistent, check them too.) */ + + base32_encode(outbuf, sizeof(outbuf), (char *)inbuf, i); + /* The "+ 1" below is an API inconsistency. */ + tt_int_op(strlen(outbuf) + 1, OP_EQ, base32_encoded_size(i)); + + base64_encode(outbuf, sizeof(outbuf), (char *)inbuf, i, 0); + tt_int_op(strlen(outbuf), OP_EQ, base64_encode_size(i, 0)); + base64_encode(outbuf, sizeof(outbuf), (char *)inbuf, i, + BASE64_ENCODE_MULTILINE); + tt_int_op(strlen(outbuf), OP_EQ, + base64_encode_size(i, BASE64_ENCODE_MULTILINE)); + } + + done: + ; +} + struct testcase_t util_format_tests[] = { { "unaligned_accessors", test_util_format_unaligned_accessors, 0, NULL, NULL }, { "base64_encode", test_util_format_base64_encode, 0, NULL, NULL }, - { "base64_decode_nopad", test_util_format_base64_decode_nopad, 0, + { "base64_decode_oddsize", test_util_format_base64_decode_oddsize, 0, NULL, NULL }, { "base64_decode", test_util_format_base64_decode, 0, NULL, NULL }, { "base16_decode", test_util_format_base16_decode, 0, NULL, NULL }, @@ -390,6 +413,7 @@ struct testcase_t util_format_tests[] = { NULL, NULL }, { "base32_decode", test_util_format_base32_decode, 0, NULL, NULL }, + { "encoded_size", test_util_format_encoded_size, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_util_process.c b/src/test/test_util_process.c index 4e75b97f3d..70292f2287 100644 --- a/src/test/test_util_process.c +++ b/src/test/test_util_process.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define UTIL_PROCESS_PRIVATE diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c index 1e7160598c..3e5d78948d 100644 --- a/src/test/test_util_slow.c +++ b/src/test/test_util_slow.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -242,7 +242,7 @@ test_util_spawn_background_partial_read_impl(int exit_early) #else /* Check that we didn't read the end of file last time */ tt_assert(!eof); - pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, NULL, &eof); #endif log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos); @@ -273,7 +273,7 @@ test_util_spawn_background_partial_read_impl(int exit_early) #else if (!eof) { /* We should have got all the data, but maybe not the EOF flag */ - pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, process_handle, &eof); tt_int_op(0,OP_EQ, pos); diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c index ccb8d0c8ca..6fa46f90d4 100644 --- a/src/test/test_workqueue.c +++ b/src/test/test_workqueue.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" @@ -200,7 +200,9 @@ add_work(threadpool_t *tp) crypto_rand((char*)w->msg, 20); w->msglen = 20; ++rsa_sent; - return threadpool_queue_work(tp, workqueue_do_rsa, handle_reply, w); + return threadpool_queue_work_priority(tp, + WQ_PRI_MED, + workqueue_do_rsa, handle_reply, w); } else { ecdh_work_t *w = tor_malloc_zero(sizeof(*w)); w->serial = n_sent++; diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 9c6580f788..d7e36edbc0 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ extern const char tor_git_revision[]; @@ -21,6 +21,7 @@ const char tor_git_revision[] = ""; #include "rephist.h" #include "backtrace.h" #include "test.h" +#include "channelpadding.h" #include <stdio.h> #ifdef HAVE_FCNTL_H @@ -38,7 +39,6 @@ const char tor_git_revision[] = ""; #ifdef USE_DMALLOC #include <dmalloc.h> -#include <openssl/crypto.h> #include "main.h" #endif @@ -178,65 +178,6 @@ remove_directory(void) rm_rf(temp_dir); } -/** Define this if unit tests spend too much time generating public keys*/ -#define CACHE_GENERATED_KEYS - -#define N_PREGEN_KEYS 11 -static crypto_pk_t *pregen_keys[N_PREGEN_KEYS]; -static int next_key_idx; - -/** Generate and return a new keypair for use in unit tests. If we're using - * the key cache optimization, we might reuse keys. "idx" is ignored. - * Our only guarantee is that we won't reuse a key till this function has been - * called several times. The order in which keys are returned is slightly - * randomized, so that tests that depend on a particular order will not be - * reliable. */ -crypto_pk_t * -pk_generate(int idx) -{ - (void) idx; -#ifdef CACHE_GENERATED_KEYS - /* Either skip 1 or 2 keys. */ - next_key_idx += crypto_rand_int_range(1,3); - next_key_idx %= N_PREGEN_KEYS; - return crypto_pk_dup_key(pregen_keys[next_key_idx]); -#else - crypto_pk_t *result; - int res; - result = crypto_pk_new(); - res = crypto_pk_generate_key__real(result); - tor_assert(!res); - return result; -#endif -} - -#ifdef CACHE_GENERATED_KEYS -static int -crypto_pk_generate_key_with_bits__get_cached(crypto_pk_t *env, int bits) -{ - if (bits != 1024) - return crypto_pk_generate_key_with_bits__real(env, bits); - - crypto_pk_t *newkey = pk_generate(0); - crypto_pk_assign_(env, newkey); - crypto_pk_free(newkey); - return 0; -} -#endif - -/** Free all storage used for the cached key optimization. */ -static void -free_pregenerated_keys(void) -{ - unsigned idx; - for (idx = 0; idx < N_PREGEN_KEYS; ++idx) { - if (pregen_keys[idx]) { - crypto_pk_free(pregen_keys[idx]); - pregen_keys[idx] = NULL; - } - } -} - static void * passthrough_test_setup(const struct testcase_t *testcase) { @@ -297,14 +238,15 @@ main(int c, const char **v) #ifdef USE_DMALLOC { - int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_); - tor_assert(r); + int r = crypto_use_tor_alloc_functions(); + tor_assert(r == 0); } #endif update_approx_time(time(NULL)); options = options_new(); tor_threads_init(); + tor_compress_init(); network_init(); @@ -363,17 +305,14 @@ main(int c, const char **v) tor_free(errmsg); return 1; } + tor_set_failed_assertion_callback(an_assertion_failed); -#ifdef CACHE_GENERATED_KEYS - for (i = 0; i < N_PREGEN_KEYS; ++i) { - pregen_keys[i] = crypto_pk_new(); - int r = crypto_pk_generate_key(pregen_keys[i]); - tor_assert(r == 0); - } - MOCK(crypto_pk_generate_key_with_bits, - crypto_pk_generate_key_with_bits__get_cached); -#endif + init_pregenerated_keys(); + + channelpadding_new_consensus_params(NULL); + + predicted_ports_init(); atexit(remove_directory); diff --git a/src/test/testing_rsakeys.c b/src/test/testing_rsakeys.c new file mode 100644 index 0000000000..5dff233a69 --- /dev/null +++ b/src/test/testing_rsakeys.c @@ -0,0 +1,546 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "or.h" +#include "test.h" + +/** Define this if unit tests spend too much time generating public keys. + * This module is meant to save time by using a bunch of pregenerated RSA +keys among */ +#define USE_PREGENERATED_RSA_KEYS + +#ifdef USE_PREGENERATED_RSA_KEYS + +static const char *PREGEN_KEYS_1024[] = { +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICWwIBAAKBgQCZa39BCgq7KWBWFSjGYHhqmTCHvQ7WNEFAb9Mujb6Xn/Zy01fu\n" +"WIpVvqmAKeLNEziItUm/gB8GwAN+/ZLwL9pufjIp2Ar+yqVXKySioZQxuCgTP2wm\n" +"Ku0OfmAra1Xbtrkc2OCJllxkyNPrJ/kxfwjWR96UP0+VMbOlkBoEH1FtvwIDAQAB\n" +"AoGAUXoygeMIYe+OdwkTt48CRHKIwH3aRE5KHSOGPyIOB05vvvmYqD8jcHgqYqNc\n" +"DNdZXdkRin9LevU8phObFq4DTXp08XggUx4Kk4AdsFKubQtJ8gHm3xlSKbZXX2m/\n" +"ZF0GRaZtVDQ3TRGh+OBLILt/2jT+BaFKGAyJ7al76F2nprECQQDJyLlteLDFBmrd\n" +"0kAjNBE50S5YskBCQeQACROfyTKW8lG1J57UBeYjXvbrDFBR4alIS9DEexGai9Gz\n" +"wxpgKg2nAkEAwqQmPstjHxvqGQRi41uXO026MLxY7dhEqs1aSw3tuT8v17pW3OEa\n" +"Qxv7JINePZ3+sNN+Ic+3RXBR0QuD7lSSKQJAZjVSF21GvMXfY7SX4D0DbLHUNAE2\n" +"I1mUz5/JXOpgwazETmpfPS4vwELd93kpRhBz2rbsbFmaNRoVgmSU+5jRiQJAZ1bV\n" +"g2NilgKxEGU2x3U6Xt8Oqo9lO6omEvUCKnUTsNWuZf/l3FGbKuQxO5qPr3Ex5tny\n" +"zqrEqBZRKgbOHfxCuQJAbJY5C3Nm5koemr031r00MY2YD1b6+hyKZyPdZ21HpyY8\n" +"z1kWShL0POjYPX/BnKE1FkpklWcKBb7wkK7dvAKkEQ==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQCyqMM2TfFGV5tVBTVabxLVln8146nDavIdR6q78DCUMh8Zfzkk\n" +"h9Lbl1NX4RU+AmrCZMPq21/EjIRxRQyRdgPYJVLdp96eGeYnEzmMkqvXiswXvDg/\n" +"tXqsjyJeYsoHMQWDTpCLfjYo4K1ol1sg8VIs4wQeq5og6QSdmhBoz7MyqQIDAQAB\n" +"AoGBAIJekey7nZeV8Bxva4ptSRIg+v0I/2VBUiG5nUX9NIW/uV/yrXERx/VDjKaw\n" +"8b5JJzxpKWnk4RJc83xwRYaT1qMYHiQfybxEI0K9SjhtaThAjtXkQGtZgLJILl3t\n" +"yh3LPTh1ocwafsKjU6eGYAe/DYn9/QwYHbtyaimcigu4etp9AkEA2DgC+HndoP1i\n" +"np26Lx+4TG0vAfrVYGSLT9FXwf2iBV3oJvdKqu6wr8ipb1SbshRPcOQd31/mCh6+\n" +"2BR+d4ddcwJBANOHrlBbGZdHnoEu6kKbPwwkc31IZYqyfSpkqm0Lb2oWZ9SInKfc\n" +"cz0qpH91p610XUpYmycaJr4K+N8jgrz86HMCQQCoqGBg1Ca2OpCf66bctWB8dTqS\n" +"z8d7rlIhC8npr1+f0hWRt5pN5Wx7YgoQpq3gZgllpPtMT7DQOhVh1fKkaDnTAkA4\n" +"XuskPPLX7t0dvhvtviOSH9CrLXTp/mD+wC7uumJpmij3aaSd01DelxOZaAhUYDNQ\n" +"UcafKAf1E0V5aaQ4qwljAkA9NVN6CtpzzcLrstTKxrx5P1Ylt/0UYQDo1lIaqwrT\n" +"aOFbXmOungiC9+p/4U7RbX0MEzjFDHCWlaHASviGVgta\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICWwIBAAKBgQDDt2V63APj3JSqaRgofUzhtB+prm0wII4uHyxfOxnpYIELOW5z\n" +"3UHmkr+B4D+Nif5jIp0i6W4OS4S+YHewKsDsXvXKRIW78KzOt6Le4JI9rSarNjy5\n" +"aJKksWQRALLCmxP/BdolaBFqF3fIPD5+Zxu8ESgxhkEQI4p7awUp3E730QIDAQAB\n" +"AoGAZktfAR4p8lkCYydW9yK2ommQ+xEuBK+fYL/uYz/yxSYpjIJSFsEYhrlA21Mo\n" +"JIRxr8MRuoOjgFk8YnztUeimuHpslDlZDaCBzjRjBRFCMepZNG9xqSEL0u7C+SH6\n" +"KU5f2x2P6PneBj6WaHZM+6Lf2xHlOoeuaVSUfq2Pk2VBF9kCQQDtawWWNwP0+xea\n" +"oCAQpanaLzYPjlqZfHJQ1AAI5eSkdf1qmlypIHwOtjAEa6XuEO/Or8RNkNy4nQdw\n" +"qhcQ7PXDAkEA0wjT6Z+Lrt67FnwPgoSvl4Nukcqw4OWHbBKhaQPsO9+oc3PAXLdD\n" +"SclUUqDF6NX1yONTV1KrPdz4zElmEua+2wJABm4inZnp2oW+cuqpU6oY+pbSwQMb\n" +"AxMyyWukgJkxYx7q+SsrHU2K7p8Sl9wOh28f/5oVGAC3aayfGfcRXtz8HwJAIqeO\n" +"dQzYGU1GF7kjquEzHIRewd4xEZ1fkaW1j9MvFd3ygZL+gbsud41yJWd1WHjaNbTu\n" +"2KYgrLX+vT1IX844hQJAbg0V7iHlttQqXL7yN09jIjQLprqVhDZCUHS9s9Dxe7fz\n" +"Ac0ZZD0D6EVNmSmBB71q7kLUWX/W/10d447TLnnfew==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXgIBAAKBgQDhCAjPEockl4lqkvoIb5O3NJJG8NWD31c63e/cPWY6MX5nOM/q\n" +"avof2eWJxFOk0HQ2BRVwIgNex6kLxtsdw7XE0A5uZorTp9DbRCGMqUqHNhHH9ci2\n" +"mMPP9jptq3ieWg310bH4Tad8h3WE2npSCDBvxyV6EmuH2rlQW9ZlHNoiRQIDAQAB\n" +"AoGBAI4PgWggPTqng7PJF5mNvsYQpSutzE0VCL977nmuNUQVjMPjRLarVD4ZU+QW\n" +"EevhQQv9R5xjjJcgGqL5pchzjeKDm0/LA+AygnZoDMs2O68Neieqvr7cPqr5ALGs\n" +"WuZvSn+bRJTenvV9sUh2ii0/u3GQbL1v7GWDkIdD7itDbmRhAkEA8iijuEY+W67w\n" +"7JusjY2MQ2Cm6xxxR0YcnYPzT6UDm+Z7NNJwKscQ6AjayNmxmXGpbUdukzLzXf8y\n" +"fccI9t6iHQJBAO3kx9nZay0Ktl51QP5o2gwoqRIbnogGfR06KJOlzIPGR0aPn8cg\n" +"uKq2SiyjewEaSBM6S/4UlxYUmvc3VKnxCEkCQQDpTjg2YQ7RPGIIRA/iLV7Wx3bq\n" +"C/QjjCwjoi44LK6mdE9928WPoUzrkSRg4EQYpwZqL6kcDrmkdSuLPMipOGQNAkA3\n" +"KtzlujPOiDNuiEaAORSHyU4b8ue6p7aP9pK+Wq6oyGxzAo+NABuTCx78ZxT5Vnzs\n" +"aJKC44d+CV0+g0hQ+KJxAkEAqFYzNWIzTHX8DVDdK9BpUaBg1DFxIeP5Kk+/X3FF\n" +"5BafG08B6OiLf8qIGGsxLXNRjIE0GVp3Sy23FUKtUymP+A==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQDMDk01VwPxQq/BAwOBmfGUP/x5BQn+uxI0Aat6bdWuz/2CsjbS\n" +"CWD/YLCaPm+DpHp9RMwk4HONJaw4B2XOw3ELPx7y9DEgdC1wZ9wRkJmqr2IJZoZR\n" +"C7x43nNv+/IXTiRkkljCcMpoL1Tld+L2VbmWR29PdZwvspWRILkEZu1mNwIDAQAB\n" +"AoGANvFK3KfXSei4xfF3yjeXEmHAKx2uOUZJenNQpqBYPr+F9ODjXd5knZ59LqrM\n" +"/9cTnBMgHHXK5yBTpKppQSjikLeQ2BF04Ktff9oGqVcS9x/rKo0CREuxsEfawZOW\n" +"OzOWENp4YcDKGP1I/Ctr185QzStaWrXVQftxmYQ53T77ShECQQDnhabwtqW7rfe4\n" +"+MfkWEJ9Y2s6iMs3JWnwPOX9G9R39PiAD4vAghHJyHHttS9Ipxmvp0hThu0x7a4g\n" +"8BfUpqgjAkEA4aFAmzarWKigREAACVTYH2RHpXbuk05vF9WqfMPiEvQUd5a1q6vc\n" +"xkGZsE3v/TExLjPRZP4FeUNV5sD7THzA3QJBAJxPoRlNx3GCEAlDdfnWGPX9JI09\n" +"hC40RWUcSI7ttjJTI1+an1kWuBnLChhaRpU/tFjikTNLmmMmPHUihIRfDI8CQG7g\n" +"3WzpKr8A7vFbOilbxnF2yDaqAYfmTXW7DHMPl/OUetJh/5kDdhT/e9VGF5+nIvH/\n" +"iPFGW85Bpt8lCtmFnQkCQQDjpp9iy2qesE7KKX4Kv3++QfCJ2w3g7lwg4iyncoDd\n" +"JrM53p29HROM21R6eekvqeWIe9tEX754b+E/N60ZjpGm\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXgIBAAKBgQDdDn3H+Eu0AW5GKohqDBntw6ubnd3VaJwZGzZyga4J2kLg8peP\n" +"RAW6GDD6pcHzW+KZbFWHtRk70FSwvmyGcf+DY0r5tfyCHyDGmbJyPR0o6OVCgSFl\n" +"ccf4eDvbyszzMdlx3uL05ABIpCShoKtEUqvyIQla3Jon+QBwuVkizMzyVwIDAQAB\n" +"AoGACoKh4Fwh3VEkGRn0mnYw1Wk0Q5Xh8j+jDF6K3C7mQ3mpLGDca+dkDlEQIxq2\n" +"egeoYnsQJf+qT3m8TRsAtfO9nj7+7IX4BfCtdIi4RNcorbs5YMWtFyaywnM6SQjS\n" +"+1qf74aL4On9WRO2FtvnTMjFAAkiWNbQp7mWwTmB59i620ECQQDwde6/PwhUzvZh\n" +"dyslKJdna5RjkDQyDIuh0zD/tFZ0Iko7Luec8q6n52ev/n0OiTLGetUh8goePsPP\n" +"HVZHidNJAkEA61eMCmmu+GCAg2vJRtL5sDakAXsbP5M9Bf/QVHXtc4EVXHC6T2ld\n" +"bldOJriNbBThBuPNmlQbssn9FApkyWT4nwJBAIuHIv3+CUuMvBJaH8L0BsaP+g67\n" +"wk24Ud2Yujnl3rSMoR4uXV8IwqfS8quAs/gXTEs3QyzrUUuzh9NKZqIkK2ECQQCz\n" +"vivBEDKIlPvSZBJYO25kfXcJgoKvLb9fw5/TwjXXD/HGpnpFiI3JZnjT7gRlVhT/\n" +"9CDmC/MTvF3EXqPXhXy1AkEAo3a2me23Ljmub21jycSKaCk09dK85QTRRMe9c/hs\n" +"i+pcGi9ZZW0Mm7cyQo47oXjNurkkv0fEvXIobVTEXAGU7w==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXAIBAAKBgQCv8R1IbfYnE3R3kNeezJ7m02XnyCBDDy0YfrQldQ+urdg1CFye\n" +"bO0iPniJb8fmV8NW7x6nUZTDznCg+igroKXtK/w0WYmJJiH4A7Oi5xNjAfRIPvJ/\n" +"J5GI8szS8rH8tp8pW1h8k/kNg2pnBjwQ2U9omhp95RGaHDQSRYzzH/fEFQIDAQAB\n" +"AoGAcy7+BcH/iZuB/xjzIIJDcUhqibCJ9n0D/+pLU85sYuZrCmUcBZe4M1gEn61v\n" +"iExilRJc1hthskL/l1POYql8lk+aqeeDuh38fWJj60TCV/sENiuXOsTmoFVA5pNn\n" +"lwlG8JlpBMsgr1fGqg1C/WLFfMmvXdKVGvpRqI06j7AYUa0CQQDfZ5rI+FhXBlxo\n" +"PR5CM1LB90DuHUMW+Kqoj0c9d2esXEQM7UqQ/9BiBQbL6Py7Z3VwCxibOqyz7+V7\n" +"2aGUMAKnAkEAyZy5Mu2tHs6YBBxPYam7huzMUYjddN7ixAZUyGwxQp9kTIF2NbSQ\n" +"yVDjKrco3s2lO4qj4pSumwVe3GGlsi6G4wJAOOS3pIqqZK84BUvbUtyjLMZ9AKbv\n" +"GQCG5ZpneB3ahyiQJAKiRL8BIJVLH87b3hYA8GHDCHUu2jwz4xCPd5+qbQJAV0TP\n" +"pYvb9AnZI25drhiaY7z8dA6aTYxs/A0Bhf/PEteLwtIHKRgP1BR/QG4n8slxTGSm\n" +"q91P9ypL9XkPECGzoQJBAIMvGEM7ZGevQHBjJ8HhU8IsgT4cYH/XEYb8jRy4F+Ui\n" +"jKxHPxLuFK4urAZunNUNrqhT0PxbB7hRjtHZrmFkrcc=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQDSpmV8ncLwc8gXzdFsZGPDtMO7C/IN9jKCIK13WIseMg1APlMt\n" +"PB5lMQ9fa3m9ZRU0L8HzRo+u/Xdos3yIBI38X2Avy0laGKnQxiOKaDT/5ZHeiBBh\n" +"nMZjP2WY5V1sgqNP9RD8enE6WaSvq1j0BM++mn9KEe//5+dWD8tboBKF4QIDAQAB\n" +"AoGBALgVoerdE1Z+WAY1XyaSNHz6o3H6ZnW9CTaex/jb7/dbVikmThnhx842qXCB\n" +"w8m3ZGhOs/edWkNaTde5wsI6+LhVGco/PWxN4v61jokxUU+5KvUvGacXhXIjzKwG\n" +"DrNCYmle62QCI1z4+TLQW/Lq+jw2Wzk70NWEvoP58gt5SJoBAkEA9wubRKRs49LW\n" +"5JNQZ9hjc+mAfP9YK/sMe4jkdloMMWXjSMlF3Z4mI9XQSpfbBqwWIBXsjU/15LIS\n" +"ftmujZsMKQJBANpJEZI7UFoRdSP7AlM0YJuXWnVGyn/K+VIeEso5AlZdKXCTpxqp\n" +"9blWq0UVC6jLesZ5UNPuBiAnrBaVwDA8YvkCQF+FQVfdK607TJO80g4VAP9EfcXX\n" +"BUScIUtytsN8NdKzzpnKGRWDnMOmXI87ABkoWLW3RGuvSyhOIhCiInfmR2ECQASc\n" +"FmroJcJBLCAeZOYs7P1cLOTdIdmhB7LcP7lVit8YCJAADj9Z536KfgNvdleSNH2M\n" +"glB3blmvfMrdTrm2DMECQQDj6GJ/Tc2rCsq534xknasVjrgtJMQFxmQCTVgBx9pc\n" +"gTflJAHAmNDvstacVqeObLCF2ZIvya8fSXGbDOJYeGDv\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXAIBAAKBgQDGgUJAm7vf/3focNGwzv4TkzYF2XwpAirnb61dyxvfug1zKv2k\n" +"AUg3qACiurR7JrI+kAbmxEnNaKV7ts7uO763wP9KE8YAuFZsp7NFA295rEZhw38T\n" +"rUlWHMCeaZ3mqW2q8gA14C/ZJCG4gS91SIHLjNGsbHwr2Jvri2ItwIP8FQIDAQAB\n" +"AoGAONceb32oiHWQkkBr6uL6ogRPPdGO2fdC7c5uqCLWsnOGEmpHAsVTNoym0fIA\n" +"aBsmgv+e2klukKDccdZg3prA+z7lHcc2a4bIFguF6ei80hLIis/dds66fFXofCzy\n" +"DMlkncSbJwIvQHG9gblxp9qSKElZF7XjABZEImarfUlakGkCQQD//msGy5N0ZhMI\n" +"yGMXkwXRJXfmRrIrOqHx6u1eUp4OuqDW+hBz4KCHnWfuRJkNGQIammSf18jPasP5\n" +"YHyr/LifAkEAxoJ8R8Vusexo9ZjuU44qXCSvJQ26UBV7mn6TGEAn2DRK1RWKDaHv\n" +"j2vnRjt3CO9WPDQL7SB/1HNAy+dIMPyqywJBAIB6tESIz8zPniX+TJ18UKMTZwXP\n" +"3YQMvVKpUdDRLjq+OBMtFizSRD9MJOlUzGvibUfkzTPcHRDcyNbUMj4vbIkCQBx4\n" +"6sqAjvgGKKfRX52sbnb47AYsieSisC/gp8h6qzxfg7w8cqix6WJw36M7ND+b1Iqe\n" +"DHfeiXc3cLvOWJRuKTECQCEYkujtSjXWb26xaESFWGtUI/nEvCyqYPQAFBpaGzQ3\n" +"tiTDeKHzypesWYoTxOiNQWCQMLrFGuUbDpYOuDOVNjw=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQCcwSAfytnspSSDX/sKmCPOMnpuCYeWA4wbz1wLyb63a8/KXhhG\n" +"6o2W0kt3x1vnGZkeWwZOeBFUqwoc+xHhoNcZFsMOyqbqA3UMZW5cx27MsexRTQHs\n" +"Go1newu/E+8NNCohY51G7z1Hdo0L6mi/Tldh7puuGsMwKqNG/Vvo/GQDgwIDAQAB\n" +"AoGBAIUdpBAbjXDe1OET0vYuOMnUKA/l29RS8tpy/zGrg1/0GCM8QNWIPfEEaL4w\n" +"+CSKonMazYI5iE4kaZQuygKXOdFqKxX8nrGK2hR0DIEUHhhiqyGMUKrf4ELkAJzK\n" +"tHtcO64OFEU2EGa72wCmyk2MhqhLxWxA7E00x24uvW6pen6xAkEAzHhbzlRgLZ+K\n" +"QuXmQHEqkGaS2Ccf6c9TA5Bf5S2/5zBl+OqVyJJQH0yrbPYR6Nn1NeSv3R4IDJYg\n" +"fSZLaVzWHQJBAMRCU6QtTnZoQ97pLvXCSKRYKJF+CnE3zDFTyoJrpK0W1FSnb1EE\n" +"DWjjdSdMLynf/InX+VOaLk3Gxwjme4NKjh8CQQCg2b4/HplayrsVzY3I/D2jw02Z\n" +"xY2RfYusrhMCU284DBbsLn8OfiuRs9rXqOyF5ZDFiNXgeROT8zYzvcBtbp7xAkBU\n" +"ZET9IvJLXjhZISItUXbVHIeNUIqC9sBaMbKx9EGioF97a2gliT2O7cgRtuPM+ODq\n" +"ETHILlNc5G3vuNRBt4x3AkBV98Y1SZA3TQlUVTsjGraxkFTfU1IlomiOdOwTQ+xZ\n" +"x+JxhhgZwZ+kgI3PidEufFCTZJ3WO6Wk9gk18Bx7CLjm\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXgIBAAKBgQDq/K7wNW3fcTbaRTjNZlM4W0G7tKeO+X0bca4+9uin3ML3ogNJ\n" +"6qT/B0QAZB6Vyi9kKa3E8plQkjmPuX8Q27zj2QjEuDZ12RGFnikeOosUhOYiDh3Z\n" +"T9CHnr6stozzgk79Xd6VI7bqRcgRwbY0uc9QVr6vwddyIfSploSpVcgspQIDAQAB\n" +"AoGBAJfUpo/sZc6uzxtfCKGmkPTj+ef3hSBbUZuu60AhtxfnC06HrwpOg0eJAUYj\n" +"aqOsHMziJTYQ7kDiCjE0UMaqxDNS5hueumznq2xM2mSN0nYoktU00kpANVkW4VPA\n" +"33TB16DyqlKq2/21Rs1g8/8+IKkKDbRLTC//1WqNHASQVoGNAkEA/+z4hxTVXZkr\n" +"9hz29tAHKURlqzxUEKLnS0eL+XGJRNfGJ+65eXL+gFiIbTnpVeidL1+lKWkZyYzl\n" +"75cNRdUHhwJBAOsOJ9mUOqTbLW5tzh18ewZGOa1JcxhOvf2E1d56N8tDK6lvoqkF\n" +"oUUb8kIweDxPLCVLCl8qFrbjn619fxDInXMCQAfEZGKNIlCd5nSoumIRPDZnagKB\n" +"aTe8CfMB7+CZLoZVWiE6IIzsDYdNqI5QFKHT1nlqmLOiCfNRAGV+GxwEdB8CQQDE\n" +"sHu4HclU2fMSTOAE3H01qt3om2WsGXfyBI3SNQMrG3IVvkymkwd4BQKbUGPMU5Pl\n" +"QP3U1CtdruuXCUSijrzxAkEAoqYub6+0zM8fakSQZcZ01TG9Fuo2xVFDCQsvqR3m\n" +"ZhRT/oinIvOxSh4fQs40bmt1RBmc2L1Is6YB2NTVQEBZDQ==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICWwIBAAKBgQCrf0rPvHYaGYQrc1ciRwaONs8TUvSVmUU98HMYXoFEkBL4CAGH\n" +"4oNHFk8kXHEOsBED0eccSYegWhqKHSz7PbjmJaXloExWrtx5ea3Twf8VTgcfDWQP\n" +"0TzD3G1TYjAFPQ1/LAZCpQFmwpMmTGGxegUhOzkpEWXdLVEVc9Uw4C4L2QIDAQAB\n" +"AoGAZXAJZA5pHM7y6nBynYe9TOkGWru6h7H8zsImkcd0VoWRcrvpi+JjG+0KKsuy\n" +"46kop0XEmWq0mhgxknfnX0QG1MKTqGMIUGN4qCaezOabIpCOdA4d/pr/mWoNgOWw\n" +"9Kc/tNCrKxPKsQMAlWP6ktHN30XRSlHgAjSeUVUiNHztvTECQQDUNin2nyIvj8ZA\n" +"QAsFW9qW+TiTkeUK6yiZ9Gvgf20gwZRWOe5/xnMxVvtN6v7Av1ew/l4VhBoj/w5g\n" +"ydIZk+2LAkEAzuJwdt+ccllG19qmEcbo9XFafgi2PvlEjPJmT1rHV2ns/7HIMu27\n" +"PJY36GgExSfFco6VmicaoOt+RKg+5acgqwJBAKQxAEjcGWQ5VsgRhTVxO3DChX7Q\n" +"TColhrWPwwPhM/s7K92HVzwvvKL5TNmdr9xMb7n3Ja56FouxZVuH6/J0XT8CQAat\n" +"Mhnz/3WFQg8HRGLAe5YoMVZt64u+uaKe1ARtlo9QoNBjqWVTXL6IzocWjEjcjrey\n" +"uEtARdC5qNqIX3dD3H8CP3pVCPvpHOTxkUaktmLYowSA1HSfO9wkE6bMCHhkLwXF\n" +"yTIJ+N7c5u5YN1B6hhVqpKbdnSv+K0MQ0xbfwOWNMw==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXAIBAAKBgQDGQmrKfO3WovoXkOTSh/shO9qjbX4izhg4pccVU3Tp45v/dgAE\n" +"uDUuaa/clToyH5AhOtuazO/asC3ZNajg1ia5VPzmQU3gtqiIZIEXFaOovPlOrXru\n" +"wyQnxaGORndJwfDXicG6bUwI+PDpNq8c4VOTujReeF0r74qMSc7TQLVlUQIDAQAB\n" +"AoGAakR/aTm9YibJVohbnl00xoOGlcLCsXU2lmaFZ3DsYdGWdD+TkvQJzW7ozJtQ\n" +"Lj2sy6L4wujGR7nXWW3hr2IaLpoc1UoyJpieAZM5os6bMN+N4MCqdcZMlazMtSWV\n" +"UDO7O7xQGFpcvvZmnfKCyluFaJ5K/tWxP+2TnS1/m0BDRIECQQD5DYvToA0eKBt+\n" +"7K4eEI8pzDot9NlcL21D86kNgpmuY4pifALU7GvXr299JpFFiYa2A1JVRfpQaoI3\n" +"hZzz0ze1AkEAy8opWJP+T2q4reD5Qq5UjjrHUXFID23KeJEjh5YF40/bHqyVpWVR\n" +"UMntNgAzs+13vRij48Zn6I8GRhStaQ3ArQJASPyFS8GN1paeaDXoWPs1WWR2cF1f\n" +"DbsAZHeVxVXOv+J//ZimI8wdVpodLCoPTLee+NxEVqUpVEPCYY8QjgwKOQJAATmj\n" +"6f5pxvxzQ8hYd0gpBfngfOLbdgxI7VSiDAyg2G8AeDy9YZMsW/n6zRpPNUO2NpLR\n" +"WWs18LX7aaxyJnGIuQJBAPPfy9pd4XEFsRBIIe3N23Gua1XkS/407RJtAGm73Vrt\n" +"QhtWh3i6D5gfpEApMoaE8aaQQ7H0z+0Uh1t8SWesy10=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICWwIBAAKBgQCc/M/X8etUqrxnmH3PyuAYLIPZhwNySch8qz9NB47izYjxzuBG\n" +"GSls6H7WeKIrB8UJY1gW8TLkdOLcrI/0hTANNHEPaueOE0xdABFj7tAaiiGPIM25\n" +"N0wc76me0ZAMYJrZTHk8JZK153y9wInYBwVZreXCVSVf11RuVwe+iFQa5QIDAQAB\n" +"AoGAQC4XJtivdhDLL6snHFF7pkZkrQTGgu3pOhakrXA+mTigGQOTqvTUe8LdP/9X\n" +"hTIK+tiTheWcAcxLhx5BSB0/VDKjYhS0ROpTc33Iq9KalOQaTJbBYGA4eagpQjwU\n" +"jGwr9u2sUsM9WI/Jg0VvLSKhfnNwYIUzLpK3BbWb2qAdh+0CQQDQ2s/8DlibFSBK\n" +"UsFK7lLpV8UgMk9CkaNM2BPzI8Hsjpp6s3pULVRd36m4YTSg15EEHv7bZ1N/+krX\n" +"mXb9xUULAkEAwGy5wHsUSjTK+kntkNXjlCU/+9R+HFpzg9Bwm/PqXTBwEWeU24hV\n" +"iRjPvqPtWFZrWi/nfcviuMaqtdliw1I1zwJAZ2mQxhtMYC2LuYFUWAe9YfClmJWQ\n" +"jUOTef8bka5I3RqW/t5TWc7AEWMnpDXtWx6hnUrDolt9Cschu7MvKeQ9lQJAL18U\n" +"46PpPNN+XNuyVoOxgRkihVasrUI/SeYYsuv7eHGiRUagyOLpW9T139LvbV3pE8zT\n" +"So7VA/Q0towL2lX01QJAGcoBNNouSpum9+5NvGQK1XXsZweawE+pFR2BE5XcjG+n\n" +"FnaLEUBX7nTxhTU2cSQET1PKRNp568a281NEna0nxw==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXAIBAAKBgQDFOqqGG/VtIScxayZYZ+BT+hcs5W1bD5qRxunbG9O36UVT18UE\n" +"CWw9HUf0Q5sDMGvVmBxwZ4GjbR5FDPfhIXaRCzobnejJXq/0k+O5NAVkcSPtJvhK\n" +"AaUqBrWA41vnjKOtJudTsZLfufKafzYwVonze7fXGyVsBRjVwHNS4iqq2QIDAQAB\n" +"AoGAJCoStI6R3RXUKvKb0GATuTJFZ50WBTmCPTK9FMkwdCuY47vPy2Ky7y3cUMTI\n" +"urf5PewrYs0H72CFyWGMXkKVi8aOYshsATEXMfGSqOcqXn+UDssRzvabZFlpnAUa\n" +"WDVt/iN092AdakXNna7/DxrLisDpq8HHJfjtlWGPfkXRg4ECQQDpHeKimTvwJcPc\n" +"iDa6Qb/n9gwLeRckfzhYtfX1luJYLIOHh+J9vjQN75thenBLQB/B6qlKtOn9ejxg\n" +"5z+3zIOpAkEA2JbxXVTCOA802p9khvHxDtLHdKi3w/BjjJiC7Mgqo69ZI+s3PB9E\n" +"F2HJA69kZqpGqvybWHDapjWsq7rcMlxrsQJBAME2yvR3y00VEAyGPc4M1vF8ZqlP\n" +"uRW/+ETWtEDUyU/JvU6lGt2bu2tdkEyv/cjxIiFIzP4litdT7B1pLc+6S9kCQBwE\n" +"usiWFGHoJbA6emiyl7qRLdg7kzo3uMkRWa6D3nA6WM+6t/SBHu/faH+fit91G5s2\n" +"/mmcf8yMmP/GNoIVTqECQFl4Pt6yGiz/YVoYSp35ljY5n3JB6T8o2pOmIrRLuPmT\n" +"6kgyygtJBAmx5nnQoeG8n08tl9QakWznKzkNJ0DIFKI=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQDCaOqJ0lsSAEBcnNB6X7BvVcEcol+evi/nJsPe0uT1SbtW50Ch\n" +"vYOHwK6aQR2C5x9VSs47cLynTL7tNt5d8oeryF3NpI8VTPLImDJCcvUZhS7p4bxn\n" +"JO+Wm+D/e3TWfyjreuWtdL+Mfimw2gzwWuBEtmj51GzQ89eYm7fh11SB6QIDAQAB\n" +"AoGAWaakMbZNxPlUtOCjyysBY/Y5vYira7rswD3CKak7aFn+CE9QIMYSN7IFUqEg\n" +"iNMoQd7jR8nvVX8wtJeO5+gF48W13C3n8FZSrW7c5N3bmfMIgo0xa/TGfeXHP98o\n" +"7vhH0I58j3ZZt0Q+3wTm7t7WPE/nJzgrCk30TqmoaEmstTkCQQDtV6YZ6juEK2Lp\n" +"LGUiqohcS/WJxvFrF5+LNpk86Xdgomf6FphZlkq42KYkvl7qibKDcfDqLKTbHHle\n" +"vQQeCgZ7AkEA0bFHi7F8o4iHtKleBvt4QCj1neA0q3CRDypCI5EqFSrNpxY4Krhh\n" +"WYSVX+xT00QYaCpKKWfYQztCw7Anylv96wJACl86Mwe5ch0zRV1bThiFvQLUyCCZ\n" +"jESMBFlueOr6/I4cXSF/puqaeVl+aTyoiTdbRcNE8/bffXPRGgLIm0d04QJBAJSY\n" +"lmTN789Lby99Xh6AkaSV4ghw26Ip8QHYJmph8npxjK69Niw/4Oy44cnKBVUPSmR2\n" +"o3tYFY7/Lb7S1D+4lOUCQQDbMQUGVsZT+ZjuOG1bAjIuXoAOfOd3mgH5VgQHjSgJ\n" +"ourZtlJ4OUpNrq9IfWqPkM+zSE8+0Dk8/9MS5ngBA/SJ\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQDNbHjwg+7tVNr9erMLowXRnIcttp4pUJbr3B7Jo/u+kD/Yo3F3\n" +"4rIKhHpJl1uEHP1QmvAD+4ApFFI2hNG54xYI8dGflxL5HOs5xxyOPpkrwzQ8Qvnv\n" +"LPg7Gf6PAW9zF4McG4wK0TkrV28G6NhqcPs5VFY6UyvfZ0fEdWAeoWTIfQIDAQAB\n" +"AoGBAKOmkMp7MLLd8QAS6eSRYSdWHdLrMyES1MjduaFGBF4SKOr7en/Zl6ENXSaX\n" +"cA7V0XCPnjpt9/HCAKTyNupx4LCeFWiqdu8VGXhlzX8bdb896OSR2brKbxgRY5tF\n" +"36uL8akrZdrYgocykQCxmRARMB7/rHwDusiamjL6RUZ3+c45AkEA6UPTVmKZQRMr\n" +"A7Qgg5nXrXo9117Lpqf3FdZ1wdni9V59Ptf5xrx9oGZNZzctJPXSAH4M4cumSJrV\n" +"sZ1V8qE7AwJBAOFx+5luLrVKrdlG7MyOhTAdhKYUvKIvL4wvVSY6y+L2nNEx/cTx\n" +"KYbxGC+H1RJbkCS09rYir3VfDRWQ3W1c1n8CQH+X4hn2hO3blkPIW6CgniD+JKWR\n" +"7MOUTMtdK7yFemfM76VYbgAPSohabSxwOfllnSE30cQQqTw9tXYaIdE98BECQG+M\n" +"QWxSS0QillB6unIgVqBPCrJOcmNhK4qWZPBMiVNcqI0Nyj2nAeAl7MyfzfqOWY0A\n" +"CU5nbR+LD2NLUXRqSisCQQCN3IGv1WOWInmA5xhU6vCFDX5u48Dcji7VLJO/Nv/i\n" +"b/zHKAgjHk5Js7bi5ZWEGaUgA4Jt6cKmGdERheqTMKxx\n" +"-----END RSA PRIVATE KEY-----\n" +}; + +static const char *PREGEN_KEYS_2048[] = { +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEpAIBAAKCAQEAoksI1qIuIaFCqT4QbgDvOQCmr9Z9F0E7ku+U5Ep/5dWNANqB\n" +"bSzAOq0+cxiisfF+H4desoqiWDUwlOwXH74qD3ZsbChhvFUD78cQBWQkF+whLVHb\n" +"296QmF0LZqosqz9HMS9CdoMUc1brZb78Hb25QIOOjrg25KYHLZHaqcet1wfhHow6\n" +"Uehc6QTuWgOWFhJnfiXzYgen2o8lnLixxZozhk7Lm7Aix9ur2ckXdQ2Wgny4xw70\n" +"JW84Hapnd8oFUD98XXrExk4VFuIcA8qo7r7y18II6wx4Cw1suKru6bhW65cM/y51\n" +"KC4lB7VkvuoJCelRFdM1PfKZLv2tJP63oAqJrQIDAQABAoIBAQCWc38PEqw3avqU\n" +"UMAEaoNa0bq1Gd8/Nq8WqVnbRSFKHO2pk+cWIb1W6BITuwvgcGKesezdEV4s7apK\n" +"9I7/U1hEm2Ep50mrwRh0KZM1nD9Fmharn851Bt//D4qpMytT2caS1yADI8NKpZJ1\n" +"8VZh7+cT4qG+txHUaAIRgbw3VrBWvTIMu6SOSOZm+e3eOr5UU3du1KvjdJHJ2c2k\n" +"TceHvUdKxV7OYt+BBSN1oBOhs3ajUSRge1v3twRDg3cmbwG0DeXvwHNhGUTcF8IH\n" +"JO1RF5njbkFvyqdAi3ltjU41zYd4OMuPtrwzFOtxUjKT62Soz109HUXXE2CGKFPZ\n" +"PVi5/BIhAoGBANN1xqS5BgHszIB0nXbw5ImYpTRmyhO0KsTblBT9+8Q/B7BCK7bM\n" +"zl+dOPeyvEadSwE7RSMMt6CAlTakWIf3Quw/VZajvXy9C9/LHf52pEKXjxMFMPKE\n" +"aGLHpQnwMtDi8/H8AEAXxI3hpxB2KVR7sAYHWihSGjRJ6oPGvEmKEkb5AoGBAMR6\n" +"G2PKz0xk1vFrjfjSY+y13gH/t7xHaXUggjggUSGKaknQh2BDUllXjadeI0fi1eLW\n" +"r98ZImZZgntAgjaIZ4bAlooTDk4gRHaz9jI+z8lsRwOKnWdiigM7txiXZTMVwMqj\n" +"o5mMNGMA+A+ACkTViRHmkDI7S/9FqAvnbOqVwgFVAoGBALUcY6WDvwx5B3Jh7tgH\n" +"XIYpEh3+h8c2gYcX1g3gtvkPTwN8uToY0gz8eOVV1YHZiHsmi4GIi+HRH3usaRMT\n" +"COOVHzYlSc8Dj57+tdLTRL6wVl9hC9o647ju64DGlI9qQquYPZKniLZIdbFYsu9j\n" +"/JA9Tc/I+h6czFpPJccKlbrpAoGAAPWXrKUQ3g6f/g3IY66jTkSVEO1uuDyhBzFh\n" +"cWS3ALLsUe/yuUWa4VTMHEUZZwB0iucBdNVqlZVaTb/C4wFHgCDwmzv8leUScIHw\n" +"cc5ctV8R+bJzkk2o3tsrybLzi4xPpK2n3tgQaWtXyruVUUC5qpy1l4kylcyBRY2b\n" +"uomAqQECgYAiCNWtuWIDlRBcvtIB+kHguzcoFT3vTCCNhalTEn0zi/tbi+voQgVJ\n" +"SDJNptZv+6vRwQ/HfcQtljKIPO6hUZPYaFWRNhgbh7Ay85lRXYXQOottE8ayReBk\n" +"zZb0fl853Qah4DPsaOugAvhjjKeBmKg6bFWO1z6hj18I3UpDf2YnVQ==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEpQIBAAKCAQEAssO0r37mSJNAkc/ISwXBsu9JjyLeWlsHPAhylQGkSAdp2rjz\n" +"E6AT0Eh3wrocNO31I4pvHReAuh1QedGY6T1cQwO/WAAhQtRCBQDK12qWRgfbC11y\n" +"Xu7zNYPd1Z7YIRy+FxhbL5f+lv3rEUv0HUG5c3CWhLtbANKg+jOieIDzA4Yp1s55\n" +"ynodQBUkTZrwQiT0P8yDSjiasf+clgJRfA1k2XK12KSAMRgyDuPTE4OtBxBvUM3L\n" +"Zvxs81PsmcOuAG4DLaFTg2a/QkCjt2VC1SYYuh/LVxpL41FFh3eMoK5g5deHkgRe\n" +"tlywKjAHIDJu/qgNzNgNW7ymwn2CfBvry9h0/wIDAQABAoIBAEMZ4wDdCWPEokAZ\n" +"Vn2Ss5qO53WrCPuxn42RPjFgZGIFJl7LfbKoK8fK6+lUIrJbf+DPXdX1tIQn7MVN\n" +"P7CNL8yX44MMyW9kbUOjgIBLqgyvdjFV6lBoMTKtRN+iuE31lATnR5Md4pqaxVnA\n" +"wOkaepoycM1x5j7w0SwZparF/HIdkYv0y/MysqT9ByupPA4Fqp/iRSrosHXahNtI\n" +"KZYj1TyERYtuDXq91P4dr/pWq3FmDNI8O3upblkL0YouvG/ZlFLdiNy77XbAyWcX\n" +"ps3YDddM+vECnXO3+sa3ZxgBYvXJdWrrIzM5A+jCkDRZQGsFAzK5I5/S7C2ljt6i\n" +"SmzqvMECgYEA16bGy2XTi6KBPb8aev/OBgK9XuGLwUqK1m15mS9Y2qPHmuc22qaZ\n" +"hw6zginPFrxAEtQWKanhZy4aVqlLkDPLwRnyeuMo1EZAc5B1gZ5ViSAKxBq99hA9\n" +"eqyakdb+IUQsEnRDxSc2gqUQ0EagksUyw5wGG5Q/CVEALmS/r1SU3KUCgYEA1DYf\n" +"6JYdzuRtule3vYeWXKf8sOJpdplgWV7tvLrKkQhdE564uwMCYB23HvYfwWqEdDYG\n" +"fsYg/ur/stk9MDZ3wZKffTEM8V3sX1t1JXnC3ogSAgMGhLZ3ILOLqkoO4BEZJnsS\n" +"dMdiNijlAtQkqs/BO/UVUAKysCtKP3v/+1775dMCgYEAvLjGFjApfnSbV/cK7IM6\n" +"wEXbhdIqZOCgOeEaXjVyM/zKbMRVW+oaR3hVHd8KzSG3jQKv1oxFpu9Qu3ByoWLC\n" +"uF3Ft0debs6ADuJoAyQWROeWpGGmxlUWCGpO5rxYL7KiQxAeUsXrTU+5NBvq4CbV\n" +"MxwyuCX3OGb7mp4upfiGQcUCgYEAuhVsDYv1P4LXJVvd5viKRV2ZG5KuYC1Ga5fu\n" +"aFxzXJI07At2eaa94oKsHR494mEBHNZzA5/BN0fiSHZuTWS1xqxH5oOokc6Gg2ez\n" +"ZdVLp88x20nD4YQPGkHW6tBeEuVrZG7vVC+yU0Ow7bYRISdkjqrusWZsQkbzqI+X\n" +"fFliEbkCgYEAu8x+47M1ordbI7NmbBGyiyP0r7nMRCZ+KEvGeCNYracWmsnCNnfV\n" +"zR2UzmwtSainw3Ho8Jv/rWDC8RIDauyBRYEi2VqOnUzT2ca0iymQyLeBCudAQuio\n" +"drOu4JU8RzZ3Ad6V3DNFnaqmX/7GA9Pa2GI8NJMyb8p1GAGv7Gi8nxc=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEowIBAAKCAQEAt01S8JuEwWy/Hzb90yO2O7oGWq3GfvfDpFOF4OQnwG3kQ/BP\n" +"4MoPDCYHdqb3iI9aD3vykZA6Q8zpdfGwjm4+bHrgRdiSmZWv8NvRwuQ5Ji9xbiGn\n" +"hA1XwqH9hvgFTiy6tRvirWSJ7kzH3Q/bEGpCbHUQkwMog4v6yCNKNrjlwjN++eCi\n" +"gFK/0RMOJMLOs8BD3zY+lKjd/pd8LBRujkMyUF5SryeRueAFjD2sq4OXq8DPABGt\n" +"zdR6vbTcsi4JwP1Q6y4x0/LIWEprzzewNU63I5E2zj0WnoRGAIM4aF+VuqcHjWUx\n" +"VWnyLZldSen6lScZ4xj4seitiDbSFvtFkDF6VwIDAQABAoIBAGTP9im2ntDyyjqU\n" +"uA0DuxomOZBtupniEouyFBOX5/UBe2WSKZxsBNKdp8UuFz3X+aRCeyprtF/NtyjT\n" +"AFOVdmebPPWtIxOtK9LAUyFo+7VwqmXzxHnwDLBS/2jXx7MzDozFBWpvvRx+xf1i\n" +"1wy0JEwaJj90oTeYKRkhr5NhJZwkX8zCNYaemBd3kHB3aGWGJasI1Y81UezeRKCn\n" +"hSbn2CrWalI7pyJ4lsavM11nIq1Eu2ZthJiNCMghbYrHoBHd+iVWiCYchP2rNEWV\n" +"sdHtaVHtQ9zdZ43bao3OzPu7lAjd6UAbxsuhUe+a2YdDz/+Up+6+BvQf1FCfYIjW\n" +"KFUdCoECgYEA4t5O+u0V9gkMUhKsevYb0zgc7O/mo8ivN+V++EpAtL0mhiwxeO8p\n" +"oef0szLyhdULQeLN9pJQDCeAbkGdwIe3L+AKU8o8BFGEWLFysZjMg9In/UTrp5MN\n" +"mMDy2SRKKu5BqsvdYH302xpZfHq1T2cMNDWE8lrZffduH06Cgq/XEtECgYEAztbj\n" +"bhFneADnrvk609VnOQvoQEjySeCQKFQFRRI6k/FguqMisL2IRXnMaWammosdeCAg\n" +"m7eZchnszHIst9cwZUKXUFqmAqeDuWSNdTI7uKZH6nT/A6IDlgdjaHsqhvpK0Ac9\n" +"ngycdHONitOZh0ZG74pdWjf828Dwzf+CuYjl9KcCgYEAmIvI6ZqvkJ8m5Kzfw1Jn\n" +"BVCOypbJK8oOX3R2Orea6KzjEYb3wQx3nwFcHX6danYFOskpmqlpH7MT/Y8rZsEa\n" +"4RsxdoPedTzm08iFiXtn0R9nejp0hlov402iPXXUVSedih3IflBTa1w9XaEY9wog\n" +"P57ZBSknYzcTmgNtaDiaUnECgYA5sWauhNw/dMEq5QmrnJK2LsQRakdqo+CR3x25\n" +"LmR4b5Nze51pfvRLrLV/kMpXwQXvQ8bUqFl8og6S2CXxAWzWUcSy/RXhF6h+RbXP\n" +"Qru1vWvB0fBvqvklF9p6giBSle3YKKzfMNVTBggs+OiR+uA+YHG5gHRfN2nzi5mC\n" +"9tRtcQKBgBnDSi4lRCjRe9pPnyAYaa4iyBUGhjPysScSLY9orel89+qmTBQ/Py6J\n" +"0+sefL4ZJaOsuaR2mSSPP/lbSkF9DMFs4tHbBqY+WkVNYLshAkauHwqv26HTVCSd\n" +"QKzeb7uZw9lNaRIzDvy/3wfCLvXfdDozPFrOUgkyaBN5pJSA/4sv\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEogIBAAKCAQEA9qtiDoJWqU/eSlpj381eG6UcDzfMguFh/q4e4s7QVdRYj5J0\n" +"Msv0PCkti8JHuvQUyncRpOPccBkhNbVjNbjIgw1pHaIZNdVotUDhP0kseRyJ6z3M\n" +"qbZ5qKn+0mHjVjPNItVDDe6tebYMT1BZpVyRrCOqY2v5z1ecLC+ReygmHgDpzg+L\n" +"0rWfIxGT10IPZ8pAlcdEn6xt5aEhi7mPCX/xwqfQChPIJz6zVLEC8UaPtvDBohPR\n" +"6NQTBTeZZAAtzrQ7+oNxfz1v6Fz6RwMei7Q+qOBnMiwpQmbcDBKABM2RnXSpD0LA\n" +"1GR7/+CiV1HQoShWVvEwrSIlM6jVAJo6iqF6WQIDAQABAoIBAHqwcdxPnfUm4aTP\n" +"4r9NcZKEhDlZgqJSoiA/0OL1BRC7xrTanmspoLhPrvTF1FG715+Aq8j9AQbMqQUC\n" +"zG7LEwiEIhV4K9vn4uXMeHy206UFud/E5EhBl695pmJUB/Q3XcAGnQyP+77++o50\n" +"o7IpIdeiAbzj1uP3aplbq5u7M4JV7fUZWA/368G4HolqFTxcAfBJ05GXlp97BBwY\n" +"AnY3/pNrKMz0NiPf3nsJHYWK18up0JCLPL3tomc94wuNZ66spIazHIL9aaKY0q3V\n" +"LkBrelndfYM1m4xRTnSOy6STu0qKTPOpX0C8XBLYs6uiXjRsChqSYwndCCeASaH3\n" +"LGNIcbUCgYEA/m4qvt8tdT4wEvnE+QUxEELmBtT4UFa3NnQISrzNlhNeI0Zd2xlp\n" +"SG0/pcw83mG2uX+V5xSaWL5LYfLBkvy83Y0yIWgYbbIkyyCOUZnTpwaDGU/FjWip\n" +"3TfXf5qpAgiez94sV+MsFpKfG05yxJh5u+3sIyGTVUAxp0HPx4LVgbMCgYEA+DD1\n" +"fu6ttpuV1UMrsFdjuk6gBvSbyJ9OilY2jT+yE7hSRc/yP3O9ikuR74tNlVrWTnO2\n" +"0kcYbyLJXE2cGUC2q5e4r8TDGiozNfQ7/OC2M3XaJ+xJk4zMf/8PuDDpWr+18ZXA\n" +"Pf+ibXWTFvZ6ZeUmpbrrfCrXdvmIZnwVuOI0FcMCgYAZn26emksxq3mb75tumJ9A\n" +"S/xuY7Q+Iv2Adl7/Z9QscPbiBowdLIn1yUrHn7Hhk2WbeMXX57NDjKZ6zr+/1cQP\n" +"a9DInHsZUP9zlWu/vAYcpAM/4VC71PaGWMFTEHhExCl6NZ2xnCcsfseXMGdOdSyN\n" +"SICnaRI1W6mkdnQ+W2a1EQKBgGEKA3KVr6XuPy8bDEHuaTe29irCCQbwAq1j+ABS\n" +"HzZGoyRYocbdYgZoda7LMJJs6c3SwHCHC66oU0KbtaTKAKImuDdBH2djiJJX4/yD\n" +"f7mvIpTpdfsS2gJRn7vMo/CvdFv4ySl0gfV6OwCHbmPYrLuv0dLCjWwfNI2dhoC7\n" +"MNIxAoGAIPSIG4BrShzbeX4c2L18iwIg+NlOcUbtl0Ccr1t6uLGI+ge/6I6T/5XH\n" +"DPKqYIf0IRYV8suxpfQNKiz/C0NPffA1d1M2hvuAg2v09o2cSwvdcQwdmakKZ5bl\n" +"sdCuYKdCIwomEUOz/4XgQrJl4XDUqxftJT6/egAjWvcIYvfNCsY=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEowIBAAKCAQEA1yHZMsgRLckL+v6rgpGq9qmxVBNDxeuul1V/QlFyOlcAk5n/\n" +"uduTalSqGQhc4NEePMxq6nFui4ucpkZOozmcEnhV0N9jld9IB9rLGt4erdg7RKl9\n" +"+gQ+zTn69j69U36E2I47H4dM69uxeSOyWP2Odxpw+biisa3o8mMz1zCmuj4GMDtG\n" +"DlnSpthFzgQR6N1pbvxLXrWg5F16GqFiJOD7kXDfy4/l6kB/mDs1T/3r8kav6DqR\n" +"c/t3aQZxgWGIpI7hc9Qgvp7coZRMey5dNOZEna3tqS8dn2tZlhkpYV5uyFUjmxjG\n" +"TERSULQ7hvUqW+eshGGsnxFtL7ANnTSc4xECowIDAQABAoIBAFhJJMhpQFuIySjd\n" +"AGeZ/g4x/3rgWQzNNp4WUR5XLEhy0eLA7ShJywp06kVRoEQGraEHxsyldldAGS5H\n" +"ZhgoGTufNKB+PHER646FpJpHE1IGjfQUloVW3qr8I1iQ0MOGBWCVpf+/V7rnMsLi\n" +"+lr421FXgYuJ0QKXuyRVv72M0q9U6i+ml3aVAhgW/19oFg+dW7YccX+9iVyD05Q5\n" +"KR64tX8xd4wrAqfAgYA3erbbE6GTyHYD5K54kIgfRr/+pIU4qc1L7XOCblnqc/rI\n" +"BilFysEC634r2MNe66uQvNui4oQTfBcFFlXg0zAmp7d5QE0ApOL6HpCsmbImm2uJ\n" +"sdFNYyECgYEA716kfEv7HfnF0P3pAP2AOuEsW6t8q0UtWvnHrwRQXQw8Yv90g7kD\n" +"pUV3/BjD9VQgsQZosbdSn5wbT4j7dypRdrzYk+8m/hBk4Q8M/tWoRGVOn46NudvK\n" +"/KX0A4ODLuulj8yAZVc7CM5Cdy4GCGJBVO+oVvBUAnHxfZziOyqBw9MCgYEA5hQg\n" +"HEORzdxvbbfAx1ggvH1Eg1lqRhmpI43PpRkaoqb8jLwXb2CyBeuv3RBft/X2Tr6F\n" +"mHpe0U1kN/5YEjii/Q/jUX8azIHaUNNSAjrriEeMQZOqFxmhCdiyeXuqg2fbFbhe\n" +"K3Q6/fsB1xj9OOSwyPMqm/M5U0LsoGjmg8TFE/ECgYAlImKUIdlwOgp1NJ7MF4eo\n" +"Gryd8AmkLFQv8+YFgb7R4I8RsJ2rva0SG6fUhScJTSbRL7RYNZ9swXP/L7oLL5Z5\n" +"vCxBLu22pmZv/7y9X/n9ulWrLRtRhQaFkV08mk9knQwPNeOJVTIEWLM49/vZmxyV\n" +"h6Ru8FOoGXMkUI1MLnj5HwKBgGJLkNhiacVYeuaWDa9c0EeXARFYvxWJ2wAMkvzG\n" +"9+ErlFQP+7ciyYvMAItidnJii8NilDLrfNzQwpNFf5zxQ3j4M7bapblfdMT5M10u\n" +"jPfhEWPm0VEjKvDI+p76HYQcd7YU2W6ZLqbZeRTLYUvQMFL5yGduBzyyJ+P0TR9Y\n" +"jpYRAoGBAM7vYGTprw4w2tTZPFICXVk1bQ0LO06oNRtwkiQTUT6UqPjWMFyvHnmN\n" +"11SVVBmRZ0RAk6e5eZLFX8WelJ4J4nSOGRcJheCtoEFlO7D1ewAUSbqWJ0pBqp2T\n" +"gV4oCS8LYe8zReVoYZJjuLwoHvxZzs/hUjc3SI2HRW2W/HQRPC25\n" +"-----END RSA PRIVATE KEY-----\n" +}; + +#define N_PREGEN_KEYS_1024 ARRAY_LENGTH(PREGEN_KEYS_1024) +static crypto_pk_t *pregen_keys_1024[N_PREGEN_KEYS_1024]; +static int next_key_idx_1024; +#define N_PREGEN_KEYS_2048 ARRAY_LENGTH(PREGEN_KEYS_2048) +static crypto_pk_t *pregen_keys_2048[N_PREGEN_KEYS_2048]; +static int next_key_idx_2048; +#endif + +/** Generate and return a new keypair for use in unit tests. If we're using + * the key cache optimization, we might reuse keys. "idx" is ignored. + * Our only guarantee is that we won't reuse a key till this function has been + * called several times. The order in which keys are returned is slightly + * randomized, so that tests that depend on a particular order will not be + * reliable. */ +static crypto_pk_t * +pk_generate_internal(int bits) +{ + tor_assert(bits == 2048 || bits == 1024); + +#ifdef USE_PREGENERATED_RSA_KEYS + int *idxp; + int n_pregen; + crypto_pk_t **pregen_array; + if (bits == 2048) { + idxp = &next_key_idx_2048; + n_pregen = N_PREGEN_KEYS_2048; + pregen_array = pregen_keys_2048; + } else { + idxp = &next_key_idx_1024; + n_pregen = N_PREGEN_KEYS_1024; + pregen_array = pregen_keys_1024; + } + /* Either skip 1 or 2 keys. */ + *idxp += crypto_rand_int_range(1,3); + *idxp %= n_pregen; + return crypto_pk_dup_key(pregen_array[*idxp]); +#else + crypto_pk_t *result; + int res; + result = crypto_pk_new(); + res = crypto_pk_generate_key_with_bits__real(result, bits); + tor_assert(!res); + return result; +#endif +} + +crypto_pk_t * +pk_generate(int idx) +{ + (void) idx; + return pk_generate_internal(1024); +} + +#ifdef USE_PREGENERATED_RSA_KEYS +static int +crypto_pk_generate_key_with_bits__get_cached(crypto_pk_t *env, int bits) +{ + if (bits == 1024 || bits == 2048) { + crypto_pk_t *newkey = pk_generate_internal(bits); + crypto_pk_assign_(env, newkey); + crypto_pk_free(newkey); + } else { + return crypto_pk_generate_key_with_bits__real(env, bits); + } + return 0; +} +#endif + +/** Free all storage used for the cached key optimization. */ +void +free_pregenerated_keys(void) +{ +#ifdef USE_PREGENERATED_RSA_KEYS + unsigned idx; + for (idx = 0; idx < N_PREGEN_KEYS_1024; ++idx) { + if (pregen_keys_1024[idx]) { + crypto_pk_free(pregen_keys_1024[idx]); + pregen_keys_1024[idx] = NULL; + } + } + for (idx = 0; idx < N_PREGEN_KEYS_2048; ++idx) { + if (pregen_keys_2048[idx]) { + crypto_pk_free(pregen_keys_2048[idx]); + pregen_keys_2048[idx] = NULL; + } + } +#endif +} + +void +init_pregenerated_keys(void) +{ +#ifdef USE_PREGENERATED_RSA_KEYS + const char *s; + crypto_pk_t *pk; + unsigned i; + for (i = 0; i < N_PREGEN_KEYS_1024; ++i) { + pk = pregen_keys_1024[i] = crypto_pk_new(); + s = PREGEN_KEYS_1024[i]; + int r = crypto_pk_read_private_key_from_string(pk, s, strlen(s)); + tor_assert(r == 0); + } + for (i = 0; i < N_PREGEN_KEYS_2048; ++i) { + pk = pregen_keys_2048[i] = crypto_pk_new(); + s = PREGEN_KEYS_2048[i]; + int r = crypto_pk_read_private_key_from_string(pk, s, strlen(s)); + tor_assert(r == 0); + } + + MOCK(crypto_pk_generate_key_with_bits, + crypto_pk_generate_key_with_bits__get_cached); +#endif +} + |