diff options
Diffstat (limited to 'src/test')
75 files changed, 9605 insertions, 1311 deletions
diff --git a/src/test/bench.c b/src/test/bench.c index 5aefda5ff2..30984fda70 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -3,6 +3,7 @@ * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +extern const char tor_git_revision[]; /* Ordinarily defined in tor_main.c; this bit is just here to provide one * since we're not linking to tor_main.c */ const char tor_git_revision[] = ""; @@ -89,7 +90,9 @@ bench_aes(void) uint64_t start, end; const int bytes_per_iter = (1<<24); reset_perftime(); - c = crypto_cipher_new(NULL); + char key[CIPHER_KEY_LEN]; + crypto_rand(key, sizeof(key)); + c = crypto_cipher_new(key); for (len = 1; len <= 8192; len *= 2) { int iters = bytes_per_iter / len; @@ -327,8 +330,9 @@ bench_cell_aes(void) char *b = tor_malloc(len+max_misalign); crypto_cipher_t *c; int i, misalign; - - c = crypto_cipher_new(NULL); + char key[CIPHER_KEY_LEN]; + crypto_rand(key, sizeof(key)); + c = crypto_cipher_new(key); reset_perftime(); for (misalign = 0; misalign <= max_misalign; ++misalign) { @@ -500,8 +504,11 @@ bench_cell_ops(void) or_circ->base_.purpose = CIRCUIT_PURPOSE_OR; /* Initialize crypto */ - or_circ->p_crypto = crypto_cipher_new(NULL); - or_circ->n_crypto = crypto_cipher_new(NULL); + char key1[CIPHER_KEY_LEN], key2[CIPHER_KEY_LEN]; + crypto_rand(key1, sizeof(key1)); + crypto_rand(key2, sizeof(key2)); + or_circ->p_crypto = crypto_cipher_new(key1); + or_circ->n_crypto = crypto_cipher_new(key2); or_circ->p_digest = crypto_digest_new(); or_circ->n_digest = crypto_digest_new(); @@ -556,7 +563,7 @@ bench_dh(void) dh_b, dh_pubkey_a, sizeof(dh_pubkey_a), secret_b, sizeof(secret_b)); tor_assert(slen_a == slen_b); - tor_assert(!memcmp(secret_a, secret_b, slen_a)); + tor_assert(fast_memeq(secret_a, secret_b, slen_a)); crypto_dh_free(dh_a); crypto_dh_free(dh_b); } @@ -594,7 +601,7 @@ bench_ecdh_impl(int nid, const char *name) NULL); tor_assert(slen_a == slen_b); - tor_assert(!memcmp(secret_a, secret_b, slen_a)); + tor_assert(fast_memeq(secret_a, secret_b, slen_a)); EC_KEY_free(dh_a); EC_KEY_free(dh_b); } @@ -661,7 +668,6 @@ main(int argc, const char **argv) { int i; int list=0, n_enabled=0; - benchmark_t *b; char *errmsg; or_options_t *options; @@ -671,10 +677,10 @@ main(int argc, const char **argv) if (!strcmp(argv[i], "--list")) { list = 1; } else { - benchmark_t *b = find_benchmark(argv[i]); + benchmark_t *benchmark = find_benchmark(argv[i]); ++n_enabled; - if (b) { - b->enabled = 1; + if (benchmark) { + benchmark->enabled = 1; } else { printf("No such benchmark as %s\n", argv[i]); } @@ -699,7 +705,7 @@ main(int argc, const char **argv) return 1; } - for (b = benchmarks; b->name; ++b) { + for (benchmark_t *b = benchmarks; b->name; ++b) { if (b->enabled || n_enabled == 0) { printf("===== %s =====\n", b->name); if (!list) diff --git a/src/test/example_extrainfo.inc b/src/test/example_extrainfo.inc index e096afd6c4..0bf2341ef5 100644 --- a/src/test/example_extrainfo.inc +++ b/src/test/example_extrainfo.inc @@ -133,7 +133,7 @@ static const char EX_EI_BAD_NICKNAME_KEY[] = "/UBWNSyXCFDMqnddb/LZ8+VgttmxfYkpeRzSSmDijN3RbOvYJhhBAgMBAAE=\n" "-----END RSA PUBLIC KEY-----\n"; -const char EX_EI_BAD_TOKENS[] = +static const char EX_EI_BAD_TOKENS[] = "extra-info bob 6F314FB01A31162BD5E473D4977AC570DC5B86BB\n" "published 2014-10-05 20:07:00\n" "published 2014-10-05 20:07:00\n" @@ -145,8 +145,9 @@ const char EX_EI_BAD_TOKENS[] = "-----END SIGNATURE-----\n" ; -const char EX_EI_BAD_TOKENS_FP[] = "6F314FB01A31162BD5E473D4977AC570DC5B86BB"; -const char EX_EI_BAD_TOKENS_KEY[] = +static const char EX_EI_BAD_TOKENS_FP[] = + "6F314FB01A31162BD5E473D4977AC570DC5B86BB"; +static const char EX_EI_BAD_TOKENS_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBAL7Z8tz45Tb4tnEFS2sAyjubBV/giSfZdmXRkDV8Jo4xqWqhWFJn7+zN\n" "AXBWBThGeVH2WXrpz5seNJXgZJPxMTMsrnSCGcRXZw0Npti2MkLuQ6+prZa+OPwE\n" @@ -210,7 +211,8 @@ static const char EX_EI_GOOD_ED_EI[] = "\n" "\n" ; -const char EX_EI_GOOD_ED_EI_FP[] = "A692FE045C32B5E3A54B52882EF678A9DAC46A73"; +static const char EX_EI_GOOD_ED_EI_FP[] = + "A692FE045C32B5E3A54B52882EF678A9DAC46A73"; static const char EX_EI_GOOD_ED_EI_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBAM3jdYwjwGxDWYj/vyFkQT7RgeCNIn89Ei6D2+L/fdtFnqrMXOreFFHL\n" @@ -237,7 +239,8 @@ static const char EX_EI_ED_MISSING_SIG[] = "\n" "\n" ; -const char EX_EI_ED_MISSING_SIG_FP[] = "2A7521497B91A8437021515308A47491164EDBA1"; +static const char EX_EI_ED_MISSING_SIG_FP[] = + "2A7521497B91A8437021515308A47491164EDBA1"; static const char EX_EI_ED_MISSING_SIG_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBAOOB8ccxbtk2dB5FuKFhGndDcO6STNjB6KiG0b9X2QwKrOZMfmXSigto\n" @@ -260,7 +263,8 @@ static const char EX_EI_ED_MISSING_CERT[] = "\n" "\n" ; -const char EX_EI_ED_MISSING_CERT_FP[] = "E88E43E86015345A323D93D825C33E4AD1028F65"; +static const char EX_EI_ED_MISSING_CERT_FP[] = + "E88E43E86015345A323D93D825C33E4AD1028F65"; static const char EX_EI_ED_MISSING_CERT_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBALjA/geb0TR9rp/UPvLhABQpB0XUDYuZAnLkrv+i7AAV7FemTDveEGnc\n" @@ -284,7 +288,8 @@ static const char EX_EI_ED_BAD_CERT1[] = "-----END SIGNATURE-----\n" "\n" ; -const char EX_EI_ED_BAD_CERT1_FP[] = "F78D8A655607D32281D02144817A4F1D26AE520F"; +static const char EX_EI_ED_BAD_CERT1_FP[] = + "F78D8A655607D32281D02144817A4F1D26AE520F"; static const char EX_EI_ED_BAD_CERT1_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBAMlR46JhxsCmWYtmIB/JjTV2TUYIhJLmHy+X7FfkK3ZVQvvl9/3GSXFL\n" @@ -309,7 +314,8 @@ static const char EX_EI_ED_BAD_CERT2[] = "cVrtU6RVmzldSbyir8V/Z4S/Cm67gYAgjM5gfoFUqDs=\n" "-----END SIGNATURE-----\n" ; -const char EX_EI_ED_BAD_CERT2_FP[] = "7C2B42E783C4E0EB0CC3BDB37385D16737BACFBD"; +static const char EX_EI_ED_BAD_CERT2_FP[] = + "7C2B42E783C4E0EB0CC3BDB37385D16737BACFBD"; static const char EX_EI_ED_BAD_CERT2_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBALAM1F/0XJEsbxIQqb3+ObX/yGVnq9of8Q9sLsmxffD6hwVpCqnV3lTg\n" @@ -335,7 +341,8 @@ static const char EX_EI_ED_BAD_SIG1[] = "-----END SIGNATURE-----\n" "\n" ; -const char EX_EI_ED_BAD_SIG1_FP[] = "5AC3A538FEEFC6F9FCC5FA0CE64704396C30D62A"; +static const char EX_EI_ED_BAD_SIG1_FP[] = + "5AC3A538FEEFC6F9FCC5FA0CE64704396C30D62A"; static const char EX_EI_ED_BAD_SIG1_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBAMvb6SuoIkPfBkJgQuo5aQDepAs1kEETZ9VXotMlhB0JJikrqBrAAz+7\n" @@ -361,7 +368,8 @@ static const char EX_EI_ED_BAD_SIG2[] = "-----END SIGNATURE-----\n" "\n" ; -const char EX_EI_ED_BAD_SIG2_FP[] = "7F1D4DD477E340C6D6B389FAC26EDC746113082F"; +static const char EX_EI_ED_BAD_SIG2_FP[] = + "7F1D4DD477E340C6D6B389FAC26EDC746113082F"; static const char EX_EI_ED_BAD_SIG2_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBALzOyfCEUZnvCyhlyMctPkdXg/XRE3Cr6QgyzdKf5kQbUiu2n0FgSHOX\n" @@ -388,7 +396,8 @@ static const char EX_EI_ED_MISPLACED_CERT[] = "-----END SIGNATURE-----\n" "\n" ; -const char EX_EI_ED_MISPLACED_CERT_FP[] = "3B788BD0CE348BC5CED48313307C78175EB6D0F3"; +static const char EX_EI_ED_MISPLACED_CERT_FP[] = + "3B788BD0CE348BC5CED48313307C78175EB6D0F3"; static const char EX_EI_ED_MISPLACED_CERT_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBALTwNqhTprg1oC6bEbDqwIYBoER6prqUXQFbwbFDn+ekXhZj8vltgGwp\n" @@ -414,7 +423,8 @@ static const char EX_EI_ED_MISPLACED_SIG[] = "-----END SIGNATURE-----\n" "\n" ; -const char EX_EI_ED_MISPLACED_SIG_FP[] = "384E40A5DEED4AB1D8A74F1FCBDB18B7C24A8284"; +static const char EX_EI_ED_MISPLACED_SIG_FP[] = + "384E40A5DEED4AB1D8A74F1FCBDB18B7C24A8284"; static const char EX_EI_ED_MISPLACED_SIG_KEY[] = "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBAK0HgOCG/6433VCrwz/vhk3cKmyOfenCp0GZ4DIUwPWt4DeyP4nTbN6T\n" diff --git a/src/test/include.am b/src/test/include.am index 7d80fdf152..8ecfaf10c6 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -9,6 +9,12 @@ TESTS_ENVIRONMENT = \ export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; TESTSCRIPTS = 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 \ + src/test/test_workqueue_pipe.sh \ + src/test/test_workqueue_pipe2.sh \ + src/test/test_workqueue_socketpair.sh \ src/test/test_switch_id.sh if USEPYTHON @@ -16,13 +22,16 @@ TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh endif TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ - src/test/test_workqueue src/test/test_keygen.sh \ + src/test/test_workqueue \ + src/test/test_keygen.sh \ + src/test/test-timers \ $(TESTSCRIPTS) # These flavors are run using automake's test-driver and test-network.sh -TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min bridges+hs +TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min single-onion # only run if we can ping6 ::1 (localhost) -TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min +TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-ipv6 \ + single-onion-ipv6 # only run if we can find a stable (or simply another) version of tor TEST_CHUTNEY_FLAVORS_MIXED = mixed @@ -40,7 +49,8 @@ noinst_PROGRAMS+= \ src/test/test-memwipe \ src/test/test-child \ src/test/test_workqueue \ - src/test/test-switch-id + src/test/test-switch-id \ + src/test/test-timers endif src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ @@ -86,6 +96,7 @@ src_test_test_SOURCES = \ src/test/test_guardfraction.c \ src/test/test_extorport.c \ src/test/test_hs.c \ + src/test/test_handles.c \ src/test/test_introduce.c \ src/test/test_keypin.c \ src/test/test_link_handshake.c \ @@ -93,10 +104,13 @@ src_test_test_SOURCES = \ src/test/test_microdesc.c \ src/test/test_nodelist.c \ src/test/test_oom.c \ + src/test/test_oos.c \ src/test/test_options.c \ src/test/test_policy.c \ src/test/test_procmon.c \ + src/test/test_protover.c \ src/test/test_pt.c \ + src/test/test_pubsub.c \ src/test/test_relay.c \ src/test/test_relaycell.c \ src/test/test_rendcache.c \ @@ -105,6 +119,7 @@ src_test_test_SOURCES = \ src/test/test_routerlist.c \ src/test/test_routerset.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_threads.c \ @@ -127,6 +142,8 @@ src_test_test_slow_SOURCES = \ src_test_test_memwipe_SOURCES = \ src/test/test-memwipe.c +src_test_test_timers_SOURCES = \ + src/test/test-timers.c src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) @@ -147,6 +164,7 @@ src_test_test_switch_id_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) 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@ src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @@ -156,6 +174,7 @@ src_test_test_LDADD = src/or/libtor-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@ \ @@ -168,13 +187,17 @@ src_test_test_slow_LDADD = $(src_test_test_LDADD) src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS) src_test_test_memwipe_CPPFLAGS = $(src_test_test_CPPFLAGS) -src_test_test_memwipe_CFLAGS = $(src_test_test_CFLAGS) +# Don't use bugtrap cflags here: memwipe tests require memory violations. +src_test_test_memwipe_CFLAGS = $(TEST_CFLAGS) src_test_test_memwipe_LDADD = $(src_test_test_LDADD) -src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS) +# The LDFLAGS need to include the bugtrap cflags, or else we won't link +# successfully with the libraries built with them. +src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS) @CFLAGS_BUGTRAP@ src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ 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 \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @@ -185,11 +208,23 @@ src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ src_test_test_workqueue_LDADD = src/or/libtor-testing.a \ src/common/libor-testing.a \ + src/common/libor-ctime-testing.a \ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \ src/common/libor-event-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ +src_test_test_timers_CPPFLAGS = $(src_test_test_CPPFLAGS) +src_test_test_timers_CFLAGS = $(src_test_test_CFLAGS) +src_test_test_timers_LDADD = \ + src/common/libor-testing.a \ + src/common/libor-ctime-testing.a \ + 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@ +src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS) + noinst_HEADERS+= \ src/test/fakechans.h \ src/test/log_test_helpers.h \ @@ -208,6 +243,7 @@ noinst_PROGRAMS+= src/test/test-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) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @@ -217,6 +253,7 @@ src_test_test_ntor_cl_AM_CPPFLAGS = \ 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 \ @TOR_LIB_MATH@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) @@ -228,7 +265,14 @@ EXTRA_DIST += \ 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_zero_length_keys.sh \ + src/test/test_ntor.sh src/test/test_bt.sh \ src/test/test-network.sh \ - src/test/test_switch_id.sh + src/test/test_switch_id.sh \ + src/test/test_workqueue_cancel.sh \ + src/test/test_workqueue_efd.sh \ + src/test/test_workqueue_efd2.sh \ + src/test/test_workqueue_pipe.sh \ + src/test/test_workqueue_pipe2.sh \ + src/test/test_workqueue_socketpair.sh + diff --git a/src/test/log_test_helpers.c b/src/test/log_test_helpers.c index 3bb36ac36c..c788a33c17 100644 --- a/src/test/log_test_helpers.c +++ b/src/test/log_test_helpers.c @@ -4,26 +4,92 @@ #include "torlog.h" #include "log_test_helpers.h" +/** + * \file log_test_helpers.c + * \brief Code to check for expected log messages during testing. + */ + +static void mock_saving_logv(int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, + const char *format, va_list ap) + CHECK_PRINTF(5, 0); + +/** + * Smartlist of all the logs we've received since we last set up + * log capture. + */ static smartlist_t *saved_logs = NULL; -int +/** Boolean: should we also send messages to the test-runner? */ +static int echo_to_real_logs = 1; + +/** Record logs at this level or more severe */ +static int record_logs_at_level = LOG_ERR; + +static int saved_log_level = 0; + +/** + * As setup_capture_of_logs, but do not relay log messages into the main + * logging system. + * + * Avoid using this function; use setup_capture_of_logs() instead if you + * can. If you must use this function, then make sure you detect any + * unexpected log messages, and treat them as test failures. */ +void +setup_full_capture_of_logs(int new_level) +{ + setup_capture_of_logs(new_level); + echo_to_real_logs = 0; +} + +/** + * Temporarily capture all the messages logged at severity <b>new_level</b> or + * higher. + * + * This function does not prevent messages from being sent to the main + * logging system. + */ +void setup_capture_of_logs(int new_level) { - int previous_log = log_global_min_severity_; - log_global_min_severity_ = new_level; + if (saved_log_level == 0) { + saved_log_level = log_global_min_severity_; + } else { + tor_assert(0); + } + + /* Only change the log_global_min_severity_ if we're making things _more_ + * verbose. Otherwise we could prevent real log messages that the test- + * runner wanted. + */ + if (log_global_min_severity_ < new_level) + log_global_min_severity_ = new_level; + + record_logs_at_level = new_level; mock_clean_saved_logs(); + saved_logs = smartlist_new(); MOCK(logv, mock_saving_logv); - return previous_log; + echo_to_real_logs = 1; } +/** + * Undo setup_capture_of_logs(). + * + * This function is safe to call more than once. + */ void -teardown_capture_of_logs(int prev) +teardown_capture_of_logs(void) { UNMOCK(logv); - log_global_min_severity_ = prev; + if (saved_log_level) + log_global_min_severity_ = saved_log_level; + saved_log_level = 0; mock_clean_saved_logs(); } +/** + * Clear all messages in mock_saved_logs() + */ void mock_clean_saved_logs(void) { @@ -35,6 +101,11 @@ mock_clean_saved_logs(void) saved_logs = NULL; } +/** + * Return a list of all the messages captured since the last + * setup_[full_]capture_of_logs() call. Each log call is recorded as a + * mock_saved_log_entry_t. + */ const smartlist_t * mock_saved_logs(void) { @@ -42,23 +113,52 @@ mock_saved_logs(void) } int +mock_saved_log_n_entries(void) +{ + return saved_logs ? smartlist_len(saved_logs) : 0; +} + +/** + * Return true iff there is a message recorded by log capture + * that is exactly equal to <b>msg</b> + */ +int mock_saved_log_has_message(const char *msg) { - int has_msg = 0; if (saved_logs) { SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m, { if (msg && m->generated_msg && !strcmp(msg, m->generated_msg)) { - has_msg = 1; + return 1; + } + }); + } + + return 0; +} + +/** + * Return true iff there is a message recorded by log capture + * that contains <b>msg</b> as a substring. + */ +int +mock_saved_log_has_message_containing(const char *msg) +{ + if (saved_logs) { + SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m, + { + if (msg && m->generated_msg && + strstr(m->generated_msg, msg)) { + return 1; } }); } - return has_msg; + return 0; } -/* Do the saved logs have any messages with severity? */ +/** Return true iff the saved logs have any messages with <b>severity</b> */ int mock_saved_log_has_severity(int severity) { @@ -75,7 +175,7 @@ mock_saved_log_has_severity(int severity) return has_sev; } -/* Do the saved logs have any messages? */ +/** Return true iff the the saved logs have at lease one message */ int mock_saved_log_has_entry(void) { @@ -85,12 +185,14 @@ mock_saved_log_has_entry(void) return 0; } -void +/* Replacement for logv: record the log message, and (maybe) send it + * into the logging system again. + */ +static void mock_saving_logv(int severity, log_domain_mask_t domain, const char *funcname, const char *suffix, const char *format, va_list ap) { - (void)domain; char *buf = tor_malloc_zero(10240); int n; n = tor_vsnprintf(buf,10240,format,ap); @@ -98,6 +200,18 @@ mock_saving_logv(int severity, log_domain_mask_t domain, buf[n]='\n'; buf[n+1]='\0'; + if (echo_to_real_logs) { + tor_log(severity, domain|LD_NO_MOCK, "%s", buf); + } + + if (severity > record_logs_at_level) { + tor_free(buf); + return; + } + + if (!saved_logs) + saved_logs = smartlist_new(); + mock_saved_log_entry_t *e = tor_malloc_zero(sizeof(mock_saved_log_entry_t)); e->severity = severity; e->funcname = funcname; @@ -106,8 +220,22 @@ mock_saving_logv(int severity, log_domain_mask_t domain, e->generated_msg = tor_strdup(buf); tor_free(buf); - if (!saved_logs) - saved_logs = smartlist_new(); smartlist_add(saved_logs, e); } +void +mock_dump_saved_logs(void) +{ + if (saved_logs == NULL) { + puts(" Captured logs: NULL"); + return; + } + + puts(" Captured logs:"); + SMARTLIST_FOREACH_BEGIN(saved_logs, const mock_saved_log_entry_t *, m) { + printf("% 5d. %s: %s\n", m_sl_idx + 1, + log_level_to_string(m->severity), + escaped(m->generated_msg)); + } SMARTLIST_FOREACH_END(m); +} + diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h index 1966f170fb..922c68b42f 100644 --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@ -6,50 +6,99 @@ #ifndef TOR_LOG_TEST_HELPERS_H #define TOR_LOG_TEST_HELPERS_H +/** An element of mock_saved_logs(); records the log element that we + * received. */ typedef struct mock_saved_log_entry_t { int severity; const char *funcname; const char *suffix; const char *format; char *generated_msg; - struct mock_saved_log_entry_t *next; } mock_saved_log_entry_t; -void mock_saving_logv(int severity, log_domain_mask_t domain, - const char *funcname, const char *suffix, - const char *format, va_list ap) - CHECK_PRINTF(5, 0); void mock_clean_saved_logs(void); const smartlist_t *mock_saved_logs(void); -int setup_capture_of_logs(int new_level); -void teardown_capture_of_logs(int prev); +void setup_capture_of_logs(int new_level); +void setup_full_capture_of_logs(int new_level); +void teardown_capture_of_logs(void); int mock_saved_log_has_message(const char *msg); +int mock_saved_log_has_message_containing(const char *msg); int mock_saved_log_has_severity(int severity); int mock_saved_log_has_entry(void); +int mock_saved_log_n_entries(void); +void mock_dump_saved_logs(void); -#define expect_log_msg(str) \ - tt_assert_msg(mock_saved_log_has_message(str), \ +#define assert_log_predicate(predicate, failure_msg) \ + do { \ + if (!(predicate)) { \ + tt_fail_msg((failure_msg)); \ + mock_dump_saved_logs(); \ + TT_EXIT_TEST_FUNCTION; \ + } \ + } while (0) + +#define expect_log_msg(str) \ + assert_log_predicate(mock_saved_log_has_message(str), \ + "expected log to contain " # str); + +#define expect_log_msg_containing(str) \ + assert_log_predicate(mock_saved_log_has_message_containing(str), \ "expected log to contain " # str); +#define expect_log_msg_containing_either(str1, str2) \ + assert_log_predicate(mock_saved_log_has_message_containing(str1) || \ + mock_saved_log_has_message_containing(str2), \ + "expected log to contain " # str1 " or " # str2); + +#define expect_log_msg_containing_either3(str1, str2, str3) \ + assert_log_predicate(mock_saved_log_has_message_containing(str1) || \ + mock_saved_log_has_message_containing(str2) || \ + mock_saved_log_has_message_containing(str3), \ + "expected log to contain " # str1 " or " # str2 \ + " or " # str3); + +#define expect_log_msg_containing_either4(str1, str2, str3, str4) \ + assert_log_predicate(mock_saved_log_has_message_containing(str1) || \ + mock_saved_log_has_message_containing(str2) || \ + mock_saved_log_has_message_containing(str3) || \ + mock_saved_log_has_message_containing(str4), \ + "expected log to contain " # str1 " or " # str2 \ + " or " # str3 " or " # str4); + +#define expect_single_log_msg(str) \ + do { \ + \ + assert_log_predicate(mock_saved_log_has_message_containing(str) && \ + mock_saved_log_n_entries() == 1, \ + "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); \ + } while (0); + #define expect_no_log_msg(str) \ - tt_assert_msg(!mock_saved_log_has_message(str), \ + assert_log_predicate(!mock_saved_log_has_message(str), \ "expected log to not contain " # str); #define expect_log_severity(severity) \ - tt_assert_msg(mock_saved_log_has_severity(severity), \ + assert_log_predicate(mock_saved_log_has_severity(severity), \ "expected log to contain severity " # severity); #define expect_no_log_severity(severity) \ - tt_assert_msg(!mock_saved_log_has_severity(severity), \ + assert_log_predicate(!mock_saved_log_has_severity(severity), \ "expected log to not contain severity " # severity); #define expect_log_entry() \ - tt_assert_msg(mock_saved_log_has_entry(), \ + assert_log_predicate(mock_saved_log_has_entry(), \ "expected log to contain entries"); #define expect_no_log_entry() \ - tt_assert_msg(!mock_saved_log_has_entry(), \ + assert_log_predicate(!mock_saved_log_has_entry(), \ "expected log to not contain entries"); #endif diff --git a/src/test/sr_commit_calc_ref.py b/src/test/sr_commit_calc_ref.py new file mode 100644 index 0000000000..45e629cfb0 --- /dev/null +++ b/src/test/sr_commit_calc_ref.py @@ -0,0 +1,51 @@ +# This is a reference implementation of the COMMIT/REVEAL calculation for +# prop250. We use it to generate a test vector for the test_encoding() +# unittest. +# +# Here is the computation formula: +# +# H = SHA3-256 +# TIMESTAMP = 8 bytes network-endian value +# RAND = H(32 bytes of random) +# +# REVEAL = base64-encode( TIMESTAMP || RAND ) +# COMMIT = base64-encode( TIMESTAMP || H(REVEAL) ) +# + +import sys +import hashlib +import struct +import base64 + +# Python 3.6+, the SHA3 is available in hashlib natively. Else this requires +# the pysha3 package (pip install pysha3). +if sys.version_info < (3, 6): + import sha3 + +# Test vector to make sure the right sha3 version will be used. pysha3 < 1.0 +# used the old Keccak implementation. During the finalization of SHA3, NIST +# changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function +# stayed the same. pysha3 1.0 provides the previous Keccak hash, too. +TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51" +if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest(): + print("pysha3 version is < 1.0. Please install from:") + print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3") + sys.exit(1) + +# TIMESTAMP +ts = 1454333590 +# RAND +data = 'A' * 32 # Yes very very random, NIST grade :). +rand = hashlib.sha3_256(data) + +reveal = struct.pack('!Q', ts) + rand.digest() +b64_reveal = base64.b64encode(reveal) +print("REVEAL: %s" % (b64_reveal)) + +# Yes we do hash the _encoded_ reveal here that is H(REVEAL) +hashed_reveal = hashlib.sha3_256(b64_reveal) +commit = struct.pack('!Q', ts) + hashed_reveal.digest() +print("COMMIT: %s" % (base64.b64encode(commit))) + +# REVEAL: AAAAAFavXpZJxbwTupvaJCTeIUCQmOPxAMblc7ChL5H2nZKuGchdaA== +# COMMIT: AAAAAFavXpbkBMzMQG7aNoaGLFNpm2Wkk1ozXhuWWqL//GynltxVAg== diff --git a/src/test/sr_srv_calc_ref.py b/src/test/sr_srv_calc_ref.py new file mode 100644 index 0000000000..492ca62b15 --- /dev/null +++ b/src/test/sr_srv_calc_ref.py @@ -0,0 +1,71 @@ +# This is a reference implementation of the SRV calculation for prop250. We +# use it to generate a test vector for the test_sr_compute_srv() unittest. +# (./test shared-random/sr_compute_srv) +# +# Here is the SRV computation formula: +# +# HASHED_REVEALS = H(ID_a | R_a | ID_b | R_b | ..) +# +# SRV = SHA3-256("shared-random" | INT_8(reveal_num) | INT_4(version) | +# HASHED_REVEALS | previous_SRV) +# + +import sys +import hashlib +import struct + +# Python 3.6+, the SHA3 is available in hashlib natively. Else this requires +# the pysha3 package (pip install pysha3). +if sys.version_info < (3, 6): + import sha3 + +# Test vector to make sure the right sha3 version will be used. pysha3 < 1.0 +# used the old Keccak implementation. During the finalization of SHA3, NIST +# changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function +# stayed the same. pysha3 1.0 provides the previous Keccak hash, too. +TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51" +if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest(): + print("pysha3 version is < 1.0. Please install from:") + print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3") + sys.exit(1) + +# In this example, we use three reveal values. +reveal_num = 3 +version = 1 + +# We set directly the ascii value because memset(buf, 'A', 20) makes it to 20 +# times "41" in the final string. + +# Identity and reveal value of dirauth a +ID_a = 20 * "41" # RSA identity of 40 base16 bytes. +R_a = 56 * 'A' # 56 base64 characters + +# Identity and reveal value of dirauth b +ID_b = 20 * "42" # RSA identity of 40 base16 bytes. +R_b = 56 * 'B' # 56 base64 characters + +# Identity and reveal value of dirauth c +ID_c = 20 * "43" # RSA identity of 40 base16 bytes. +R_c = 56 * 'C' # 56 base64 characters + +# Concatenate them all together and hash them to form HASHED_REVEALS. +REVEALS = (ID_a + R_a + ID_b + R_b + ID_c + R_c).encode() +hashed_reveals_object = hashlib.sha3_256(REVEALS) +hashed_reveals = hashed_reveals_object.digest() + +previous_SRV = (32 * 'Z').encode() + +# Now form the message. +#srv_msg = struct.pack('13sQL256ss', "shared-random", reveal_num, version, +# hashed_reveals, previous_SRV) +invariant_token = b"shared-random" +srv_msg = invariant_token + \ + struct.pack('!QL', reveal_num, version) + \ + hashed_reveals + \ + previous_SRV + +# Now calculate the HMAC +srv = hashlib.sha3_256(srv_msg) +print("%s" % srv.hexdigest().upper()) + +# 2A9B1D6237DAB312A40F575DA85C147663E7ED3F80E9555395F15B515C74253D diff --git a/src/test/test-child.c b/src/test/test-child.c index e2552a499d..fdf3ccec0a 100644 --- a/src/test/test-child.c +++ b/src/test/test-child.c @@ -1,8 +1,8 @@ /* Copyright (c) 2011-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#include <stdio.h> #include "orconfig.h" +#include <stdio.h> #ifdef _WIN32 #define WINDOWS_LEAN_AND_MEAN #include <windows.h> diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index 5d4fcec664..21882448c3 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -1,3 +1,4 @@ +#include "orconfig.h" #include <string.h> #include <stdio.h> #include <sys/types.h> @@ -5,9 +6,7 @@ #include "crypto.h" #include "compat.h" - -#undef MIN -#define MIN(a,b) ( ((a)<(b)) ? (a) : (b) ) +#include "util.h" static unsigned fill_a_buffer_memset(void) __attribute__((noinline)); static unsigned fill_a_buffer_memwipe(void) __attribute__((noinline)); @@ -17,6 +16,7 @@ static unsigned fill_heap_buffer_memwipe(void) __attribute__((noinline)); static unsigned fill_heap_buffer_nothing(void) __attribute__((noinline)); static unsigned check_a_buffer(void) __attribute__((noinline)); +extern const char *s; /* Make the linkage global */ const char *s = NULL; #define BUF_LEN 2048 @@ -36,6 +36,12 @@ const char *s = NULL; sum += (unsigned char)buf[i]; \ } +#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"; +#endif + static unsigned fill_a_buffer_memset(void) { @@ -100,29 +106,29 @@ static char *heap_buf = NULL; static unsigned fill_heap_buffer_memset(void) { - char *buf = heap_buf = malloc(BUF_LEN); + char *buf = heap_buf = raw_malloc(BUF_LEN); FILL_BUFFER_IMPL() memset(buf, 0, BUF_LEN); - free(buf); + raw_free(buf); return sum; } static unsigned fill_heap_buffer_memwipe(void) { - char *buf = heap_buf = malloc(BUF_LEN); + char *buf = heap_buf = raw_malloc(BUF_LEN); FILL_BUFFER_IMPL() memwipe(buf, 0, BUF_LEN); - free(buf); + raw_free(buf); return sum; } static unsigned fill_heap_buffer_nothing(void) { - char *buf = heap_buf = malloc(BUF_LEN); + char *buf = heap_buf = raw_malloc(BUF_LEN); FILL_BUFFER_IMPL() - free(buf); + raw_free(buf); return sum; } diff --git a/src/test/test-network.sh b/src/test/test-network.sh index 05080e0c52..4d9776822b 100755 --- a/src/test/test-network.sh +++ b/src/test/test-network.sh @@ -1,7 +1,43 @@ #! /bin/sh +# Please do not modify this script, it has been moved to chutney/tools + ECHO_N="/bin/echo -n" -use_coverage_binary=false + +# 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 until [ -z "$1" ] do @@ -14,6 +50,9 @@ do 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 @@ -30,8 +69,8 @@ do ;; # Make this many connections per client (1) # Note: If you create 7 or more connections to a hidden service from - # a single client, you'll likely get a verification failure due to - # https://trac.torproject.org/projects/tor/ticket/15937 + # 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 @@ -44,44 +83,101 @@ do shift ;; --coverage) - use_coverage_binary=true + export USE_COVERAGE_BINARY=true + ;; + --dry-run) + # process arguments, but don't call any other scripts + export NETWORK_DRY_RUN=true ;; *) - echo "Sorry, I don't know what to do with '$1'." - exit 2 + 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 ;; esac shift done -TOR_DIR="${TOR_DIR:-$PWD}" -NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-"bridges+hs"} -CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR -myname=$(basename $0) - -[ -n "$CHUTNEY_PATH" ] || { - echo "$myname: \$CHUTNEY_PATH not set, trying $TOR_DIR/../chutney" - CHUTNEY_PATH="$TOR_DIR/../chutney" -} +# 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 +# - 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" + 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" + else + echo "$myname: no \$TOR_DIR, chutney will use \$PATH for tor binaries" + unset TOR_DIR + fi +fi -[ -d "$CHUTNEY_PATH" ] && [ -x "$CHUTNEY_PATH/chutney" ] || { - echo "$myname: missing 'chutney' in CHUTNEY_PATH ($CHUTNEY_PATH)" - echo "$myname: Get chutney: git clone https://git.torproject.org/\ +# mandatory: $CHUTNEY_PATH is the path to the chutney launch script +# if it's not set: +# - if $PWD looks like a chutney directory, set it to $PWD, or +# - set it based on $TOR_DIR, expecting chutney to be next to tor, or +# - 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" + 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" + 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/\ chutney.git" - echo "$myname: Set \$CHUTNEY_PATH to a non-standard location: export CHUTNEY_PATH=\`pwd\`/chutney" - exit 1 -} + 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 -cd "$CHUTNEY_PATH" # For picking up the right tor binaries. -tor_name=tor -tor_gencert_name=tor-gencert -if test "$use_coverage_binary" = true; then - tor_name=tor-cov +# 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 -export CHUTNEY_TOR="${TOR_DIR}/src/or/${tor_name}" -export CHUTNEY_TOR_GENCERT="${TOR_DIR}/src/tools/${tor_gencert_name}" +# 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 +fi + +cd "$CHUTNEY_PATH" ./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 2 # Sleep some, waiting for the network to bootstrap. diff --git a/src/test/test-timers.c b/src/test/test-timers.c new file mode 100644 index 0000000000..b5fcade7f8 --- /dev/null +++ b/src/test/test-timers.c @@ -0,0 +1,144 @@ +/* Copyright 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#include <math.h> +#include <stdio.h> +#include <string.h> + +#include <event2/event.h> + +#include "compat.h" +#include "compat_libevent.h" +#include "crypto.h" +#include "timers.h" +#include "util.h" + +#define N_TIMERS 1000 +#define MAX_DURATION 30 +#define N_DISABLE 5 + +static struct timeval fire_at[N_TIMERS] = { {0,0} }; +static int is_disabled[N_TIMERS] = {0}; +static int fired[N_TIMERS] = {0}; +static struct timeval difference[N_TIMERS] = { {0,0} }; +static tor_timer_t *timers[N_TIMERS] = {NULL}; + +static int n_active_timers = 0; +static int n_fired = 0; + +static monotime_t started_at; +static int64_t delay_usec[N_TIMERS]; +static int64_t diffs_mono_usec[N_TIMERS]; + +static void +timer_cb(tor_timer_t *t, void *arg, const monotime_t *now_mono) +{ + struct timeval now; + + tor_gettimeofday(&now); + tor_timer_t **t_ptr = arg; + tor_assert(*t_ptr == t); + int idx = (int) (t_ptr - timers); + ++fired[idx]; + timersub(&now, &fire_at[idx], &difference[idx]); + diffs_mono_usec[idx] = + monotime_diff_usec(&started_at, now_mono) - + delay_usec[idx]; + ++n_fired; + + // printf("%d / %d\n",n_fired, N_TIMERS); + if (n_fired == n_active_timers) { + event_base_loopbreak(tor_libevent_get_base()); + } +} + +int +main(int argc, char **argv) +{ + (void)argc; + (void)argv; + tor_libevent_cfg cfg; + memset(&cfg, 0, sizeof(cfg)); + tor_libevent_initialize(&cfg); + timers_initialize(); + + int i; + int ret; + struct timeval now; + tor_gettimeofday(&now); + monotime_get(&started_at); + for (i = 0; i < N_TIMERS; ++i) { + struct timeval delay; + delay.tv_sec = crypto_rand_int_range(0,MAX_DURATION); + delay.tv_usec = crypto_rand_int_range(0,1000000); + delay_usec[i] = delay.tv_sec * 1000000 + delay.tv_usec; + timeradd(&now, &delay, &fire_at[i]); + timers[i] = timer_new(timer_cb, &timers[i]); + timer_schedule(timers[i], &delay); + ++n_active_timers; + } + + /* Disable some; we'll make sure they don't trigger. */ + for (i = 0; i < N_DISABLE; ++i) { + int idx = crypto_rand_int_range(0, N_TIMERS); + if (is_disabled[idx]) + continue; + is_disabled[idx] = 1; + timer_disable(timers[idx]); + --n_active_timers; + } + + event_base_loop(tor_libevent_get_base(), 0); + + int64_t total_difference = 0; + uint64_t total_square_difference = 0; + tor_assert(n_fired == n_active_timers); + for (i = 0; i < N_TIMERS; ++i) { + if (is_disabled[i]) { + tor_assert(fired[i] == 0); + continue; + } + tor_assert(fired[i] == 1); + //int64_t diff = difference[i].tv_usec + difference[i].tv_sec * 1000000; + int64_t diff = diffs_mono_usec[i]; + total_difference += diff; + total_square_difference += diff*diff; + } + const int64_t mean_diff = total_difference / n_active_timers; + printf("mean difference: "I64_FORMAT" usec\n", + I64_PRINTF_ARG(mean_diff)); + + const double mean_sq = ((double)total_square_difference)/ n_active_timers; + const double sq_mean = mean_diff * mean_diff; + const double stddev = sqrt(mean_sq - sq_mean); + printf("standard deviation: %lf usec\n", stddev); + +#define MAX_DIFF_USEC (500*1000) +#define MAX_STDDEV_USEC (500*1000) +#define ODD_DIFF_USEC (2000) +#define ODD_STDDEV_USEC (2000) + + if (mean_diff < 0 || mean_diff > MAX_DIFF_USEC || stddev > MAX_STDDEV_USEC) { + printf("Either your system is under ridiculous load, or the " + "timer backend is broken.\n"); + ret = 1; + } else if (mean_diff > ODD_DIFF_USEC || stddev > ODD_STDDEV_USEC) { + printf("Either your system is a bit slow or the " + "timer backend is odd.\n"); + ret = 0; + } else { + printf("Looks good enough.\n"); + ret = 0; + } + + timer_free(NULL); + + for (i = 0; i < N_TIMERS; ++i) { + timer_free(timers[i]); + } + timers_shutdown(); + return ret; +} + diff --git a/src/test/test.c b/src/test/test.c index ed167a3e67..9a41b976b8 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -55,6 +55,7 @@ double fabs(double x); #include "memarea.h" #include "onion.h" #include "onion_ntor.h" +#include "onion_fast.h" #include "onion_tap.h" #include "policies.h" #include "rephist.h" @@ -178,20 +179,26 @@ test_bad_onion_handshake(void *arg) s_buf, s_keys, 40)); /* Client: Case 1: The server sent back junk. */ + const char *msg = NULL; s_buf[64] ^= 33; tt_int_op(-1, OP_EQ, - onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, NULL)); + onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, &msg)); s_buf[64] ^= 33; + tt_str_op(msg, OP_EQ, "Digest DOES NOT MATCH on onion handshake. " + "Bug or attack."); /* Let the client finish; make sure it can. */ + msg = NULL; tt_int_op(0, OP_EQ, - onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, NULL)); + onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, &msg)); tt_mem_op(s_keys,OP_EQ, c_keys, 40); + tt_ptr_op(msg, OP_EQ, NULL); /* Client: Case 2: The server sent back a degenerate DH. */ memset(s_buf, 0, sizeof(s_buf)); tt_int_op(-1, OP_EQ, - onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, NULL)); + onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, &msg)); + tt_str_op(msg, OP_EQ, "DH computation failed."); done: crypto_dh_free(c_dh); @@ -246,11 +253,56 @@ test_ntor_handshake(void *arg) memset(s_buf, 0, 40); tt_mem_op(c_keys,OP_NE, s_buf, 40); + /* Now try with a bogus server response. Zero input should trigger + * All The Problems. */ + memset(c_keys, 0, 400); + memset(s_buf, 0, NTOR_REPLY_LEN); + const char *msg = NULL; + tt_int_op(-1, OP_EQ, onion_skin_ntor_client_handshake(c_state, s_buf, + c_keys, 400, &msg)); + tt_str_op(msg, OP_EQ, "Zero output from curve25519 handshake"); + done: ntor_handshake_state_free(c_state); dimap_free(s_keymap, NULL); } +static void +test_fast_handshake(void *arg) +{ + /* tests for the obsolete "CREATE_FAST" handshake. */ + (void) arg; + fast_handshake_state_t *state = NULL; + uint8_t client_handshake[CREATE_FAST_LEN]; + uint8_t server_handshake[CREATED_FAST_LEN]; + uint8_t s_keys[100], c_keys[100]; + + /* First, test an entire handshake. */ + memset(client_handshake, 0, sizeof(client_handshake)); + tt_int_op(0, OP_EQ, fast_onionskin_create(&state, client_handshake)); + tt_assert(! tor_mem_is_zero((char*)client_handshake, + sizeof(client_handshake))); + + tt_int_op(0, OP_EQ, + fast_server_handshake(client_handshake, server_handshake, + s_keys, 100)); + const char *msg = NULL; + tt_int_op(0, OP_EQ, + fast_client_handshake(state, server_handshake, c_keys, 100, &msg)); + tt_ptr_op(msg, OP_EQ, NULL); + tt_mem_op(s_keys, OP_EQ, c_keys, 100); + + /* Now test a failing handshake. */ + server_handshake[0] ^= 3; + tt_int_op(-1, OP_EQ, + fast_client_handshake(state, server_handshake, c_keys, 100, &msg)); + tt_str_op(msg, OP_EQ, "Digest DOES NOT MATCH on fast handshake. " + "Bug or attack."); + + done: + fast_handshake_state_free(state); +} + /** Run unit tests for the onion queues. */ static void test_onion_queues(void *arg) @@ -1115,6 +1167,7 @@ static struct testcase_t test_array[] = { { "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL }, ENT(onion_queues), { "ntor_handshake", test_ntor_handshake, 0, NULL, NULL }, + { "fast_handshake", test_fast_handshake, 0, NULL, NULL }, FORK(circuit_timeout), FORK(rend_fns), ENT(geoip), @@ -1124,60 +1177,6 @@ static struct testcase_t test_array[] = { END_OF_TESTCASES }; -extern struct testcase_t accounting_tests[]; -extern struct testcase_t addr_tests[]; -extern struct testcase_t address_tests[]; -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 channeltls_tests[]; -extern struct testcase_t checkdir_tests[]; -extern struct testcase_t circuitlist_tests[]; -extern struct testcase_t circuitmux_tests[]; -extern struct testcase_t compat_libevent_tests[]; -extern struct testcase_t config_tests[]; -extern struct testcase_t connection_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 dir_tests[]; -extern struct testcase_t dir_handle_get_tests[]; -extern struct testcase_t entryconn_tests[]; -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 introduce_tests[]; -extern struct testcase_t keypin_tests[]; -extern struct testcase_t link_handshake_tests[]; -extern struct testcase_t logging_tests[]; -extern struct testcase_t microdesc_tests[]; -extern struct testcase_t nodelist_tests[]; -extern struct testcase_t oom_tests[]; -extern struct testcase_t options_tests[]; -extern struct testcase_t policy_tests[]; -extern struct testcase_t procmon_tests[]; -extern struct testcase_t pt_tests[]; -extern struct testcase_t relay_tests[]; -extern struct testcase_t relaycell_tests[]; -extern struct testcase_t rend_cache_tests[]; -extern struct testcase_t replaycache_tests[]; -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 scheduler_tests[]; -extern struct testcase_t socks_tests[]; -extern struct testcase_t status_tests[]; -extern struct testcase_t thread_tests[]; -extern struct testcase_t tortls_tests[]; -extern struct testcase_t util_tests[]; -extern struct testcase_t util_format_tests[]; -extern struct testcase_t util_process_tests[]; -extern struct testcase_t dns_tests[]; - struct testgroup_t testgroups[] = { { "", test_array }, { "accounting/", accounting_tests }, @@ -1211,9 +1210,11 @@ struct testgroup_t testgroups[] = { { "link-handshake/", link_handshake_tests }, { "nodelist/", nodelist_tests }, { "oom/", oom_tests }, + { "oos/", oos_tests }, { "options/", options_tests }, { "policy/" , policy_tests }, { "procmon/", procmon_tests }, + { "protover/", protover_tests }, { "pt/", pt_tests }, { "relay/" , relay_tests }, { "relaycell/", relaycell_tests }, @@ -1224,13 +1225,16 @@ struct testgroup_t testgroups[] = { { "routerset/" , routerset_tests }, { "scheduler/", scheduler_tests }, { "socks/", socks_tests }, + { "shared-random/", sr_tests }, { "status/" , status_tests }, { "tortls/", tortls_tests }, { "util/", util_tests }, { "util/format/", util_format_tests }, { "util/logging/", logging_tests }, { "util/process/", util_process_tests }, + { "util/pubsub/", pubsub_tests }, { "util/thread/", thread_tests }, + { "util/handle/", handle_tests }, { "dns/", dns_tests }, END_OF_GROUPS }; diff --git a/src/test/test.h b/src/test/test.h index e618ce1224..25336ac83e 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -73,7 +73,8 @@ {print_ = (I64_PRINTF_TYPE) value_;}, {}, TT_EXIT_TEST_FUNCTION) const char *get_fname(const char *name); -crypto_pk_t *pk_generate(int idx); +const char *get_fname_rnd(const char *name); +struct crypto_pk_t *pk_generate(int idx); #define US2_CONCAT_2__(a, b) a ## __ ## b #define US_CONCAT_2__(a, b) a ## _ ## b @@ -163,11 +164,90 @@ crypto_pk_t *pk_generate(int idx); #define CALLED(mock_name) US_CONCAT_2_(NS(mock_name), called) #define NS_DECL(retval, mock_fn, args) \ + extern int CALLED(mock_fn); \ static retval NS(mock_fn) args; int CALLED(mock_fn) = 0 #define NS_MOCK(name) MOCK(name, NS(name)) #define NS_UNMOCK(name) UNMOCK(name) extern const struct testcase_setup_t passthrough_setup; +extern const struct testcase_setup_t ed25519_test_setup; + +extern struct testcase_t accounting_tests[]; +extern struct testcase_t addr_tests[]; +extern struct testcase_t address_tests[]; +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 channeltls_tests[]; +extern struct testcase_t checkdir_tests[]; +extern struct testcase_t circuitlist_tests[]; +extern struct testcase_t circuitmux_tests[]; +extern struct testcase_t compat_libevent_tests[]; +extern struct testcase_t config_tests[]; +extern struct testcase_t connection_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 dir_tests[]; +extern struct testcase_t dir_handle_get_tests[]; +extern struct testcase_t entryconn_tests[]; +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 introduce_tests[]; +extern struct testcase_t keypin_tests[]; +extern struct testcase_t link_handshake_tests[]; +extern struct testcase_t logging_tests[]; +extern struct testcase_t microdesc_tests[]; +extern struct testcase_t nodelist_tests[]; +extern struct testcase_t oom_tests[]; +extern struct testcase_t oos_tests[]; +extern struct testcase_t options_tests[]; +extern struct testcase_t policy_tests[]; +extern struct testcase_t procmon_tests[]; +extern struct testcase_t protover_tests[]; +extern struct testcase_t pubsub_tests[]; +extern struct testcase_t pt_tests[]; +extern struct testcase_t relay_tests[]; +extern struct testcase_t relaycell_tests[]; +extern struct testcase_t rend_cache_tests[]; +extern struct testcase_t replaycache_tests[]; +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 scheduler_tests[]; +extern struct testcase_t socks_tests[]; +extern struct testcase_t status_tests[]; +extern struct testcase_t thread_tests[]; +extern struct testcase_t tortls_tests[]; +extern struct testcase_t util_tests[]; +extern struct testcase_t util_format_tests[]; +extern struct testcase_t util_process_tests[]; +extern struct testcase_t dns_tests[]; +extern struct testcase_t handle_tests[]; +extern struct testcase_t sr_tests[]; + +extern struct testcase_t slow_crypto_tests[]; +extern struct testcase_t slow_util_tests[]; + +extern struct testgroup_t testgroups[]; + +extern const char AUTHORITY_CERT_1[]; +extern const char AUTHORITY_SIGNKEY_1[]; +extern const char AUTHORITY_SIGNKEY_A_DIGEST[]; +extern const char AUTHORITY_SIGNKEY_A_DIGEST256[]; +extern const char AUTHORITY_CERT_2[]; +extern const char AUTHORITY_SIGNKEY_2[]; +extern const char AUTHORITY_SIGNKEY_B_DIGEST[]; +extern const char AUTHORITY_SIGNKEY_B_DIGEST256[]; +extern const char AUTHORITY_CERT_3[]; +extern const char AUTHORITY_SIGNKEY_3[]; +extern const char AUTHORITY_SIGNKEY_C_DIGEST[]; +extern const char AUTHORITY_SIGNKEY_C_DIGEST256[]; #endif diff --git a/src/test/test_addr.c b/src/test/test_addr.c index 56e79d707a..be440a0925 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -81,14 +81,14 @@ test_addr_basic(void *arg) #define test_op_ip6_(a,op,b,e1,e2) \ STMT_BEGIN \ tt_assert_test_fmt_type(a,b,e1" "#op" "e2,struct in6_addr*, \ - (memcmp(val1_->s6_addr, val2_->s6_addr, 16) op 0), \ + (fast_memcmp(val1_->s6_addr, val2_->s6_addr, 16) op 0), \ char *, "%s", \ - { int i; char *cp; \ + { char *cp; \ cp = print_ = tor_malloc(64); \ - for (i=0;i<16;++i) { \ - tor_snprintf(cp, 3,"%02x", (unsigned)value_->s6_addr[i]);\ + for (int ii_=0;ii_<16;++ii_) { \ + tor_snprintf(cp, 3,"%02x", (unsigned)value_->s6_addr[ii_]); \ cp += 2; \ - if (i != 15) *cp++ = ':'; \ + if (ii_ != 15) *cp++ = ':'; \ } \ }, \ { tor_free(print_); }, \ @@ -1046,17 +1046,17 @@ test_addr_make_null(void *data) (void) data; /* Ensure that before tor_addr_make_null, addr != 0's */ memset(addr, 1, sizeof(*addr)); - tt_int_op(memcmp(addr, zeros, sizeof(*addr)), OP_NE, 0); + tt_int_op(fast_memcmp(addr, zeros, sizeof(*addr)), OP_NE, 0); /* Test with AF == AF_INET */ zeros->family = AF_INET; tor_addr_make_null(addr, AF_INET); - tt_int_op(memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0); + tt_int_op(fast_memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0); tt_str_op(tor_addr_to_str(buf, addr, sizeof(buf), 0), OP_EQ, "0.0.0.0"); /* Test with AF == AF_INET6 */ memset(addr, 1, sizeof(*addr)); zeros->family = AF_INET6; tor_addr_make_null(addr, AF_INET6); - tt_int_op(memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0); + tt_int_op(fast_memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0); tt_str_op(tor_addr_to_str(buf, addr, sizeof(buf), 0), OP_EQ, "::"); done: tor_free(addr); diff --git a/src/test/test_address.c b/src/test/test_address.c index 3e5af56c52..0d142ad483 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -26,6 +26,7 @@ #include "or.h" #include "address.h" #include "test.h" +#include "log_test_helpers.h" /** Return 1 iff <b>sockaddr1</b> and <b>sockaddr2</b> represent * the same IP address and port combination. Otherwise, return 0. @@ -556,18 +557,25 @@ fake_open_socket(int domain, int type, int protocol) return FAKE_SOCKET_FD; } +static int +fake_close_socket(tor_socket_t s) +{ + (void)s; + return 0; +} + static int last_connected_socket_fd = 0; static int connect_retval = 0; static tor_socket_t -pretend_to_connect(tor_socket_t socket, const struct sockaddr *address, +pretend_to_connect(tor_socket_t sock, const struct sockaddr *address, socklen_t address_len) { (void)address; (void)address_len; - last_connected_socket_fd = socket; + last_connected_socket_fd = sock; return connect_retval; } @@ -575,11 +583,11 @@ pretend_to_connect(tor_socket_t socket, const struct sockaddr *address, static struct sockaddr *mock_addr = NULL; static int -fake_getsockname(tor_socket_t socket, struct sockaddr *address, +fake_getsockname(tor_socket_t sock, struct sockaddr *address, socklen_t *address_len) { socklen_t bytes_to_copy = 0; - (void) socket; + (void) sock; if (!mock_addr) return -1; @@ -616,6 +624,7 @@ test_address_udp_socket_trick_whitebox(void *arg) MOCK(tor_open_socket,fake_open_socket); MOCK(tor_connect_socket,pretend_to_connect); MOCK(tor_getsockname,fake_getsockname); + MOCK(tor_close_socket,fake_close_socket); mock_addr = tor_malloc_zero(sizeof(struct sockaddr_storage)); sockaddr_in_from_string("23.32.246.118",(struct sockaddr_in *)mock_addr); @@ -646,11 +655,12 @@ test_address_udp_socket_trick_whitebox(void *arg) tt_assert(sockaddr_in6_are_equal(mock_addr6,ipv6_to_check)); + done: UNMOCK(tor_open_socket); UNMOCK(tor_connect_socket); UNMOCK(tor_getsockname); + UNMOCK(tor_close_socket); - done: tor_free(ipv6_to_check); tor_free(mock_addr); tor_free(addr_from_hack); @@ -793,7 +803,20 @@ test_address_get_if_addrs6_list_internal(void *arg) (void)arg; + /* We might drop a log_err */ + setup_full_capture_of_logs(LOG_ERR); results = get_interface_address6_list(LOG_ERR, AF_INET6, 1); + tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1); + if (smartlist_len(mock_saved_logs()) == 1) { + expect_log_msg_containing_either4("connect() failed", + "unable to create socket", + "Address that we determined via UDP " + "socket magic is unsuitable for public " + "comms.", + "getsockname() to determine interface " + "failed"); + } + teardown_capture_of_logs(); tt_assert(results != NULL); /* Work even on systems without IPv6 interfaces */ @@ -812,6 +835,7 @@ test_address_get_if_addrs6_list_internal(void *arg) done: free_interface_address6_list(results); + teardown_capture_of_logs(); return; } @@ -822,7 +846,20 @@ test_address_get_if_addrs6_list_no_internal(void *arg) (void)arg; + /* We might drop a log_err */ + setup_full_capture_of_logs(LOG_ERR); results = get_interface_address6_list(LOG_ERR, AF_INET6, 0); + tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1); + if (smartlist_len(mock_saved_logs()) == 1) { + expect_log_msg_containing_either4("connect() failed", + "unable to create socket", + "Address that we determined via UDP " + "socket magic is unsuitable for public " + "comms.", + "getsockname() to determine interface " + "failed"); + } + teardown_capture_of_logs(); tt_assert(results != NULL); /* Work even on systems without IPv6 interfaces */ @@ -840,6 +877,7 @@ test_address_get_if_addrs6_list_no_internal(void *arg) } done: + teardown_capture_of_logs(); free_interface_address6_list(results); return; } @@ -1110,7 +1148,7 @@ struct testcase_t address_tests[] = { ADDRESS_TEST(get_if_addrs_list_internal, 0), ADDRESS_TEST(get_if_addrs_list_no_internal, 0), ADDRESS_TEST(get_if_addrs6_list_internal, 0), - ADDRESS_TEST(get_if_addrs6_list_no_internal, 0), + ADDRESS_TEST(get_if_addrs6_list_no_internal, TT_FORK), ADDRESS_TEST(get_if_addrs_internal_fail, 0), ADDRESS_TEST(get_if_addrs_no_internal_fail, 0), ADDRESS_TEST(get_if_addrs, 0), diff --git a/src/test/test_bt.sh b/src/test/test_bt.sh index 033acac955..312905a4e2 100755 --- a/src/test/test_bt.sh +++ b/src/test/test_bt.sh @@ -3,8 +3,11 @@ exitcode=0 +export ASAN_OPTIONS="handle_segv=0:allow_user_segv_handler=1" "${builddir:-.}/src/test/test-bt-cl" backtraces || exit $? -"${builddir:-.}/src/test/test-bt-cl" assert | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?" -"${builddir:-.}/src/test/test-bt-cl" crash | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?" +"${builddir:-.}/src/test/test-bt-cl" assert 2>&1 | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?" +"${builddir:-.}/src/test/test-bt-cl" crash 2>&1 | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?" + +"${builddir:-.}/src/test/test-bt-cl" none || exitcode="$?" exit ${exitcode} diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c index 2f5e50fbf5..95b4f48f11 100644 --- a/src/test/test_bt_cl.c +++ b/src/test/test_bt_cl.c @@ -28,6 +28,9 @@ 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) +#endif int crash(int x) { @@ -47,6 +50,9 @@ crash(int x) crashtype *= x; return crashtype; } +#ifdef HAVE_CFLAG_WNULL_DEREFERENCE +ENABLE_GCC_WARNING(null-dereference) +#endif int oh_what(int x) diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index e5e56edf75..3408da3aa9 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -178,10 +178,10 @@ test_buffers_basic(void *arg) /* Try adding a string too long for any freelist. */ { - char *cp = tor_malloc_zero(65536); + char *mem = tor_malloc_zero(65536); buf = buf_new(); - write_to_buf(cp, 65536, buf); - tor_free(cp); + write_to_buf(mem, 65536, buf); + tor_free(mem); tt_int_op(buf_datalen(buf), OP_EQ, 65536); buf_free(buf); @@ -303,42 +303,42 @@ test_buffer_pullup(void *arg) static void test_buffer_copy(void *arg) { - generic_buffer_t *buf=NULL, *buf2=NULL; + buf_t *buf=NULL, *buf2=NULL; const char *s; size_t len; char b[256]; int i; (void)arg; - buf = generic_buffer_new(); + buf = buf_new(); tt_assert(buf); /* Copy an empty buffer. */ - tt_int_op(0, OP_EQ, generic_buffer_set_to_copy(&buf2, buf)); + tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf)); tt_assert(buf2); - tt_int_op(0, OP_EQ, generic_buffer_len(buf2)); + tt_int_op(0, OP_EQ, buf_datalen(buf2)); /* Now try with a short buffer. */ s = "And now comes an act of enormous enormance!"; len = strlen(s); - generic_buffer_add(buf, s, len); - tt_int_op(len, OP_EQ, generic_buffer_len(buf)); + write_to_buf(s, len, buf); + tt_int_op(len, OP_EQ, buf_datalen(buf)); /* Add junk to buf2 so we can test replacing.*/ - generic_buffer_add(buf2, "BLARG", 5); - tt_int_op(0, OP_EQ, generic_buffer_set_to_copy(&buf2, buf)); - tt_int_op(len, OP_EQ, generic_buffer_len(buf2)); - generic_buffer_get(buf2, b, len); + write_to_buf("BLARG", 5, buf2); + tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf)); + tt_int_op(len, OP_EQ, buf_datalen(buf2)); + fetch_from_buf(b, len, buf2); tt_mem_op(b, OP_EQ, s, len); /* Now free buf2 and retry so we can test allocating */ - generic_buffer_free(buf2); + buf_free(buf2); buf2 = NULL; - tt_int_op(0, OP_EQ, generic_buffer_set_to_copy(&buf2, buf)); - tt_int_op(len, OP_EQ, generic_buffer_len(buf2)); - generic_buffer_get(buf2, b, len); + tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf)); + tt_int_op(len, OP_EQ, buf_datalen(buf2)); + fetch_from_buf(b, len, buf2); tt_mem_op(b, OP_EQ, s, len); /* Clear buf for next test */ - generic_buffer_get(buf, b, len); - tt_int_op(generic_buffer_len(buf),OP_EQ,0); + fetch_from_buf(b, len, buf); + tt_int_op(buf_datalen(buf),OP_EQ,0); /* Okay, now let's try a bigger buffer. */ s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit " @@ -347,95 +347,94 @@ test_buffer_copy(void *arg) len = strlen(s); for (i = 0; i < 256; ++i) { b[0]=i; - generic_buffer_add(buf, b, 1); - generic_buffer_add(buf, s, len); + write_to_buf(b, 1, buf); + write_to_buf(s, len, buf); } - tt_int_op(0, OP_EQ, generic_buffer_set_to_copy(&buf2, buf)); - tt_int_op(generic_buffer_len(buf2), OP_EQ, generic_buffer_len(buf)); + tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf)); + tt_int_op(buf_datalen(buf2), OP_EQ, buf_datalen(buf)); for (i = 0; i < 256; ++i) { - generic_buffer_get(buf2, b, len+1); + fetch_from_buf(b, len+1, buf2); tt_int_op((unsigned char)b[0],OP_EQ,i); tt_mem_op(b+1, OP_EQ, s, len); } done: if (buf) - generic_buffer_free(buf); + buf_free(buf); if (buf2) - generic_buffer_free(buf2); + buf_free(buf2); } static void test_buffer_ext_or_cmd(void *arg) { ext_or_cmd_t *cmd = NULL; - generic_buffer_t *buf = generic_buffer_new(); + buf_t *buf = buf_new(); char *tmp = NULL; (void) arg; /* Empty -- should give "not there. */ - tt_int_op(0, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd)); tt_ptr_op(NULL, OP_EQ, cmd); /* Three bytes: shouldn't work. */ - generic_buffer_add(buf, "\x00\x20\x00", 3); - tt_int_op(0, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + write_to_buf("\x00\x20\x00", 3, buf); + tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd)); tt_ptr_op(NULL, OP_EQ, cmd); - tt_int_op(3, OP_EQ, generic_buffer_len(buf)); + tt_int_op(3, OP_EQ, buf_datalen(buf)); /* 0020 0000: That's a nil command. It should work. */ - generic_buffer_add(buf, "\x00", 1); - tt_int_op(1, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + write_to_buf("\x00", 1, buf); + tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd)); tt_ptr_op(NULL, OP_NE, cmd); tt_int_op(0x20, OP_EQ, cmd->cmd); tt_int_op(0, OP_EQ, cmd->len); - tt_int_op(0, OP_EQ, generic_buffer_len(buf)); + tt_int_op(0, OP_EQ, buf_datalen(buf)); ext_or_cmd_free(cmd); cmd = NULL; /* Now try a length-6 command with one byte missing. */ - generic_buffer_add(buf, "\x10\x21\x00\x06""abcde", 9); - tt_int_op(0, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + write_to_buf("\x10\x21\x00\x06""abcde", 9, buf); + tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd)); tt_ptr_op(NULL, OP_EQ, cmd); - generic_buffer_add(buf, "f", 1); - tt_int_op(1, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + write_to_buf("f", 1, buf); + tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd)); tt_ptr_op(NULL, OP_NE, cmd); tt_int_op(0x1021, OP_EQ, cmd->cmd); tt_int_op(6, OP_EQ, cmd->len); tt_mem_op("abcdef", OP_EQ, cmd->body, 6); - tt_int_op(0, OP_EQ, generic_buffer_len(buf)); + tt_int_op(0, OP_EQ, buf_datalen(buf)); ext_or_cmd_free(cmd); cmd = NULL; /* Now try a length-10 command with 4 extra bytes. */ - generic_buffer_add(buf, "\xff\xff\x00\x0a" - "loremipsum\x10\x00\xff\xff", 18); - tt_int_op(1, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + write_to_buf("\xff\xff\x00\x0aloremipsum\x10\x00\xff\xff", 18, buf); + tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd)); tt_ptr_op(NULL, OP_NE, cmd); tt_int_op(0xffff, OP_EQ, cmd->cmd); tt_int_op(10, OP_EQ, cmd->len); tt_mem_op("loremipsum", OP_EQ, cmd->body, 10); - tt_int_op(4, OP_EQ, generic_buffer_len(buf)); + tt_int_op(4, OP_EQ, buf_datalen(buf)); ext_or_cmd_free(cmd); cmd = NULL; /* Finally, let's try a maximum-length command. We already have the header * waiting. */ - tt_int_op(0, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd)); tmp = tor_malloc_zero(65535); - generic_buffer_add(buf, tmp, 65535); - tt_int_op(1, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + write_to_buf(tmp, 65535, buf); + tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd)); tt_ptr_op(NULL, OP_NE, cmd); tt_int_op(0x1000, OP_EQ, cmd->cmd); tt_int_op(0xffff, OP_EQ, cmd->len); tt_mem_op(tmp, OP_EQ, cmd->body, 65535); - tt_int_op(0, OP_EQ, generic_buffer_len(buf)); + tt_int_op(0, OP_EQ, buf_datalen(buf)); ext_or_cmd_free(cmd); cmd = NULL; done: ext_or_cmd_free(cmd); - generic_buffer_free(buf); + buf_free(buf); tor_free(tmp); } @@ -511,26 +510,26 @@ static void test_buffer_time_tracking(void *arg) { buf_t *buf=NULL, *buf2=NULL; - struct timeval tv0; const time_t START = 1389288246; - const uint32_t START_MSEC = (uint32_t) ((uint64_t)START * 1000); + const uint64_t START_NSEC = ((uint64_t)START) * 1000000000; int i; char tmp[4096]; (void)arg; crypto_rand(tmp, sizeof(tmp)); - tv0.tv_sec = START; - tv0.tv_usec = 0; + monotime_enable_test_mocking(); buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */ tt_assert(buf); + monotime_coarse_set_mock_time_nsec(START_NSEC); + const uint32_t START_MSEC = (uint32_t)monotime_coarse_absolute_msec(); + /* Empty buffer means the timestamp is 0. */ tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC)); tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000)); - tor_gettimeofday_cache_set(&tv0); write_to_buf("ABCDEFG", 7, buf); tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000)); @@ -540,8 +539,7 @@ test_buffer_time_tracking(void *arg) buf_get_oldest_chunk_timestamp(buf2, START_MSEC+1234)); /* Now add more bytes; enough to overflow the first chunk. */ - tv0.tv_usec += 123 * 1000; - tor_gettimeofday_cache_set(&tv0); + monotime_coarse_set_mock_time_nsec(START_NSEC + 123 * (uint64_t)1000000); for (i = 0; i < 600; ++i) write_to_buf("ABCDEFG", 7, buf); tt_int_op(4207, OP_EQ, buf_datalen(buf)); @@ -562,9 +560,7 @@ test_buffer_time_tracking(void *arg) /* This time we'll be grabbing a chunk from the freelist, and making sure its time gets updated */ - tv0.tv_sec += 5; - tv0.tv_usec = 617*1000; - tor_gettimeofday_cache_set(&tv0); + monotime_coarse_set_mock_time_nsec(START_NSEC + 5617 * (uint64_t)1000000); for (i = 0; i < 600; ++i) write_to_buf("ABCDEFG", 7, buf); tt_int_op(4307, OP_EQ, buf_datalen(buf)); @@ -578,6 +574,7 @@ test_buffer_time_tracking(void *arg) done: buf_free(buf); buf_free(buf2); + monotime_disable_test_mocking(); } static void @@ -695,9 +692,9 @@ test_buffers_zlib_fin_at_chunk_end(void *arg) tor_free(msg); } -const uint8_t *tls_read_ptr; -int n_remaining; -int next_reply_val[16]; +static const uint8_t *tls_read_ptr; +static int n_remaining; +static int next_reply_val[16]; static int mock_tls_read(tor_tls_t *tls, char *cp, size_t len) @@ -747,6 +744,27 @@ test_buffers_tls_read_mocked(void *arg) buf_free(buf); } +static void +test_buffers_chunk_size(void *arg) +{ + (void)arg; + const int min = 256; + const int max = 65536; + tt_uint_op(preferred_chunk_size(3), OP_EQ, min); + tt_uint_op(preferred_chunk_size(25), OP_EQ, min); + tt_uint_op(preferred_chunk_size(0), OP_EQ, min); + tt_uint_op(preferred_chunk_size(256), OP_EQ, 512); + tt_uint_op(preferred_chunk_size(65400), OP_EQ, max); + /* Here, we're implicitly saying that the chunk header overhead is + * between 1 and 100 bytes. 24..48 would probably be more accurate. */ + tt_uint_op(preferred_chunk_size(65536), OP_GT, 65536); + tt_uint_op(preferred_chunk_size(65536), OP_LT, 65536+100); + tt_uint_op(preferred_chunk_size(165536), OP_GT, 165536); + tt_uint_op(preferred_chunk_size(165536), OP_LT, 165536+100); + done: + ; +} + struct testcase_t buffer_tests[] = { { "basic", test_buffers_basic, TT_FORK, NULL, NULL }, { "copy", test_buffer_copy, TT_FORK, NULL, NULL }, @@ -761,6 +779,7 @@ struct testcase_t buffer_tests[] = { NULL, NULL}, { "tls_read_mocked", test_buffers_tls_read_mocked, 0, NULL, NULL }, + { "chunk_size", test_buffers_chunk_size, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index 499a637959..f839a5b72c 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -882,8 +882,8 @@ test_cfmt_resolved_cells(void *arg) memset(&rh, 0, sizeof(rh)); \ } while (0) #define CLEAR_ADDRS() do { \ - SMARTLIST_FOREACH(addrs, address_ttl_t *, a, \ - address_ttl_free(a); ); \ + SMARTLIST_FOREACH(addrs, address_ttl_t *, aa_, \ + address_ttl_free(aa_); ); \ smartlist_clear(addrs); \ } while (0) #define SET_CELL(s) do { \ diff --git a/src/test/test_channel.c b/src/test/test_channel.c index 846e419fea..a9e0634d9e 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -20,9 +20,6 @@ #include "test.h" #include "fakechans.h" -/* This comes from channel.c */ -extern uint64_t estimated_total_queue_size; - static int test_chan_accept_cells = 0; static int test_chan_fixed_cells_recved = 0; static cell_t * test_chan_last_seen_fixed_cell_ptr = NULL; @@ -33,7 +30,7 @@ static int test_destroy_not_pending_calls = 0; static int test_doesnt_want_writes_count = 0; static int test_dumpstats_calls = 0; static int test_has_waiting_cells_count = 0; -static double test_overhead_estimate = 1.0f; +static double test_overhead_estimate = 1.0; static int test_releases_count = 0; static circuitmux_t *test_target_cmux = NULL; static unsigned int test_cmux_cells = 0; @@ -792,7 +789,7 @@ test_channel_incoming(void *arg) /* Accept cells to lower layer */ test_chan_accept_cells = 1; /* Use default overhead factor */ - test_overhead_estimate = 1.0f; + test_overhead_estimate = 1.0; ch = new_fake_channel(); tt_assert(ch); @@ -881,7 +878,7 @@ test_channel_lifecycle(void *arg) /* Accept cells to lower layer */ test_chan_accept_cells = 1; /* Use default overhead factor */ - test_overhead_estimate = 1.0f; + test_overhead_estimate = 1.0; ch1 = new_fake_channel(); tt_assert(ch1); @@ -989,7 +986,7 @@ test_channel_lifecycle_2(void *arg) /* Accept cells to lower layer */ test_chan_accept_cells = 1; /* Use default overhead factor */ - test_overhead_estimate = 1.0f; + test_overhead_estimate = 1.0; ch = new_fake_channel(); tt_assert(ch); @@ -1136,7 +1133,7 @@ test_channel_multi(void *arg) /* Accept cells to lower layer */ test_chan_accept_cells = 1; /* Use default overhead factor */ - test_overhead_estimate = 1.0f; + test_overhead_estimate = 1.0; ch1 = new_fake_channel(); tt_assert(ch1); @@ -1444,7 +1441,7 @@ test_channel_queue_incoming(void *arg) /* Accept cells to lower layer */ test_chan_accept_cells = 1; /* Use default overhead factor */ - test_overhead_estimate = 1.0f; + test_overhead_estimate = 1.0; ch = new_fake_channel(); tt_assert(ch); @@ -1584,16 +1581,16 @@ test_channel_queue_size(void *arg) /* One cell, times an overhead factor of 1.0 */ tt_u64_op(ch->bytes_queued_for_xmit, ==, 512); /* Try a different overhead factor */ - test_overhead_estimate = 0.5f; + test_overhead_estimate = 0.5; /* This one should be ignored since it's below 1.0 */ channel_update_xmit_queue_size(ch); tt_u64_op(ch->bytes_queued_for_xmit, ==, 512); /* Now try a larger one */ - test_overhead_estimate = 2.0f; + test_overhead_estimate = 2.0; channel_update_xmit_queue_size(ch); tt_u64_op(ch->bytes_queued_for_xmit, ==, 1024); /* Go back to 1.0 */ - test_overhead_estimate = 1.0f; + test_overhead_estimate = 1.0; channel_update_xmit_queue_size(ch); tt_u64_op(ch->bytes_queued_for_xmit, ==, 512); /* Check the global estimate too */ diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c index 04ae9a6da7..08442e01b6 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -1,6 +1,8 @@ /* Copyright (c) 2014-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +#include "orconfig.h" + #include <math.h> #define TOR_CHANNEL_INTERNAL_ @@ -124,8 +126,7 @@ test_channeltls_num_bytes_queued(void *arg) * Next, we have to test ch->num_bytes_queued, which is * channel_tls_num_bytes_queued_method. We can't mock * connection_get_outbuf_len() directly because it's static inline - * in connection.h, but we can mock buf_datalen(). Note that - * if bufferevents ever work, this will break with them enabled. + * in connection.h, but we can mock buf_datalen(). */ tt_assert(ch->num_bytes_queued != NULL); @@ -185,7 +186,7 @@ test_channeltls_overhead_estimate(void *arg) const char test_digest[DIGEST_LEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14 }; - float r; + double r; channel_tls_t *tlschan = NULL; (void)arg; @@ -206,31 +207,31 @@ test_channeltls_overhead_estimate(void *arg) ch = channel_tls_connect(&test_addr, 567, test_digest); tt_assert(ch != NULL); - /* First case: silly low ratios should get clamped to 1.0f */ + /* First case: silly low ratios should get clamped to 1.0 */ tlschan = BASE_CHAN_TO_TLS(ch); tt_assert(tlschan != NULL); tlschan->conn->bytes_xmitted = 128; tlschan->conn->bytes_xmitted_by_tls = 64; r = ch->get_overhead_estimate(ch); - tt_assert(fabsf(r - 1.0f) < 1E-12); + tt_assert(fabs(r - 1.0) < 1E-12); tlschan->conn->bytes_xmitted_by_tls = 127; r = ch->get_overhead_estimate(ch); - tt_assert(fabsf(r - 1.0f) < 1E-12); + tt_assert(fabs(r - 1.0) < 1E-12); /* Now middle of the range */ tlschan->conn->bytes_xmitted_by_tls = 192; r = ch->get_overhead_estimate(ch); - tt_assert(fabsf(r - 1.5f) < 1E-12); + tt_assert(fabs(r - 1.5) < 1E-12); - /* Now above the 2.0f clamp */ + /* Now above the 2.0 clamp */ tlschan->conn->bytes_xmitted_by_tls = 257; r = ch->get_overhead_estimate(ch); - tt_assert(fabsf(r - 2.0f) < 1E-12); + tt_assert(fabs(r - 2.0) < 1E-12); tlschan->conn->bytes_xmitted_by_tls = 512; r = ch->get_overhead_estimate(ch); - tt_assert(fabsf(r - 2.0f) < 1E-12); + tt_assert(fabs(r - 2.0) < 1E-12); done: if (ch) { diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c index 1e640b5709..e996c42115 100644 --- a/src/test/test_circuitlist.c +++ b/src/test/test_circuitlist.c @@ -9,6 +9,7 @@ #include "circuitbuild.h" #include "circuitlist.h" #include "test.h" +#include "log_test_helpers.h" static channel_t * new_fake_channel(void) @@ -270,6 +271,13 @@ test_rend_token_maps(void *arg) } static void +mock_channel_dump_statistics(channel_t *chan, int severity) +{ + (void)chan; + (void)severity; +} + +static void test_pick_circid(void *arg) { bitarray_t *ba = NULL; @@ -278,12 +286,22 @@ test_pick_circid(void *arg) int i; (void) arg; + MOCK(channel_dump_statistics, mock_channel_dump_statistics); + chan1 = tor_malloc_zero(sizeof(channel_t)); chan2 = tor_malloc_zero(sizeof(channel_t)); chan2->wide_circ_ids = 1; + chan1->cmux = circuitmux_alloc(); + chan2->cmux = circuitmux_alloc(); + + /* CIRC_ID_TYPE_NEITHER is supposed to create a warning. */ chan1->circ_id_type = CIRC_ID_TYPE_NEITHER; + setup_full_capture_of_logs(LOG_WARN); tt_int_op(0, OP_EQ, get_unique_circ_id_by_chan(chan1)); + expect_single_log_msg_containing("Trying to pick a circuit ID for a " + "connection from a client with no identity."); + teardown_capture_of_logs(); /* Basic tests, with no collisions */ chan1->circ_id_type = CIRC_ID_TYPE_LOWER; @@ -337,10 +355,14 @@ test_pick_circid(void *arg) } done: + circuitmux_free(chan1->cmux); + circuitmux_free(chan2->cmux); tor_free(chan1); tor_free(chan2); bitarray_free(ba); circuit_free_all(); + teardown_capture_of_logs(); + UNMOCK(channel_dump_statistics); } struct testcase_t circuitlist_tests[] = { diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c index 266ebbcf3b..0443cc0b1c 100644 --- a/src/test/test_compat_libevent.c +++ b/src/test/test_compat_libevent.c @@ -9,15 +9,8 @@ #include "compat_libevent.h" -#ifdef HAVE_EVENT2_EVENT_H #include <event2/event.h> #include <event2/thread.h> -#ifdef USE_BUFFEREVENTS -#include <event2/bufferevent.h> -#endif -#else -#include <event.h> -#endif #include "log_test_helpers.h" @@ -27,31 +20,36 @@ static void test_compat_libevent_logging_callback(void *ignored) { (void)ignored; - int previous_log = setup_capture_of_logs(LOG_DEBUG); + setup_full_capture_of_logs(LOG_DEBUG); libevent_logging_callback(_EVENT_LOG_DEBUG, "hello world"); expect_log_msg("Message from libevent: hello world\n"); expect_log_severity(LOG_DEBUG); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_MSG, "hello world another time"); expect_log_msg("Message from libevent: hello world another time\n"); expect_log_severity(LOG_INFO); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_WARN, "hello world a third time"); expect_log_msg("Warning from libevent: hello world a third time\n"); expect_log_severity(LOG_WARN); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_ERR, "hello world a fourth time"); expect_log_msg("Error from libevent: hello world a fourth time\n"); expect_log_severity(LOG_ERR); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(42, "hello world a fifth time"); expect_log_msg("Message [42] from libevent: hello world a fifth time\n"); expect_log_severity(LOG_WARN); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_DEBUG, @@ -82,130 +80,43 @@ test_compat_libevent_logging_callback(void *ignored) "012345678901234567890123456789" "012345678901234567890123456789\n"); expect_log_severity(LOG_DEBUG); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(42, "xxx\n"); expect_log_msg("Message [42] from libevent: xxx\n"); expect_log_severity(LOG_WARN); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); suppress_libevent_log_msg("something"); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_MSG, "hello there"); expect_log_msg("Message from libevent: hello there\n"); expect_log_severity(LOG_INFO); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_MSG, "hello there something else"); expect_no_log_msg("hello there something else"); + if (mock_saved_logs()) + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 0); // No way of verifying the result of this, it seems =/ configure_libevent_logging(); done: suppress_libevent_log_msg(NULL); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); } static void -test_compat_libevent_le_versions_compatibility(void *ignored) -{ - (void)ignored; - int res; - - res = le_versions_compatibility(LE_OTHER); - tt_int_op(res, OP_EQ, 0); - - res = le_versions_compatibility(V_OLD(0,9,'c')); - tt_int_op(res, OP_EQ, 1); - - res = le_versions_compatibility(V(1,3,98)); - tt_int_op(res, OP_EQ, 2); - - res = le_versions_compatibility(V(1,4,98)); - tt_int_op(res, OP_EQ, 3); - - res = le_versions_compatibility(V(1,5,0)); - tt_int_op(res, OP_EQ, 4); - - res = le_versions_compatibility(V(2,0,0)); - tt_int_op(res, OP_EQ, 4); - - res = le_versions_compatibility(V(2,0,2)); - tt_int_op(res, OP_EQ, 5); - - done: - (void)0; -} - -static void -test_compat_libevent_tor_decode_libevent_version(void *ignored) -{ - (void)ignored; - le_version_t res; - - res = tor_decode_libevent_version("SOMETHING WRONG"); - tt_int_op(res, OP_EQ, LE_OTHER); - - res = tor_decode_libevent_version("1.4.11"); - tt_int_op(res, OP_EQ, V(1,4,11)); - - res = tor_decode_libevent_version("1.4.12b-stable"); - tt_int_op(res, OP_EQ, V(1,4,12)); - - res = tor_decode_libevent_version("1.4.17b_stable"); - tt_int_op(res, OP_EQ, V(1,4,17)); - - res = tor_decode_libevent_version("1.4.12!stable"); - tt_int_op(res, OP_EQ, LE_OTHER); - - res = tor_decode_libevent_version("1.4.12b!stable"); - tt_int_op(res, OP_EQ, LE_OTHER); - - res = tor_decode_libevent_version("1.4.13-"); - tt_int_op(res, OP_EQ, V(1,4,13)); - - res = tor_decode_libevent_version("1.4.14_"); - tt_int_op(res, OP_EQ, V(1,4,14)); - - res = tor_decode_libevent_version("1.4.15c-"); - tt_int_op(res, OP_EQ, V(1,4,15)); - - res = tor_decode_libevent_version("1.4.16c_"); - tt_int_op(res, OP_EQ, V(1,4,16)); - - res = tor_decode_libevent_version("1.4.17-s"); - tt_int_op(res, OP_EQ, V(1,4,17)); - - res = tor_decode_libevent_version("1.5"); - tt_int_op(res, OP_EQ, V(1,5,0)); - - res = tor_decode_libevent_version("1.2"); - tt_int_op(res, OP_EQ, V(1,2,0)); - - res = tor_decode_libevent_version("1.2-"); - tt_int_op(res, OP_EQ, LE_OTHER); - - res = tor_decode_libevent_version("1.6e"); - tt_int_op(res, OP_EQ, V_OLD(1,6,'e')); - - done: - (void)0; -} - -#if defined(LIBEVENT_VERSION) -#define HEADER_VERSION LIBEVENT_VERSION -#elif defined(_EVENT_VERSION) -#define HEADER_VERSION _EVENT_VERSION -#endif - -static void test_compat_libevent_header_version(void *ignored) { (void)ignored; const char *res; res = tor_libevent_get_header_version_str(); - tt_str_op(res, OP_EQ, HEADER_VERSION); + tt_str_op(res, OP_EQ, LIBEVENT_VERSION); done: (void)0; @@ -214,10 +125,6 @@ test_compat_libevent_header_version(void *ignored) struct testcase_t compat_libevent_tests[] = { { "logging_callback", test_compat_libevent_logging_callback, TT_FORK, NULL, NULL }, - { "le_versions_compatibility", - test_compat_libevent_le_versions_compatibility, 0, NULL, NULL }, - { "tor_decode_libevent_version", - test_compat_libevent_tor_decode_libevent_version, 0, NULL, NULL }, { "header_version", test_compat_libevent_header_version, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_config.c b/src/test/test_config.c index 90ea4da87d..2fc37b0bb8 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -3480,10 +3480,12 @@ test_config_default_dir_servers(void *arg) static int mock_router_pick_published_address_result = 0; static int -mock_router_pick_published_address(const or_options_t *options, uint32_t *addr) +mock_router_pick_published_address(const or_options_t *options, + uint32_t *addr, int cache_only) { (void)options; (void)addr; + (void)cache_only; return mock_router_pick_published_address_result; } @@ -3710,6 +3712,144 @@ test_config_default_fallback_dirs(void *arg) clear_dir_servers(); } +static void +test_config_port_cfg_line_extract_addrport(void *arg) +{ + (void)arg; + int unixy = 0; + const char *rest = NULL; + char *a = NULL; + + tt_int_op(port_cfg_line_extract_addrport("", &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 0); + tt_str_op(a, OP_EQ, "");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("hello", &a, &unixy, &rest), + OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 0); + tt_str_op(a, OP_EQ, "hello");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport(" flipperwalt gersplut", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 0); + tt_str_op(a, OP_EQ, "flipperwalt");; + tt_str_op(rest, OP_EQ, "gersplut"); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport(" flipperwalt \t gersplut", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 0); + tt_str_op(a, OP_EQ, "flipperwalt");; + tt_str_op(rest, OP_EQ, "gersplut"); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("flipperwalt \t gersplut", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 0); + tt_str_op(a, OP_EQ, "flipperwalt");; + tt_str_op(rest, OP_EQ, "gersplut"); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:flipperwalt \t gersplut", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 1); + tt_str_op(a, OP_EQ, "flipperwalt");; + tt_str_op(rest, OP_EQ, "gersplut"); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("lolol", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 0); + tt_str_op(a, OP_EQ, "lolol");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:lolol", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 1); + tt_str_op(a, OP_EQ, "lolol");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:lolol ", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 1); + tt_str_op(a, OP_EQ, "lolol");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport(" unix:lolol", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 1); + tt_str_op(a, OP_EQ, "lolol");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("foobar:lolol", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 0); + tt_str_op(a, OP_EQ, "foobar:lolol");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport(":lolol", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 0); + tt_str_op(a, OP_EQ, ":lolol");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:\"lolol\"", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 1); + tt_str_op(a, OP_EQ, "lolol");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:\"lolol\" ", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 1); + tt_str_op(a, OP_EQ, "lolol");; + tt_str_op(rest, OP_EQ, ""); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:\"lolol\" foo ", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 1); + tt_str_op(a, OP_EQ, "lolol");; + tt_str_op(rest, OP_EQ, "foo "); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:\"lol ol\" foo ", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 1); + tt_str_op(a, OP_EQ, "lol ol");; + tt_str_op(rest, OP_EQ, "foo "); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:\"lol\\\" ol\" foo ", + &a, &unixy, &rest), OP_EQ, 0); + tt_int_op(unixy, OP_EQ, 1); + tt_str_op(a, OP_EQ, "lol\" ol");; + tt_str_op(rest, OP_EQ, "foo "); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:\"lol\\\" ol foo ", + &a, &unixy, &rest), OP_EQ, -1); + tor_free(a); + + tt_int_op(port_cfg_line_extract_addrport("unix:\"lol\\0\" ol foo ", + &a, &unixy, &rest), OP_EQ, -1); + tor_free(a); + + done: + tor_free(a); +} + static config_line_t * mock_config_line(const char *key, const char *val) { @@ -3832,6 +3972,8 @@ test_config_parse_port_config__listenaddress(void *data) 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); @@ -3952,7 +4094,7 @@ test_config_parse_port_config__ports__ports_given(void *data) tt_int_op(ret, OP_EQ, -1); // Test error when encounters a unix domain specification but the listener - // doesnt support domain sockets + // 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", CONN_TYPE_AP_DNS_LISTENER, NULL, 0, 0); @@ -3961,7 +4103,7 @@ test_config_parse_port_config__ports__ports_given(void *data) // 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, "DNS", + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, 0); #ifdef _WIN32 tt_int_op(ret, OP_EQ, -1); @@ -3972,26 +4114,153 @@ test_config_parse_port_config__ports__ports_given(void *data) tt_int_op(port_cfg->port, OP_EQ, 0); tt_int_op(port_cfg->is_unix_addr, OP_EQ, 1); tt_str_op(port_cfg->unix_addr, OP_EQ, "/tmp/foo/bar"); + /* Test entry port defaults as initialised in parse_port_config */ + tt_int_op(port_cfg->entry_cfg.dns_request, OP_EQ, 1); + tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1); + tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1); + tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1); + tt_int_op(port_cfg->entry_cfg.prefer_ipv6_virtaddr, OP_EQ, 1); #endif - // Test failure if we have no ipv4 and no ipv6 (for unix domain sockets, - // this makes no sense - it should be fixed) + // Test failure if we have no ipv4 and no ipv6 and no onion (DNS only) + config_free_lines(config_port_invalid); config_port_invalid = NULL; + config_port_invalid = mock_config_line("SOCKSPort", + "unix:/tmp/foo/bar NoIPv4Traffic " + "NoOnionTraffic"); + ret = parse_port_config(NULL, config_port_invalid, NULL, "SOCKS", + CONN_TYPE_AP_LISTENER, NULL, 0, + CL_PORT_TAKES_HOSTNAMES); + tt_int_op(ret, OP_EQ, -1); + + // Test failure if we have no DNS and we're a DNSPort config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("DNSPort", - "unix:/tmp/foo/bar NoIPv4Traffic"); + "127.0.0.1:80 NoDNSRequest"); ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", + CONN_TYPE_AP_DNS_LISTENER, NULL, 0, + CL_PORT_TAKES_HOSTNAMES); + tt_int_op(ret, OP_EQ, -1); + + // If we're a DNSPort, DNS only is ok + // Use a port because DNSPort doesn't support sockets + config_free_lines(config_port_valid); config_port_valid = NULL; + 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 " + "NoIPv4Traffic NoOnionTraffic"); + ret = parse_port_config(slout, config_port_valid, NULL, "DNS", + CONN_TYPE_AP_DNS_LISTENER, NULL, 0, + CL_PORT_TAKES_HOSTNAMES); + 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->entry_cfg.dns_request, OP_EQ, 1); + tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 0); + + // 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", + "unix:/tmp/foo/bar NoIPv4Traffic"); + ret = parse_port_config(NULL, config_port_invalid, NULL, "SOCKS", + CONN_TYPE_AP_LISTENER, NULL, 0, + CL_PORT_TAKES_HOSTNAMES); + tt_int_op(ret, OP_EQ, -1); + + // Test success with no DNS, no ipv4, no ipv6 (only onion, using separate + // options) + config_free_lines(config_port_valid); config_port_valid = NULL; + 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 " + "NoDNSRequest NoIPv4Traffic"); + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); +#ifdef _WIN32 tt_int_op(ret, OP_EQ, -1); +#else + 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->entry_cfg.dns_request, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1); +#endif - // Test success with no ipv4 but take ipv6 (for unix domain sockets, this - // makes no sense - it should be fixed) + // Test success with quoted unix: address. config_free_lines(config_port_valid); config_port_valid = NULL; SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar " + config_port_valid = mock_config_line("SOCKSPort", "unix:\"/tmp/foo/ bar\" " + "NoDNSRequest NoIPv4Traffic"); + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + CONN_TYPE_AP_LISTENER, NULL, 0, + CL_PORT_TAKES_HOSTNAMES); +#ifdef _WIN32 + tt_int_op(ret, OP_EQ, -1); +#else + 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->entry_cfg.dns_request, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1); +#endif + + // Test failure with broken quoted unix: address. + config_free_lines(config_port_valid); config_port_valid = NULL; + 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 " + "NoDNSRequest NoIPv4Traffic"); + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + CONN_TYPE_AP_LISTENER, NULL, 0, + CL_PORT_TAKES_HOSTNAMES); + tt_int_op(ret, OP_EQ, -1); + + // Test failure with empty quoted unix: address. + config_free_lines(config_port_valid); config_port_valid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_valid = mock_config_line("SOCKSPort", "unix:\"\" " + "NoDNSRequest NoIPv4Traffic"); + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + CONN_TYPE_AP_LISTENER, NULL, 0, + CL_PORT_TAKES_HOSTNAMES); + tt_int_op(ret, OP_EQ, -1); + + // Test success with OnionTrafficOnly (no DNS, no ipv4, no ipv6) + config_free_lines(config_port_valid); config_port_valid = NULL; + 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 " + "OnionTrafficOnly"); + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", + CONN_TYPE_AP_LISTENER, NULL, 0, + CL_PORT_TAKES_HOSTNAMES); +#ifdef _WIN32 + tt_int_op(ret, OP_EQ, -1); +#else + 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->entry_cfg.dns_request, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1); +#endif + + // Test success with no ipv4 but take ipv6 + config_free_lines(config_port_valid); config_port_valid = NULL; + 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 " "NoIPv4Traffic IPv6Traffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); #ifdef _WIN32 @@ -4004,14 +4273,13 @@ test_config_parse_port_config__ports__ports_given(void *data) tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1); #endif - // Test success with both ipv4 and ipv6 (for unix domain sockets, - // this makes no sense - it should be fixed) + // Test success with both ipv4 and ipv6 config_free_lines(config_port_valid); config_port_valid = NULL; SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar " + config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar " "IPv4Traffic IPv6Traffic"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, CL_PORT_TAKES_HOSTNAMES); #ifdef _WIN32 @@ -4153,8 +4421,9 @@ test_config_parse_port_config__ports__ports_given(void *data) config_free_lines(config_port_valid); config_port_valid = NULL; SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - config_port_valid = mock_config_line("DNSPort", "42 IPv6Traffic PreferIPv6"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", + config_port_valid = mock_config_line("SOCKSPort", + "42 IPv6Traffic PreferIPv6"); + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, "127.0.0.42", 0, CL_PORT_TAKES_HOSTNAMES); tt_int_op(ret, OP_EQ, 0); @@ -4292,7 +4561,7 @@ test_config_parse_port_config__ports__ports_given(void *data) // 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, "DNS", + ret = parse_port_config(slout, config_port_valid, NULL, "Control", CONN_TYPE_CONTROL_LISTENER, "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL); tt_int_op(ret, OP_EQ, 0); @@ -4300,7 +4569,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, "DNS", + ret = parse_port_config(slout, config_port_valid, NULL, "ExtOR", CONN_TYPE_EXT_OR_LISTENER, "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL); tt_int_op(ret, OP_EQ, 0); @@ -4472,8 +4741,8 @@ test_config_parse_port_config__ports__ports_given(void *data) config_free_lines(config_port_valid); config_port_valid = NULL; SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); - config_port_valid = mock_config_line("DNSPort", "unix:/tmp/somewhere"); - ret = parse_port_config(slout, config_port_valid, NULL, "DNS", + config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/somewhere"); + ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, "127.0.0.46", 0, CL_PORT_DFLT_GROUP_WRITABLE); #ifdef _WIN32 @@ -4604,6 +4873,15 @@ test_config_parse_port_config__ports__server_options(void *data) 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, -1); + // Check for failure with empty unix: address. + 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("ORPort", "unix:\"\""); + ret = parse_port_config(slout, config_port_invalid, NULL, "ORPort", 0, NULL, + 0, CL_PORT_SERVER_OPTIONS); + tt_int_op(ret, OP_EQ, -1); + done: if (slout) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); @@ -4633,6 +4911,7 @@ struct testcase_t config_tests[] = { CONFIG_TEST(write_to_data_subdir, TT_FORK), 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), diff --git a/src/test/test_connection.c b/src/test/test_connection.c index bf95b0b59f..d394fc9852 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -95,6 +95,13 @@ mock_connection_connect_sockaddr(connection_t *conn, return 1; } +static int +fake_close_socket(evutil_socket_t sock) +{ + (void)sock; + return 0; +} + static void test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr) { @@ -124,6 +131,7 @@ test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose) MOCK(connection_connect_sockaddr, mock_connection_connect_sockaddr); + MOCK(tor_close_socket, fake_close_socket); init_connection_lists(); @@ -148,12 +156,13 @@ test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose) assert_connection_ok(conn, time(NULL)); UNMOCK(connection_connect_sockaddr); - + UNMOCK(tor_close_socket); return conn; /* On failure */ done: UNMOCK(connection_connect_sockaddr); + UNMOCK(tor_close_socket); return NULL; } @@ -192,6 +201,15 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg) if (!conn->linked_conn->marked_for_close) { connection_close_immediate(conn->linked_conn); + if (CONN_IS_EDGE(conn->linked_conn)) { + /* Suppress warnings about all the stuff we didn't do */ + TO_EDGE_CONN(conn->linked_conn)->edge_has_sent_end = 1; + TO_EDGE_CONN(conn->linked_conn)->end_reason = + END_STREAM_REASON_INTERNAL; + if (conn->linked_conn->type == CONN_TYPE_AP) { + TO_ENTRY_CONN(conn->linked_conn)->socks_request->has_finished = 1; + } + } connection_mark_for_close(conn->linked_conn); } @@ -212,6 +230,14 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg) if (!conn->marked_for_close) { connection_close_immediate(conn); + if (CONN_IS_EDGE(conn)) { + /* Suppress warnings about all the stuff we didn't do */ + TO_EDGE_CONN(conn)->edge_has_sent_end = 1; + TO_EDGE_CONN(conn)->end_reason = END_STREAM_REASON_INTERNAL; + if (conn->type == CONN_TYPE_AP) { + TO_ENTRY_CONN(conn)->socks_request->has_finished = 1; + } + } connection_mark_for_close(conn); } @@ -340,10 +366,7 @@ test_conn_get_rsrc_teardown(const struct testcase_t *tc, void *arg) static void * test_conn_download_status_setup(const struct testcase_t *tc) { - (void)tc; - - /* Don't return NULL, that causes the test to fail */ - return (void*)"ok"; + return (void*)tc; } static int @@ -655,7 +678,8 @@ test_conn_download_status(void *arg) dir_connection_t *conn4 = NULL; connection_t *ap_conn = NULL; - consensus_flavor_t usable_flavor = (consensus_flavor_t)arg; + const struct testcase_t *tc = arg; + consensus_flavor_t usable_flavor = (consensus_flavor_t)tc->setup_data; /* The "other flavor" trick only works if there are two flavors */ tor_assert(N_CONSENSUS_FLAVORS == 2); diff --git a/src/test/test_containers.c b/src/test/test_containers.c index fd896760c0..d8b82e0661 100644 --- a/src/test/test_containers.c +++ b/src/test/test_containers.c @@ -132,7 +132,7 @@ test_container_smartlist_strings(void *arg) tt_str_op("def",OP_EQ, smartlist_get(sl, 5)); tt_str_op(" ",OP_EQ, smartlist_get(sl, 6)); tt_str_op("ghijk",OP_EQ, smartlist_get(sl, 7)); - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_clear(sl); smartlist_split_string(sl, "a,bbd,cdef", ",", SPLIT_SKIP_SPACE, 0); @@ -149,7 +149,7 @@ test_container_smartlist_strings(void *arg) tt_str_op("bnud",OP_EQ, smartlist_get(sl,6)); tt_str_op("",OP_EQ, smartlist_get(sl,7)); - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_clear(sl); smartlist_split_string(sl, " ab\tc \td ef ", NULL, @@ -165,7 +165,7 @@ test_container_smartlist_strings(void *arg) tt_str_op("ghi",OP_EQ, smartlist_get(sl,4)); tt_str_op("j",OP_EQ, smartlist_get(sl,5)); - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_clear(sl); cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL); @@ -186,7 +186,7 @@ test_container_smartlist_strings(void *arg) tt_int_op(5,OP_EQ, smartlist_len(sl)); tt_str_op("z",OP_EQ, smartlist_get(sl, 3)); tt_str_op("zhasd <> <> bnud<>",OP_EQ, smartlist_get(sl, 4)); - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_clear(sl); smartlist_split_string(sl, "abcd\n", "\n", @@ -198,7 +198,7 @@ test_container_smartlist_strings(void *arg) tt_int_op(2,OP_EQ, smartlist_len(sl)); tt_str_op("efgh",OP_EQ, smartlist_get(sl, 1)); - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_clear(sl); /* Test swapping, shuffling, and sorting. */ @@ -286,7 +286,7 @@ test_container_smartlist_strings(void *arg) tt_str_op(cp_alloc,OP_EQ, "and"); tor_free(cp_alloc); tt_int_op(smartlist_len(sl),OP_EQ, 6); - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_clear(sl); cp_alloc = smartlist_pop_last(sl); tt_ptr_op(cp_alloc,OP_EQ, NULL); @@ -326,7 +326,7 @@ test_container_smartlist_strings(void *arg) tt_assert(!allsame); tt_assert(allin); } - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_clear(sl); /* Test string_remove and remove and join_strings2 */ @@ -348,7 +348,7 @@ test_container_smartlist_strings(void *arg) done: - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_free(sl); tor_free(cp_alloc); } @@ -437,7 +437,7 @@ test_container_smartlist_digests(void *arg) tt_mem_op(smartlist_get(sl, 1),OP_EQ, "AAAAAAAAAAAAAAAAAAAA", DIGEST_LEN); done: - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_free(sl); } @@ -490,7 +490,7 @@ test_container_smartlist_join(void *arg) smartlist_free(sl3); SMARTLIST_FOREACH(sl2, char *, cp, tor_free(cp)); smartlist_free(sl2); - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_free(sl); tor_free(joined); } @@ -528,7 +528,7 @@ test_container_smartlist_pos(void *arg) tt_int_op(smartlist_pos(sl, smartlist_get(sl,6)), ==, 6); done: - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_free(sl); } @@ -1140,7 +1140,7 @@ test_container_smartlist_most_frequent(void *arg) tt_str_op(cp, ==, "def"); /* No tie */ done: - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl, char *, str, tor_free(str)); smartlist_free(sl); } diff --git a/src/test/test_controller.c b/src/test/test_controller.c index 7f9db4312f..f19c846144 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -4,7 +4,10 @@ #define CONTROL_PRIVATE #include "or.h" #include "control.h" +#include "entrynodes.h" +#include "networkstatus.h" #include "rendservice.h" +#include "routerlist.h" #include "test.h" static void @@ -134,6 +137,8 @@ test_rend_service_parse_port_config(void *arg) cfg = rend_service_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg); tt_assert(cfg); tt_assert(!err_msg); + rend_service_port_config_free(cfg); + cfg = NULL; /* XXX: Someone should add tests for AF_UNIX targets if supported. */ @@ -148,16 +153,1191 @@ test_rend_service_parse_port_config(void *arg) cfg = rend_service_parse_port_config("90001", sep, &err_msg); tt_assert(!cfg); tt_assert(err_msg); + tor_free(err_msg); + + /* unix port */ + cfg = NULL; + + /* quoted unix port */ + tor_free(err_msg); + cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"", + " ", &err_msg); + tt_assert(cfg); + tt_assert(!err_msg); + rend_service_port_config_free(cfg); + cfg = NULL; + + /* quoted unix port */ + tor_free(err_msg); + cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"", + " ", &err_msg); + tt_assert(cfg); + tt_assert(!err_msg); + rend_service_port_config_free(cfg); + cfg = NULL; + + /* quoted unix port, missing end quote */ + cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar", + " ", &err_msg); + tt_assert(!cfg); + tt_str_op(err_msg, OP_EQ, "Couldn't process address <unix:\"/tmp/foo bar> " + "from hidden service configuration"); + tor_free(err_msg); + + /* bogus IP address */ + cfg = rend_service_parse_port_config("100 1.2.3.4.5:9000", + " ", &err_msg); + tt_assert(!cfg); + tt_str_op(err_msg, OP_EQ, "Unparseable address in hidden service port " + "configuration."); + tor_free(err_msg); + + /* bogus port port */ + cfg = rend_service_parse_port_config("100 99999", + " ", &err_msg); + tt_assert(!cfg); + tt_str_op(err_msg, OP_EQ, "Unparseable or out-of-range port \"99999\" " + "in hidden service port configuration."); + tor_free(err_msg); done: rend_service_port_config_free(cfg); tor_free(err_msg); } +static void +test_add_onion_helper_clientauth(void *arg) +{ + rend_authorized_client_t *client = NULL; + char *err_msg = NULL; + int created = 0; + + (void)arg; + + /* Test "ClientName" only. */ + client = add_onion_helper_clientauth("alice", &created, &err_msg); + tt_assert(client); + tt_assert(created); + tt_assert(!err_msg); + rend_authorized_client_free(client); + + /* Test "ClientName:Blob" */ + client = add_onion_helper_clientauth("alice:475hGBHPlq7Mc0cRZitK/B", + &created, &err_msg); + tt_assert(client); + tt_assert(!created); + tt_assert(!err_msg); + rend_authorized_client_free(client); + + /* Test invalid client names */ + client = add_onion_helper_clientauth("no*asterisks*allowed", &created, + &err_msg); + tt_assert(!client); + tt_assert(err_msg); + tor_free(err_msg); + + /* Test invalid auth cookie */ + client = add_onion_helper_clientauth("alice:12345", &created, &err_msg); + tt_assert(!client); + tt_assert(err_msg); + tor_free(err_msg); + + /* Test invalid syntax */ + client = add_onion_helper_clientauth(":475hGBHPlq7Mc0cRZitK/B", &created, + &err_msg); + tt_assert(!client); + tt_assert(err_msg); + tor_free(err_msg); + + done: + rend_authorized_client_free(client); + tor_free(err_msg); +} + +/* Mocks and data/variables used for GETINFO download status tests */ + +static const download_status_t dl_status_default = + { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER, + DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 }; +static download_status_t ns_dl_status[N_CONSENSUS_FLAVORS]; +static download_status_t ns_dl_status_bootstrap[N_CONSENSUS_FLAVORS]; +static download_status_t ns_dl_status_running[N_CONSENSUS_FLAVORS]; + +/* + * These should explore all the possible cases of download_status_to_string() + * in control.c + */ +static const download_status_t dls_sample_1 = + { 1467163900, 0, 0, DL_SCHED_GENERIC, DL_WANT_ANY_DIRSERVER, + DL_SCHED_INCREMENT_FAILURE, DL_SCHED_DETERMINISTIC, 0, 0 }; +static const char * dls_sample_1_str = + "next-attempt-at 2016-06-29 01:31:40\n" + "n-download-failures 0\n" + "n-download-attempts 0\n" + "schedule DL_SCHED_GENERIC\n" + "want-authority DL_WANT_ANY_DIRSERVER\n" + "increment-on DL_SCHED_INCREMENT_FAILURE\n" + "backoff DL_SCHED_DETERMINISTIC\n"; +static const download_status_t dls_sample_2 = + { 1467164400, 1, 2, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY, + DL_SCHED_INCREMENT_FAILURE, DL_SCHED_DETERMINISTIC, 0, 0 }; +static const char * dls_sample_2_str = + "next-attempt-at 2016-06-29 01:40:00\n" + "n-download-failures 1\n" + "n-download-attempts 2\n" + "schedule DL_SCHED_CONSENSUS\n" + "want-authority DL_WANT_AUTHORITY\n" + "increment-on DL_SCHED_INCREMENT_FAILURE\n" + "backoff DL_SCHED_DETERMINISTIC\n"; +static const download_status_t dls_sample_3 = + { 1467154400, 12, 25, DL_SCHED_BRIDGE, DL_WANT_ANY_DIRSERVER, + DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_DETERMINISTIC, 0, 0 }; +static const char * dls_sample_3_str = + "next-attempt-at 2016-06-28 22:53:20\n" + "n-download-failures 12\n" + "n-download-attempts 25\n" + "schedule DL_SCHED_BRIDGE\n" + "want-authority DL_WANT_ANY_DIRSERVER\n" + "increment-on DL_SCHED_INCREMENT_ATTEMPT\n" + "backoff DL_SCHED_DETERMINISTIC\n"; +static const download_status_t dls_sample_4 = + { 1467166600, 3, 0, DL_SCHED_GENERIC, DL_WANT_ANY_DIRSERVER, + DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 }; +static const char * dls_sample_4_str = + "next-attempt-at 2016-06-29 02:16:40\n" + "n-download-failures 3\n" + "n-download-attempts 0\n" + "schedule DL_SCHED_GENERIC\n" + "want-authority DL_WANT_ANY_DIRSERVER\n" + "increment-on DL_SCHED_INCREMENT_FAILURE\n" + "backoff DL_SCHED_RANDOM_EXPONENTIAL\n" + "last-backoff-position 0\n" + "last-delay-used 0\n"; +static const download_status_t dls_sample_5 = + { 1467164600, 3, 7, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER, + DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 1, 2112, }; +static const char * dls_sample_5_str = + "next-attempt-at 2016-06-29 01:43:20\n" + "n-download-failures 3\n" + "n-download-attempts 7\n" + "schedule DL_SCHED_CONSENSUS\n" + "want-authority DL_WANT_ANY_DIRSERVER\n" + "increment-on DL_SCHED_INCREMENT_FAILURE\n" + "backoff DL_SCHED_RANDOM_EXPONENTIAL\n" + "last-backoff-position 1\n" + "last-delay-used 2112\n"; +static const download_status_t dls_sample_6 = + { 1467164200, 4, 9, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY, + DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_RANDOM_EXPONENTIAL, 3, 432 }; +static const char * dls_sample_6_str = + "next-attempt-at 2016-06-29 01:36:40\n" + "n-download-failures 4\n" + "n-download-attempts 9\n" + "schedule DL_SCHED_CONSENSUS\n" + "want-authority DL_WANT_AUTHORITY\n" + "increment-on DL_SCHED_INCREMENT_ATTEMPT\n" + "backoff DL_SCHED_RANDOM_EXPONENTIAL\n" + "last-backoff-position 3\n" + "last-delay-used 432\n"; + +/* Simulated auth certs */ +static const char *auth_id_digest_1_str = + "63CDD326DFEF0CA020BDD3FEB45A3286FE13A061"; +static download_status_t auth_def_cert_download_status_1; +static const char *auth_id_digest_2_str = + "2C209FCDD8D48DC049777B8DC2C0F94A0408BE99"; +static download_status_t auth_def_cert_download_status_2; +/* Expected form of digest list returned for GETINFO downloads/cert/fps */ +static const char *auth_id_digest_expected_list = + "63CDD326DFEF0CA020BDD3FEB45A3286FE13A061\n" + "2C209FCDD8D48DC049777B8DC2C0F94A0408BE99\n"; + +/* Signing keys for simulated auth 1 */ +static const char *auth_1_sk_1_str = + "AA69566029B1F023BA09451B8F1B10952384EB58"; +static download_status_t auth_1_sk_1_dls; +static const char *auth_1_sk_2_str = + "710865C7F06B73C5292695A8C34F1C94F769FF72"; +static download_status_t auth_1_sk_2_dls; +/* + * Expected form of sk digest list for + * GETINFO downloads/cert/<auth_id_digest_1_str>/sks + */ +static const char *auth_1_sk_digest_expected_list = + "AA69566029B1F023BA09451B8F1B10952384EB58\n" + "710865C7F06B73C5292695A8C34F1C94F769FF72\n"; + +/* Signing keys for simulated auth 2 */ +static const char *auth_2_sk_1_str = + "4299047E00D070AD6703FE00BE7AA756DB061E62"; +static download_status_t auth_2_sk_1_dls; +static const char *auth_2_sk_2_str = + "9451B8F1B10952384EB58B5F230C0BB701626C9B"; +static download_status_t auth_2_sk_2_dls; +/* + * Expected form of sk digest list for + * GETINFO downloads/cert/<auth_id_digest_2_str>/sks + */ +static const char *auth_2_sk_digest_expected_list = + "4299047E00D070AD6703FE00BE7AA756DB061E62\n" + "9451B8F1B10952384EB58B5F230C0BB701626C9B\n"; + +/* Simulated router descriptor digests or bridge identity digests */ +static const char *descbr_digest_1_str = + "616408544C7345822696074A1A3DFA16AB381CBD"; +static download_status_t descbr_digest_1_dl; +static const char *descbr_digest_2_str = + "06E8067246967265DBCB6641631B530EFEC12DC3"; +static download_status_t descbr_digest_2_dl; +/* Expected form of digest list returned for GETINFO downloads/desc/descs */ +static const char *descbr_expected_list = + "616408544C7345822696074A1A3DFA16AB381CBD\n" + "06E8067246967265DBCB6641631B530EFEC12DC3\n"; +/* + * Flag to make all descbr queries fail, to simulate not being + * configured such that such queries make sense. + */ +static int disable_descbr = 0; + +static void +reset_mocked_dl_statuses(void) +{ + int i; + + for (i = 0; i < N_CONSENSUS_FLAVORS; ++i) { + memcpy(&(ns_dl_status[i]), &dl_status_default, + sizeof(download_status_t)); + memcpy(&(ns_dl_status_bootstrap[i]), &dl_status_default, + sizeof(download_status_t)); + memcpy(&(ns_dl_status_running[i]), &dl_status_default, + sizeof(download_status_t)); + } + + memcpy(&auth_def_cert_download_status_1, &dl_status_default, + sizeof(download_status_t)); + memcpy(&auth_def_cert_download_status_2, &dl_status_default, + sizeof(download_status_t)); + memcpy(&auth_1_sk_1_dls, &dl_status_default, + sizeof(download_status_t)); + memcpy(&auth_1_sk_2_dls, &dl_status_default, + sizeof(download_status_t)); + memcpy(&auth_2_sk_1_dls, &dl_status_default, + sizeof(download_status_t)); + memcpy(&auth_2_sk_2_dls, &dl_status_default, + sizeof(download_status_t)); + + memcpy(&descbr_digest_1_dl, &dl_status_default, + sizeof(download_status_t)); + memcpy(&descbr_digest_2_dl, &dl_status_default, + sizeof(download_status_t)); +} + +static download_status_t * +ns_dl_status_mock(consensus_flavor_t flavor) +{ + return &(ns_dl_status[flavor]); +} + +static download_status_t * +ns_dl_status_bootstrap_mock(consensus_flavor_t flavor) +{ + return &(ns_dl_status_bootstrap[flavor]); +} + +static download_status_t * +ns_dl_status_running_mock(consensus_flavor_t flavor) +{ + return &(ns_dl_status_running[flavor]); +} + +static void +setup_ns_mocks(void) +{ + MOCK(networkstatus_get_dl_status_by_flavor, ns_dl_status_mock); + MOCK(networkstatus_get_dl_status_by_flavor_bootstrap, + ns_dl_status_bootstrap_mock); + MOCK(networkstatus_get_dl_status_by_flavor_running, + ns_dl_status_running_mock); + reset_mocked_dl_statuses(); +} + +static void +clear_ns_mocks(void) +{ + UNMOCK(networkstatus_get_dl_status_by_flavor); + UNMOCK(networkstatus_get_dl_status_by_flavor_bootstrap); + UNMOCK(networkstatus_get_dl_status_by_flavor_running); +} + +static smartlist_t * +cert_dl_status_auth_ids_mock(void) +{ + char digest[DIGEST_LEN], *tmp; + int len; + smartlist_t *list = NULL; + + /* Just pretend we have only the two hard-coded digests listed above */ + list = smartlist_new(); + len = base16_decode(digest, DIGEST_LEN, + auth_id_digest_1_str, strlen(auth_id_digest_1_str)); + tt_int_op(len, OP_EQ, DIGEST_LEN); + tmp = tor_malloc(DIGEST_LEN); + memcpy(tmp, digest, DIGEST_LEN); + smartlist_add(list, tmp); + len = base16_decode(digest, DIGEST_LEN, + auth_id_digest_2_str, strlen(auth_id_digest_2_str)); + tt_int_op(len, OP_EQ, DIGEST_LEN); + tmp = tor_malloc(DIGEST_LEN); + memcpy(tmp, digest, DIGEST_LEN); + smartlist_add(list, tmp); + + done: + return list; +} + +static download_status_t * +cert_dl_status_def_for_auth_mock(const char *digest) +{ + download_status_t *dl = NULL; + char digest_str[HEX_DIGEST_LEN+1]; + + tt_assert(digest != NULL); + base16_encode(digest_str, HEX_DIGEST_LEN + 1, + digest, DIGEST_LEN); + digest_str[HEX_DIGEST_LEN] = '\0'; + + if (strcmp(digest_str, auth_id_digest_1_str) == 0) { + dl = &auth_def_cert_download_status_1; + } else if (strcmp(digest_str, auth_id_digest_2_str) == 0) { + dl = &auth_def_cert_download_status_2; + } + + done: + return dl; +} + +static smartlist_t * +cert_dl_status_sks_for_auth_id_mock(const char *digest) +{ + smartlist_t *list = NULL; + char sk[DIGEST_LEN]; + char digest_str[HEX_DIGEST_LEN+1]; + char *tmp; + int len; + + tt_assert(digest != NULL); + base16_encode(digest_str, HEX_DIGEST_LEN + 1, + digest, DIGEST_LEN); + digest_str[HEX_DIGEST_LEN] = '\0'; + + /* + * Build a list of two hard-coded digests, depending on what we + * were just passed. + */ + if (strcmp(digest_str, auth_id_digest_1_str) == 0) { + list = smartlist_new(); + len = base16_decode(sk, DIGEST_LEN, + auth_1_sk_1_str, strlen(auth_1_sk_1_str)); + tt_int_op(len, OP_EQ, DIGEST_LEN); + tmp = tor_malloc(DIGEST_LEN); + memcpy(tmp, sk, DIGEST_LEN); + smartlist_add(list, tmp); + len = base16_decode(sk, DIGEST_LEN, + auth_1_sk_2_str, strlen(auth_1_sk_2_str)); + tt_int_op(len, OP_EQ, DIGEST_LEN); + tmp = tor_malloc(DIGEST_LEN); + memcpy(tmp, sk, DIGEST_LEN); + smartlist_add(list, tmp); + } else if (strcmp(digest_str, auth_id_digest_2_str) == 0) { + list = smartlist_new(); + len = base16_decode(sk, DIGEST_LEN, + auth_2_sk_1_str, strlen(auth_2_sk_1_str)); + tt_int_op(len, OP_EQ, DIGEST_LEN); + tmp = tor_malloc(DIGEST_LEN); + memcpy(tmp, sk, DIGEST_LEN); + smartlist_add(list, tmp); + len = base16_decode(sk, DIGEST_LEN, + auth_2_sk_2_str, strlen(auth_2_sk_2_str)); + tt_int_op(len, OP_EQ, DIGEST_LEN); + tmp = tor_malloc(DIGEST_LEN); + memcpy(tmp, sk, DIGEST_LEN); + smartlist_add(list, tmp); + } + + done: + return list; +} + +static download_status_t * +cert_dl_status_fp_sk_mock(const char *fp_digest, const char *sk_digest) +{ + download_status_t *dl = NULL; + char fp_digest_str[HEX_DIGEST_LEN+1], sk_digest_str[HEX_DIGEST_LEN+1]; + + /* + * Unpack the digests so we can compare them and figure out which + * dl status we want. + */ + + tt_assert(fp_digest != NULL); + base16_encode(fp_digest_str, HEX_DIGEST_LEN + 1, + fp_digest, DIGEST_LEN); + fp_digest_str[HEX_DIGEST_LEN] = '\0'; + tt_assert(sk_digest != NULL); + base16_encode(sk_digest_str, HEX_DIGEST_LEN + 1, + sk_digest, DIGEST_LEN); + sk_digest_str[HEX_DIGEST_LEN] = '\0'; + + if (strcmp(fp_digest_str, auth_id_digest_1_str) == 0) { + if (strcmp(sk_digest_str, auth_1_sk_1_str) == 0) { + dl = &auth_1_sk_1_dls; + } else if (strcmp(sk_digest_str, auth_1_sk_2_str) == 0) { + dl = &auth_1_sk_2_dls; + } + } else if (strcmp(fp_digest_str, auth_id_digest_2_str) == 0) { + if (strcmp(sk_digest_str, auth_2_sk_1_str) == 0) { + dl = &auth_2_sk_1_dls; + } else if (strcmp(sk_digest_str, auth_2_sk_2_str) == 0) { + dl = &auth_2_sk_2_dls; + } + } + + done: + return dl; +} + +static void +setup_cert_mocks(void) +{ + MOCK(list_authority_ids_with_downloads, cert_dl_status_auth_ids_mock); + MOCK(id_only_download_status_for_authority_id, + cert_dl_status_def_for_auth_mock); + MOCK(list_sk_digests_for_authority_id, + cert_dl_status_sks_for_auth_id_mock); + MOCK(download_status_for_authority_id_and_sk, + cert_dl_status_fp_sk_mock); + reset_mocked_dl_statuses(); +} + +static void +clear_cert_mocks(void) +{ + UNMOCK(list_authority_ids_with_downloads); + UNMOCK(id_only_download_status_for_authority_id); + UNMOCK(list_sk_digests_for_authority_id); + UNMOCK(download_status_for_authority_id_and_sk); +} + +static smartlist_t * +descbr_get_digests_mock(void) +{ + char digest[DIGEST_LEN], *tmp; + int len; + smartlist_t *list = NULL; + + if (!disable_descbr) { + /* Just pretend we have only the two hard-coded digests listed above */ + list = smartlist_new(); + len = base16_decode(digest, DIGEST_LEN, + descbr_digest_1_str, strlen(descbr_digest_1_str)); + tt_int_op(len, OP_EQ, DIGEST_LEN); + tmp = tor_malloc(DIGEST_LEN); + memcpy(tmp, digest, DIGEST_LEN); + smartlist_add(list, tmp); + len = base16_decode(digest, DIGEST_LEN, + descbr_digest_2_str, strlen(descbr_digest_2_str)); + tt_int_op(len, OP_EQ, DIGEST_LEN); + tmp = tor_malloc(DIGEST_LEN); + memcpy(tmp, digest, DIGEST_LEN); + smartlist_add(list, tmp); + } + + done: + return list; +} + +static download_status_t * +descbr_get_dl_by_digest_mock(const char *digest) +{ + download_status_t *dl = NULL; + char digest_str[HEX_DIGEST_LEN+1]; + + if (!disable_descbr) { + tt_assert(digest != NULL); + base16_encode(digest_str, HEX_DIGEST_LEN + 1, + digest, DIGEST_LEN); + digest_str[HEX_DIGEST_LEN] = '\0'; + + if (strcmp(digest_str, descbr_digest_1_str) == 0) { + dl = &descbr_digest_1_dl; + } else if (strcmp(digest_str, descbr_digest_2_str) == 0) { + dl = &descbr_digest_2_dl; + } + } + + done: + return dl; +} + +static void +setup_desc_mocks(void) +{ + MOCK(router_get_descriptor_digests, + descbr_get_digests_mock); + MOCK(router_get_dl_status_by_descriptor_digest, + descbr_get_dl_by_digest_mock); + reset_mocked_dl_statuses(); +} + +static void +clear_desc_mocks(void) +{ + UNMOCK(router_get_descriptor_digests); + UNMOCK(router_get_dl_status_by_descriptor_digest); +} + +static void +setup_bridge_mocks(void) +{ + disable_descbr = 0; + + MOCK(list_bridge_identities, + descbr_get_digests_mock); + MOCK(get_bridge_dl_status_by_id, + descbr_get_dl_by_digest_mock); + reset_mocked_dl_statuses(); +} + +static void +clear_bridge_mocks(void) +{ + UNMOCK(list_bridge_identities); + UNMOCK(get_bridge_dl_status_by_id); + + disable_descbr = 0; +} + +static void +test_download_status_consensus(void *arg) +{ + /* We just need one of these to pass, it doesn't matter what's in it */ + control_connection_t dummy; + /* Get results out */ + char *answer = NULL; + const char *errmsg = NULL; + + (void)arg; + + /* Check that the unknown prefix case works; no mocks needed yet */ + getinfo_helper_downloads(&dummy, "downloads/foo", &answer, &errmsg); + tt_assert(answer == NULL); + tt_str_op(errmsg, OP_EQ, "Unknown download status query"); + + setup_ns_mocks(); + + /* + * Check returning serialized dlstatuses, and implicitly also test + * download_status_to_string(). + */ + + /* Case 1 default/FLAV_NS*/ + memcpy(&(ns_dl_status[FLAV_NS]), &dls_sample_1, + sizeof(download_status_t)); + getinfo_helper_downloads(&dummy, "downloads/networkstatus/ns", + &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_1_str); + tor_free(answer); + errmsg = NULL; + + /* Case 2 default/FLAV_MICRODESC */ + memcpy(&(ns_dl_status[FLAV_MICRODESC]), &dls_sample_2, + sizeof(download_status_t)); + getinfo_helper_downloads(&dummy, "downloads/networkstatus/microdesc", + &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_2_str); + tor_free(answer); + errmsg = NULL; + + /* Case 3 bootstrap/FLAV_NS */ + memcpy(&(ns_dl_status_bootstrap[FLAV_NS]), &dls_sample_3, + sizeof(download_status_t)); + getinfo_helper_downloads(&dummy, "downloads/networkstatus/ns/bootstrap", + &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_3_str); + tor_free(answer); + errmsg = NULL; + + /* Case 4 bootstrap/FLAV_MICRODESC */ + memcpy(&(ns_dl_status_bootstrap[FLAV_MICRODESC]), &dls_sample_4, + sizeof(download_status_t)); + getinfo_helper_downloads(&dummy, + "downloads/networkstatus/microdesc/bootstrap", + &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_4_str); + tor_free(answer); + errmsg = NULL; + + /* Case 5 running/FLAV_NS */ + memcpy(&(ns_dl_status_running[FLAV_NS]), &dls_sample_5, + sizeof(download_status_t)); + getinfo_helper_downloads(&dummy, + "downloads/networkstatus/ns/running", + &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_5_str); + tor_free(answer); + errmsg = NULL; + + /* Case 6 running/FLAV_MICRODESC */ + memcpy(&(ns_dl_status_running[FLAV_MICRODESC]), &dls_sample_6, + sizeof(download_status_t)); + getinfo_helper_downloads(&dummy, + "downloads/networkstatus/microdesc/running", + &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_6_str); + tor_free(answer); + errmsg = NULL; + + /* Now check the error case */ + getinfo_helper_downloads(&dummy, "downloads/networkstatus/foo", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "Unknown flavor"); + errmsg = NULL; + + done: + clear_ns_mocks(); + tor_free(answer); + + return; +} + +static void +test_download_status_cert(void *arg) +{ + /* We just need one of these to pass, it doesn't matter what's in it */ + control_connection_t dummy; + /* Get results out */ + char *question = NULL; + char *answer = NULL; + const char *errmsg = NULL; + + (void)arg; + + setup_cert_mocks(); + + /* + * Check returning serialized dlstatuses and digest lists, and implicitly + * also test download_status_to_string() and digest_list_to_string(). + */ + + /* Case 1 - list of authority identity fingerprints */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fps", + &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, auth_id_digest_expected_list); + tor_free(answer); + errmsg = NULL; + + /* Case 2 - download status for default cert for 1st auth id */ + memcpy(&auth_def_cert_download_status_1, &dls_sample_1, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/cert/fp/%s", auth_id_digest_1_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_1_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Case 3 - download status for default cert for 2nd auth id */ + memcpy(&auth_def_cert_download_status_2, &dls_sample_2, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/cert/fp/%s", auth_id_digest_2_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_2_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Case 4 - list of signing key digests for 1st auth id */ + tor_asprintf(&question, "downloads/cert/fp/%s/sks", auth_id_digest_1_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, auth_1_sk_digest_expected_list); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Case 5 - list of signing key digests for 2nd auth id */ + tor_asprintf(&question, "downloads/cert/fp/%s/sks", auth_id_digest_2_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, auth_2_sk_digest_expected_list); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Case 6 - download status for 1st auth id, 1st sk */ + memcpy(&auth_1_sk_1_dls, &dls_sample_3, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/cert/fp/%s/%s", + auth_id_digest_1_str, auth_1_sk_1_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_3_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Case 7 - download status for 1st auth id, 2nd sk */ + memcpy(&auth_1_sk_2_dls, &dls_sample_4, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/cert/fp/%s/%s", + auth_id_digest_1_str, auth_1_sk_2_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_4_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Case 8 - download status for 2nd auth id, 1st sk */ + memcpy(&auth_2_sk_1_dls, &dls_sample_5, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/cert/fp/%s/%s", + auth_id_digest_2_str, auth_2_sk_1_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_5_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Case 9 - download status for 2nd auth id, 2nd sk */ + memcpy(&auth_2_sk_2_dls, &dls_sample_6, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/cert/fp/%s/%s", + auth_id_digest_2_str, auth_2_sk_2_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_6_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Now check the error cases */ + + /* Case 1 - query is garbage after downloads/cert/ part */ + getinfo_helper_downloads(&dummy, "downloads/cert/blahdeblah", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "Unknown certificate download status query"); + errmsg = NULL; + + /* + * Case 2 - looks like downloads/cert/fp/<fp>, but <fp> isn't even + * the right length for a digest. + */ + getinfo_helper_downloads(&dummy, "downloads/cert/fp/2B1D36D32B2942406", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "That didn't look like a digest"); + errmsg = NULL; + + /* + * Case 3 - looks like downloads/cert/fp/<fp>, and <fp> is digest-sized, + * but not parseable as one. + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/82F52AF55D250115FE44D3GC81D49643241D56A1", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "That didn't look like a digest"); + errmsg = NULL; + + /* + * Case 4 - downloads/cert/fp/<fp>, and <fp> is not a known authority + * identity digest + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, + "Failed to get download status for this authority identity digest"); + errmsg = NULL; + + /* + * Case 5 - looks like downloads/cert/fp/<fp>/<anything>, but <fp> doesn't + * parse as a sensible digest. + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/82F52AF55D250115FE44D3GC81D49643241D56A1/blah", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "That didn't look like an identity digest"); + errmsg = NULL; + + /* + * Case 6 - looks like downloads/cert/fp/<fp>/<anything>, but <fp> doesn't + * parse as a sensible digest. + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/82F52AF55D25/blah", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "That didn't look like an identity digest"); + errmsg = NULL; + + /* + * Case 7 - downloads/cert/fp/<fp>/sks, and <fp> is not a known authority + * digest. + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61/sks", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, + "Failed to get list of signing key digests for this authority " + "identity digest"); + errmsg = NULL; + + /* + * Case 8 - looks like downloads/cert/fp/<fp>/<sk>, but <sk> doesn't + * parse as a signing key digest. + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61/" + "82F52AF55D250115FE44D3GC81D49643241D56A1", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "That didn't look like a signing key digest"); + errmsg = NULL; + + /* + * Case 9 - looks like downloads/cert/fp/<fp>/<sk>, but <sk> doesn't + * parse as a signing key digest. + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61/" + "82F52AF55D250115FE44D", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "That didn't look like a signing key digest"); + errmsg = NULL; + + /* + * Case 10 - downloads/cert/fp/<fp>/<sk>, but <fp> isn't a known + * authority identity digest. + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/C6B05DF332F74DB9A13498EE3BBC7AA2F69FCB45/" + "3A214FC21AE25B012C2ECCB5F4EC8A3602D0545D", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, + "Failed to get download status for this identity/" + "signing key digest pair"); + errmsg = NULL; + + /* + * Case 11 - downloads/cert/fp/<fp>/<sk>, but <sk> isn't a known + * signing key digest. + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/63CDD326DFEF0CA020BDD3FEB45A3286FE13A061/" + "3A214FC21AE25B012C2ECCB5F4EC8A3602D0545D", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, + "Failed to get download status for this identity/" + "signing key digest pair"); + errmsg = NULL; + + /* + * Case 12 - downloads/cert/fp/<fp>/<sk>, but <sk> is on the list for + * a different authority identity digest. + */ + getinfo_helper_downloads(&dummy, + "downloads/cert/fp/63CDD326DFEF0CA020BDD3FEB45A3286FE13A061/" + "9451B8F1B10952384EB58B5F230C0BB701626C9B", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, + "Failed to get download status for this identity/" + "signing key digest pair"); + errmsg = NULL; + + done: + clear_cert_mocks(); + tor_free(answer); + + return; +} + +static void +test_download_status_desc(void *arg) +{ + /* We just need one of these to pass, it doesn't matter what's in it */ + control_connection_t dummy; + /* Get results out */ + char *question = NULL; + char *answer = NULL; + const char *errmsg = NULL; + + (void)arg; + + setup_desc_mocks(); + + /* + * Check returning serialized dlstatuses and digest lists, and implicitly + * also test download_status_to_string() and digest_list_to_string(). + */ + + /* Case 1 - list of router descriptor digests */ + getinfo_helper_downloads(&dummy, + "downloads/desc/descs", + &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, descbr_expected_list); + tor_free(answer); + errmsg = NULL; + + /* Case 2 - get download status for router descriptor 1 */ + memcpy(&descbr_digest_1_dl, &dls_sample_1, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/desc/%s", descbr_digest_1_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_1_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Case 3 - get download status for router descriptor 1 */ + memcpy(&descbr_digest_2_dl, &dls_sample_2, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/desc/%s", descbr_digest_2_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_2_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Now check the error cases */ + + /* Case 1 - non-digest-length garbage after downloads/desc */ + getinfo_helper_downloads(&dummy, "downloads/desc/blahdeblah", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "Unknown router descriptor download status query"); + errmsg = NULL; + + /* Case 2 - nonparseable digest-shaped thing */ + getinfo_helper_downloads( + &dummy, + "downloads/desc/774EC52FD9A5B80A6FACZE536616E8022E3470AG", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "That didn't look like a digest"); + errmsg = NULL; + + /* Case 3 - digest we have no descriptor for */ + getinfo_helper_downloads( + &dummy, + "downloads/desc/B05B46135B0B2C04EBE1DD6A6AE4B12D7CD2226A", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "No such descriptor digest found"); + errmsg = NULL; + + /* Case 4 - microdescs only */ + disable_descbr = 1; + getinfo_helper_downloads(&dummy, + "downloads/desc/descs", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, + "We don't seem to have a networkstatus-flavored consensus"); + errmsg = NULL; + disable_descbr = 0; + + done: + clear_desc_mocks(); + tor_free(answer); + + return; +} + +static void +test_download_status_bridge(void *arg) +{ + /* We just need one of these to pass, it doesn't matter what's in it */ + control_connection_t dummy; + /* Get results out */ + char *question = NULL; + char *answer = NULL; + const char *errmsg = NULL; + + (void)arg; + + setup_bridge_mocks(); + + /* + * Check returning serialized dlstatuses and digest lists, and implicitly + * also test download_status_to_string() and digest_list_to_string(). + */ + + /* Case 1 - list of bridge identity digests */ + getinfo_helper_downloads(&dummy, + "downloads/bridge/bridges", + &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, descbr_expected_list); + tor_free(answer); + errmsg = NULL; + + /* Case 2 - get download status for bridge descriptor 1 */ + memcpy(&descbr_digest_1_dl, &dls_sample_3, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/bridge/%s", descbr_digest_1_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_3_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Case 3 - get download status for router descriptor 1 */ + memcpy(&descbr_digest_2_dl, &dls_sample_4, + sizeof(download_status_t)); + tor_asprintf(&question, "downloads/bridge/%s", descbr_digest_2_str); + tt_assert(question != NULL); + getinfo_helper_downloads(&dummy, question, &answer, &errmsg); + tt_assert(answer != NULL); + tt_assert(errmsg == NULL); + tt_str_op(answer, OP_EQ, dls_sample_4_str); + tor_free(question); + tor_free(answer); + errmsg = NULL; + + /* Now check the error cases */ + + /* Case 1 - non-digest-length garbage after downloads/bridge */ + getinfo_helper_downloads(&dummy, "downloads/bridge/blahdeblah", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "Unknown bridge descriptor download status query"); + errmsg = NULL; + + /* Case 2 - nonparseable digest-shaped thing */ + getinfo_helper_downloads( + &dummy, + "downloads/bridge/774EC52FD9A5B80A6FACZE536616E8022E3470AG", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "That didn't look like a digest"); + errmsg = NULL; + + /* Case 3 - digest we have no descriptor for */ + getinfo_helper_downloads( + &dummy, + "downloads/bridge/B05B46135B0B2C04EBE1DD6A6AE4B12D7CD2226A", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "No such bridge identity digest found"); + errmsg = NULL; + + /* Case 4 - bridges disabled */ + disable_descbr = 1; + getinfo_helper_downloads(&dummy, + "downloads/bridge/bridges", + &answer, &errmsg); + tt_assert(answer == NULL); + tt_assert(errmsg != NULL); + tt_str_op(errmsg, OP_EQ, "We don't seem to be using bridges"); + errmsg = NULL; + disable_descbr = 0; + + done: + clear_bridge_mocks(); + tor_free(answer); + + return; +} + struct testcase_t controller_tests[] = { { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 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, + NULL }, + { "download_status_consensus", test_download_status_consensus, 0, NULL, + NULL }, + { "download_status_cert", test_download_status_cert, 0, NULL, + NULL }, + { "download_status_desc", test_download_status_desc, 0, NULL, NULL }, + { "download_status_bridge", test_download_status_bridge, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 6a95e92733..64a46f7914 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -18,15 +18,12 @@ #include <openssl/evp.h> #include <openssl/rand.h> -extern const char AUTHORITY_SIGNKEY_3[]; -extern const char AUTHORITY_SIGNKEY_A_DIGEST[]; -extern const char AUTHORITY_SIGNKEY_A_DIGEST256[]; - /** Run unit tests for Diffie-Hellman functionality. */ static void test_crypto_dh(void *arg) { crypto_dh_t *dh1 = crypto_dh_new(DH_TYPE_CIRCUIT); + crypto_dh_t *dh1_dup = NULL; crypto_dh_t *dh2 = crypto_dh_new(DH_TYPE_CIRCUIT); char p1[DH_BYTES]; char p2[DH_BYTES]; @@ -41,6 +38,9 @@ test_crypto_dh(void *arg) memset(p1, 0, DH_BYTES); memset(p2, 0, DH_BYTES); tt_mem_op(p1,OP_EQ, p2, DH_BYTES); + + tt_int_op(-1, OP_EQ, crypto_dh_get_public(dh1, p1, 6)); /* too short */ + tt_assert(! crypto_dh_get_public(dh1, p1, DH_BYTES)); tt_mem_op(p1,OP_NE, p2, DH_BYTES); tt_assert(! crypto_dh_get_public(dh2, p2, DH_BYTES)); @@ -54,15 +54,119 @@ test_crypto_dh(void *arg) tt_int_op(s1len,OP_EQ, s2len); tt_mem_op(s1,OP_EQ, s2, s1len); + /* test dh_dup; make sure it works the same. */ + dh1_dup = crypto_dh_dup(dh1); + s1len = crypto_dh_compute_secret(LOG_WARN, dh1_dup, p2, DH_BYTES, s1, 50); + tt_mem_op(s1,OP_EQ, s2, s1len); + { - /* XXXX Now fabricate some bad values and make sure they get caught, - * Check 0, 1, N-1, >= N, etc. - */ + /* Now fabricate some bad values and make sure they get caught. */ + + /* 1 and 0 should both fail. */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x01", 1, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x00", 1, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + + memset(p1, 0, DH_BYTES); /* 0 with padding. */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + + p1[DH_BYTES-1] = 1; /* 1 with padding*/ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + + /* 2 is okay, though weird. */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x02", 1, s1, 50); + tt_int_op(50, OP_EQ, s1len); + + const char P[] = + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE65381FFFFFFFFFFFFFFFF"; + + /* p-1, p, and so on are not okay. */ + base16_decode(p1, sizeof(p1), P, strlen(P)); + + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + + p1[DH_BYTES-1] = 0xFE; /* p-1 */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + + p1[DH_BYTES-1] = 0xFD; /* p-2 works fine */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50); + tt_int_op(50, OP_EQ, s1len); + + const char P_plus_one[] = + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE653820000000000000000"; + + base16_decode(p1, sizeof(p1), P_plus_one, strlen(P_plus_one)); + + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + + p1[DH_BYTES-1] = 0x01; /* p+2 */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + + p1[DH_BYTES-1] = 0xff; /* p+256 */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + + memset(p1, 0xff, DH_BYTES), /* 2^1024-1 */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50); + tt_int_op(-1, OP_EQ, s1len); + } + + { + /* provoke an error in the openssl DH_compute_key function; make sure we + * survive. */ + tt_assert(! crypto_dh_get_public(dh1, p1, DH_BYTES)); + + crypto_dh_free(dh2); + dh2= crypto_dh_new(DH_TYPE_CIRCUIT); /* no private key set */ + s1len = crypto_dh_compute_secret(LOG_WARN, dh2, + p1, DH_BYTES, + s1, 50); + tt_int_op(s1len, OP_EQ, -1); } done: crypto_dh_free(dh1); crypto_dh_free(dh2); + crypto_dh_free(dh1_dup); +} + +static void +test_crypto_openssl_version(void *arg) +{ + (void)arg; + const char *version = crypto_openssl_get_version_str(); + const char *h_version = crypto_openssl_get_header_version_str(); + tt_assert(version); + tt_assert(h_version); + tt_assert(!strcmpstart(version, h_version)); /* "-fips" suffix, etc */ + tt_assert(!strstr(version, "OpenSSL")); + int a=-1,b=-1,c=-1; + if (!strcmpstart(version, "LibreSSL") || !strcmpstart(version, "BoringSSL")) + return; + int r = tor_sscanf(version, "%d.%d.%d", &a,&b,&c); + tt_int_op(r, OP_EQ, 3); + tt_int_op(a, OP_GE, 0); + tt_int_op(b, OP_GE, 0); + tt_int_op(c, OP_GE, 0); + + done: + ; } /** Run unit tests for our random number generation function and its wrappers. @@ -73,6 +177,7 @@ test_crypto_rng(void *arg) int i, j, allok; char data1[100], data2[100]; double d; + char *h=NULL; /* Try out RNG. */ (void)arg; @@ -104,9 +209,16 @@ test_crypto_rng(void *arg) allok = 0; tor_free(host); } + + /* Make sure crypto_random_hostname clips its inputs properly. */ + h = crypto_random_hostname(20000, 9000, "www.", ".onion"); + tt_assert(! strcmpstart(h,"www.")); + tt_assert(! strcmpend(h,".onion")); + tt_int_op(63+4+6, OP_EQ, strlen(h)); + tt_assert(allok); done: - ; + tor_free(h); } static void @@ -125,14 +237,100 @@ test_crypto_rng_range(void *arg) if (x == 8) got_largest = 1; } - /* These fail with probability 1/10^603. */ tt_assert(got_smallest); tt_assert(got_largest); + + got_smallest = got_largest = 0; + const uint64_t ten_billion = 10 * ((uint64_t)1000000000000); + for (i = 0; i < 1000; ++i) { + uint64_t x = crypto_rand_uint64_range(ten_billion, ten_billion+10); + tt_u64_op(x, OP_GE, ten_billion); + tt_u64_op(x, OP_LT, ten_billion+10); + if (x == ten_billion) + got_smallest = 1; + if (x == ten_billion+9) + got_largest = 1; + } + + tt_assert(got_smallest); + tt_assert(got_largest); + + const time_t now = time(NULL); + for (i = 0; i < 2000; ++i) { + time_t x = crypto_rand_time_range(now, now+60); + tt_i64_op(x, OP_GE, now); + tt_i64_op(x, OP_LT, now+60); + if (x == now) + got_smallest = 1; + if (x == now+59) + got_largest = 1; + } + + tt_assert(got_smallest); + tt_assert(got_largest); done: ; } +static void +test_crypto_rng_strongest(void *arg) +{ + const char *how = arg; + int broken = 0; + + if (how == NULL) { + ; + } else if (!strcmp(how, "nosyscall")) { + break_strongest_rng_syscall = 1; + } else if (!strcmp(how, "nofallback")) { + break_strongest_rng_fallback = 1; + } else if (!strcmp(how, "broken")) { + broken = break_strongest_rng_syscall = break_strongest_rng_fallback = 1; + } + +#define N 128 + uint8_t combine_and[N]; + uint8_t combine_or[N]; + int i, j; + + memset(combine_and, 0xff, N); + memset(combine_or, 0, N); + + for (i = 0; i < 100; ++i) { /* 2^-100 chances just don't happen. */ + uint8_t output[N]; + memset(output, 0, N); + if (how == NULL) { + /* this one can't fail. */ + crypto_strongest_rand(output, sizeof(output)); + } else { + int r = crypto_strongest_rand_raw(output, sizeof(output)); + if (r == -1) { + if (broken) { + goto done; /* we're fine. */ + } + /* This function is allowed to break, but only if it always breaks. */ + tt_int_op(i, OP_EQ, 0); + tt_skip(); + } else { + tt_assert(! broken); + } + } + for (j = 0; j < N; ++j) { + combine_and[j] &= output[j]; + combine_or[j] |= output[j]; + } + } + + for (j = 0; j < N; ++j) { + tt_int_op(combine_and[j], OP_EQ, 0); + tt_int_op(combine_or[j], OP_EQ, 0xff); + } + done: + ; +#undef N +} + /* Test for rectifying openssl RAND engine. */ static void test_crypto_rng_engine(void *arg) @@ -165,15 +363,15 @@ test_crypto_rng_engine(void *arg) ; } -/** Run unit tests for our AES functionality */ +/** Run unit tests for our AES128 functionality */ static void -test_crypto_aes(void *arg) +test_crypto_aes128(void *arg) { char *data1 = NULL, *data2 = NULL, *data3 = NULL; crypto_cipher_t *env1 = NULL, *env2 = NULL; int i, j; char *mem_op_hex_tmp=NULL; - + char key[CIPHER_KEY_LEN]; int use_evp = !strcmp(arg,"evp"); evaluate_evp_for_aes(use_evp); evaluate_ctr_for_aes(); @@ -189,9 +387,10 @@ test_crypto_aes(void *arg) memset(data2, 0, 1024); memset(data3, 0, 1024); - env1 = crypto_cipher_new(NULL); + crypto_rand(key, sizeof(key)); + env1 = crypto_cipher_new(key); tt_ptr_op(env1, OP_NE, NULL); - env2 = crypto_cipher_new(crypto_cipher_get_key(env1)); + env2 = crypto_cipher_new(key); tt_ptr_op(env2, OP_NE, NULL); /* Try encrypting 512 chars. */ @@ -222,7 +421,7 @@ test_crypto_aes(void *arg) env2 = NULL; memset(data3, 0, 1024); - env2 = crypto_cipher_new(crypto_cipher_get_key(env1)); + env2 = crypto_cipher_new(key); tt_ptr_op(env2, OP_NE, NULL); for (j = 0; j < 1024-16; j += 17) { crypto_cipher_encrypt(env2, data3+j, data1+j, 17); @@ -312,6 +511,72 @@ test_crypto_aes(void *arg) tor_free(data3); } +static void +test_crypto_aes_ctr_testvec(void *arg) +{ + const char *bitstr = arg; + char *mem_op_hex_tmp=NULL; + crypto_cipher_t *c=NULL; + + /* from NIST SP800-38a, section F.5 */ + const char ctr16[] = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + const char plaintext16[] = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710"; + const char *ciphertext16; + const char *key16; + int bits; + + if (!strcmp(bitstr, "128")) { + ciphertext16 = /* section F.5.1 */ + "874d6191b620e3261bef6864990db6ce" + "9806f66b7970fdff8617187bb9fffdff" + "5ae4df3edbd5d35e5b4f09020db03eab" + "1e031dda2fbe03d1792170a0f3009cee"; + key16 = "2b7e151628aed2a6abf7158809cf4f3c"; + bits = 128; + } else if (!strcmp(bitstr, "192")) { + ciphertext16 = /* section F.5.3 */ + "1abc932417521ca24f2b0459fe7e6e0b" + "090339ec0aa6faefd5ccc2c6f4ce8e94" + "1e36b26bd1ebc670d1bd1d665620abf7" + "4f78a7f6d29809585a97daec58c6b050"; + key16 = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + bits = 192; + } else if (!strcmp(bitstr, "256")) { + ciphertext16 = /* section F.5.5 */ + "601ec313775789a5b7a7f504bbf3d228" + "f443e3ca4d62b59aca84e990cacaf5c5" + "2b0930daa23de94ce87017ba2d84988d" + "dfc9c58db67aada613c2dd08457941a6"; + key16 = + "603deb1015ca71be2b73aef0857d7781" + "1f352c073b6108d72d9810a30914dff4"; + bits = 256; + } else { + tt_abort_msg("AES doesn't support this number of bits."); + } + + char key[32]; + char iv[16]; + char plaintext[16*4]; + memset(key, 0xf9, sizeof(key)); /* poison extra bytes */ + base16_decode(key, sizeof(key), key16, strlen(key16)); + base16_decode(iv, sizeof(iv), ctr16, strlen(ctr16)); + base16_decode(plaintext, sizeof(plaintext), + plaintext16, strlen(plaintext16)); + + c = crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)iv, bits); + crypto_cipher_crypt_inplace(c, plaintext, sizeof(plaintext)); + test_memeq_hex(plaintext, ciphertext16); + + done: + tor_free(mem_op_hex_tmp); + crypto_cipher_free(c); +} + /** Run unit tests for our SHA-1 functionality */ static void test_crypto_sha(void *arg) @@ -764,11 +1029,11 @@ test_crypto_sha3(void *arg) crypto_digest_free(d1); /* Attempt to exercise the incremental hashing code by creating a randomized - * 100 KiB buffer, and hashing rand[1, 5 * Rate] bytes at a time. SHA3-512 + * 30 KiB buffer, and hashing rand[1, 5 * Rate] bytes at a time. SHA3-512 * is used because it has a lowest rate of the family (the code is common, * but the slower rate exercises more of it). */ - const size_t bufsz = 100 * 1024; + const size_t bufsz = 30 * 1024; size_t j = 0; large = tor_malloc(bufsz); crypto_rand(large, bufsz); @@ -1084,6 +1349,29 @@ test_crypto_pk_base64(void *arg) tor_free(encoded); } +#ifdef HAVE_TRUNCATE +#define do_truncate truncate +#else +static int +do_truncate(const char *fname, size_t len) +{ + struct stat st; + char *bytes; + + bytes = read_file_to_str(fname, RFTS_BIN, &st); + if (!bytes) + return -1; + /* This cast isn't so great, but it should be safe given the actual files + * and lengths we're using. */ + if (st.st_size < (off_t)len) + len = MIN(len, (size_t)st.st_size); + + int r = write_bytes_to_file(fname, bytes, len, 1); + tor_free(bytes); + return r; +} +#endif + /** Sanity check for crypto pk digests */ static void test_crypto_digests(void *arg) @@ -1114,6 +1402,33 @@ test_crypto_digests(void *arg) crypto_pk_free(k); } +static void +test_crypto_digest_names(void *arg) +{ + static const struct { + int a; const char *n; + } names[] = { + { DIGEST_SHA1, "sha1" }, + { DIGEST_SHA256, "sha256" }, + { DIGEST_SHA512, "sha512" }, + { DIGEST_SHA3_256, "sha3-256" }, + { DIGEST_SHA3_512, "sha3-512" }, + { -1, NULL } + }; + (void)arg; + + int i; + for (i = 0; names[i].n; ++i) { + tt_str_op(names[i].n, OP_EQ,crypto_digest_algorithm_get_name(names[i].a)); + tt_int_op(names[i].a, + OP_EQ,crypto_digest_algorithm_parse_name(names[i].n)); + } + tt_int_op(-1, OP_EQ, + crypto_digest_algorithm_parse_name("TimeCubeHash-4444")); + 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) @@ -1236,7 +1551,7 @@ test_crypto_formats(void *arg) strlcpy(data1, "f0d678affc000100", 1024); i = base16_decode(data2, 8, data1, 16); - tt_int_op(i,OP_EQ, 0); + tt_int_op(i,OP_EQ, 8); tt_mem_op(data2,OP_EQ, "\xf0\xd6\x78\xaf\xfc\x00\x01\x00",8); /* now try some failing base16 decodes */ @@ -1507,13 +1822,98 @@ test_crypto_hkdf_sha256(void *arg) "b206fa34e5bc78d063fc291501beec53b36e5a0e434561200c" "5f8bd13e0f88b3459600b4dc21d69363e2895321c06184879d" "94b18f078411be70b767c7fc40679a9440a0c95ea83a23efbf"); - done: tor_free(mem_op_hex_tmp); #undef EXPAND } static void +test_crypto_hkdf_sha256_testvecs(void *arg) +{ + (void) arg; + /* Test vectors from RFC5869, sections A.1 through A.3 */ + const struct { + const char *ikm16, *salt16, *info16; + int L; + const char *okm16; + } vecs[] = { + { /* from A.1 */ + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + 42, + "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf" + "34007208d5b887185865" + }, + { /* from A.2 */ + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f", + "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + 82, + "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c" + "59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71" + "cc30c58179ec3e87c14c01d5c1f3434f1d87" + }, + { /* from A.3 */ + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "", + "", + 42, + "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d" + "9d201395faa4b61a96c8", + }, + { NULL, NULL, NULL, -1, NULL } + }; + + int i; + char *ikm = NULL; + char *salt = NULL; + char *info = NULL; + char *okm = NULL; + char *mem_op_hex_tmp = NULL; + + for (i = 0; vecs[i].ikm16; ++i) { + size_t ikm_len = strlen(vecs[i].ikm16)/2; + size_t salt_len = strlen(vecs[i].salt16)/2; + size_t info_len = strlen(vecs[i].info16)/2; + size_t okm_len = vecs[i].L; + + ikm = tor_malloc(ikm_len); + salt = tor_malloc(salt_len); + info = tor_malloc(info_len); + okm = tor_malloc(okm_len); + + base16_decode(ikm, ikm_len, vecs[i].ikm16, strlen(vecs[i].ikm16)); + base16_decode(salt, salt_len, vecs[i].salt16, strlen(vecs[i].salt16)); + base16_decode(info, info_len, vecs[i].info16, strlen(vecs[i].info16)); + + int r = crypto_expand_key_material_rfc5869_sha256( + (const uint8_t*)ikm, ikm_len, + (const uint8_t*)salt, salt_len, + (const uint8_t*)info, info_len, + (uint8_t*)okm, okm_len); + tt_int_op(r, OP_EQ, 0); + test_memeq_hex(okm, vecs[i].okm16); + tor_free(ikm); + tor_free(salt); + tor_free(info); + tor_free(okm); + } + done: + tor_free(ikm); + tor_free(salt); + tor_free(info); + tor_free(okm); + tor_free(mem_op_hex_tmp); +} + +static void test_crypto_curve25519_impl(void *arg) { /* adapted from curve25519_donna, which adapted it from test-curve25519 @@ -1605,6 +2005,47 @@ test_crypto_curve25519_basepoint(void *arg) } static void +test_crypto_curve25519_testvec(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp = NULL; + + /* From RFC 7748, section 6.1 */ + /* Alice's private key, a: */ + const char a16[] = + "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"; + /* Alice's public key, X25519(a, 9): */ + const char a_pub16[] = + "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"; + /* Bob's private key, b: */ + const char b16[] = + "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"; + /* Bob's public key, X25519(b, 9): */ + const char b_pub16[] = + "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"; + /* Their shared secret, K: */ + const char k16[] = + "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"; + + uint8_t a[32], b[32], a_pub[32], b_pub[32], k1[32], k2[32]; + base16_decode((char*)a, sizeof(a), a16, strlen(a16)); + base16_decode((char*)b, sizeof(b), b16, strlen(b16)); + curve25519_basepoint_impl(a_pub, a); + curve25519_basepoint_impl(b_pub, b); + curve25519_impl(k1, a, b_pub); + curve25519_impl(k2, b, a_pub); + + test_memeq_hex(a, a16); + test_memeq_hex(b, b16); + test_memeq_hex(a_pub, a_pub16); + test_memeq_hex(b_pub, b_pub16); + test_memeq_hex(k1, k16); + test_memeq_hex(k2, k16); + done: + tor_free(mem_op_hex_tmp); +} + +static void test_crypto_curve25519_wrappers(void *arg) { curve25519_public_key_t pubkey1, pubkey2; @@ -1728,24 +2169,6 @@ test_crypto_curve25519_persist(void *arg) tor_free(tag); } -static void * -ed25519_testcase_setup(const struct testcase_t *testcase) -{ - crypto_ed25519_testing_force_impl(testcase->setup_data); - return testcase->setup_data; -} -static int -ed25519_testcase_cleanup(const struct testcase_t *testcase, void *ptr) -{ - (void)testcase; - (void)ptr; - crypto_ed25519_testing_restore_impl(); - return 1; -} -static const struct testcase_setup_t ed25519_test_setup = { - ed25519_testcase_setup, ed25519_testcase_cleanup -}; - static void test_crypto_ed25519_simple(void *arg) { @@ -1831,6 +2254,41 @@ test_crypto_ed25519_simple(void *arg) tt_int_op(0, OP_EQ, ed25519_checksig_batch(NULL, ch, 2)); } + /* Test the string-prefixed sign/checksig functions */ + { + ed25519_signature_t manual_sig; + char *prefixed_msg; + + /* Generate a signature with a prefixed msg. */ + tt_int_op(0, OP_EQ, ed25519_sign_prefixed(&sig1, msg, msg_len, + "always in the mood", + &kp1)); + + /* First, check that ed25519_sign_prefixed() returns the exact same sig as + if we had manually prefixed the msg ourselves. */ + tor_asprintf(&prefixed_msg, "%s%s", "always in the mood", msg); + tt_int_op(0, OP_EQ, ed25519_sign(&manual_sig, (uint8_t *)prefixed_msg, + strlen(prefixed_msg), &kp1)); + tor_free(prefixed_msg); + tt_assert(fast_memeq(sig1.sig, manual_sig.sig, sizeof(sig1.sig))); + + /* Test that prefixed checksig verifies it properly. */ + tt_int_op(0, OP_EQ, ed25519_checksig_prefixed(&sig1, msg, msg_len, + "always in the mood", + &pub1)); + + /* Test that checksig with wrong prefix fails. */ + tt_int_op(-1, OP_EQ, ed25519_checksig_prefixed(&sig1, msg, msg_len, + "always in the moo", + &pub1)); + tt_int_op(-1, OP_EQ, ed25519_checksig_prefixed(&sig1, msg, msg_len, + "always in the moon", + &pub1)); + tt_int_op(-1, OP_EQ, ed25519_checksig_prefixed(&sig1, msg, msg_len, + "always in the mood!", + &pub1)); + } + done: ; } @@ -1896,7 +2354,67 @@ test_crypto_ed25519_test_vectors(void *arg) "1fbc1e08682f2cc0c92efe8f4985dec61dcbd54d4b94a22547d24451271c8b00", "0a688e79be24f866286d4646b5d81c" }, - + /* These come from draft-irtf-cfrg-eddsa-05 section 7.1 */ + { + "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", + "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", + "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155" + "5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b", + "" + }, + { + "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb", + "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c", + "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da" + "085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00", + "72" + }, + { + "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5", + "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e", + "0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350" + "aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03", + "08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98" + "fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8" + "79de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d" + "658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc" + "1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4fe" + "ba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e" + "06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbef" + "efd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7" + "aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed1" + "85ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2" + "d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24" + "554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f270" + "88d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc" + "2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b07" + "07e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128ba" + "b27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51a" + "ddd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429e" + "c96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb7" + "51fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c" + "42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8" + "ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34df" + "f7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08" + "d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649" + "de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e4" + "88acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a3" + "2ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e" + "6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5f" + "b93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b5" + "0d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1" + "369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380d" + "b2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c" + "0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0" + }, + { + "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42", + "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", + "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b589" + "09351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704", + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" + "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" + }, { NULL, NULL, NULL, NULL} }; @@ -2066,8 +2584,9 @@ test_crypto_ed25519_testvectors(void *arg) #define DECODE(p,s) base16_decode((char*)(p),sizeof(p),(s),strlen(s)) #define EQ(a,h) test_memeq_hex((const char*)(a), (h)) - tt_int_op(0, OP_EQ, DECODE(sk, ED25519_SECRET_KEYS[i])); - tt_int_op(0, OP_EQ, DECODE(blinding_param, ED25519_BLINDING_PARAMS[i])); + tt_int_op(sizeof(sk), OP_EQ, DECODE(sk, ED25519_SECRET_KEYS[i])); + tt_int_op(sizeof(blinding_param), OP_EQ, DECODE(blinding_param, + ED25519_BLINDING_PARAMS[i])); tt_int_op(0, OP_EQ, ed25519_secret_key_from_seed(&esk, sk)); EQ(esk.seckey, ED25519_EXPANDED_SECRET_KEYS[i]); @@ -2112,74 +2631,51 @@ test_crypto_ed25519_testvectors(void *arg) } static void -test_crypto_ed25519_fuzz_donna(void *arg) +test_crypto_ed25519_storage(void *arg) { - const unsigned iters = 1024; - uint8_t msg[1024]; - unsigned i; (void)arg; + ed25519_keypair_t *keypair = NULL; + ed25519_public_key_t pub; + ed25519_secret_key_t sec; + char *fname_1 = tor_strdup(get_fname("ed_seckey_1")); + char *fname_2 = tor_strdup(get_fname("ed_pubkey_2")); + char *contents = NULL; + char *tag = NULL; - tt_assert(sizeof(msg) == iters); - crypto_rand((char*) msg, sizeof(msg)); - - /* Fuzz Ed25519-donna vs ref10, alternating the implementation used to - * generate keys/sign per iteration. - */ - for (i = 0; i < iters; ++i) { - const int use_donna = i & 1; - uint8_t blinding[32]; - curve25519_keypair_t ckp; - ed25519_keypair_t kp, kp_blind, kp_curve25519; - ed25519_public_key_t pk, pk_blind, pk_curve25519; - ed25519_signature_t sig, sig_blind; - int bit = 0; - - crypto_rand((char*) blinding, sizeof(blinding)); - - /* Impl. A: - * 1. Generate a keypair. - * 2. Blinded the keypair. - * 3. Sign a message (unblinded). - * 4. Sign a message (blinded). - * 5. Generate a curve25519 keypair, and convert it to Ed25519. - */ - ed25519_set_impl_params(use_donna); - tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, i&1)); - tt_int_op(0, OP_EQ, ed25519_keypair_blind(&kp_blind, &kp, blinding)); - tt_int_op(0, OP_EQ, ed25519_sign(&sig, msg, i, &kp)); - tt_int_op(0, OP_EQ, ed25519_sign(&sig_blind, msg, i, &kp_blind)); - - tt_int_op(0, OP_EQ, curve25519_keypair_generate(&ckp, i&1)); - tt_int_op(0, OP_EQ, ed25519_keypair_from_curve25519_keypair( - &kp_curve25519, &bit, &ckp)); - - /* Impl. B: - * 1. Validate the public key by rederiving it. - * 2. Validate the blinded public key by rederiving it. - * 3. Validate the unblinded signature (and test a invalid signature). - * 4. Validate the blinded signature. - * 5. Validate the public key (from Curve25519) by rederiving it. - */ - ed25519_set_impl_params(!use_donna); - tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pk, &kp.seckey)); - tt_mem_op(pk.pubkey, OP_EQ, kp.pubkey.pubkey, 32); - - tt_int_op(0, OP_EQ, ed25519_public_blind(&pk_blind, &kp.pubkey, blinding)); - tt_mem_op(pk_blind.pubkey, OP_EQ, kp_blind.pubkey.pubkey, 32); + keypair = tor_malloc_zero(sizeof(ed25519_keypair_t)); + tt_int_op(0,OP_EQ,ed25519_keypair_generate(keypair, 0)); + tt_int_op(0,OP_EQ, + ed25519_seckey_write_to_file(&keypair->seckey, fname_1, "foo")); + tt_int_op(0,OP_EQ, + ed25519_pubkey_write_to_file(&keypair->pubkey, fname_2, "bar")); - tt_int_op(0, OP_EQ, ed25519_checksig(&sig, msg, i, &pk)); - sig.sig[0] ^= 15; - tt_int_op(-1, OP_EQ, ed25519_checksig(&sig, msg, sizeof(msg), &pk)); + tt_int_op(-1, OP_EQ, ed25519_pubkey_read_from_file(&pub, &tag, fname_1)); + tt_ptr_op(tag, OP_EQ, NULL); + tt_int_op(-1, OP_EQ, ed25519_seckey_read_from_file(&sec, &tag, fname_2)); + tt_ptr_op(tag, OP_EQ, NULL); - tt_int_op(0, OP_EQ, ed25519_checksig(&sig_blind, msg, i, &pk_blind)); + tt_int_op(0, OP_EQ, ed25519_pubkey_read_from_file(&pub, &tag, fname_2)); + tt_str_op(tag, OP_EQ, "bar"); + tor_free(tag); + tt_int_op(0, OP_EQ, ed25519_seckey_read_from_file(&sec, &tag, fname_1)); + tt_str_op(tag, OP_EQ, "foo"); + tor_free(tag); - tt_int_op(0, OP_EQ, ed25519_public_key_from_curve25519_public_key( - &pk_curve25519, &ckp.pubkey, bit)); - tt_mem_op(pk_curve25519.pubkey, OP_EQ, kp_curve25519.pubkey.pubkey, 32); - } + /* whitebox test: truncated keys. */ + tt_int_op(0, ==, do_truncate(fname_1, 40)); + tt_int_op(0, ==, do_truncate(fname_2, 40)); + tt_int_op(-1, OP_EQ, ed25519_pubkey_read_from_file(&pub, &tag, fname_2)); + tt_ptr_op(tag, OP_EQ, NULL); + tor_free(tag); + tt_int_op(-1, OP_EQ, ed25519_seckey_read_from_file(&sec, &tag, fname_1)); + tt_ptr_op(tag, OP_EQ, NULL); done: - ; + tor_free(fname_1); + tor_free(fname_2); + tor_free(contents); + tor_free(tag); + ed25519_keypair_free(keypair); } static void @@ -2398,13 +2894,28 @@ struct testcase_t crypto_tests[] = { CRYPTO_LEGACY(rng), { "rng_range", test_crypto_rng_range, 0, NULL, NULL }, { "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL }, - { "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" }, - { "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" }, + { "rng_strongest", test_crypto_rng_strongest, TT_FORK, NULL, NULL }, + { "rng_strongest_nosyscall", test_crypto_rng_strongest, TT_FORK, + &passthrough_setup, (void*)"nosyscall" }, + { "rng_strongest_nofallback", test_crypto_rng_strongest, TT_FORK, + &passthrough_setup, (void*)"nofallback" }, + { "rng_strongest_broken", test_crypto_rng_strongest, TT_FORK, + &passthrough_setup, (void*)"broken" }, + { "openssl_version", test_crypto_openssl_version, TT_FORK, NULL, NULL }, + { "aes_AES", test_crypto_aes128, TT_FORK, &passthrough_setup, (void*)"aes" }, + { "aes_EVP", test_crypto_aes128, TT_FORK, &passthrough_setup, (void*)"evp" }, + { "aes128_ctr_testvec", test_crypto_aes_ctr_testvec, 0, + &passthrough_setup, (void*)"128" }, + { "aes192_ctr_testvec", test_crypto_aes_ctr_testvec, 0, + &passthrough_setup, (void*)"192" }, + { "aes256_ctr_testvec", test_crypto_aes_ctr_testvec, 0, + &passthrough_setup, (void*)"256" }, CRYPTO_LEGACY(sha), CRYPTO_LEGACY(pk), { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL }, { "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL }, CRYPTO_LEGACY(digests), + { "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}, CRYPTO_LEGACY(dh), @@ -2415,8 +2926,10 @@ struct testcase_t crypto_tests[] = { CRYPTO_LEGACY(base32_decode), { "kdf_TAP", test_crypto_kdf_TAP, 0, NULL, NULL }, { "hkdf_sha256", test_crypto_hkdf_sha256, 0, NULL, NULL }, + { "hkdf_sha256_testvecs", test_crypto_hkdf_sha256_testvecs, 0, NULL, NULL }, { "curve25519_impl", test_crypto_curve25519_impl, 0, NULL, NULL }, { "curve25519_impl_hibit", test_crypto_curve25519_impl, 0, NULL, (void*)"y"}, + { "curve25516_testvec", test_crypto_curve25519_testvec, 0, NULL, NULL }, { "curve25519_basepoint", test_crypto_curve25519_basepoint, TT_FORK, NULL, NULL }, { "curve25519_wrappers", test_crypto_curve25519_wrappers, 0, NULL, NULL }, @@ -2428,7 +2941,7 @@ struct testcase_t crypto_tests[] = { ED25519_TEST(convert, 0), ED25519_TEST(blinding, 0), ED25519_TEST(testvectors, 0), - ED25519_TEST(fuzz_donna, TT_FORK), + { "ed25519_storage", test_crypto_ed25519_storage, 0, NULL, NULL }, { "siphash", test_crypto_siphash, 0, NULL, NULL }, { "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL }, END_OF_TESTCASES diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c index 6f3e40e0ab..0d7d65ac73 100644 --- a/src/test/test_crypto_slow.c +++ b/src/test/test_crypto_slow.c @@ -421,12 +421,16 @@ test_crypto_pbkdf2_vectors(void *arg) secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1"); + /* This is the very slow one here. When enabled, it accounts for roughly + * half the time spent in test-slow. */ + /* base16_decode((char*)spec, sizeof(spec), "73616c74" "18" , 10); memset(out, 0x00, sizeof(out)); tt_int_op(20, OP_EQ, secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"); + */ base16_decode((char*)spec, sizeof(spec), "73616c7453414c5473616c7453414c5473616c745" @@ -503,9 +507,88 @@ test_crypto_pwbox(void *arg) tor_free(decoded); } +static void +test_crypto_ed25519_fuzz_donna(void *arg) +{ + const unsigned iters = 1024; + uint8_t msg[1024]; + unsigned i; + (void)arg; + + tt_assert(sizeof(msg) == iters); + crypto_rand((char*) msg, sizeof(msg)); + + /* Fuzz Ed25519-donna vs ref10, alternating the implementation used to + * generate keys/sign per iteration. + */ + for (i = 0; i < iters; ++i) { + const int use_donna = i & 1; + uint8_t blinding[32]; + curve25519_keypair_t ckp; + ed25519_keypair_t kp, kp_blind, kp_curve25519; + ed25519_public_key_t pk, pk_blind, pk_curve25519; + ed25519_signature_t sig, sig_blind; + int bit = 0; + + crypto_rand((char*) blinding, sizeof(blinding)); + + /* Impl. A: + * 1. Generate a keypair. + * 2. Blinded the keypair. + * 3. Sign a message (unblinded). + * 4. Sign a message (blinded). + * 5. Generate a curve25519 keypair, and convert it to Ed25519. + */ + ed25519_set_impl_params(use_donna); + tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, i&1)); + tt_int_op(0, OP_EQ, ed25519_keypair_blind(&kp_blind, &kp, blinding)); + tt_int_op(0, OP_EQ, ed25519_sign(&sig, msg, i, &kp)); + tt_int_op(0, OP_EQ, ed25519_sign(&sig_blind, msg, i, &kp_blind)); + + tt_int_op(0, OP_EQ, curve25519_keypair_generate(&ckp, i&1)); + tt_int_op(0, OP_EQ, ed25519_keypair_from_curve25519_keypair( + &kp_curve25519, &bit, &ckp)); + + /* Impl. B: + * 1. Validate the public key by rederiving it. + * 2. Validate the blinded public key by rederiving it. + * 3. Validate the unblinded signature (and test a invalid signature). + * 4. Validate the blinded signature. + * 5. Validate the public key (from Curve25519) by rederiving it. + */ + ed25519_set_impl_params(!use_donna); + tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pk, &kp.seckey)); + tt_mem_op(pk.pubkey, OP_EQ, kp.pubkey.pubkey, 32); + + tt_int_op(0, OP_EQ, ed25519_public_blind(&pk_blind, &kp.pubkey, blinding)); + tt_mem_op(pk_blind.pubkey, OP_EQ, kp_blind.pubkey.pubkey, 32); + + tt_int_op(0, OP_EQ, ed25519_checksig(&sig, msg, i, &pk)); + sig.sig[0] ^= 15; + tt_int_op(-1, OP_EQ, ed25519_checksig(&sig, msg, sizeof(msg), &pk)); + + tt_int_op(0, OP_EQ, ed25519_checksig(&sig_blind, msg, i, &pk_blind)); + + tt_int_op(0, OP_EQ, ed25519_public_key_from_curve25519_public_key( + &pk_curve25519, &ckp.pubkey, bit)); + tt_mem_op(pk_curve25519.pubkey, OP_EQ, kp_curve25519.pubkey.pubkey, 32); + } + + done: + ; +} + #define CRYPTO_LEGACY(name) \ { #name, test_crypto_ ## name , 0, NULL, NULL } +#define ED25519_TEST_ONE(name, fl, which) \ + { #name "/ed25519_" which, test_crypto_ed25519_ ## name, (fl), \ + &ed25519_test_setup, (void*)which } + +#define ED25519_TEST(name, fl) \ + ED25519_TEST_ONE(name, (fl), "donna"), \ + ED25519_TEST_ONE(name, (fl), "ref10") + struct testcase_t slow_crypto_tests[] = { CRYPTO_LEGACY(s2k_rfc2440), #ifdef HAVE_LIBSCRYPT @@ -527,6 +610,7 @@ struct testcase_t slow_crypto_tests[] = { { "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL }, { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL }, { "pwbox", test_crypto_pwbox, 0, NULL, NULL }, + ED25519_TEST(fuzz_donna, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_data.c b/src/test/test_data.c index 32de54bc84..788489a097 100644 --- a/src/test/test_data.c +++ b/src/test/test_data.c @@ -3,6 +3,8 @@ * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +#include "test.h" + /* Our unit test expect that the AUTHORITY_CERT_* public keys will sort * in this order. */ #define AUTHORITY_CERT_A AUTHORITY_CERT_3 diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 26b0e72a9a..cdc56acb89 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -11,6 +11,7 @@ #define DIRVOTE_PRIVATE #define ROUTER_PRIVATE #define ROUTERLIST_PRIVATE +#define ROUTERPARSE_PRIVATE #define HIBERNATE_PRIVATE #define NETWORKSTATUS_PRIVATE #define RELAY_PRIVATE @@ -30,10 +31,12 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" +#include "shared_random_state.h" #include "test.h" #include "test_dir_common.h" #include "torcert.h" #include "relay.h" +#include "log_test_helpers.h" #define NS_MODULE dir @@ -114,6 +117,7 @@ test_dir_formats(void *arg) const addr_policy_t *p; time_t now = time(NULL); port_cfg_t orport, dirport; + char cert_buf[256]; (void)arg; pk1 = pk_generate(0); @@ -133,6 +137,11 @@ test_dir_formats(void *arg) tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::"); r1->ipv6_orport = 9999; r1->onion_pkey = crypto_pk_dup_key(pk1); + /* Fake just enough of an ntor key to get by */ + curve25519_keypair_t r1_onion_keypair; + curve25519_keypair_generate(&r1_onion_keypair, 0); + r1->onion_curve25519_pkey = tor_memdup(&r1_onion_keypair.pubkey, + sizeof(curve25519_public_key_t)); r1->identity_pkey = crypto_pk_dup_key(pk2); r1->bandwidthrate = 1000; r1->bandwidthburst = 5000; @@ -165,11 +174,6 @@ test_dir_formats(void *arg) &kp2.pubkey, now, 86400, CERT_FLAG_INCLUDE_SIGNING_KEY); - char cert_buf[256]; - base64_encode(cert_buf, sizeof(cert_buf), - (const char*)r2->cache_info.signing_key_cert->encoded, - r2->cache_info.signing_key_cert->encoded_len, - BASE64_ENCODE_MULTILINE); r2->platform = tor_strdup(platform); r2->cache_info.published_on = 5; r2->or_port = 9005; @@ -192,7 +196,7 @@ test_dir_formats(void *arg) tt_assert(!crypto_pk_write_public_key_to_string(pk2 , &pk2_str, &pk2_str_len)); - /* XXXX025 router_dump_to_string should really take this from ri.*/ + /* XXXX+++ router_dump_to_string should really take this from ri.*/ options->ContactInfo = tor_strdup("Magri White " "<magri@elsewhere.example.com>"); /* Skip reachability checks for DirPort and tunnelled-dir-server */ @@ -228,7 +232,6 @@ test_dir_formats(void *arg) "platform Tor "VERSION" on ", sizeof(buf2)); strlcat(buf2, get_uname(), sizeof(buf2)); strlcat(buf2, "\n" - "protocols Link 1 2 Circuit 1\n" "published 1970-01-01 00:00:00\n" "fingerprint ", sizeof(buf2)); tt_assert(!crypto_pk_get_fingerprint(pk2, fingerprint, 1)); @@ -245,6 +248,11 @@ test_dir_formats(void *arg) strlcat(buf2, "hidden-service-dir\n", sizeof(buf2)); strlcat(buf2, "contact Magri White <magri@elsewhere.example.com>\n", sizeof(buf2)); + strlcat(buf2, "ntor-onion-key ", sizeof(buf2)); + base64_encode(cert_buf, sizeof(cert_buf), + (const char*)r1_onion_keypair.pubkey.public_key, 32, + BASE64_ENCODE_MULTILINE); + strlcat(buf2, cert_buf, sizeof(buf2)); strlcat(buf2, "reject *:*\n", sizeof(buf2)); strlcat(buf2, "tunnelled-dir-server\nrouter-signature\n", sizeof(buf2)); buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same @@ -274,6 +282,10 @@ test_dir_formats(void *arg) "router Fred 10.3.2.1 9005 0 0\n" "identity-ed25519\n" "-----BEGIN ED25519 CERT-----\n", sizeof(buf2)); + base64_encode(cert_buf, sizeof(cert_buf), + (const char*)r2->cache_info.signing_key_cert->encoded, + r2->cache_info.signing_key_cert->encoded_len, + BASE64_ENCODE_MULTILINE); strlcat(buf2, cert_buf, sizeof(buf2)); strlcat(buf2, "-----END ED25519 CERT-----\n", sizeof(buf2)); strlcat(buf2, "master-key-ed25519 ", sizeof(buf2)); @@ -288,7 +300,6 @@ test_dir_formats(void *arg) strlcat(buf2, "platform Tor "VERSION" on ", sizeof(buf2)); strlcat(buf2, get_uname(), sizeof(buf2)); strlcat(buf2, "\n" - "protocols Link 1 2 Circuit 1\n" "published 1970-01-01 00:00:05\n" "fingerprint ", sizeof(buf2)); tt_assert(!crypto_pk_get_fingerprint(pk1, fingerprint, 1)); @@ -580,7 +591,7 @@ test_dir_extrainfo_parsing(void *arg) crypto_pk_t *pk = ri->identity_pkey = crypto_pk_new(); \ tt_assert(! crypto_pk_read_public_key_from_string(pk, \ name##_KEY, strlen(name##_KEY))); \ - tt_int_op(0,OP_EQ,base16_decode(d, 20, name##_FP, strlen(name##_FP))); \ + tt_int_op(20,OP_EQ,base16_decode(d, 20, name##_FP, strlen(name##_FP))); \ digestmap_set((digestmap_t*)map, d, ri); \ ri = NULL; \ } while (0) @@ -702,8 +713,8 @@ test_dir_parse_router_list(void *arg) "9a651ee03b64325959e8f1b46f2b689b30750b4c"); /* Now tidy up */ - SMARTLIST_FOREACH(dest, routerinfo_t *, ri, routerinfo_free(ri)); - SMARTLIST_FOREACH(invalid, uint8_t *, d, tor_free(d)); + SMARTLIST_FOREACH(dest, routerinfo_t *, rinfo, routerinfo_free(rinfo)); + SMARTLIST_FOREACH(invalid, uint8_t *, dig, tor_free(dig)); smartlist_clear(dest); smartlist_clear(invalid); @@ -739,9 +750,9 @@ test_dir_parse_router_list(void *arg) else SMARTLIST_FOREACH(dest, extrainfo_t *, ei, extrainfo_free(ei)); smartlist_free(dest); - SMARTLIST_FOREACH(invalid, uint8_t *, d, tor_free(d)); + SMARTLIST_FOREACH(invalid, uint8_t *, dig, tor_free(dig)); smartlist_free(invalid); - SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(chunks, char *, chunk, tor_free(chunk)); smartlist_free(chunks); routerinfo_free(ri); if (map) { @@ -1167,7 +1178,7 @@ test_dir_fp_pairs(void *arg) tt_mem_op(pair->second,OP_EQ, "Use AES-256 instead.", DIGEST_LEN); done: - SMARTLIST_FOREACH(sl, fp_pair_t *, pair, tor_free(pair)); + SMARTLIST_FOREACH(sl, fp_pair_t *, pair_to_free, tor_free(pair_to_free)); smartlist_free(sl); } @@ -1435,6 +1446,20 @@ test_dir_measured_bw_kb_cache(void *arg) return; } +static char * +my_dirvote_compute_params(smartlist_t *votes, int method, + int total_authorities) +{ + smartlist_t *s = dirvote_compute_params(votes, method, total_authorities); + tor_assert(s); + char *res = smartlist_join_strings(s, " ", 0, NULL); + SMARTLIST_FOREACH(s, char *, cp, tor_free(cp)); + smartlist_free(s); + return res; +} + +#define dirvote_compute_params my_dirvote_compute_params + static void test_dir_param_voting(void *arg) { @@ -1544,6 +1569,67 @@ test_dir_param_voting(void *arg) return; } +static void +test_dir_param_voting_lookup(void *arg) +{ + (void)arg; + smartlist_t *lst = smartlist_new(); + + smartlist_split_string(lst, + "moomin=9 moomin=10 moomintroll=5 fred " + "jack= electricity=sdk opa=6z abc=9 abcd=99", + NULL, 0, 0); + + tt_int_op(1000, + OP_EQ, dirvote_get_intermediate_param_value(lst, "ab", 1000)); + tt_int_op(9, OP_EQ, dirvote_get_intermediate_param_value(lst, "abc", 1000)); + tt_int_op(99, OP_EQ, + dirvote_get_intermediate_param_value(lst, "abcd", 1000)); + + /* moomin appears twice. That's a bug. */ + tor_capture_bugs_(1); + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "moomin", -100)); + 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, + "!(n_found > 1)"); + tor_end_capture_bugs_(); + /* There is no 'fred=', so that is treated as not existing. */ + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "fred", -100)); + /* jack is truncated */ + tor_capture_bugs_(1); + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "jack", -100)); + 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, + "!(! ok)"); + tor_end_capture_bugs_(); + /* electricity and opa aren't integers. */ + tor_capture_bugs_(1); + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "electricity", -100)); + 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, + "!(! ok)"); + tor_end_capture_bugs_(); + + tor_capture_bugs_(1); + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "opa", -100)); + 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, + "!(! ok)"); + tor_end_capture_bugs_(); + + done: + SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp)); + smartlist_free(lst); + tor_end_capture_bugs_(); +} + +#undef dirvote_compute_params + /** Helper: Test that two networkstatus_voter_info_t do in fact represent the * same voting authority, and that they do in fact have all the same * information. */ @@ -1650,8 +1736,8 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now) tt_int_op(rs->addr,OP_EQ, 0x99008801); tt_int_op(rs->or_port,OP_EQ, 443); tt_int_op(rs->dir_port,OP_EQ, 8000); - /* no flags except "running" (16) and "v2dir" (64) */ - tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(80)); + /* no flags except "running" (16) and "v2dir" (64) and "valid" (128) */ + tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(0xd0)); } else if (tor_memeq(rs->identity_digest, "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5" "\x5\x5\x5\x5", @@ -1748,7 +1834,7 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now) tt_assert(!rs->is_stable); /* (If it wasn't running it wouldn't be here) */ tt_assert(rs->is_flagged_running); - tt_assert(!rs->is_valid); + tt_assert(rs->is_valid); tt_assert(!rs->is_named); tt_assert(rs->is_v2_dir); /* XXXX check version */ @@ -1788,6 +1874,15 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now) return; } +static authority_cert_t *mock_cert; + +static authority_cert_t * +get_my_v3_authority_cert_m(void) +{ + tor_assert(mock_cert); + return mock_cert; +} + /** Run a unit tests for generating and parsing networkstatuses, with * the supply test fns. */ static void @@ -1831,10 +1926,30 @@ test_a_networkstatus( tt_assert(rs_test); tt_assert(vrs_test); - tt_assert(!dir_common_authority_pk_init(&cert1, &cert2, &cert3, - &sign_skey_1, &sign_skey_2, - &sign_skey_3)); + MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); + + /* Parse certificates and keys. */ + cert1 = mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); + tt_assert(cert1); + cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL); + tt_assert(cert2); + cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL); + tt_assert(cert3); + sign_skey_1 = crypto_pk_new(); + sign_skey_2 = crypto_pk_new(); + sign_skey_3 = crypto_pk_new(); sign_skey_leg1 = pk_generate(4); + sr_state_init(0, 0); + + tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_1, + AUTHORITY_SIGNKEY_1, -1)); + tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_2, + AUTHORITY_SIGNKEY_2, -1)); + tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_3, + AUTHORITY_SIGNKEY_3, -1)); + + tt_assert(!crypto_pk_cmp_keys(sign_skey_1, cert1->signing_key)); + tt_assert(!crypto_pk_cmp_keys(sign_skey_2, cert2->signing_key)); tt_assert(!dir_common_construct_vote_1(&vote, cert1, sign_skey_1, vrs_gen, &v1, &n_vrs, now, 1)); @@ -1959,9 +2074,9 @@ test_a_networkstatus( tt_int_op(4,OP_EQ, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/ /* The voter id digests should be in this order. */ - tt_assert(memcmp(cert2->cache_info.identity_digest, + tt_assert(fast_memcmp(cert2->cache_info.identity_digest, cert1->cache_info.identity_digest,DIGEST_LEN)<0); - tt_assert(memcmp(cert1->cache_info.identity_digest, + tt_assert(fast_memcmp(cert1->cache_info.identity_digest, cert3->cache_info.identity_digest,DIGEST_LEN)<0); test_same_voter(smartlist_get(con->voters, 1), smartlist_get(v2->voters, 0)); @@ -2196,56 +2311,57 @@ test_dir_scale_bw(void *testdata) 1.0/7, 12.0, 24.0 }; - u64_dbl_t vals[8]; + double vals_dbl[8]; + uint64_t vals_u64[8]; uint64_t total; int i; (void) testdata; for (i=0; i<8; ++i) - vals[i].dbl = v[i]; + vals_dbl[i] = v[i]; - scale_array_elements_to_u64(vals, 8, &total); + scale_array_elements_to_u64(vals_u64, vals_dbl, 8, &total); tt_int_op((int)total, OP_EQ, 48); total = 0; for (i=0; i<8; ++i) { - total += vals[i].u64; + total += vals_u64[i]; } tt_assert(total >= (U64_LITERAL(1)<<60)); tt_assert(total <= (U64_LITERAL(1)<<62)); for (i=0; i<8; ++i) { /* vals[2].u64 is the scaled value of 1.0 */ - double ratio = ((double)vals[i].u64) / vals[2].u64; + double ratio = ((double)vals_u64[i]) / vals_u64[2]; tt_double_op(fabs(ratio - v[i]), OP_LT, .00001); } /* test handling of no entries */ total = 1; - scale_array_elements_to_u64(vals, 0, &total); + scale_array_elements_to_u64(vals_u64, vals_dbl, 0, &total); tt_assert(total == 0); /* make sure we don't read the array when we have no entries * may require compiler flags to catch NULL dereferences */ total = 1; - scale_array_elements_to_u64(NULL, 0, &total); + scale_array_elements_to_u64(NULL, NULL, 0, &total); tt_assert(total == 0); - scale_array_elements_to_u64(NULL, 0, NULL); + scale_array_elements_to_u64(NULL, NULL, 0, NULL); /* test handling of zero totals */ total = 1; - vals[0].dbl = 0.0; - scale_array_elements_to_u64(vals, 1, &total); + vals_dbl[0] = 0.0; + scale_array_elements_to_u64(vals_u64, vals_dbl, 1, &total); tt_assert(total == 0); - tt_assert(vals[0].u64 == 0); + tt_assert(vals_u64[0] == 0); - vals[0].dbl = 0.0; - vals[1].dbl = 0.0; - scale_array_elements_to_u64(vals, 2, NULL); - tt_assert(vals[0].u64 == 0); - tt_assert(vals[1].u64 == 0); + vals_dbl[0] = 0.0; + vals_dbl[1] = 0.0; + scale_array_elements_to_u64(vals_u64, vals_dbl, 2, NULL); + tt_assert(vals_u64[0] == 0); + tt_assert(vals_u64[1] == 0); done: ; @@ -2256,7 +2372,7 @@ test_dir_random_weighted(void *testdata) { int histogram[10]; uint64_t vals[10] = {3,1,2,4,6,0,7,5,8,9}, total=0; - u64_dbl_t inp[10]; + uint64_t inp_u64[10]; int i, choice; const int n = 50000; double max_sq_error; @@ -2266,12 +2382,12 @@ test_dir_random_weighted(void *testdata) * in a scrambled order to make sure we don't depend on order. */ memset(histogram,0,sizeof(histogram)); for (i=0; i<10; ++i) { - inp[i].u64 = vals[i]; + inp_u64[i] = vals[i]; total += vals[i]; } tt_u64_op(total, OP_EQ, 45); for (i=0; i<n; ++i) { - choice = choose_array_element_by_weight(inp, 10); + choice = choose_array_element_by_weight(inp_u64, 10); tt_int_op(choice, OP_GE, 0); tt_int_op(choice, OP_LT, 10); histogram[choice]++; @@ -2298,16 +2414,16 @@ test_dir_random_weighted(void *testdata) /* Now try a singleton; do we choose it? */ for (i = 0; i < 100; ++i) { - choice = choose_array_element_by_weight(inp, 1); + choice = choose_array_element_by_weight(inp_u64, 1); tt_int_op(choice, OP_EQ, 0); } /* Now try an array of zeros. We should choose randomly. */ memset(histogram,0,sizeof(histogram)); for (i = 0; i < 5; ++i) - inp[i].u64 = 0; + inp_u64[i] = 0; for (i = 0; i < n; ++i) { - choice = choose_array_element_by_weight(inp, 5); + choice = choose_array_element_by_weight(inp_u64, 5); tt_int_op(choice, OP_GE, 0); tt_int_op(choice, OP_LT, 5); histogram[choice]++; @@ -2847,7 +2963,7 @@ test_dir_dirserv_set_routerstatus_testing(void *arg) (void)arg; /* Init options */ - mock_options = malloc(sizeof(or_options_t)); + mock_options = tor_malloc(sizeof(or_options_t)); reset_options(mock_options, &mock_get_options_calls); MOCK(get_options, mock_get_options); @@ -2865,10 +2981,10 @@ test_dir_dirserv_set_routerstatus_testing(void *arg) routerset_parse(routerset_none, ROUTERSET_NONE_STR, "No routers"); /* Init routerstatuses */ - routerstatus_t *rs_a = malloc(sizeof(routerstatus_t)); + routerstatus_t *rs_a = tor_malloc(sizeof(routerstatus_t)); reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); - routerstatus_t *rs_b = malloc(sizeof(routerstatus_t)); + routerstatus_t *rs_b = tor_malloc(sizeof(routerstatus_t)); reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); /* Sanity check that routersets correspond to routerstatuses. @@ -3053,7 +3169,7 @@ test_dir_dirserv_set_routerstatus_testing(void *arg) tt_assert(rs_b->is_hs_dir == 1); done: - free(mock_options); + tor_free(mock_options); mock_options = NULL; UNMOCK(get_options); @@ -3062,8 +3178,8 @@ test_dir_dirserv_set_routerstatus_testing(void *arg) routerset_free(routerset_a); routerset_free(routerset_none); - free(rs_a); - free(rs_b); + tor_free(rs_a); + tor_free(rs_b); } static void @@ -3151,6 +3267,7 @@ static void test_dir_fetch_type(void *arg) { (void)arg; + tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_EXTRAINFO, ROUTER_PURPOSE_BRIDGE, NULL), OP_EQ, EXTRAINFO_DIRINFO | BRIDGE_DIRINFO); tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_EXTRAINFO, ROUTER_PURPOSE_GENERAL, @@ -3176,9 +3293,14 @@ test_dir_fetch_type(void *arg) tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_MICRODESC, ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, MICRODESC_DIRINFO); + /* This will give a warning, because this function isn't supposed to be + * used for HS descriptors. */ + setup_full_capture_of_logs(LOG_WARN); tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_RENDDESC_V2, ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, NO_DIRINFO); - done: ; + expect_single_log_msg_containing("Unexpected purpose"); + done: + teardown_capture_of_logs(); } static void @@ -3332,13 +3454,16 @@ test_dir_download_status_schedule(void *arg) (void)arg; download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC, DL_WANT_AUTHORITY, - DL_SCHED_INCREMENT_FAILURE }; + DL_SCHED_INCREMENT_FAILURE, + DL_SCHED_DETERMINISTIC, 0, 0 }; download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER, - DL_SCHED_INCREMENT_ATTEMPT}; + DL_SCHED_INCREMENT_ATTEMPT, + DL_SCHED_DETERMINISTIC, 0, 0 }; download_status_t dls_bridge = { 0, 0, 0, DL_SCHED_BRIDGE, DL_WANT_AUTHORITY, - DL_SCHED_INCREMENT_FAILURE}; + DL_SCHED_INCREMENT_FAILURE, + DL_SCHED_DETERMINISTIC, 0, 0 }; int increment = -1; int expected_increment = -1; time_t current_time = time(NULL); @@ -3354,6 +3479,7 @@ test_dir_download_status_schedule(void *arg) delay1 = 1000; increment = download_status_schedule_get_delay(&dls_failure, schedule, + 0, INT_MAX, TIME_MIN); expected_increment = delay1; tt_assert(increment == expected_increment); @@ -3362,6 +3488,7 @@ test_dir_download_status_schedule(void *arg) delay1 = INT_MAX; increment = download_status_schedule_get_delay(&dls_failure, schedule, + 0, INT_MAX, -1); expected_increment = delay1; tt_assert(increment == expected_increment); @@ -3370,6 +3497,7 @@ test_dir_download_status_schedule(void *arg) delay1 = 0; increment = download_status_schedule_get_delay(&dls_attempt, schedule, + 0, INT_MAX, 0); expected_increment = delay1; tt_assert(increment == expected_increment); @@ -3378,6 +3506,7 @@ test_dir_download_status_schedule(void *arg) delay1 = 1000; increment = download_status_schedule_get_delay(&dls_attempt, schedule, + 0, INT_MAX, 1); expected_increment = delay1; tt_assert(increment == expected_increment); @@ -3386,6 +3515,7 @@ test_dir_download_status_schedule(void *arg) delay1 = INT_MAX; increment = download_status_schedule_get_delay(&dls_bridge, schedule, + 0, INT_MAX, current_time); expected_increment = delay1; tt_assert(increment == expected_increment); @@ -3394,6 +3524,7 @@ test_dir_download_status_schedule(void *arg) delay1 = 1; increment = download_status_schedule_get_delay(&dls_bridge, schedule, + 0, INT_MAX, TIME_MAX); expected_increment = delay1; tt_assert(increment == expected_increment); @@ -3406,6 +3537,7 @@ test_dir_download_status_schedule(void *arg) delay2 = 100; increment = download_status_schedule_get_delay(&dls_attempt, schedule, + 0, INT_MAX, current_time); expected_increment = delay2; tt_assert(increment == expected_increment); @@ -3414,6 +3546,7 @@ test_dir_download_status_schedule(void *arg) delay2 = 1; increment = download_status_schedule_get_delay(&dls_bridge, schedule, + 0, INT_MAX, current_time); expected_increment = delay2; tt_assert(increment == expected_increment); @@ -3426,6 +3559,7 @@ test_dir_download_status_schedule(void *arg) delay2 = 5; increment = download_status_schedule_get_delay(&dls_attempt, schedule, + 0, INT_MAX, current_time); expected_increment = delay2; tt_assert(increment == expected_increment); @@ -3434,6 +3568,7 @@ test_dir_download_status_schedule(void *arg) delay2 = 17; increment = download_status_schedule_get_delay(&dls_bridge, schedule, + 0, INT_MAX, current_time); expected_increment = delay2; tt_assert(increment == expected_increment); @@ -3446,6 +3581,7 @@ test_dir_download_status_schedule(void *arg) delay2 = 35; increment = download_status_schedule_get_delay(&dls_attempt, schedule, + 0, INT_MAX, current_time); expected_increment = INT_MAX; tt_assert(increment == expected_increment); @@ -3454,6 +3590,7 @@ test_dir_download_status_schedule(void *arg) delay2 = 99; increment = download_status_schedule_get_delay(&dls_bridge, schedule, + 0, INT_MAX, current_time); expected_increment = INT_MAX; tt_assert(increment == expected_increment); @@ -3465,15 +3602,58 @@ test_dir_download_status_schedule(void *arg) } static void +test_dir_download_status_random_backoff(void *arg) +{ + download_status_t dls_random = + { 0, 0, 0, DL_SCHED_GENERIC, DL_WANT_AUTHORITY, + DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 }; + int increment = -1; + int old_increment; + time_t current_time = time(NULL); + const int min_delay = 0; + const int max_delay = 1000000; + + (void)arg; + + /* Check the random backoff cases */ + old_increment = 0; + do { + increment = download_status_schedule_get_delay(&dls_random, + NULL, + min_delay, max_delay, + current_time); + /* Test */ + tt_int_op(increment, OP_GE, min_delay); + tt_int_op(increment, OP_LE, max_delay); + tt_int_op(increment, OP_GE, old_increment); + /* We at most quadruple, and maybe add one */ + tt_int_op(increment, OP_LE, 4 * old_increment + 1); + + /* Advance */ + current_time += increment; + ++(dls_random.n_download_attempts); + ++(dls_random.n_download_failures); + + /* Try another maybe */ + old_increment = increment; + } while (increment < max_delay); + + done: + return; +} + +static void test_dir_download_status_increment(void *arg) { (void)arg; download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC, DL_WANT_AUTHORITY, - DL_SCHED_INCREMENT_FAILURE }; + DL_SCHED_INCREMENT_FAILURE, + DL_SCHED_DETERMINISTIC, 0, 0 }; download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_BRIDGE, DL_WANT_ANY_DIRSERVER, - DL_SCHED_INCREMENT_ATTEMPT}; + DL_SCHED_INCREMENT_ATTEMPT, + DL_SCHED_DETERMINISTIC, 0, 0 }; int delay0 = -1; int delay1 = -1; int delay2 = -1; @@ -3552,14 +3732,14 @@ test_dir_download_status_increment(void *arg) current_time + delay1 + 10, 0) == 0); - /* Check that failure increments don't happen on 503 for clients, but that - * attempt increments do. */ + /* Check that failure increments do happen on 503 for clients, and + * attempt increments do too. */ mock_get_options_calls = 0; next_at = download_status_increment_failure(&dls_failure, 503, "test", 0, current_time); - tt_assert(next_at == current_time + delay1); - tt_assert(download_status_get_n_failures(&dls_failure) == 1); - tt_assert(download_status_get_n_attempts(&dls_failure) == 2); + tt_i64_op(next_at, ==, current_time + delay2); + tt_int_op(download_status_get_n_failures(&dls_failure), ==, 2); + tt_int_op(download_status_get_n_attempts(&dls_failure), ==, 2); tt_assert(mock_get_options_calls >= 1); /* Check that failure increments do happen on 503 for servers */ @@ -3567,7 +3747,7 @@ test_dir_download_status_increment(void *arg) next_at = download_status_increment_failure(&dls_failure, 503, "test", 1, current_time); tt_assert(next_at == current_time + delay2); - tt_assert(download_status_get_n_failures(&dls_failure) == 2); + tt_assert(download_status_get_n_failures(&dls_failure) == 3); tt_assert(download_status_get_n_attempts(&dls_failure) == 3); tt_assert(mock_get_options_calls >= 1); @@ -3576,7 +3756,7 @@ test_dir_download_status_increment(void *arg) next_at = download_status_increment_failure(&dls_failure, 404, "test", 0, current_time); tt_assert(next_at == current_time + delay2); - tt_assert(download_status_get_n_failures(&dls_failure) == 3); + tt_assert(download_status_get_n_failures(&dls_failure) == 4); tt_assert(download_status_get_n_attempts(&dls_failure) == 4); tt_assert(mock_get_options_calls >= 1); @@ -3738,8 +3918,13 @@ test_dir_download_status_increment(void *arg) /* Check that attempt increments don't happen on failure-based schedules, * and that the attempt is set at the end of time */ mock_get_options_calls = 0; + setup_full_capture_of_logs(LOG_WARN); next_at = download_status_increment_attempt(&dls_failure, "test", current_time); + expect_single_log_msg_containing( + "Tried to launch an attempt-based connection on a failure-based " + "schedule."); + teardown_capture_of_logs(); tt_assert(next_at == TIME_MAX); tt_assert(download_status_get_n_failures(&dls_failure) == 0); tt_assert(download_status_get_n_attempts(&dls_failure) == 0); @@ -3751,6 +3936,7 @@ test_dir_download_status_increment(void *arg) UNMOCK(get_options); mock_options = NULL; mock_get_options_calls = 0; + teardown_capture_of_logs(); } static void @@ -3809,9 +3995,14 @@ test_dir_conn_purpose_to_string(void *data) EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_RENDDESC_V2, "hidden-service v2 descriptor upload"); EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_MICRODESC, "microdescriptor fetch"); + + /* This will give a warning, because there is no purpose 1024. */ + setup_full_capture_of_logs(LOG_WARN); EXPECT_CONN_PURPOSE(1024, "(unknown)"); + expect_single_log_msg_containing("Called with unknown purpose 1024"); - done: ; + done: + teardown_capture_of_logs(); } NS_DECL(int, @@ -4042,6 +4233,1003 @@ test_dir_choose_compression_level(void* data) done: ; } +/* + * Mock check_private_dir(), and always succeed - no need to actually + * look at or create anything on the filesystem. + */ + +static int +mock_check_private_dir(const char *dirname, cpd_check_t check, + const char *effective_user) +{ + (void)dirname; + (void)check; + (void)effective_user; + + return 0; +} + +/* + * This really mocks options_get_datadir_fname2_suffix(), but for testing + * dump_desc(), we only care about get_datadir_fname(sub1), which is defined + * in config.h as: + * + * options_get_datadir_fname2_suffix(get_options(), sub1, NULL, NULL) + */ + +static char * +mock_get_datadir_fname(const or_options_t *options, + const char *sub1, const char *sub2, + const char *suffix) +{ + char *rv = NULL; + + /* + * Assert we were called like get_datadir_fname2() or get_datadir_fname(), + * since that's all we implement here. + */ + tt_assert(options != NULL); + tt_assert(sub1 != NULL); + /* + * No particular assertions about sub2, since we could be in the + * get_datadir_fname() or get_datadir_fname2() case. + */ + tt_assert(suffix == NULL); + + /* Just duplicate the basename and return it for this mock */ + if (sub2) { + /* If we have sub2, it's the basename, otherwise sub1 */ + rv = tor_strdup(sub2); + } else { + rv = tor_strdup(sub1); + } + + done: + return rv; +} + +static char *last_unlinked_path = NULL; +static int unlinked_count = 0; + +static void +mock_unlink_reset(void) +{ + tor_free(last_unlinked_path); + unlinked_count = 0; +} + +static int +mock_unlink(const char *path) +{ + tt_assert(path != NULL); + + tor_free(last_unlinked_path); + last_unlinked_path = tor_strdup(path); + ++unlinked_count; + + done: + return 0; +} + +static char *last_write_str_path = NULL; +static uint8_t last_write_str_hash[DIGEST256_LEN]; +static int write_str_count = 0; + +static void +mock_write_str_to_file_reset(void) +{ + tor_free(last_write_str_path); + write_str_count = 0; +} + +static int +mock_write_str_to_file(const char *path, const char *str, int bin) +{ + size_t len; + uint8_t hash[DIGEST256_LEN]; + + (void)bin; + + tt_assert(path != NULL); + tt_assert(str != NULL); + + len = strlen(str); + crypto_digest256((char *)hash, str, len, DIGEST_SHA256); + + tor_free(last_write_str_path); + last_write_str_path = tor_strdup(path); + memcpy(last_write_str_hash, hash, sizeof(last_write_str_hash)); + ++write_str_count; + + done: + return 0; +} + +static void +test_dir_dump_unparseable_descriptors(void *data) +{ + /* + * These bogus descriptors look nothing at all like real bogus descriptors + * we might see, but we're only testing dump_desc() here, not the parser. + */ + const char *test_desc_type = "squamous"; + /* strlen(test_desc_1) = 583 bytes */ + const char *test_desc_1 = + "The most merciful thing in the world, I think, is the inability of the " + "human mind to correlate all its contents. We live on a placid island of" + " ignorance in the midst of black seas of infinity, and it was not meant" + " that we should voyage far. The sciences, each straining in its own dir" + "ection, have hitherto harmed us little; but some day the piecing togeth" + "er of dissociated knowledge will open up such terrifying vistas of real" + "ity, and of our frightful position therein, that we shall either go mad" + "from the revelation or flee from the light into the peace and safety of" + "a new dark age."; + uint8_t test_desc_1_hash[DIGEST256_LEN]; + char test_desc_1_hash_str[HEX_DIGEST256_LEN+1]; + /* strlen(test_desc_2) = 650 bytes */ + const char *test_desc_2 = + "I think their predominant colour was a greyish-green, though they had w" + "hite bellies. They were mostly shiny and slippery, but the ridges of th" + "eir backs were scaly. Their forms vaguely suggested the anthropoid, whi" + "le their heads were the heads of fish, with prodigious bulging eyes tha" + "t never closed. At the sides of their necks were palpitating gills, and" + "their long paws were webbed. They hopped irregularly, sometimes on two " + "legs and sometimes on four. I was somehow glad that they had no more th" + "an four limbs. Their croaking, baying voices, clearly wed tar articulat" + "e speech, held all the dark shades of expression which their staring fa" + "ces lacked."; + uint8_t test_desc_2_hash[DIGEST256_LEN]; + char test_desc_2_hash_str[HEX_DIGEST256_LEN+1]; + /* strlen(test_desc_3) = 700 bytes */ + const char *test_desc_3 = + "Without knowing what futurism is like, Johansen achieved something very" + "close to it when he spoke of the city; for instead of describing any de" + "finite structure or building, he dwells only on broad impressions of va" + "st angles and stone surfaces - surfaces too great to belong to anything" + "right or proper for this earth, and impious with horrible images and hi" + "eroglyphs. I mention his talk about angles because it suggests somethin" + "g Wilcox had told me of his awful dreams. He said that the geometry of " + "the dream-place he saw was abnormal, non-Euclidean, and loathsomely red" + "olent of spheres and dimensions apart from ours. Now an unlettered seam" + "an felt the same thing whilst gazing at the terrible reality."; + uint8_t test_desc_3_hash[DIGEST256_LEN]; + char test_desc_3_hash_str[HEX_DIGEST256_LEN+1]; + /* strlen(test_desc_3) = 604 bytes */ + const char *test_desc_4 = + "So we glanced back simultaneously, it would appear; though no doubt the" + "incipient motion of one prompted the imitation of the other. As we did " + "so we flashed both torches full strength at the momentarily thinned mis" + "t; either from sheer primitive anxiety to see all we could, or in a les" + "s primitive but equally unconscious effort to dazzle the entity before " + "we dimmed our light and dodged among the penguins of the labyrinth cent" + "er ahead. Unhappy act! Not Orpheus himself, or Lot's wife, paid much mo" + "re dearly for a backward glance. And again came that shocking, wide-ran" + "ged piping - \"Tekeli-li! Tekeli-li!\""; + uint8_t test_desc_4_hash[DIGEST256_LEN]; + char test_desc_4_hash_str[HEX_DIGEST256_LEN+1]; + (void)data; + + /* + * Set up options mock so we can force a tiny FIFO size and generate + * cleanups. + */ + mock_options = tor_malloc(sizeof(or_options_t)); + reset_options(mock_options, &mock_get_options_calls); + mock_options->MaxUnparseableDescSizeToLog = 1536; + MOCK(get_options, mock_get_options); + MOCK(check_private_dir, mock_check_private_dir); + MOCK(options_get_datadir_fname2_suffix, + mock_get_datadir_fname); + + /* + * Set up unlink and write mocks + */ + MOCK(tor_unlink, mock_unlink); + mock_unlink_reset(); + MOCK(write_str_to_file, mock_write_str_to_file); + mock_write_str_to_file_reset(); + + /* + * Compute hashes we'll need to recognize which descriptor is which + */ + crypto_digest256((char *)test_desc_1_hash, test_desc_1, + strlen(test_desc_1), DIGEST_SHA256); + base16_encode(test_desc_1_hash_str, sizeof(test_desc_1_hash_str), + (const char *)test_desc_1_hash, + sizeof(test_desc_1_hash)); + crypto_digest256((char *)test_desc_2_hash, test_desc_2, + strlen(test_desc_2), DIGEST_SHA256); + base16_encode(test_desc_2_hash_str, sizeof(test_desc_2_hash_str), + (const char *)test_desc_2_hash, + sizeof(test_desc_2_hash)); + crypto_digest256((char *)test_desc_3_hash, test_desc_3, + strlen(test_desc_3), DIGEST_SHA256); + base16_encode(test_desc_3_hash_str, sizeof(test_desc_3_hash_str), + (const char *)test_desc_3_hash, + sizeof(test_desc_3_hash)); + crypto_digest256((char *)test_desc_4_hash, test_desc_4, + strlen(test_desc_4), DIGEST_SHA256); + base16_encode(test_desc_4_hash_str, sizeof(test_desc_4_hash_str), + (const char *)test_desc_4_hash, + sizeof(test_desc_4_hash)); + + /* + * Reset the FIFO and check its state + */ + dump_desc_fifo_cleanup(); + tt_u64_op(len_descs_dumped, ==, 0); + tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0); + + /* + * (1) Fire off dump_desc() once; these descriptors should all be safely + * smaller than configured FIFO size. + */ + + dump_desc(test_desc_1, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 1); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_1_hash, DIGEST_SHA256); + + /* + * Reset the FIFO and check its state + */ + dump_desc_fifo_cleanup(); + tt_u64_op(len_descs_dumped, ==, 0); + tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0); + + /* + * Reset the mocks and check their state + */ + mock_unlink_reset(); + mock_write_str_to_file_reset(); + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 0); + + /* + * (2) Fire off dump_desc() twice; this still should trigger no cleanup. + */ + + /* First time */ + dump_desc(test_desc_2, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 1); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256); + + /* Second time */ + dump_desc(test_desc_3, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2) + strlen(test_desc_3)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256); + + /* + * Reset the FIFO and check its state + */ + dump_desc_fifo_cleanup(); + tt_u64_op(len_descs_dumped, ==, 0); + tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0); + + /* + * Reset the mocks and check their state + */ + mock_unlink_reset(); + mock_write_str_to_file_reset(); + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 0); + + /* + * (3) Three calls to dump_desc cause a FIFO cleanup + */ + + /* First time */ + dump_desc(test_desc_4, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_4)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 1); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256); + + /* Second time */ + dump_desc(test_desc_1, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_4) + strlen(test_desc_1)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_1_hash, DIGEST_SHA256); + + /* Third time - we should unlink the dump of test_desc_4 here */ + dump_desc(test_desc_2, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1) + strlen(test_desc_2)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 1); + tt_int_op(write_str_count, ==, 3); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256); + + /* + * Reset the FIFO and check its state + */ + dump_desc_fifo_cleanup(); + tt_u64_op(len_descs_dumped, ==, 0); + tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0); + + /* + * Reset the mocks and check their state + */ + mock_unlink_reset(); + mock_write_str_to_file_reset(); + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 0); + + /* + * (4) But repeating one (A B B) doesn't overflow and cleanup + */ + + /* First time */ + dump_desc(test_desc_3, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 1); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256); + + /* Second time */ + dump_desc(test_desc_4, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3) + strlen(test_desc_4)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256); + + /* Third time */ + dump_desc(test_desc_4, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3) + strlen(test_desc_4)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256); + + /* + * Reset the FIFO and check its state + */ + dump_desc_fifo_cleanup(); + tt_u64_op(len_descs_dumped, ==, 0); + tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0); + + /* + * Reset the mocks and check their state + */ + mock_unlink_reset(); + mock_write_str_to_file_reset(); + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 0); + + /* + * (5) Same for the (A B A) repetition + */ + + /* First time */ + dump_desc(test_desc_1, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 1); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_1_hash, DIGEST_SHA256); + + /* Second time */ + dump_desc(test_desc_2, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1) + strlen(test_desc_2)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256); + + /* Third time */ + dump_desc(test_desc_1, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1) + strlen(test_desc_2)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256); + + /* + * Reset the FIFO and check its state + */ + dump_desc_fifo_cleanup(); + tt_u64_op(len_descs_dumped, ==, 0); + tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0); + + /* + * Reset the mocks and check their state + */ + mock_unlink_reset(); + mock_write_str_to_file_reset(); + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 0); + + /* + * (6) (A B B C) triggering overflow on C causes A, not B to be unlinked + */ + + /* First time */ + dump_desc(test_desc_3, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 1); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256); + + /* Second time */ + dump_desc(test_desc_4, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3) + strlen(test_desc_4)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256); + + /* Third time */ + dump_desc(test_desc_4, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3) + strlen(test_desc_4)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256); + + /* Fourth time - we should unlink the dump of test_desc_3 here */ + dump_desc(test_desc_1, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_4) + strlen(test_desc_1)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 1); + tt_int_op(write_str_count, ==, 3); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_1_hash, DIGEST_SHA256); + + /* + * Reset the FIFO and check its state + */ + dump_desc_fifo_cleanup(); + tt_u64_op(len_descs_dumped, ==, 0); + tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0); + + /* + * Reset the mocks and check their state + */ + mock_unlink_reset(); + mock_write_str_to_file_reset(); + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 0); + + /* + * (7) (A B A C) triggering overflow on C causes B, not A to be unlinked + */ + + /* First time */ + dump_desc(test_desc_2, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 1); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256); + + /* Second time */ + dump_desc(test_desc_3, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2) + strlen(test_desc_3)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256); + + /* Third time */ + dump_desc(test_desc_2, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2) + strlen(test_desc_3)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 2); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256); + + /* Fourth time - we should unlink the dump of test_desc_3 here */ + dump_desc(test_desc_4, test_desc_type); + + /* + * Assert things about the FIFO state + */ + tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2) + strlen(test_desc_4)); + tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2); + + /* + * Assert things about the mocks + */ + tt_int_op(unlinked_count, ==, 1); + tt_int_op(write_str_count, ==, 3); + tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256); + + /* + * Reset the FIFO and check its state + */ + dump_desc_fifo_cleanup(); + tt_u64_op(len_descs_dumped, ==, 0); + tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0); + + /* + * Reset the mocks and check their state + */ + mock_unlink_reset(); + mock_write_str_to_file_reset(); + tt_int_op(unlinked_count, ==, 0); + tt_int_op(write_str_count, ==, 0); + + done: + + /* Clean up the fifo */ + dump_desc_fifo_cleanup(); + + /* Remove mocks */ + UNMOCK(tor_unlink); + mock_unlink_reset(); + UNMOCK(write_str_to_file); + mock_write_str_to_file_reset(); + UNMOCK(options_get_datadir_fname2_suffix); + UNMOCK(check_private_dir); + UNMOCK(get_options); + tor_free(mock_options); + mock_options = NULL; + + return; +} + +/* Variables for reset_read_file_to_str_mock() */ + +static int enforce_expected_filename = 0; +static char *expected_filename = NULL; +static char *file_content = NULL; +static size_t file_content_len = 0; +static struct stat file_stat; +static int read_count = 0, read_call_count = 0; + +static void +reset_read_file_to_str_mock(void) +{ + tor_free(expected_filename); + tor_free(file_content); + file_content_len = 0; + memset(&file_stat, 0, sizeof(file_stat)); + read_count = 0; + read_call_count = 0; +} + +static char * +read_file_to_str_mock(const char *filename, int flags, + struct stat *stat_out) { + char *result = NULL; + + /* Insist we got a filename */ + tt_assert(filename != NULL); + + /* We ignore flags */ + (void)flags; + + /* Bump the call count */ + ++read_call_count; + + if (enforce_expected_filename) { + tt_assert(expected_filename); + tt_str_op(filename, OP_EQ, expected_filename); + } + + if (expected_filename != NULL && + file_content != NULL && + strcmp(filename, expected_filename) == 0) { + /* You asked for it, you got it */ + + /* + * This is the same behavior as the real read_file_to_str(); + * if there's a NUL, the real size ends up in stat_out. + */ + result = tor_malloc(file_content_len + 1); + if (file_content_len > 0) { + memcpy(result, file_content, file_content_len); + } + result[file_content_len] = '\0'; + + /* Do we need to set up stat_out? */ + if (stat_out != NULL) { + memcpy(stat_out, &file_stat, sizeof(file_stat)); + /* We always return the correct length here */ + stat_out->st_size = file_content_len; + } + + /* Wooo, we have a return value - bump the counter */ + ++read_count; + } + /* else no match, return NULL */ + + done: + return result; +} + +/* This one tests dump_desc_populate_one_file() */ +static void +test_dir_populate_dump_desc_fifo(void *data) +{ + const char *dirname = "foo"; + const char *fname = NULL; + dumped_desc_t *ent; + + (void)data; + + /* + * Set up unlink and read_file_to_str mocks + */ + MOCK(tor_unlink, mock_unlink); + mock_unlink_reset(); + MOCK(read_file_to_str, read_file_to_str_mock); + reset_read_file_to_str_mock(); + + /* Check state of unlink mock */ + tt_int_op(unlinked_count, ==, 0); + + /* Some cases that should fail before trying to read the file */ + ent = dump_desc_populate_one_file(dirname, "bar"); + tt_assert(ent == NULL); + tt_int_op(unlinked_count, ==, 1); + tt_int_op(read_count, ==, 0); + tt_int_op(read_call_count, ==, 0); + + ent = dump_desc_populate_one_file(dirname, "unparseable-desc"); + tt_assert(ent == NULL); + tt_int_op(unlinked_count, ==, 2); + tt_int_op(read_count, ==, 0); + tt_int_op(read_call_count, ==, 0); + + ent = dump_desc_populate_one_file(dirname, "unparseable-desc.baz"); + tt_assert(ent == NULL); + tt_int_op(unlinked_count, ==, 3); + tt_int_op(read_count, ==, 0); + tt_int_op(read_call_count, ==, 0); + + ent = dump_desc_populate_one_file( + dirname, + "unparseable-desc.08AE85E90461F59E"); + tt_assert(ent == NULL); + tt_int_op(unlinked_count, ==, 4); + tt_int_op(read_count, ==, 0); + tt_int_op(read_call_count, ==, 0); + + ent = dump_desc_populate_one_file( + dirname, + "unparseable-desc.08AE85E90461F59EDF0981323F3A70D02B55AB54B44B04F" + "287D72F7B72F242E85C8CB0EDA8854A99"); + tt_assert(ent == NULL); + tt_int_op(unlinked_count, ==, 5); + tt_int_op(read_count, ==, 0); + tt_int_op(read_call_count, ==, 0); + + /* This is a correct-length digest but base16_decode() will fail */ + ent = dump_desc_populate_one_file( + dirname, + "unparseable-desc.68219B8BGE64B705A6FFC728C069DC596216D60A7D7520C" + "D5ECE250D912E686B"); + tt_assert(ent == NULL); + tt_int_op(unlinked_count, ==, 6); + tt_int_op(read_count, ==, 0); + tt_int_op(read_call_count, ==, 0); + + /* This one has a correctly formed filename and should try reading */ + + /* Read fails */ + ent = dump_desc_populate_one_file( + dirname, + "unparseable-desc.DF0981323F3A70D02B55AB54B44B04F287D72F7B72F242E" + "85C8CB0EDA8854A99"); + tt_assert(ent == NULL); + tt_int_op(unlinked_count, ==, 7); + tt_int_op(read_count, ==, 0); + tt_int_op(read_call_count, ==, 1); + + /* This read will succeed but the digest won't match the file content */ + fname = + "unparseable-desc." + "DF0981323F3A70D02B55AB54B44B04F287D72F7B72F242E85C8CB0EDA8854A99"; + enforce_expected_filename = 1; + tor_asprintf(&expected_filename, "%s%s%s", dirname, PATH_SEPARATOR, fname); + file_content = tor_strdup("hanc culpam maiorem an illam dicam?"); + file_content_len = strlen(file_content); + file_stat.st_mtime = 123456; + ent = dump_desc_populate_one_file(dirname, fname); + enforce_expected_filename = 0; + tt_assert(ent == NULL); + tt_int_op(unlinked_count, ==, 8); + tt_int_op(read_count, ==, 1); + tt_int_op(read_call_count, ==, 2); + tor_free(expected_filename); + tor_free(file_content); + + /* This one will match */ + fname = + "unparseable-desc." + "0786C7173447B7FB033FFCA2FC47C3CF71C30DD47CA8236D3FC7FF35853271C6"; + tor_asprintf(&expected_filename, "%s%s%s", dirname, PATH_SEPARATOR, fname); + file_content = tor_strdup("hanc culpam maiorem an illam dicam?"); + file_content_len = strlen(file_content); + file_stat.st_mtime = 789012; + ent = dump_desc_populate_one_file(dirname, fname); + tt_assert(ent != NULL); + tt_int_op(unlinked_count, ==, 8); + tt_int_op(read_count, ==, 2); + tt_int_op(read_call_count, ==, 3); + tt_str_op(ent->filename, OP_EQ, expected_filename); + tt_int_op(ent->len, ==, file_content_len); + tt_int_op(ent->when, ==, file_stat.st_mtime); + tor_free(ent->filename); + tor_free(ent); + tor_free(expected_filename); + + /* + * Reset the mocks and check their state + */ + mock_unlink_reset(); + tt_int_op(unlinked_count, ==, 0); + reset_read_file_to_str_mock(); + tt_int_op(read_count, ==, 0); + + done: + + UNMOCK(tor_unlink); + mock_unlink_reset(); + UNMOCK(read_file_to_str); + reset_read_file_to_str_mock(); + + tor_free(file_content); + + return; +} + +static smartlist_t * +listdir_mock(const char *dname) +{ + smartlist_t *l; + + /* Ignore the name, always return this list */ + (void)dname; + + l = smartlist_new(); + smartlist_add(l, tor_strdup("foo")); + smartlist_add(l, tor_strdup("bar")); + smartlist_add(l, tor_strdup("baz")); + + return l; +} + +static dumped_desc_t * +pop_one_mock(const char *dirname, const char *f) +{ + dumped_desc_t *ent = NULL; + + if (dirname != NULL && strcmp(dirname, "d") == 0) { + if (f != NULL && strcmp(f, "foo") == 0) { + ent = tor_malloc_zero(sizeof(*ent)); + ent->filename = tor_strdup("d/foo"); + ent->len = 123; + ent->digest_sha256[0] = 1; + ent->when = 1024; + } else if (f != NULL && strcmp(f, "bar") == 0) { + ent = tor_malloc_zero(sizeof(*ent)); + ent->filename = tor_strdup("d/bar"); + ent->len = 456; + ent->digest_sha256[0] = 2; + /* + * Note that the timestamps are in a different order than + * listdir_mock() returns; we're testing the sort order. + */ + ent->when = 512; + } else if (f != NULL && strcmp(f, "baz") == 0) { + ent = tor_malloc_zero(sizeof(*ent)); + ent->filename = tor_strdup("d/baz"); + ent->len = 789; + ent->digest_sha256[0] = 3; + ent->when = 768; + } + } + + return ent; +} + +/* This one tests dump_desc_populate_fifo_from_directory() */ +static void +test_dir_populate_dump_desc_fifo_2(void *data) +{ + dumped_desc_t *ent = NULL; + + (void)data; + + /* Set up the mocks */ + MOCK(tor_listdir, listdir_mock); + MOCK(dump_desc_populate_one_file, pop_one_mock); + + /* Run dump_desc_populate_fifo_from_directory() */ + descs_dumped = NULL; + len_descs_dumped = 0; + dump_desc_populate_fifo_from_directory("d"); + tt_assert(descs_dumped != NULL); + tt_int_op(smartlist_len(descs_dumped), OP_EQ, 3); + tt_u64_op(len_descs_dumped, OP_EQ, 1368); + ent = smartlist_get(descs_dumped, 0); + tt_str_op(ent->filename, OP_EQ, "d/bar"); + tt_int_op(ent->len, OP_EQ, 456); + tt_int_op(ent->when, OP_EQ, 512); + ent = smartlist_get(descs_dumped, 1); + tt_str_op(ent->filename, OP_EQ, "d/baz"); + tt_int_op(ent->len, OP_EQ, 789); + tt_int_op(ent->when, OP_EQ, 768); + ent = smartlist_get(descs_dumped, 2); + tt_str_op(ent->filename, OP_EQ, "d/foo"); + tt_int_op(ent->len, OP_EQ, 123); + tt_int_op(ent->when, OP_EQ, 1024); + + done: + dump_desc_fifo_cleanup(); + + UNMOCK(dump_desc_populate_one_file); + UNMOCK(tor_listdir); + + return; +} + static int mock_networkstatus_consensus_is_bootstrapping_value = 0; static int mock_networkstatus_consensus_is_bootstrapping(time_t now) @@ -4093,7 +5281,7 @@ test_dir_find_dl_schedule(void* data) smartlist_t client_boot_auth_only_cons, client_boot_auth_cons; smartlist_t client_boot_fallback_cons, bridge; - mock_options = malloc(sizeof(or_options_t)); + mock_options = tor_malloc(sizeof(or_options_t)); reset_options(mock_options, &mock_get_options_calls); MOCK(get_options, mock_get_options); @@ -4202,10 +5390,53 @@ test_dir_find_dl_schedule(void* data) UNMOCK(networkstatus_consensus_is_bootstrapping); UNMOCK(networkstatus_consensus_can_use_extra_fallbacks); UNMOCK(get_options); - free(mock_options); + tor_free(mock_options); mock_options = NULL; } +static void +test_dir_assumed_flags(void *arg) +{ + (void)arg; + smartlist_t *tokens = smartlist_new(); + memarea_t *area = memarea_new(); + routerstatus_t *rs = NULL; + + /* First, we should always assume that the Running flag is set, even + * when it isn't listed, since the consensus method is always + * higher than 4. */ + const char *str1 = + "r example hereiswhereyouridentitygoes 2015-08-30 12:00:00 " + "192.168.0.1 9001 0\n" + "m thisoneislongerbecauseitisa256bitmddigest33\n" + "s Fast Guard Stable\n"; + + const char *cp = str1; + rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL, + 23, FLAV_MICRODESC); + tt_assert(rs); + tt_assert(rs->is_flagged_running); + tt_assert(! rs->is_valid); + tt_assert(! rs->is_exit); + tt_assert(rs->is_fast); + routerstatus_free(rs); + + /* With method 24 or later, we can assume "valid" is set. */ + cp = str1; + rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL, + 24, FLAV_MICRODESC); + tt_assert(rs); + tt_assert(rs->is_flagged_running); + tt_assert(rs->is_valid); + tt_assert(! rs->is_exit); + tt_assert(rs->is_fast); + + done: + smartlist_free(tokens); + memarea_drop_all(area); + routerstatus_free(rs); +} + #define DIR_LEGACY(name) \ { #name, test_dir_ ## name , TT_FORK, NULL, NULL } @@ -4230,6 +5461,7 @@ struct testcase_t dir_tests[] = { DIR_LEGACY(measured_bw_kb), DIR_LEGACY(measured_bw_kb_cache), DIR_LEGACY(param_voting), + DIR(param_voting_lookup, 0), DIR_LEGACY(v3_networkstatus), DIR(random_weighted, 0), DIR(scale_bw, 0), @@ -4242,6 +5474,7 @@ struct testcase_t dir_tests[] = { DIR(fetch_type, 0), DIR(packages, 0), DIR(download_status_schedule, 0), + DIR(download_status_random_backoff, 0), DIR(download_status_increment, 0), DIR(authdir_type_to_string, 0), DIR(conn_purpose_to_string, 0), @@ -4250,10 +5483,14 @@ struct testcase_t dir_tests[] = { DIR(should_not_init_request_to_dir_auths_without_v3_info, 0), DIR(should_init_request_to_dir_auths, 0), DIR(choose_compression_level, 0), + DIR(dump_unparseable_descriptors, 0), + DIR(populate_dump_desc_fifo, 0), + DIR(populate_dump_desc_fifo_2, 0), DIR_ARG(find_dl_schedule, TT_FORK, "bf"), DIR_ARG(find_dl_schedule, TT_FORK, "ba"), DIR_ARG(find_dl_schedule, TT_FORK, "cf"), DIR_ARG(find_dl_schedule, TT_FORK, "ca"), + DIR(assumed_flags, 0), END_OF_TESTCASES }; diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c index 0b446c2dfd..ca43dd4c04 100644 --- a/src/test/test_dir_common.c +++ b/src/test/test_dir_common.c @@ -21,13 +21,6 @@ networkstatus_t * dir_common_add_rs_and_parse(networkstatus_t *vote, crypto_pk_t *sign_skey, int *n_vrs, time_t now, int clear_rl); -extern const char AUTHORITY_CERT_1[]; -extern const char AUTHORITY_SIGNKEY_1[]; -extern const char AUTHORITY_CERT_2[]; -extern const char AUTHORITY_SIGNKEY_2[]; -extern const char AUTHORITY_CERT_3[]; -extern const char AUTHORITY_SIGNKEY_3[]; - /** Initialize and set auth certs and keys * Returns 0 on success, -1 on failure. Clean up handled by caller. */ @@ -96,6 +89,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now) /* all flags but running and v2dir cleared */ rs->is_flagged_running = 1; rs->is_v2_dir = 1; + rs->is_valid = 1; /* xxxxx */ break; case 1: /* Generate the second routerstatus. */ diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 05657ca452..a0f22f1f0c 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -38,7 +38,15 @@ #include <dirent.h> #endif +#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 "vote_descriptors.inc" +#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS +ENABLE_GCC_WARNING(overlength-strings) +#endif #define NS_MODULE dir_handle_get @@ -61,7 +69,13 @@ connection_write_to_buf_mock(const char *string, size_t len, #define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \ "Consensus not signed by sufficient number of requested authorities\r\n\r\n" -static tor_addr_t MOCK_TOR_ADDR; +static dir_connection_t * +new_dir_conn(void) +{ + dir_connection_t *conn = dir_connection_new(AF_INET); + tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001); + return conn; +} static void test_dir_handle_get_bad_request(void *data) @@ -72,7 +86,7 @@ test_dir_handle_get_bad_request(void *data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(directory_handle_command_get(conn, "", NULL, 0), OP_EQ, 0); fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, @@ -95,7 +109,7 @@ test_dir_handle_get_v1_command_not_found(void *data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); // no frontpage configured tt_ptr_op(get_dirportfrontpage(), OP_EQ, NULL); @@ -137,7 +151,7 @@ test_dir_handle_get_v1_command(void *data) exp_body = get_dirportfrontpage(); body_len = strlen(exp_body); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0), OP_EQ, 0); @@ -172,7 +186,7 @@ test_dir_handle_get_not_found(void *data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); /* Unrecognized path */ tt_int_op(directory_handle_command_get(conn, GET("/anything"), NULL, 0), @@ -199,7 +213,7 @@ test_dir_handle_get_robots_txt(void *data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(directory_handle_command_get(conn, GET("/tor/robots.txt"), NULL, 0), OP_EQ, 0); @@ -224,51 +238,6 @@ test_dir_handle_get_robots_txt(void *data) tor_free(body); } -static void -test_dir_handle_get_bytes_txt(void *data) -{ - dir_connection_t *conn = NULL; - char *header = NULL; - char *body = NULL; - size_t body_used = 0, body_len = 0; - char buff[30]; - char *exp_body = NULL; - (void) data; - - exp_body = directory_dump_request_log(); - body_len = strlen(exp_body); - - MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); - - tt_int_op(directory_handle_command_get(conn, GET("/tor/bytes.txt"), NULL, 0), - OP_EQ, 0); - fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, - &body, &body_used, body_len+1, 0); - - tt_assert(header); - tt_assert(body); - - tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header); - tt_assert(strstr(header, "Content-Type: text/plain\r\n")); - tt_assert(strstr(header, "Content-Encoding: identity\r\n")); - tt_assert(strstr(header, "Pragma: no-cache\r\n")); - - tor_snprintf(buff, sizeof(buff), "Content-Length: %ld\r\n", (long) body_len); - tt_assert(strstr(header, buff)); - - tt_int_op(body_used, OP_EQ, strlen(body)); - tt_str_op(body, OP_EQ, exp_body); - - done: - UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); - tor_free(header); - tor_free(body); - tor_free(exp_body); -} - #define RENDEZVOUS2_GET(descid) GET("/tor/rendezvous2/" descid) static void test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data) @@ -279,7 +248,7 @@ test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); // connection is not encrypted tt_assert(!connection_dir_is_encrypted(conn)) @@ -306,7 +275,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id( (void) data; MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); // connection is encrypted TO_CONN(conn)->linked = 1; @@ -333,7 +302,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data) (void) data; MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); // connection is encrypted TO_CONN(conn)->linked = 1; @@ -366,7 +335,7 @@ test_dir_handle_get_rendezvous2_not_found(void *data) (void) data; MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); rend_cache_init(); @@ -432,13 +401,13 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data) base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); // connection is encrypted TO_CONN(conn)->linked = 1; tt_assert(connection_dir_is_encrypted(conn)); - sprintf(req, RENDEZVOUS2_GET("%s"), desc_id_base32); + tor_snprintf(req, sizeof(req), RENDEZVOUS2_GET("%s"), desc_id_base32); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -453,7 +422,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data) tt_assert(strstr(header, "Content-Type: text/plain\r\n")); tt_assert(strstr(header, "Content-Encoding: identity\r\n")); tt_assert(strstr(header, "Pragma: no-cache\r\n")); - sprintf(buff, "Content-Length: %ld\r\n", (long) body_len); + tor_snprintf(buff, sizeof(buff), "Content-Length: %ld\r\n", (long) body_len); tt_assert(strstr(header, buff)); tt_int_op(body_used, OP_EQ, strlen(body)); @@ -483,7 +452,7 @@ test_dir_handle_get_micro_d_not_found(void *data) #define B64_256_1 "8/Pz8/u7vz8/Pz+7vz8/Pz+7u/Pz8/P7u/Pz8/P7u78" #define B64_256_2 "zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMw" - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = MICRODESC_GET(B64_256_1 "-" B64_256_2); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -504,7 +473,7 @@ static or_options_t *mock_options = NULL; static void init_mock_options(void) { - mock_options = malloc(sizeof(or_options_t)); + mock_options = tor_malloc(sizeof(or_options_t)); memset(mock_options, 0, sizeof(or_options_t)); mock_options->TestingTorNetwork = 1; } @@ -563,9 +532,9 @@ test_dir_handle_get_micro_d(void *data) tt_int_op(1, OP_EQ, smartlist_len(list)); /* Make the request */ - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); - sprintf(path, MICRODESC_GET("%s"), digest_base64); + tor_snprintf(path, sizeof(path), MICRODESC_GET("%s"), digest_base64); tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0); fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, @@ -633,9 +602,9 @@ test_dir_handle_get_micro_d_server_busy(void *data) mock_options->CountPrivateBandwidth = 1; /* Make the request */ - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); - sprintf(path, MICRODESC_GET("%s"), digest_base64); + tor_snprintf(path, sizeof(path), MICRODESC_GET("%s"), digest_base64); tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0); fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, @@ -670,7 +639,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_without_auth(void *data) mock_options->BridgeAuthoritativeDir = 1; mock_options->BridgePassword_AuthDigest_ = tor_strdup("digest"); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); TO_CONN(conn)->linked = 1; const char *req = GET(BRIDGES_PATH); @@ -706,7 +675,7 @@ test_dir_handle_get_networkstatus_bridges(void *data) crypto_digest256(mock_options->BridgePassword_AuthDigest_, "abcdefghijklm12345", 18, DIGEST_SHA256); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); TO_CONN(conn)->linked = 1; const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n" @@ -746,7 +715,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_wrong_auth(void *data) crypto_digest256(mock_options->BridgePassword_AuthDigest_, "abcdefghijklm12345", 18, DIGEST_SHA256); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); TO_CONN(conn)->linked = 1; const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n" @@ -776,7 +745,7 @@ test_dir_handle_get_server_descriptors_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = SERVER_DESC_GET("invalid"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -819,7 +788,7 @@ test_dir_handle_get_server_descriptors_all(void* data) /* Treat "all" requests as if they were unencrypted */ mock_routerinfo->cache_info.send_unencrypted = 1; - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = SERVER_DESC_GET("all"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -925,7 +894,7 @@ test_dir_handle_get_server_descriptors_authority(void* data) strlen(TEST_DESCRIPTOR) - annotation_len;; mock_routerinfo->cache_info.annotations_len = annotation_len; - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = SERVER_DESC_GET("authority"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -989,7 +958,7 @@ test_dir_handle_get_server_descriptors_fp(void* data) strlen(TEST_DESCRIPTOR) - annotation_len; mock_routerinfo->cache_info.annotations_len = annotation_len; - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); #define HEX1 "Fe0daff89127389bc67558691231234551193EEE" #define HEX2 "Deadbeef99999991111119999911111111f00ba4" @@ -997,7 +966,8 @@ test_dir_handle_get_server_descriptors_fp(void* data) DIGEST_LEN); char req[155]; - sprintf(req, SERVER_DESC_GET("fp/%s+" HEX1 "+" HEX2), hex_digest); + tor_snprintf(req, sizeof(req), SERVER_DESC_GET("fp/%s+" HEX1 "+" HEX2), + hex_digest); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); //TODO: Is this a BUG? @@ -1054,10 +1024,11 @@ test_dir_handle_get_server_descriptors_d(void* data) const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest, DIGEST_LEN); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); - char req_header[155]; - sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest); + char req_header[155]; /* XXX Why 155? What kind of number is that?? */ + tor_snprintf(req_header, sizeof(req_header), + SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest); tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0); //TODO: Is this a BUG? @@ -1121,12 +1092,13 @@ test_dir_handle_get_server_descriptors_busy(void* data) const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest, DIGEST_LEN); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); #define HEX1 "Fe0daff89127389bc67558691231234551193EEE" #define HEX2 "Deadbeef99999991111119999911111111f00ba4" - char req_header[155]; - sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest); + char req_header[155]; /* XXX 155? Why 155? */ + tor_snprintf(req_header, sizeof(req_header), + SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest); tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0); fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, @@ -1159,7 +1131,7 @@ test_dir_handle_get_server_keys_bad_req(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = GET("/tor/keys/"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -1185,7 +1157,7 @@ test_dir_handle_get_server_keys_all_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = GET("/tor/keys/all"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -1204,8 +1176,6 @@ test_dir_handle_get_server_keys_all_not_found(void* data) #define TEST_CERTIFICATE AUTHORITY_CERT_3 #define TEST_SIGNING_KEY AUTHORITY_SIGNKEY_A_DIGEST -extern const char AUTHORITY_CERT_3[]; -extern const char AUTHORITY_SIGNKEY_A_DIGEST[]; static const char TEST_CERT_IDENT_KEY[] = "D867ACF56A9D229B35C25F0090BC9867E906BE69"; @@ -1237,9 +1207,9 @@ test_dir_handle_get_server_keys_all(void* data) base16_decode(ds->v3_identity_digest, DIGEST_LEN, TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN); tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE, - TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1)); + TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL)); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = GET("/tor/keys/all"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -1276,7 +1246,7 @@ test_dir_handle_get_server_keys_authority_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = GET("/tor/keys/authority"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -1316,7 +1286,7 @@ test_dir_handle_get_server_keys_authority(void* data) MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = GET("/tor/keys/authority"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -1352,7 +1322,7 @@ test_dir_handle_get_server_keys_fp_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = GET("/tor/keys/fp/somehex"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -1396,11 +1366,12 @@ test_dir_handle_get_server_keys_fp(void* data) TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN); tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE, - TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1)); + TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL)); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); char req[71]; - sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY); + tor_snprintf(req, sizeof(req), + GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, @@ -1434,7 +1405,7 @@ test_dir_handle_get_server_keys_sk_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = GET("/tor/keys/sk/somehex"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -1468,11 +1439,12 @@ test_dir_handle_get_server_keys_sk(void* data) routerlist_free_all(); tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE, - TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1)); + TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL)); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); char req[71]; - sprintf(req, GET("/tor/keys/sk/%s"), TEST_SIGNING_KEY); + tor_snprintf(req, sizeof(req), + GET("/tor/keys/sk/%s"), TEST_SIGNING_KEY); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, @@ -1506,7 +1478,7 @@ test_dir_handle_get_server_keys_fpsk_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); const char *req = GET("/tor/keys/fp-sk/somehex"); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -1550,13 +1522,14 @@ test_dir_handle_get_server_keys_fpsk(void* data) dir_server_add(ds); tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE, - TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1)); + TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL)); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); char req[115]; - sprintf(req, GET("/tor/keys/fp-sk/%s-%s"), - TEST_CERT_IDENT_KEY, TEST_SIGNING_KEY); + tor_snprintf(req, sizeof(req), + GET("/tor/keys/fp-sk/%s-%s"), + TEST_CERT_IDENT_KEY, TEST_SIGNING_KEY); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); @@ -1606,7 +1579,7 @@ test_dir_handle_get_server_keys_busy(void* data) dir_server_add(ds); tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE, - TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1)); + TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL)); MOCK(get_options, mock_get_options); MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); @@ -1615,9 +1588,9 @@ test_dir_handle_get_server_keys_busy(void* data) init_mock_options(); mock_options->CountPrivateBandwidth = 1; - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); char req[71]; - sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY); + tor_snprintf(req, sizeof(req), GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY); tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0); fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, @@ -1669,7 +1642,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d) mock_options->DirReqStatistics = 1; geoip_dirreq_stats_init(time(NULL)); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/current/consensus-ns/" HEX1 "+" HEX2), NULL, 0)); @@ -1714,7 +1687,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data) mock_options->DirReqStatistics = 1; geoip_dirreq_stats_init(time(NULL)); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/current/consensus-ns"), NULL, 0)); @@ -1767,7 +1740,7 @@ status_vote_current_consensus_ns_test(char **header, char **body, geoip_parse_entry("10,50,AB", AF_INET); tt_str_op("ab", OP_EQ, geoip_get_country_name(1)); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + 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, @@ -1890,7 +1863,7 @@ test_dir_handle_get_status_vote_current_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/current/" HEX1), NULL, 0)); @@ -1914,7 +1887,7 @@ status_vote_current_d_test(char **header, char **body, size_t *body_l) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/current/d/" VOTE_DIGEST), NULL, 0)); @@ -1934,7 +1907,7 @@ status_vote_next_d_test(char **header, char **body, size_t *body_l) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/next/d/" VOTE_DIGEST), NULL, 0)); @@ -2058,7 +2031,7 @@ test_dir_handle_get_status_vote_next_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/next/" HEX1), NULL, 0)); @@ -2080,7 +2053,7 @@ status_vote_next_consensus_test(char **header, char **body, size_t *body_used) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/next/consensus"), NULL, 0)); @@ -2117,7 +2090,7 @@ test_dir_handle_get_status_vote_current_authority_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/current/authority"), NULL, 0)); @@ -2141,7 +2114,7 @@ test_dir_handle_get_status_vote_next_authority_not_found(void* data) MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/next/authority"), NULL, 0)); @@ -2226,7 +2199,7 @@ status_vote_next_consensus_signatures_test(char **header, char **body, MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/next/consensus-signatures"), NULL, 0)); @@ -2344,7 +2317,7 @@ test_dir_handle_get_status_vote_next_authority(void* data) base16_decode(ds->v3_identity_digest, DIGEST_LEN, TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN); tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE, - TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1)); + TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL)); init_mock_options(); mock_options->AuthoritativeDir = 1; @@ -2364,7 +2337,7 @@ test_dir_handle_get_status_vote_next_authority(void* data) MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/next/authority"), NULL, 0)); @@ -2423,7 +2396,7 @@ test_dir_handle_get_status_vote_current_authority(void* data) TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN); tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE, - TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1)); + TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL)); init_mock_options(); mock_options->AuthoritativeDir = 1; @@ -2446,7 +2419,7 @@ test_dir_handle_get_status_vote_current_authority(void* data) MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); - conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); + conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, GET("/tor/status-vote/current/authority"), NULL, 0)); @@ -2484,7 +2457,6 @@ struct testcase_t dir_handle_get_tests[] = { DIR_HANDLE_CMD(v1_command_not_found, 0), DIR_HANDLE_CMD(v1_command, 0), DIR_HANDLE_CMD(robots_txt, 0), - DIR_HANDLE_CMD(bytes_txt, 0), DIR_HANDLE_CMD(rendezvous2_not_found_if_not_encrypted, 0), DIR_HANDLE_CMD(rendezvous2_not_found, 0), DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_with_invalid_desc_id, 0), diff --git a/src/test/test_dns.c b/src/test/test_dns.c index 5289ca58ff..8346c0a33f 100644 --- a/src/test/test_dns.c +++ b/src/test/test_dns.c @@ -52,7 +52,7 @@ NS(test_main)(void *arg) static int resolve_retval = 0; static int resolve_made_conn_pending = 0; static char *resolved_name = NULL; -static cached_resolve_t *cache_entry = NULL; +static cached_resolve_t *cache_entry_mock = NULL; static int n_fake_impl = 0; @@ -85,8 +85,8 @@ NS(dns_resolve_impl)(edge_connection_t *exitconn, int is_resolve, if (hostname_out && resolved_name) *hostname_out = tor_strdup(resolved_name); - if (resolve_out && cache_entry) - *resolve_out = cache_entry; + if (resolve_out && cache_entry_mock) + *resolve_out = cache_entry_mock; n_fake_impl++; @@ -213,7 +213,7 @@ NS(test_main)(void *arg) exitconn->on_circuit = &(on_circuit->base_); - cache_entry = fake_resolved; + cache_entry_mock = fake_resolved; prev_n_send_resolved_cell_replacement = n_send_resolved_cell_replacement; diff --git a/src/test/test_guardfraction.c b/src/test/test_guardfraction.c index 300590a3d9..8173e44d47 100644 --- a/src/test/test_guardfraction.c +++ b/src/test/test_guardfraction.c @@ -17,6 +17,7 @@ #include "test.h" #include "test_helpers.h" +#include "log_test_helpers.h" /** Generate a vote_routerstatus_t for a router with identity digest * <b>digest_in_hex</b>. */ @@ -40,7 +41,7 @@ gen_vote_routerstatus_for_tests(const char *digest_in_hex, int is_guard) tt_int_op(strlen(digest_in_hex), ==, HEX_DIGEST_LEN); retval = base16_decode(digest_tmp, sizeof(digest_tmp), digest_in_hex, HEX_DIGEST_LEN); - tt_int_op(retval, ==, 0); + tt_int_op(retval, ==, sizeof(digest_tmp)); memcpy(rs->identity_digest, digest_tmp, DIGEST_LEN); } @@ -304,11 +305,15 @@ test_parse_guardfraction_consensus(void *arg) memset(&rs_no_guard, 0, sizeof(routerstatus_t)); tt_assert(!rs_no_guard.is_possible_guard); + setup_full_capture_of_logs(LOG_WARN); retval = routerstatus_parse_guardfraction(guardfraction_str_good, NULL, NULL, &rs_no_guard); tt_int_op(retval, ==, 0); tt_assert(!rs_no_guard.has_guardfraction); + expect_single_log_msg_containing("Got GuardFraction for non-guard . " + "This is not supposed to happen."); + teardown_capture_of_logs(); } { /* Bad GuardFraction. Function should fail and not apply. */ @@ -334,7 +339,7 @@ test_parse_guardfraction_consensus(void *arg) } done: - ; + teardown_capture_of_logs(); } /** Make sure that we use GuardFraction information when we should, diff --git a/src/test/test_handles.c b/src/test/test_handles.c new file mode 100644 index 0000000000..536a478689 --- /dev/null +++ b/src/test/test_handles.c @@ -0,0 +1,95 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "test.h" + +#include "util.h" +#include "handles.h" + +typedef struct demo_t { + HANDLE_ENTRY(demo, demo_t); + int val; +} demo_t; + +HANDLE_DECL(demo, demo_t, static) +HANDLE_IMPL(demo, demo_t, static) + +static demo_t * +demo_new(int val) +{ + demo_t *d = tor_malloc_zero(sizeof(demo_t)); + d->val = val; + return d; +} + +static void +demo_free(demo_t *d) +{ + if (d == NULL) + return; + demo_handles_clear(d); + tor_free(d); +} + +static void +test_handle_basic(void *arg) +{ + (void) arg; + demo_t *d1 = NULL, *d2 = NULL; + demo_handle_t *wr1 = NULL, *wr2 = NULL, *wr3 = NULL, *wr4 = NULL; + + d1 = demo_new(9000); + d2 = demo_new(9009); + + wr1 = demo_handle_new(d1); + wr2 = demo_handle_new(d1); + wr3 = demo_handle_new(d1); + wr4 = demo_handle_new(d2); + + tt_assert(wr1); + tt_assert(wr2); + tt_assert(wr3); + tt_assert(wr4); + + tt_ptr_op(demo_handle_get(wr1), OP_EQ, d1); + tt_ptr_op(demo_handle_get(wr2), OP_EQ, d1); + tt_ptr_op(demo_handle_get(wr3), OP_EQ, d1); + tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2); + + demo_handle_free(wr1); + wr1 = NULL; + tt_ptr_op(demo_handle_get(wr2), OP_EQ, d1); + tt_ptr_op(demo_handle_get(wr3), OP_EQ, d1); + tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2); + + demo_free(d1); + d1 = NULL; + tt_ptr_op(demo_handle_get(wr2), OP_EQ, NULL); + tt_ptr_op(demo_handle_get(wr3), OP_EQ, NULL); + tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2); + + demo_handle_free(wr2); + wr2 = NULL; + tt_ptr_op(demo_handle_get(wr3), OP_EQ, NULL); + tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2); + + demo_handle_free(wr3); + wr3 = NULL; + done: + demo_handle_free(wr1); + demo_handle_free(wr2); + demo_handle_free(wr3); + demo_handle_free(wr4); + demo_free(d1); + demo_free(d2); +} + +#define HANDLE_TEST(name, flags) \ + { #name, test_handle_ ##name, (flags), NULL, NULL } + +struct testcase_t handle_tests[] = { + HANDLE_TEST(basic, 0), + END_OF_TESTCASES +}; + diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index c6daaf220a..ae9fc7a243 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -16,7 +16,15 @@ #include "test.h" #include "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_descriptors.inc" +#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS +ENABLE_GCC_WARNING(overlength-strings) +#endif /* Return a statically allocated string representing yesterday's date * in ISO format. We use it so that state file items are not found to diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 49939a53cf..8237bbc50e 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -8,12 +8,14 @@ #define CONTROL_PRIVATE #define CIRCUITBUILD_PRIVATE +#define RENDSERVICE_PRIVATE #include "or.h" #include "test.h" #include "control.h" #include "config.h" #include "rendcommon.h" +#include "rendservice.h" #include "routerset.h" #include "circuitbuild.h" #include "test_helpers.h" @@ -435,6 +437,347 @@ test_hs_rend_data(void *arg) rend_data_free(client_dup); } +/* Test encoding and decoding service authorization cookies */ +static void +test_hs_auth_cookies(void *arg) +{ +#define TEST_COOKIE_RAW ((const uint8_t *) "abcdefghijklmnop") +#define TEST_COOKIE_ENCODED "YWJjZGVmZ2hpamtsbW5vcA" +#define TEST_COOKIE_ENCODED_STEALTH "YWJjZGVmZ2hpamtsbW5vcB" +#define TEST_COOKIE_ENCODED_INVALID "YWJjZGVmZ2hpamtsbW5vcD" + + char *encoded_cookie; + uint8_t raw_cookie[REND_DESC_COOKIE_LEN]; + rend_auth_type_t auth_type; + char *err_msg; + int re; + + (void)arg; + + /* Test that encoding gives the expected result */ + encoded_cookie = rend_auth_encode_cookie(TEST_COOKIE_RAW, REND_BASIC_AUTH); + tt_str_op(encoded_cookie, OP_EQ, TEST_COOKIE_ENCODED); + tor_free(encoded_cookie); + + encoded_cookie = rend_auth_encode_cookie(TEST_COOKIE_RAW, REND_STEALTH_AUTH); + tt_str_op(encoded_cookie, OP_EQ, TEST_COOKIE_ENCODED_STEALTH); + tor_free(encoded_cookie); + + /* Decoding should give the original value */ + re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED, raw_cookie, &auth_type, + &err_msg); + tt_assert(!re); + tt_assert(!err_msg); + tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN); + tt_int_op(auth_type, OP_EQ, REND_BASIC_AUTH); + memset(raw_cookie, 0, sizeof(raw_cookie)); + + re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED_STEALTH, raw_cookie, + &auth_type, &err_msg); + tt_assert(!re); + tt_assert(!err_msg); + tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN); + tt_int_op(auth_type, OP_EQ, REND_STEALTH_AUTH); + memset(raw_cookie, 0, sizeof(raw_cookie)); + + /* Decoding with padding characters should also work */ + re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED "==", raw_cookie, NULL, + &err_msg); + tt_assert(!re); + tt_assert(!err_msg); + tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN); + + /* Decoding with an unknown type should fail */ + re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED_INVALID, raw_cookie, + &auth_type, &err_msg); + tt_int_op(re, OP_LT, 0); + tt_assert(err_msg); + tor_free(err_msg); + + done: + return; +} + +static int mock_get_options_calls = 0; +static or_options_t *mock_options = NULL; + +static void +reset_options(or_options_t *options, int *get_options_calls) +{ + memset(options, 0, sizeof(or_options_t)); + options->TestingTorNetwork = 1; + + *get_options_calls = 0; +} + +static const or_options_t * +mock_get_options(void) +{ + ++mock_get_options_calls; + tor_assert(mock_options); + return mock_options; +} + +/* arg can't be 0 (the test fails) or 2 (the test is skipped) */ +#define CREATE_HS_DIR_NONE ((intptr_t)0x04) +#define CREATE_HS_DIR1 ((intptr_t)0x08) +#define CREATE_HS_DIR2 ((intptr_t)0x10) + +/* Test that single onion poisoning works. */ +static void +test_single_onion_poisoning(void *arg) +{ + or_options_t opt; + mock_options = &opt; + reset_options(mock_options, &mock_get_options_calls); + MOCK(get_options, mock_get_options); + + int ret = -1; + intptr_t create_dir_mask = (intptr_t)arg; + /* Get directories with a random suffix so we can repeat the tests */ + mock_options->DataDirectory = tor_strdup(get_fname_rnd("test_data_dir")); + rend_service_t *service_1 = tor_malloc_zero(sizeof(rend_service_t)); + char *dir1 = tor_strdup(get_fname_rnd("test_hs_dir1")); + rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t)); + char *dir2 = tor_strdup(get_fname_rnd("test_hs_dir2")); + smartlist_t *services = smartlist_new(); + char *poison_path = NULL; + + /* No services, no service to verify, no problem! */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_config_services(mock_options, 1); + tt_assert(ret == 0); + + /* Either way, no problem. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_config_services(mock_options, 1); + tt_assert(ret == 0); + + /* Create the data directory, and, if the correct bit in arg is set, + * create a directory for that service. + * The data directory is required for the lockfile, which is used when + * loading keys. */ + ret = check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL); + tt_assert(ret == 0); + if (create_dir_mask & CREATE_HS_DIR1) { + ret = check_private_dir(dir1, CPD_CREATE, NULL); + tt_assert(ret == 0); + } + if (create_dir_mask & CREATE_HS_DIR2) { + ret = check_private_dir(dir2, CPD_CREATE, NULL); + tt_assert(ret == 0); + } + + service_1->directory = dir1; + service_2->directory = dir2; + /* The services own the directory pointers now */ + dir1 = dir2 = NULL; + /* 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); + tt_assert(!err_msg); + smartlist_add(service_1->ports, port1); + + rend_service_port_config_t *port2 = rend_service_parse_port_config("90", " ", + &err_msg); + /* Add port to service 2 */ + tt_assert(port2); + tt_assert(!err_msg); + smartlist_add(service_2->ports, port2); + + /* No services, a service to verify, no problem! */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Either way, no problem. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Add the first service */ + ret = rend_service_check_dir_and_add(services, mock_options, service_1, 0); + tt_assert(ret == 0); + /* But don't add the second service yet. */ + + /* Service directories, but no previous keys, no problem! */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Either way, no problem. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Poison! Poison! Poison! + * This can only be done in HiddenServiceSingleHopMode. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); + tt_assert(ret == 0); + /* Poisoning twice is a no-op. */ + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); + tt_assert(ret == 0); + + /* Poisoned service directories, but no previous keys, no problem! */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Either way, no problem. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Now add some keys, and we'll have a problem. */ + ret = rend_service_load_all_keys(services); + tt_assert(ret == 0); + + /* Poisoned service directories with previous keys are not allowed. */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* But they are allowed if we're in non-anonymous mode. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Re-poisoning directories with existing keys is a no-op, because + * directories with existing keys are ignored. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); + tt_assert(ret == 0); + /* And it keeps the poison. */ + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Now add the second service: it has no key and no poison file */ + ret = rend_service_check_dir_and_add(services, mock_options, service_2, 0); + tt_assert(ret == 0); + + /* A new service, and an existing poisoned service. Not ok. */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* But ok to add in non-anonymous mode. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Now remove the poisoning from the first service, and we have the opposite + * problem. */ + poison_path = rend_service_sos_poison_path(service_1); + tt_assert(poison_path); + ret = unlink(poison_path); + tt_assert(ret == 0); + + /* Unpoisoned service directories with previous keys are ok, as are empty + * directories. */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* But the existing unpoisoned key is not ok in non-anonymous mode, even if + * there is an empty service. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Poisoning directories with existing keys is a no-op, because directories + * with existing keys are ignored. But the new directory should poison. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_poison_new_single_onion_dir(service_2, mock_options); + tt_assert(ret == 0); + /* And the old directory remains unpoisoned. */ + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* And the new directory should be ignored, because it has no key. */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Re-poisoning directories without existing keys is a no-op. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_poison_new_single_onion_dir(service_2, mock_options); + tt_assert(ret == 0); + /* And the old directory remains unpoisoned. */ + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + done: + /* The test harness deletes the directories at exit */ + tor_free(poison_path); + tor_free(dir1); + tor_free(dir2); + smartlist_free(services); + rend_service_free(service_1); + rend_service_free(service_2); + UNMOCK(get_options); + tor_free(mock_options->DataDirectory); +} + struct testcase_t hs_tests[] = { { "hs_rend_data", test_hs_rend_data, TT_FORK, NULL, NULL }, @@ -445,6 +788,16 @@ struct testcase_t hs_tests[] = { { "pick_bad_tor2web_rendezvous_node", test_pick_bad_tor2web_rendezvous_node, TT_FORK, NULL, NULL }, + { "hs_auth_cookies", test_hs_auth_cookies, TT_FORK, + NULL, NULL }, + { "single_onion_poisoning_create_dir_none", test_single_onion_poisoning, + TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR_NONE) }, + { "single_onion_poisoning_create_dir1", test_single_onion_poisoning, + TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1) }, + { "single_onion_poisoning_create_dir2", test_single_onion_poisoning, + 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) }, END_OF_TESTCASES }; diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c index 9c7a86da66..810b03c93d 100644 --- a/src/test/test_introduce.c +++ b/src/test/test_introduce.c @@ -9,8 +9,6 @@ #define RENDSERVICE_PRIVATE #include "rendservice.h" -extern const char AUTHORITY_SIGNKEY_1[]; - static uint8_t v0_test_plaintext[] = /* 20 bytes of rendezvous point nickname */ { 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index e8856c60de..ddf66f4d34 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -15,8 +15,9 @@ #include "scheduler.h" #include "test.h" +#include "log_test_helpers.h" -var_cell_t *mock_got_var_cell = NULL; +static var_cell_t *mock_got_var_cell = NULL; static void mock_write_var_cell(const var_cell_t *vc, or_connection_t *conn) @@ -65,6 +66,14 @@ mock_send_authenticate(or_connection_t *conn, int type) return 0; } +static tor_x509_cert_t *mock_own_cert = NULL; +static tor_x509_cert_t * +mock_get_own_cert(tor_tls_t *tls) +{ + (void)tls; + return tor_x509_cert_dup(mock_own_cert); +} + /* Test good certs cells */ static void test_link_handshake_certs_ok(void *arg) @@ -83,6 +92,7 @@ test_link_handshake_certs_ok(void *arg) 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_own_cert, mock_get_own_cert); key1 = pk_generate(2); key2 = pk_generate(3); @@ -93,6 +103,12 @@ test_link_handshake_certs_ok(void *arg) tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, key1, key2, 86400), ==, 0); + { + const tor_x509_cert_t *link_cert = NULL; + tt_assert(!tor_tls_get_my_certs(1, &link_cert, NULL)); + mock_own_cert = tor_x509_cert_dup(link_cert); + } + c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; c1->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0); @@ -173,6 +189,11 @@ test_link_handshake_certs_ok(void *arg) UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_write_var_cell_to_buf); UNMOCK(connection_or_send_netinfo); + UNMOCK(tor_tls_get_own_cert); + tor_x509_cert_free(mock_own_cert); + mock_own_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)); connection_free_(TO_CONN(c2)); tor_free(cell1); @@ -209,6 +230,7 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj) if (d) { tor_free(d->cell); certs_cell_free(d->ccell); + connection_or_remove_from_identity_map(d->c); connection_free_(TO_CONN(d->c)); circuitmux_free(d->chan->base_.cmux); tor_free(d->chan); @@ -332,30 +354,50 @@ test_link_handshake_recv_certs_ok_server(void *arg) test_link_handshake_recv_certs_ ## name(void *arg) \ { \ certs_data_t *d = arg; \ + const char *require_failure_message = NULL; \ + setup_capture_of_logs(LOG_INFO); \ { code ; } \ channel_tls_process_certs_cell(d->cell, d->chan); \ tt_int_op(1, ==, mock_close_called); \ tt_int_op(0, ==, mock_send_authenticate_called); \ tt_int_op(0, ==, mock_send_netinfo_called); \ + if (require_failure_message) { \ + expect_log_msg_containing(require_failure_message); \ + } \ done: \ - ; \ + teardown_capture_of_logs(); \ } -CERTS_FAIL(badstate, d->c->base_.state = OR_CONN_STATE_CONNECTING) -CERTS_FAIL(badproto, d->c->link_proto = 2) -CERTS_FAIL(duplicate, d->c->handshake_state->received_certs_cell = 1) +CERTS_FAIL(badstate, + require_failure_message = "We're not doing a v3 handshake!"; + d->c->base_.state = OR_CONN_STATE_CONNECTING;) +CERTS_FAIL(badproto, + require_failure_message = "not using link protocol >= 3"; + d->c->link_proto = 2) +CERTS_FAIL(duplicate, + require_failure_message = "We already got one"; + d->c->handshake_state->received_certs_cell = 1) CERTS_FAIL(already_authenticated, + require_failure_message = "We're already authenticated!"; d->c->handshake_state->authenticated = 1) -CERTS_FAIL(empty, d->cell->payload_len = 0) -CERTS_FAIL(bad_circid, d->cell->circ_id = 1) -CERTS_FAIL(truncated_1, d->cell->payload[0] = 5) +CERTS_FAIL(empty, + require_failure_message = "It had no body"; + d->cell->payload_len = 0) +CERTS_FAIL(bad_circid, + require_failure_message = "It had a nonzero circuit ID"; + d->cell->circ_id = 1) +CERTS_FAIL(truncated_1, + require_failure_message = "It couldn't be parsed"; + d->cell->payload[0] = 5) CERTS_FAIL(truncated_2, { + require_failure_message = "It couldn't be parsed"; d->cell->payload_len = 4; memcpy(d->cell->payload, "\x01\x01\x00\x05", 4); }) CERTS_FAIL(truncated_3, { + require_failure_message = "It couldn't be parsed"; d->cell->payload_len = 7; memcpy(d->cell->payload, "\x01\x01\x00\x05""abc", 7); }) @@ -367,30 +409,35 @@ CERTS_FAIL(truncated_3, CERTS_FAIL(not_x509, { + require_failure_message = "Received undecodable certificate"; certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 0), 3); certs_cell_get_certs(d->ccell, 0)->cert_len = 3; REENCODE(); }) CERTS_FAIL(both_link, { + require_failure_message = "Duplicate x509 certificate"; certs_cell_get_certs(d->ccell, 0)->cert_type = 1; certs_cell_get_certs(d->ccell, 1)->cert_type = 1; REENCODE(); }) CERTS_FAIL(both_id_rsa, { + require_failure_message = "Duplicate x509 certificate"; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 2; REENCODE(); }) CERTS_FAIL(both_auth, { + require_failure_message = "Duplicate x509 certificate"; certs_cell_get_certs(d->ccell, 0)->cert_type = 3; certs_cell_get_certs(d->ccell, 1)->cert_type = 3; REENCODE(); }) CERTS_FAIL(wrong_labels_1, { + require_failure_message = "The link certificate was not valid"; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 1; REENCODE(); @@ -401,6 +448,7 @@ CERTS_FAIL(wrong_labels_2, const tor_x509_cert_t *b; const uint8_t *enca; size_t lena; + require_failure_message = "The link certificate was not valid"; tor_tls_get_my_certs(1, &a, &b); tor_x509_cert_get_der(a, &enca, &lena); certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 1), lena); @@ -411,16 +459,20 @@ CERTS_FAIL(wrong_labels_2, }) CERTS_FAIL(wrong_labels_3, { + require_failure_message = "The certs we wanted 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"; d->c->handshake_state->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; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 3; @@ -579,38 +631,55 @@ test_link_handshake_recv_authchallenge_ok_unrecognized(void *arg) test_link_handshake_recv_authchallenge_ ## name(void *arg) \ { \ authchallenge_data_t *d = arg; \ + const char *require_failure_message = NULL; \ + setup_capture_of_logs(LOG_INFO); \ { code ; } \ channel_tls_process_auth_challenge_cell(d->cell, d->chan); \ tt_int_op(1, ==, mock_close_called); \ tt_int_op(0, ==, mock_send_authenticate_called); \ tt_int_op(0, ==, mock_send_netinfo_called); \ + if (require_failure_message) { \ + expect_log_msg_containing(require_failure_message); \ + } \ done: \ - ; \ + teardown_capture_of_logs(); \ } AUTHCHALLENGE_FAIL(badstate, + require_failure_message = "We're not currently doing a " + "v3 handshake"; d->c->base_.state = OR_CONN_STATE_CONNECTING) AUTHCHALLENGE_FAIL(badproto, + require_failure_message = "not using link protocol >= 3"; d->c->link_proto = 2) AUTHCHALLENGE_FAIL(as_server, + require_failure_message = "We didn't originate this " + "connection"; d->c->handshake_state->started_here = 0;) AUTHCHALLENGE_FAIL(duplicate, + require_failure_message = "We already received one"; d->c->handshake_state->received_auth_challenge = 1) AUTHCHALLENGE_FAIL(nocerts, + require_failure_message = "We haven't gotten a CERTS " + "cell yet"; d->c->handshake_state->received_certs_cell = 0) AUTHCHALLENGE_FAIL(tooshort, + require_failure_message = "It was not well-formed"; d->cell->payload_len = 33) AUTHCHALLENGE_FAIL(truncated, + require_failure_message = "It was not well-formed"; d->cell->payload_len = 34) 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 mock_peer_cert; + return tor_x509_cert_dup(mock_peer_cert); } static int @@ -644,12 +713,15 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) (void) test; UNMOCK(connection_or_write_var_cell_to_buf); UNMOCK(tor_tls_get_peer_cert); + UNMOCK(tor_tls_get_own_cert); UNMOCK(tor_tls_get_tlssecrets); UNMOCK(connection_or_close_for_error); UNMOCK(channel_set_circid_type); 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_free_(TO_CONN(d->c1)); connection_free_(TO_CONN(d->c2)); circuitmux_free(d->chan2->base_.cmux); @@ -658,7 +730,10 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) crypto_pk_free(d->key2); tor_free(d); } + tor_x509_cert_free(mock_peer_cert); + tor_x509_cert_free(mock_own_cert); mock_peer_cert = NULL; + mock_own_cert = NULL; return 1; } @@ -672,11 +747,14 @@ authenticate_data_setup(const struct testcase_t *test) MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); MOCK(tor_tls_get_peer_cert, mock_get_peer_cert); + MOCK(tor_tls_get_own_cert, mock_get_own_cert); 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); 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); + tor_addr_from_ipv4h(&d->c2->base_.addr, 0x05060708); d->key1 = pk_generate(2); d->key2 = pk_generate(3); @@ -719,6 +797,8 @@ authenticate_data_setup(const struct testcase_t *test) 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); @@ -798,57 +878,83 @@ test_link_handshake_auth_cell(void *arg) test_link_handshake_auth_ ## name(void *arg) \ { \ authenticate_data_t *d = arg; \ + const char *require_failure_message = NULL; \ + setup_capture_of_logs(LOG_INFO); \ { code ; } \ tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \ channel_tls_process_authenticate_cell(d->cell, d->chan2); \ tt_int_op(mock_close_called, ==, 1); \ tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \ - done: \ - ; \ + if (require_failure_message) { \ + expect_log_msg_containing(require_failure_message); \ + } \ + done: \ + teardown_capture_of_logs(); \ } AUTHENTICATE_FAIL(badstate, + require_failure_message = "We're not doing a v3 handshake"; d->c2->base_.state = OR_CONN_STATE_CONNECTING) AUTHENTICATE_FAIL(badproto, + require_failure_message = "not using link protocol >= 3"; d->c2->link_proto = 2) AUTHENTICATE_FAIL(atclient, + require_failure_message = "We originated this connection"; d->c2->handshake_state->started_here = 1) AUTHENTICATE_FAIL(duplicate, + require_failure_message = "We already got one"; d->c2->handshake_state->received_authenticate = 1) static void test_link_handshake_auth_already_authenticated(void *arg) { authenticate_data_t *d = arg; + setup_capture_of_logs(LOG_INFO); d->c2->handshake_state->authenticated = 1; channel_tls_process_authenticate_cell(d->cell, d->chan2); tt_int_op(mock_close_called, ==, 1); tt_int_op(d->c2->handshake_state->authenticated, ==, 1); + expect_log_msg_containing("The peer is already authenticated"); done: - ; + teardown_capture_of_logs(); } + AUTHENTICATE_FAIL(nocerts, + require_failure_message = "We never got a certs cell"; d->c2->handshake_state->received_certs_cell = 0) 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) 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) AUTHENTICATE_FAIL(tooshort, + require_failure_message = "Cell was way too short"; d->cell->payload_len = 3) AUTHENTICATE_FAIL(badtype, + require_failure_message = "Authenticator type was not " + "recognized"; d->cell->payload[0] = 0xff) AUTHENTICATE_FAIL(truncated_1, + require_failure_message = "Authenticator was truncated"; d->cell->payload[2]++) AUTHENTICATE_FAIL(truncated_2, + require_failure_message = "Authenticator was truncated"; d->cell->payload[3]++) AUTHENTICATE_FAIL(tooshort_1, + require_failure_message = "Authenticator was too short"; tt_int_op(d->cell->payload_len, >=, 260); d->cell->payload[2] -= 1; d->cell->payload_len -= 256;) AUTHENTICATE_FAIL(badcontent, + require_failure_message = "Some field in the AUTHENTICATE " + "cell body was not as expected"; d->cell->payload[10] ^= 0xff) AUTHENTICATE_FAIL(badsig_1, + require_failure_message = "Signature wasn't valid"; d->cell->payload[d->cell->payload_len - 5] ^= 0xff) #define TEST(name, flags) \ diff --git a/src/test/test_logging.c b/src/test/test_logging.c index eb294fe6f8..15471e46d0 100644 --- a/src/test/test_logging.c +++ b/src/test/test_logging.c @@ -127,9 +127,47 @@ test_sigsafe_err(void *arg) smartlist_free(lines); } +static void +test_ratelim(void *arg) +{ + (void) arg; + ratelim_t ten_min = RATELIM_INIT(10*60); + + const time_t start = 1466091600; + time_t now = start; + /* Initially, we're ready. */ + + char *msg = NULL; + + msg = rate_limit_log(&ten_min, now); + tt_assert(msg != NULL); + tt_str_op(msg, OP_EQ, ""); /* nothing was suppressed. */ + + tt_int_op(ten_min.last_allowed, OP_EQ, now); + tor_free(msg); + + int i; + for (i = 0; i < 9; ++i) { + now += 60; /* one minute has passed. */ + msg = rate_limit_log(&ten_min, now); + tt_assert(msg == NULL); + tt_int_op(ten_min.last_allowed, OP_EQ, start); + tt_int_op(ten_min.n_calls_since_last_time, OP_EQ, i + 1); + } + + now += 240; /* Okay, we can be done. */ + msg = rate_limit_log(&ten_min, now); + tt_assert(msg != NULL); + tt_str_op(msg, OP_EQ, + " [9 similar message(s) suppressed in last 600 seconds]"); + done: + tor_free(msg); +} + struct testcase_t logging_tests[] = { { "sigsafe_err_fds", test_get_sigsafe_err_fds, TT_FORK, NULL, NULL }, { "sigsafe_err", test_sigsafe_err, TT_FORK, NULL, NULL }, + { "ratelim", test_ratelim, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index dbd1e5ac48..2ae605b8db 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -14,30 +14,11 @@ #include "test.h" -#ifdef __GNUC__ -#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -#endif - -#if __GNUC__ && GCC_VERSION >= 402 -#if GCC_VERSION >= 406 -#pragma GCC diagnostic push -#endif -/* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice. - * Suppress the GCC warning so we can build with -Wredundant-decl. */ -#pragma GCC diagnostic ignored "-Wredundant-decls" -#endif - +DISABLE_GCC_WARNING(redundant-decls) #include <openssl/rsa.h> #include <openssl/bn.h> #include <openssl/pem.h> - -#if __GNUC__ && GCC_VERSION >= 402 -#if GCC_VERSION >= 406 -#pragma GCC diagnostic pop -#else -#pragma GCC diagnostic warning "-Wredundant-decls" -#endif -#endif +ENABLE_GCC_WARNING(redundant-decls) #ifdef _WIN32 /* For mkdir() */ @@ -511,6 +492,11 @@ test_md_generate(void *arg) routerinfo_free(ri); } +#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 /* Taken at random from my ~/.tor/cached-microdescs file and then * hand-munged */ static const char MD_PARSE_TEST_DATA[] = @@ -666,6 +652,9 @@ static const char MD_PARSE_TEST_DATA[] = "id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n" "p6 allow 80\n" ; +#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS +ENABLE_GCC_WARNING(overlength-strings) +#endif /** More tests for parsing different kinds of microdescriptors, and getting * invalid digests trackd from them. */ @@ -727,7 +716,7 @@ test_md_parse(void *arg) tt_int_op(md->ipv6_orport, OP_EQ, 9090); done: - SMARTLIST_FOREACH(mds, microdesc_t *, md, microdesc_free(md)); + SMARTLIST_FOREACH(mds, microdesc_t *, mdsc, microdesc_free(mdsc)); smartlist_free(mds); SMARTLIST_FOREACH(invalid, char *, cp, tor_free(cp)); smartlist_free(invalid); @@ -794,7 +783,8 @@ test_md_reject_cache(void *arg) mc = get_microdesc_cache(); #define ADD(hex) \ do { \ - tt_int_op(0,OP_EQ,base16_decode(buf,sizeof(buf),hex,strlen(hex))); \ + tt_int_op(sizeof(buf),OP_EQ,base16_decode(buf,sizeof(buf), \ + hex,strlen(hex)));\ smartlist_add(wanted, tor_memdup(buf, DIGEST256_LEN)); \ } while (0) diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c index 6df123162e..a560e5fc5e 100644 --- a/src/test/test_ntor_cl.c +++ b/src/test/test_ntor_cl.c @@ -21,7 +21,7 @@ } STMT_END #define BASE16(idx, var, n) STMT_BEGIN { \ const char *s = argv[(idx)]; \ - if (base16_decode((char*)var, n, s, strlen(s)) < 0 ) { \ + if (base16_decode((char*)var, n, s, strlen(s)) < (int)n ) { \ fprintf(stderr, "couldn't decode argument %d (%s)\n",idx,s); \ return 1; \ } \ @@ -153,7 +153,10 @@ main(int argc, char **argv) if (argc < 2) { fprintf(stderr, "I need arguments. Read source for more info.\n"); return 1; - } else if (!strcmp(argv[1], "client1")) { + } + + curve25519_init(); + if (!strcmp(argv[1], "client1")) { return client1(argc, argv); } else if (!strcmp(argv[1], "server1")) { return server1(argc, argv); diff --git a/src/test/test_oom.c b/src/test/test_oom.c index 2569b6e00f..6102af01f5 100644 --- a/src/test/test_oom.c +++ b/src/test/test_oom.c @@ -77,14 +77,14 @@ dummy_origin_circuit_new(int n_cells) } static void -add_bytes_to_buf(generic_buffer_t *buf, size_t n_bytes) +add_bytes_to_buf(buf_t *buf, size_t n_bytes) { char b[3000]; while (n_bytes) { size_t this_add = n_bytes > sizeof(b) ? sizeof(b) : n_bytes; crypto_rand(b, this_add); - generic_buffer_add(buf, b, this_add); + write_to_buf(b, this_add, buf); n_bytes -= this_add; } } @@ -94,20 +94,15 @@ dummy_edge_conn_new(circuit_t *circ, int type, size_t in_bytes, size_t out_bytes) { edge_connection_t *conn; - generic_buffer_t *inbuf, *outbuf; + buf_t *inbuf, *outbuf; if (type == CONN_TYPE_EXIT) conn = edge_connection_new(type, AF_INET); else conn = ENTRY_TO_EDGE_CONN(entry_connection_new(type, AF_INET)); -#ifdef USE_BUFFEREVENTS - inbuf = bufferevent_get_input(TO_CONN(conn)->bufev); - outbuf = bufferevent_get_output(TO_CONN(conn)->bufev); -#else inbuf = TO_CONN(conn)->inbuf; outbuf = TO_CONN(conn)->outbuf; -#endif /* We add these bytes directly to the buffers, to avoid all the * edge connection read/write machinery. */ @@ -134,10 +129,12 @@ test_oom_circbuf(void *arg) { or_options_t *options = get_options_mutable(); circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL; - struct timeval tv = { 1389631048, 0 }; + uint64_t now_ns = 1389631048 * (uint64_t)1000000000; + const uint64_t start_ns = now_ns; (void) arg; + monotime_enable_test_mocking(); MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_); /* Far too low for real life. */ @@ -150,11 +147,11 @@ test_oom_circbuf(void *arg) /* Now we're going to fake up some circuits and get them added to the global circuit list. */ - tv.tv_usec = 0; - tor_gettimeofday_cache_set(&tv); + monotime_coarse_set_mock_time_nsec(now_ns); c1 = dummy_origin_circuit_new(30); - tv.tv_usec = 10*1000; - tor_gettimeofday_cache_set(&tv); + + now_ns += 10 * 1000000; + monotime_coarse_set_mock_time_nsec(now_ns); c2 = dummy_or_circuit_new(20, 20); tt_int_op(packed_cell_mem_cost(), OP_EQ, @@ -163,15 +160,15 @@ test_oom_circbuf(void *arg) packed_cell_mem_cost() * 70); tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */ - tv.tv_usec = 20*1000; - tor_gettimeofday_cache_set(&tv); + now_ns += 10 * 1000000; + monotime_coarse_set_mock_time_nsec(now_ns); c3 = dummy_or_circuit_new(100, 85); tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */ tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 255); - tv.tv_usec = 30*1000; - tor_gettimeofday_cache_set(&tv); + now_ns += 10 * 1000000; + monotime_coarse_set_mock_time_nsec(now_ns); /* Adding this cell will trigger our OOM handler. */ c4 = dummy_or_circuit_new(2, 0); @@ -189,12 +186,12 @@ test_oom_circbuf(void *arg) packed_cell_mem_cost() * (257 - 30)); circuit_free(c1); - tv.tv_usec = 0; - tor_gettimeofday_cache_set(&tv); /* go back in time */ + + monotime_coarse_set_mock_time_nsec(start_ns); /* go back in time */ c1 = dummy_or_circuit_new(90, 0); - tv.tv_usec = 40*1000; /* go back to the future */ - tor_gettimeofday_cache_set(&tv); + now_ns += 10 * 1000000; + monotime_coarse_set_mock_time_nsec(now_ns); tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */ @@ -213,6 +210,7 @@ test_oom_circbuf(void *arg) circuit_free(c4); UNMOCK(circuit_mark_for_close_); + monotime_disable_test_mocking(); } /** Run unit tests for buffers.c */ @@ -221,12 +219,14 @@ test_oom_streambuf(void *arg) { or_options_t *options = get_options_mutable(); circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL; - struct timeval tv = { 1389641159, 0 }; uint32_t tvms; int i; smartlist_t *edgeconns = smartlist_new(); + const uint64_t start_ns = 1389641159 * (uint64_t)1000000000; + uint64_t now_ns = start_ns; (void) arg; + monotime_enable_test_mocking(); MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_); @@ -238,54 +238,56 @@ test_oom_streambuf(void *arg) tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 0); tt_int_op(buf_get_total_allocation(), OP_EQ, 0); + monotime_coarse_set_mock_time_nsec(start_ns); + /* Start all circuits with a bit of data queued in cells */ - tv.tv_usec = 500*1000; /* go halfway into the second. */ - tor_gettimeofday_cache_set(&tv); + + /* go halfway into the second. */ + monotime_coarse_set_mock_time_nsec(start_ns + 500 * 1000000); c1 = dummy_or_circuit_new(10,10); - tv.tv_usec = 510*1000; - tor_gettimeofday_cache_set(&tv); + + monotime_coarse_set_mock_time_nsec(start_ns + 510 * 1000000); c2 = dummy_origin_circuit_new(20); - tv.tv_usec = 520*1000; - tor_gettimeofday_cache_set(&tv); + monotime_coarse_set_mock_time_nsec(start_ns + 520 * 1000000); c3 = dummy_or_circuit_new(20,20); - tv.tv_usec = 530*1000; - tor_gettimeofday_cache_set(&tv); + monotime_coarse_set_mock_time_nsec(start_ns + 530 * 1000000); c4 = dummy_or_circuit_new(0,0); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 80); - tv.tv_usec = 600*1000; - tor_gettimeofday_cache_set(&tv); + now_ns = start_ns + 600 * 1000000; + monotime_coarse_set_mock_time_nsec(now_ns); /* Add some connections to c1...c4. */ for (i = 0; i < 4; ++i) { edge_connection_t *ec; /* link it to a circuit */ - tv.tv_usec += 10*1000; - tor_gettimeofday_cache_set(&tv); + now_ns += 10 * 1000000; + monotime_coarse_set_mock_time_nsec(now_ns); ec = dummy_edge_conn_new(c1, CONN_TYPE_EXIT, 1000, 1000); tt_assert(ec); smartlist_add(edgeconns, ec); - tv.tv_usec += 10*1000; - tor_gettimeofday_cache_set(&tv); + now_ns += 10 * 1000000; + monotime_coarse_set_mock_time_nsec(now_ns); ec = dummy_edge_conn_new(c2, CONN_TYPE_AP, 1000, 1000); tt_assert(ec); smartlist_add(edgeconns, ec); - tv.tv_usec += 10*1000; - tor_gettimeofday_cache_set(&tv); + now_ns += 10 * 1000000; + monotime_coarse_set_mock_time_nsec(now_ns); ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); /* Yes, 4 twice*/ tt_assert(ec); smartlist_add(edgeconns, ec); - tv.tv_usec += 10*1000; - tor_gettimeofday_cache_set(&tv); + now_ns += 10 * 1000000; + monotime_coarse_set_mock_time_nsec(now_ns); ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); smartlist_add(edgeconns, ec); tt_assert(ec); } - tv.tv_sec += 1; - tv.tv_usec = 0; - tvms = (uint32_t) tv_to_msec(&tv); + now_ns -= now_ns % 1000000000; + now_ns += 1000000000; + monotime_coarse_set_mock_time_nsec(now_ns); + tvms = (uint32_t) monotime_coarse_absolute_msec(); tt_int_op(circuit_max_queued_cell_age(c1, tvms), OP_EQ, 500); tt_int_op(circuit_max_queued_cell_age(c2, tvms), OP_EQ, 490); @@ -309,9 +311,8 @@ test_oom_streambuf(void *arg) /* Now give c4 a very old buffer of modest size */ { edge_connection_t *ec; - tv.tv_sec -= 1; - tv.tv_usec = 0; - tor_gettimeofday_cache_set(&tv); + now_ns -= 1000000000; + monotime_coarse_set_mock_time_nsec(now_ns); ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); tt_assert(ec); smartlist_add(edgeconns, ec); @@ -322,8 +323,8 @@ test_oom_streambuf(void *arg) tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* And run over the limit. */ - tv.tv_usec = 800*1000; - tor_gettimeofday_cache_set(&tv); + now_ns += 800*1000000; + monotime_coarse_set_mock_time_nsec(now_ns); c5 = dummy_or_circuit_new(0,5); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, @@ -355,6 +356,7 @@ test_oom_streambuf(void *arg) smartlist_free(edgeconns); UNMOCK(circuit_mark_for_close_); + monotime_disable_test_mocking(); } struct testcase_t oom_tests[] = { diff --git a/src/test/test_oos.c b/src/test/test_oos.c new file mode 100644 index 0000000000..db06625116 --- /dev/null +++ b/src/test/test_oos.c @@ -0,0 +1,456 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Unit tests for OOS handler */ + +#define CONNECTION_PRIVATE + +#include "or.h" +#include "config.h" +#include "connection.h" +#include "connection_or.h" +#include "main.h" +#include "test.h" + +static or_options_t mock_options; + +static void +reset_options_mock(void) +{ + memset(&mock_options, 0, sizeof(or_options_t)); +} + +static const or_options_t * +mock_get_options(void) +{ + return &mock_options; +} + +static int moribund_calls = 0; +static int moribund_conns = 0; + +static int +mock_connection_count_moribund(void) +{ + ++moribund_calls; + + return moribund_conns; +} + +/* + * For unit test purposes it's sufficient to tell that + * kill_conn_list_for_oos() was called with an approximately + * sane argument; it's just the thing we returned from the + * mock for pick_oos_victims(). + */ + +static int kill_conn_list_calls = 0; +static int kill_conn_list_killed = 0; + +static void +kill_conn_list_mock(smartlist_t *conns) +{ + ++kill_conn_list_calls; + + tt_assert(conns != NULL); + + kill_conn_list_killed += smartlist_len(conns); + + done: + return; +} + +static int pick_oos_mock_calls = 0; +static int pick_oos_mock_fail = 0; +static int pick_oos_mock_last_n = 0; + +static smartlist_t * +pick_oos_victims_mock(int n) +{ + smartlist_t *l = NULL; + int i; + + ++pick_oos_mock_calls; + + tt_int_op(n, OP_GT, 0); + + if (!pick_oos_mock_fail) { + /* + * connection_check_oos() just passes the list onto + * kill_conn_list_for_oos(); we don't need to simulate + * its content for this mock, just its existence, but + * we do need to check the parameter. + */ + l = smartlist_new(); + for (i = 0; i < n; ++i) smartlist_add(l, NULL); + } else { + l = NULL; + } + + pick_oos_mock_last_n = n; + + done: + return l; +} + +/** Unit test for the logic in connection_check_oos(), which is concerned + * with comparing thresholds and connection counts to decide if an OOS has + * occurred and if so, how many connections to try to kill, and then using + * pick_oos_victims() and kill_conn_list_for_oos() to carry out its grim + * duty. + */ +static void +test_oos_connection_check_oos(void *arg) +{ + (void)arg; + + /* Set up mocks */ + reset_options_mock(); + /* OOS handling is only sensitive to these fields */ + mock_options.ConnLimit = 32; + mock_options.ConnLimit_ = 64; + mock_options.ConnLimit_high_thresh = 60; + mock_options.ConnLimit_low_thresh = 50; + MOCK(get_options, mock_get_options); + moribund_calls = 0; + moribund_conns = 0; + MOCK(connection_count_moribund, mock_connection_count_moribund); + kill_conn_list_calls = 0; + kill_conn_list_killed = 0; + MOCK(kill_conn_list_for_oos, kill_conn_list_mock); + pick_oos_mock_calls = 0; + pick_oos_mock_fail = 0; + MOCK(pick_oos_victims, pick_oos_victims_mock); + + /* No OOS case */ + connection_check_oos(50, 0); + tt_int_op(moribund_calls, OP_EQ, 0); + tt_int_op(pick_oos_mock_calls, OP_EQ, 0); + tt_int_op(kill_conn_list_calls, OP_EQ, 0); + + /* OOS from socket count, nothing moribund */ + connection_check_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 1); + tt_int_op(pick_oos_mock_calls, OP_EQ, 1); + /* 12 == 62 - ConnLimit_low_thresh */ + tt_int_op(pick_oos_mock_last_n, OP_EQ, 12); + tt_int_op(kill_conn_list_calls, OP_EQ, 1); + tt_int_op(kill_conn_list_killed, OP_EQ, 12); + + /* OOS from socket count, some are moribund */ + kill_conn_list_killed = 0; + moribund_conns = 5; + connection_check_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 2); + tt_int_op(pick_oos_mock_calls, OP_EQ, 2); + /* 7 == 62 - ConnLimit_low_thresh - moribund_conns */ + tt_int_op(pick_oos_mock_last_n, OP_EQ, 7); + tt_int_op(kill_conn_list_calls, OP_EQ, 2); + tt_int_op(kill_conn_list_killed, OP_EQ, 7); + + /* OOS from socket count, but pick fails */ + kill_conn_list_killed = 0; + moribund_conns = 0; + pick_oos_mock_fail = 1; + connection_check_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 3); + tt_int_op(pick_oos_mock_calls, OP_EQ, 3); + tt_int_op(kill_conn_list_calls, OP_EQ, 2); + tt_int_op(kill_conn_list_killed, OP_EQ, 0); + pick_oos_mock_fail = 0; + + /* + * OOS from socket count with so many moribund conns + * we have none to kill. + */ + kill_conn_list_killed = 0; + moribund_conns = 15; + connection_check_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 4); + tt_int_op(pick_oos_mock_calls, OP_EQ, 3); + tt_int_op(kill_conn_list_calls, OP_EQ, 2); + + /* + * OOS from socket exhaustion; OOS handler will try to + * kill 1/10 (5) of the connections. + */ + kill_conn_list_killed = 0; + moribund_conns = 0; + connection_check_oos(50, 1); + tt_int_op(moribund_calls, OP_EQ, 5); + tt_int_op(pick_oos_mock_calls, OP_EQ, 4); + tt_int_op(kill_conn_list_calls, OP_EQ, 3); + tt_int_op(kill_conn_list_killed, OP_EQ, 5); + + /* OOS from socket exhaustion with moribund conns */ + kill_conn_list_killed = 0; + moribund_conns = 2; + connection_check_oos(50, 1); + tt_int_op(moribund_calls, OP_EQ, 6); + tt_int_op(pick_oos_mock_calls, OP_EQ, 5); + tt_int_op(kill_conn_list_calls, OP_EQ, 4); + tt_int_op(kill_conn_list_killed, OP_EQ, 3); + + /* OOS from socket exhaustion with many moribund conns */ + kill_conn_list_killed = 0; + moribund_conns = 7; + connection_check_oos(50, 1); + tt_int_op(moribund_calls, OP_EQ, 7); + tt_int_op(pick_oos_mock_calls, OP_EQ, 5); + tt_int_op(kill_conn_list_calls, OP_EQ, 4); + + /* OOS with both socket exhaustion and above-threshold */ + kill_conn_list_killed = 0; + moribund_conns = 0; + connection_check_oos(62, 1); + tt_int_op(moribund_calls, OP_EQ, 8); + tt_int_op(pick_oos_mock_calls, OP_EQ, 6); + tt_int_op(kill_conn_list_calls, OP_EQ, 5); + tt_int_op(kill_conn_list_killed, OP_EQ, 12); + + /* + * OOS with both socket exhaustion and above-threshold with some + * moribund conns + */ + kill_conn_list_killed = 0; + moribund_conns = 5; + connection_check_oos(62, 1); + tt_int_op(moribund_calls, OP_EQ, 9); + tt_int_op(pick_oos_mock_calls, OP_EQ, 7); + tt_int_op(kill_conn_list_calls, OP_EQ, 6); + tt_int_op(kill_conn_list_killed, OP_EQ, 7); + + /* + * OOS with both socket exhaustion and above-threshold with many + * moribund conns + */ + kill_conn_list_killed = 0; + moribund_conns = 15; + connection_check_oos(62, 1); + tt_int_op(moribund_calls, OP_EQ, 10); + tt_int_op(pick_oos_mock_calls, OP_EQ, 7); + tt_int_op(kill_conn_list_calls, OP_EQ, 6); + + done: + + UNMOCK(pick_oos_victims); + UNMOCK(kill_conn_list_for_oos); + UNMOCK(connection_count_moribund); + UNMOCK(get_options); + + return; +} + +static int cfe_calls = 0; + +static void +close_for_error_mock(or_connection_t *orconn, int flush) +{ + (void)flush; + + tt_assert(orconn != NULL); + ++cfe_calls; + + done: + return; +} + +static int mark_calls = 0; + +static void +mark_for_close_oos_mock(connection_t *conn, + int line, const char *file) +{ + (void)line; + (void)file; + + tt_assert(conn != NULL); + ++mark_calls; + + done: + return; +} + +static void +test_oos_kill_conn_list(void *arg) +{ + connection_t *c1, *c2; + or_connection_t *or_c1 = NULL; + dir_connection_t *dir_c2 = NULL; + smartlist_t *l = NULL; + (void)arg; + + /* Set up mocks */ + mark_calls = 0; + MOCK(connection_mark_for_close_internal_, mark_for_close_oos_mock); + cfe_calls = 0; + MOCK(connection_or_close_for_error, close_for_error_mock); + + /* Make fake conns */ + or_c1 = tor_malloc_zero(sizeof(*or_c1)); + or_c1->base_.magic = OR_CONNECTION_MAGIC; + or_c1->base_.type = CONN_TYPE_OR; + c1 = TO_CONN(or_c1); + dir_c2 = tor_malloc_zero(sizeof(*dir_c2)); + dir_c2->base_.magic = DIR_CONNECTION_MAGIC; + dir_c2->base_.type = CONN_TYPE_DIR; + dir_c2->base_.state = DIR_CONN_STATE_MIN_; + dir_c2->base_.purpose = DIR_PURPOSE_MIN_; + c2 = TO_CONN(dir_c2); + + tt_assert(c1 != NULL); + tt_assert(c2 != NULL); + + /* Make list */ + l = smartlist_new(); + smartlist_add(l, c1); + smartlist_add(l, c2); + + /* Run kill_conn_list_for_oos() */ + kill_conn_list_for_oos(l); + + /* Check call counters */ + tt_int_op(mark_calls, OP_EQ, 1); + tt_int_op(cfe_calls, OP_EQ, 1); + + done: + + UNMOCK(connection_or_close_for_error); + UNMOCK(connection_mark_for_close_internal_); + + if (l) smartlist_free(l); + tor_free(or_c1); + tor_free(dir_c2); + + return; +} + +static smartlist_t *conns_for_mock = NULL; + +static smartlist_t * +get_conns_mock(void) +{ + return conns_for_mock; +} + +/* + * For this mock, we pretend all conns have either zero or one circuits, + * depending on if this appears on the list of things to say have a circuit. + */ + +static smartlist_t *conns_with_circs = NULL; + +static int +get_num_circuits_mock(or_connection_t *conn) +{ + int circs = 0; + + tt_assert(conn != NULL); + + if (conns_with_circs && + smartlist_contains(conns_with_circs, TO_CONN(conn))) { + circs = 1; + } + + done: + return circs; +} + +static void +test_oos_pick_oos_victims(void *arg) +{ + (void)arg; + or_connection_t *ortmp; + dir_connection_t *dirtmp; + smartlist_t *picked; + + /* Set up mocks */ + conns_for_mock = smartlist_new(); + MOCK(get_connection_array, get_conns_mock); + conns_with_circs = smartlist_new(); + MOCK(connection_or_get_num_circuits, get_num_circuits_mock); + + /* Make some fake connections */ + ortmp = tor_malloc_zero(sizeof(*ortmp)); + ortmp->base_.magic = OR_CONNECTION_MAGIC; + ortmp->base_.type = CONN_TYPE_OR; + smartlist_add(conns_for_mock, TO_CONN(ortmp)); + /* We'll pretend this one has a circuit too */ + smartlist_add(conns_with_circs, TO_CONN(ortmp)); + /* Next one */ + ortmp = tor_malloc_zero(sizeof(*ortmp)); + ortmp->base_.magic = OR_CONNECTION_MAGIC; + ortmp->base_.type = CONN_TYPE_OR; + smartlist_add(conns_for_mock, TO_CONN(ortmp)); + /* Next one is moribund */ + ortmp = tor_malloc_zero(sizeof(*ortmp)); + ortmp->base_.magic = OR_CONNECTION_MAGIC; + ortmp->base_.type = CONN_TYPE_OR; + ortmp->base_.marked_for_close = 1; + smartlist_add(conns_for_mock, TO_CONN(ortmp)); + /* Last one isn't an orconn */ + dirtmp = tor_malloc_zero(sizeof(*dirtmp)); + dirtmp->base_.magic = DIR_CONNECTION_MAGIC; + dirtmp->base_.type = CONN_TYPE_DIR; + smartlist_add(conns_for_mock, TO_CONN(dirtmp)); + + /* Try picking one */ + picked = pick_oos_victims(1); + /* It should be the one with circuits */ + tt_assert(picked != NULL); + tt_int_op(smartlist_len(picked), OP_EQ, 1); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0))); + smartlist_free(picked); + + /* Try picking none */ + picked = pick_oos_victims(0); + /* We should get an empty list */ + tt_assert(picked != NULL); + tt_int_op(smartlist_len(picked), OP_EQ, 0); + smartlist_free(picked); + + /* Try picking two */ + picked = pick_oos_victims(2); + /* We should get both active orconns */ + tt_assert(picked != NULL); + tt_int_op(smartlist_len(picked), OP_EQ, 2); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0))); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1))); + smartlist_free(picked); + + /* Try picking three - only two are eligible */ + picked = pick_oos_victims(3); + tt_int_op(smartlist_len(picked), OP_EQ, 2); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0))); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1))); + smartlist_free(picked); + + done: + + /* Free leftover stuff */ + if (conns_with_circs) { + smartlist_free(conns_with_circs); + conns_with_circs = NULL; + } + + UNMOCK(connection_or_get_num_circuits); + + if (conns_for_mock) { + SMARTLIST_FOREACH(conns_for_mock, connection_t *, c, tor_free(c)); + smartlist_free(conns_for_mock); + conns_for_mock = NULL; + } + + UNMOCK(get_connection_array); + + return; +} + +struct testcase_t oos_tests[] = { + { "connection_check_oos", test_oos_connection_check_oos, + TT_FORK, NULL, NULL }, + { "kill_conn_list", test_oos_kill_conn_list, TT_FORK, NULL, NULL }, + { "pick_oos_victims", test_oos_pick_oos_victims, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_options.c b/src/test/test_options.c index 4f24757a85..e85e11805b 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -12,7 +12,7 @@ #define ROUTERSET_PRIVATE #include "routerset.h" - +#include "main.h" #include "log_test_helpers.h" #include "sandbox.h" @@ -121,7 +121,7 @@ test_options_validate_impl(const char *configuration, r = config_get_lines(configuration, &cl, 1); tt_int_op(r, OP_EQ, 0); - r = config_assign(&options_format, opt, cl, 0, 0, &msg); + r = config_assign(&options_format, opt, cl, 0, &msg); tt_int_op(r, OP_EQ, 0); r = options_validate(NULL, opt, dflt, 0, &msg); @@ -223,7 +223,7 @@ test_have_enough_mem_for_dircache(void *arg) r = config_get_lines(configuration, &cl, 1); tt_int_op(r, OP_EQ, 0); - r = config_assign(&options_format, opt, cl, 0, 0, &msg); + r = config_assign(&options_format, opt, cl, 0, &msg); tt_int_op(r, OP_EQ, 0); /* 300 MB RAM available, DirCache enabled */ @@ -246,7 +246,7 @@ test_have_enough_mem_for_dircache(void *arg) r = config_get_lines(configuration, &cl, 1); tt_int_op(r, OP_EQ, 0); - r = config_assign(&options_format, opt, cl, 0, 0, &msg); + r = config_assign(&options_format, opt, cl, 0, &msg); tt_int_op(r, OP_EQ, 0); /* 300 MB RAM available, DirCache enabled, Bridge */ @@ -269,7 +269,7 @@ test_have_enough_mem_for_dircache(void *arg) r = config_get_lines(configuration, &cl, 1); tt_int_op(r, OP_EQ, 0); - r = config_assign(&options_format, opt, cl, 0, 0, &msg); + r = config_assign(&options_format, opt, cl, 0, &msg); tt_int_op(r, OP_EQ, 0); /* 200 MB RAM available, DirCache disabled */ @@ -354,7 +354,7 @@ get_options_test_data(const char *conf) result->def_opt = options_new(); rv = config_get_lines(conf, &cl, 1); tt_assert(rv == 0); - rv = config_assign(&options_format, result->opt, cl, 0, 0, &msg); + rv = config_assign(&options_format, result->opt, cl, 0, &msg); if (msg) { /* Display the parse error message by comparing it with an empty string */ tt_str_op(msg, OP_EQ, ""); @@ -365,7 +365,7 @@ get_options_test_data(const char *conf) result->opt->TokenBucketRefillInterval = 1; rv = config_get_lines(TEST_OPTIONS_OLD_VALUES, &cl, 1); tt_assert(rv == 0); - rv = config_assign(&options_format, result->def_opt, cl, 0, 0, &msg); + rv = config_assign(&options_format, result->def_opt, cl, 0, &msg); if (msg) { /* Display the parse error message by comparing it with an empty string */ tt_str_op(msg, OP_EQ, ""); @@ -394,14 +394,6 @@ free_options_test_data(options_test_data_t *td) tor_free(td); } -#define expect_log_msg(str) \ - tt_assert_msg(mock_saved_log_has_message(str), \ - "expected log to contain " # str); - -#define expect_no_log_msg(str) \ - tt_assert_msg(!mock_saved_log_has_message(str), \ - "expected log to not contain " # str); - static void test_options_validate__uname_for_server(void *ignored) { @@ -409,7 +401,7 @@ test_options_validate__uname_for_server(void *ignored) char *msg; options_test_data_t *tdata = get_options_test_data( "ORListenAddress 127.0.0.1:5555"); - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); MOCK(get_uname, fixed_get_uname); fixed_get_uname_result = "Windows 95"; @@ -445,7 +437,7 @@ test_options_validate__uname_for_server(void *ignored) UNMOCK(get_uname); free_options_test_data(tdata); tor_free(msg); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); } static void @@ -513,8 +505,9 @@ test_options_validate__nickname(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, - "Nickname 'ThisNickNameIsABitTooLong' is wrong length or" - " contains illegal characters."); + "Nickname 'ThisNickNameIsABitTooLong', nicknames must be between " + "1 and 19 characters inclusive, and must contain only the " + "characters [a-zA-Z0-9]."); tor_free(msg); free_options_test_data(tdata); @@ -542,7 +535,7 @@ test_options_validate__contactinfo(void *ignored) char *msg; options_test_data_t *tdata = get_options_test_data( "ORListenAddress 127.0.0.1:5555\nORPort 955"); - int previous_log = setup_capture_of_logs(LOG_DEBUG); + setup_capture_of_logs(LOG_DEBUG); tdata->opt->ContactInfo = NULL; ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); @@ -566,13 +559,11 @@ test_options_validate__contactinfo(void *ignored) tor_free(msg); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } -extern int quiet_level; - static void test_options_validate__logs(void *ignored) { @@ -656,7 +647,7 @@ test_options_validate__authdir(void *ignored) (void)ignored; int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_INFO); + 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"); @@ -949,7 +940,7 @@ test_options_validate__authdir(void *ignored) /* "but ClientOnly also set."); */ done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); // sandbox_free_getaddrinfo_cache(); free_options_test_data(tdata); tor_free(msg); @@ -960,7 +951,7 @@ test_options_validate__relay_with_hidden_services(void *ignored) { (void)ignored; char *msg; - int previous_log = setup_capture_of_logs(LOG_DEBUG); + 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" @@ -977,7 +968,7 @@ test_options_validate__relay_with_hidden_services(void *ignored) "https://trac.torproject.org/8742\n"); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -990,7 +981,7 @@ test_options_validate__relay_with_hidden_services(void *ignored) /* (void)ignored; */ /* int ret; */ /* char *msg; */ -/* int previous_log = setup_capture_of_logs(LOG_WARN); */ +/* setup_capture_of_logs(LOG_WARN); */ /* options_test_data_t *tdata = get_options_test_data(""); */ /* ret = options_validate(tdata->old_opt, tdata->opt, */ /* tdata->def_opt, 0, &msg); */ @@ -999,7 +990,7 @@ test_options_validate__relay_with_hidden_services(void *ignored) /* "configured. " */ /* " Tor will still run, but probably won't do anything.\n"); */ /* done: */ -/* teardown_capture_of_logs(previous_log); */ +/* teardown_capture_of_logs(); */ /* free_options_test_data(tdata); */ /* tor_free(msg); */ /* } */ @@ -1059,7 +1050,7 @@ test_options_validate__transproxy(void *ignored) tt_int_op(ret, OP_EQ, -1); #ifndef KERNEL_MAY_SUPPORT_IPFW - tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specificand OS X/Darwin-specific " + tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specific and OS X/Darwin-specific " "feature."); #else tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_IPFW); @@ -1080,26 +1071,38 @@ test_options_validate__transproxy(void *ignored) free_options_test_data(tdata); tdata = NULL; -#if defined(linux) +#if defined(__linux__) tdata = get_options_test_data("TransProxyType tproxy\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); - tt_assert(!msg); -#endif -#if defined(__FreeBSD_kernel__) || defined( DARWIN ) + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } +#elif defined(KERNEL_MAY_SUPPORT_IPFW) tdata = get_options_test_data("TransProxyType ipfw\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); - tt_assert(!msg); -#endif -#if defined(__OpenBSD__) + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } +#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); tt_int_op(ret, OP_EQ, -1); - tt_assert(!msg); + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } +#elif defined(__NetBSD__) + tdata = get_options_test_data("TransProxyType default\n" + "TransPort 127.0.0.1:123\n"); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } #endif // Assert that a test has run for some TransProxyType @@ -1140,7 +1143,7 @@ test_options_validate__exclude_nodes(void *ignored) int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); options_test_data_t *tdata = get_options_test_data( "ExcludeExitNodes {us}\n"); @@ -1205,7 +1208,7 @@ test_options_validate__exclude_nodes(void *ignored) done: NS_UNMOCK(geoip_get_country); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -1216,7 +1219,7 @@ test_options_validate__scheduler(void *ignored) (void)ignored; int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_DEBUG); + setup_capture_of_logs(LOG_DEBUG); options_test_data_t *tdata = get_options_test_data( "SchedulerLowWaterMark__ 0\n"); @@ -1248,7 +1251,7 @@ test_options_validate__scheduler(void *ignored) tor_free(msg); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -1311,7 +1314,7 @@ test_options_validate__tlsec(void *ignored) (void)ignored; int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_DEBUG); + setup_capture_of_logs(LOG_DEBUG); options_test_data_t *tdata = get_options_test_data( "TLSECGroup ed25519\n" "SchedulerHighWaterMark__ 42\n" @@ -1348,7 +1351,7 @@ test_options_validate__tlsec(void *ignored) tor_free(msg); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -1386,7 +1389,7 @@ test_options_validate__recommended_packages(void *ignored) (void)ignored; int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); options_test_data_t *tdata = get_options_test_data( "RecommendedPackages foo 1.2 http://foo.com sha1=123123123123\n" "RecommendedPackages invalid-package-line\n" @@ -1400,7 +1403,7 @@ test_options_validate__recommended_packages(void *ignored) done: escaped(NULL); // This will free the leaking memory from the previous escaped - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -1478,7 +1481,7 @@ test_options_validate__paths_needed(void *ignored) (void)ignored; int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); options_test_data_t *tdata = get_options_test_data( "PathsNeededToBuildCircuits 0.1\n" "ConnLimit 1\n" @@ -1523,7 +1526,7 @@ test_options_validate__paths_needed(void *ignored) tor_free(msg); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -1660,7 +1663,7 @@ test_options_validate__reachable_addresses(void *ignored) (void)ignored; int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_NOTICE); + setup_capture_of_logs(LOG_NOTICE); options_test_data_t *tdata = get_options_test_data( "FascistFirewall 1\n" "MaxClientCircuitsPending 1\n" @@ -1874,7 +1877,7 @@ test_options_validate__reachable_addresses(void *ignored) tt_ptr_op(msg, OP_EQ, NULL); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -2124,7 +2127,7 @@ test_options_validate__publish_server_descriptor(void *ignored) (void)ignored; int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); options_test_data_t *tdata = get_options_test_data( "PublishServerDescriptor bridge\n" TEST_OPTIONS_DEFAULT_VALUES ); @@ -2188,7 +2191,7 @@ test_options_validate__publish_server_descriptor(void *ignored) tt_assert(!tdata->opt->DirPort_set); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); policies_free_all(); free_options_test_data(tdata); tor_free(msg); @@ -2275,7 +2278,7 @@ test_options_validate__hidserv(void *ignored) (void)ignored; int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); options_test_data_t *tdata = get_options_test_data( TEST_OPTIONS_DEFAULT_VALUES); @@ -2310,7 +2313,7 @@ test_options_validate__hidserv(void *ignored) tor_free(msg); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); policies_free_all(); free_options_test_data(tdata); tor_free(msg); @@ -2322,7 +2325,7 @@ test_options_validate__predicted_ports(void *ignored) (void)ignored; int ret; char *msg; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); options_test_data_t *tdata = get_options_test_data( "PredictedPortsRelevanceTime 100000000\n" @@ -2334,7 +2337,7 @@ test_options_validate__predicted_ports(void *ignored) tt_int_op(tdata->opt->PredictedPortsRelevanceTime, OP_EQ, 3600); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); policies_free_all(); free_options_test_data(tdata); tor_free(msg); @@ -2548,7 +2551,7 @@ test_options_validate__circuits(void *ignored) (void)ignored; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -2625,7 +2628,7 @@ test_options_validate__circuits(void *ignored) done: policies_free_all(); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -2698,7 +2701,7 @@ test_options_validate__rend(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -2753,7 +2756,156 @@ test_options_validate__rend(void *ignored) done: policies_free_all(); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); + free_options_test_data(tdata); + tor_free(msg); +} + +static void +test_options_validate__single_onion(void *ignored) +{ + (void)ignored; + int ret; + char *msg; + options_test_data_t *tdata = NULL; + setup_capture_of_logs(LOG_WARN); + + /* Test that HiddenServiceSingleHopMode must come with + * HiddenServiceNonAnonymousMode */ + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 0\n" + "HiddenServiceSingleHopMode 1\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, "HiddenServiceSingleHopMode does not provide any " + "server anonymity. It must be used with " + "HiddenServiceNonAnonymousMode set to 1."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 0\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 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, "HiddenServiceSingleHopMode does not provide any " + "server anonymity. It must be used with " + "HiddenServiceNonAnonymousMode set to 1."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 0\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 1\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + /* Test that SOCKSPort must come with Tor2webMode if + * HiddenServiceSingleHopMode is 1 */ + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 5000\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 1\n" + "Tor2webMode 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, "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."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 0\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 1\n" + "Tor2webMode 0\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 5000\n" + "HiddenServiceSingleHopMode 0\n" + "Tor2webMode 0\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 5000\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 1\n" + "Tor2webMode 1\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + /* Test that a hidden service can't be run with Tor2web + * Use HiddenServiceNonAnonymousMode instead of Tor2webMode, because + * Tor2webMode requires a compilation #define */ + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "HiddenServiceNonAnonymousMode 1\n" + "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n" + "HiddenServicePort 80 127.0.0.1:8080\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, "HiddenServiceNonAnonymousMode does not provide any " + "server anonymity. It must be used with " + "HiddenServiceSingleHopMode set to 1."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "HiddenServiceNonAnonymousMode 1\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, "HiddenServiceNonAnonymousMode does not provide any " + "server anonymity. It must be used with " + "HiddenServiceSingleHopMode set to 1."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n" + "HiddenServicePort 80 127.0.0.1:8080\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "HiddenServiceNonAnonymousMode 1\n" + "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n" + "HiddenServicePort 80 127.0.0.1:8080\n" + "HiddenServiceSingleHopMode 1\n" + "SOCKSPort 0\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + done: + policies_free_all(); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -2765,7 +2917,7 @@ test_options_validate__accounting(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -2870,7 +3022,7 @@ test_options_validate__accounting(void *ignored) tor_free(msg); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); policies_free_all(); free_options_test_data(tdata); tor_free(msg); @@ -2884,7 +3036,7 @@ test_options_validate__proxy(void *ignored) char *msg; options_test_data_t *tdata = NULL; sandbox_disable_getaddrinfo_cache(); - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -3200,7 +3352,7 @@ test_options_validate__proxy(void *ignored) tor_free(msg); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); policies_free_all(); // sandbox_free_getaddrinfo_cache(); @@ -3214,7 +3366,7 @@ test_options_validate__control(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -3428,7 +3580,7 @@ test_options_validate__control(void *ignored) tor_free(msg); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); policies_free_all(); free_options_test_data(tdata); tor_free(msg); @@ -3441,7 +3593,7 @@ test_options_validate__families(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -3500,7 +3652,7 @@ test_options_validate__families(void *ignored) tor_free(msg); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); policies_free_all(); free_options_test_data(tdata); tor_free(msg); @@ -3537,7 +3689,7 @@ test_options_validate__dir_auth(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -3612,7 +3764,7 @@ test_options_validate__dir_auth(void *ignored) done: policies_free_all(); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -3624,7 +3776,7 @@ test_options_validate__transport(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_NOTICE); + setup_capture_of_logs(LOG_NOTICE); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -3737,7 +3889,7 @@ test_options_validate__transport(void *ignored) done: escaped(NULL); // This will free the leaking memory from the previous escaped policies_free_all(); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -3749,7 +3901,7 @@ test_options_validate__constrained_sockets(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -3820,7 +3972,7 @@ test_options_validate__constrained_sockets(void *ignored) done: policies_free_all(); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -3832,7 +3984,7 @@ test_options_validate__v3_auth(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -4039,7 +4191,7 @@ test_options_validate__v3_auth(void *ignored) done: policies_free_all(); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -4084,7 +4236,7 @@ test_options_validate__exits(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -4114,7 +4266,7 @@ test_options_validate__exits(void *ignored) done: policies_free_all(); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -4126,7 +4278,7 @@ test_options_validate__testing_options(void *ignored) int ret; char *msg; options_test_data_t *tdata = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); #define TEST_TESTING_OPTION(name, low_val, high_val, err_low) \ STMT_BEGIN \ @@ -4282,7 +4434,7 @@ test_options_validate__testing_options(void *ignored) done: policies_free_all(); - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); free_options_test_data(tdata); tor_free(msg); } @@ -4380,6 +4532,7 @@ struct testcase_t options_tests[] = { LOCAL_VALIDATE_TEST(port_forwarding), LOCAL_VALIDATE_TEST(tor2web), LOCAL_VALIDATE_TEST(rend), + LOCAL_VALIDATE_TEST(single_onion), LOCAL_VALIDATE_TEST(accounting), LOCAL_VALIDATE_TEST(proxy), LOCAL_VALIDATE_TEST(control), diff --git a/src/test/test_policy.c b/src/test/test_policy.c index a939ebf54f..1ffdc2cd51 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -32,12 +32,14 @@ test_short_policy_parse(const char *input, short_policy_free(short_policy); } -/** Helper: Parse the exit policy string in <b>policy_str</b>, and make sure - * that policies_summarize() produces the string <b>expected_summary</b> from - * it. */ +/** Helper: Parse the exit policy string in <b>policy_str</b> with + * <b>options</b>, and make sure that policies_summarize() produces the string + * <b>expected_summary</b> from it when called with family. */ static void -test_policy_summary_helper(const char *policy_str, - const char *expected_summary) +test_policy_summary_helper_family_flags(const char *policy_str, + const char *expected_summary, + sa_family_t family, + exit_policy_parser_cfg_t options) { config_line_t line; smartlist_t *policy = smartlist_new(); @@ -45,17 +47,17 @@ test_policy_summary_helper(const char *policy_str, char *summary_after = NULL; int r; short_policy_t *short_policy = NULL; + int success = 0; line.key = (char*)"foo"; line.value = (char *)policy_str; line.next = NULL; r = policies_parse_exit_policy(&line, &policy, - EXIT_POLICY_IPV6_ENABLED | - EXIT_POLICY_ADD_DEFAULT, NULL); + options, NULL); tt_int_op(r,OP_EQ, 0); - summary = policy_summarize(policy, AF_INET); + summary = policy_summarize(policy, family); tt_assert(summary != NULL); tt_str_op(summary,OP_EQ, expected_summary); @@ -65,7 +67,12 @@ test_policy_summary_helper(const char *policy_str, summary_after = write_short_policy(short_policy); tt_str_op(summary,OP_EQ, summary_after); + success = 1; done: + /* If we don't print the flags on failure, it's very hard to diagnose bugs */ + if (!success) + TT_DECLARE("CTXT", ("\n IPv%d\n Options: %x\n Policy: %s", + family == AF_INET ? 4 : 6, options, policy_str)); tor_free(summary_after); tor_free(summary); if (policy) @@ -73,6 +80,50 @@ test_policy_summary_helper(const char *policy_str, short_policy_free(short_policy); } +/** Like test_policy_summary_helper_family_flags, but tries all the different + * flag combinations */ +static void +test_policy_summary_helper_family(const char *policy_str, + const char *expected_summary, + sa_family_t family) +{ + for (exit_policy_parser_cfg_t opt = 0; + opt <= EXIT_POLICY_OPTION_ALL; + opt++) { + if (family == AF_INET6 && !(opt & EXIT_POLICY_IPV6_ENABLED)) + /* Skip the test: IPv6 addresses need IPv6 enabled */ + continue; + + if (opt & EXIT_POLICY_REJECT_LOCAL_INTERFACES) + /* Skip the test: local interfaces are machine-specific */ + continue; + + test_policy_summary_helper_family_flags(policy_str, expected_summary, + family, opt); + } +} + +/** Like test_policy_summary_helper_family, but uses expected_summary for + * both IPv4 and IPv6. */ +static void +test_policy_summary_helper(const char *policy_str, + const char *expected_summary) +{ + test_policy_summary_helper_family(policy_str, expected_summary, AF_INET); + test_policy_summary_helper_family(policy_str, expected_summary, AF_INET6); +} + +/** Like test_policy_summary_helper_family, but uses expected_summary4 for + * IPv4 and expected_summary6 for IPv6. */ +static void +test_policy_summary_helper6(const char *policy_str, + const char *expected_summary4, + const char *expected_summary6) +{ + test_policy_summary_helper_family(policy_str, expected_summary4, AF_INET); + test_policy_summary_helper_family(policy_str, expected_summary6, AF_INET6); +} + /** Run unit tests for generating summary lines of exit policies */ static void test_policies_general(void *arg) @@ -253,19 +304,21 @@ test_policies_general(void *arg) tt_assert(!exit_policy_is_general_exit(policy10)); tt_assert(!exit_policy_is_general_exit(policy11)); - tt_assert(cmp_addr_policies(policy, policy2)); - tt_assert(cmp_addr_policies(policy, NULL)); - tt_assert(!cmp_addr_policies(policy2, policy2)); - tt_assert(!cmp_addr_policies(NULL, NULL)); - - tt_assert(!policy_is_reject_star(policy2, AF_INET)); - tt_assert(policy_is_reject_star(policy, AF_INET)); - tt_assert(policy_is_reject_star(policy10, AF_INET)); - tt_assert(!policy_is_reject_star(policy10, AF_INET6)); - tt_assert(policy_is_reject_star(policy11, AF_INET)); - tt_assert(policy_is_reject_star(policy11, AF_INET6)); - tt_assert(policy_is_reject_star(NULL, AF_INET)); - tt_assert(policy_is_reject_star(NULL, AF_INET6)); + tt_assert(!addr_policies_eq(policy, policy2)); + tt_assert(!addr_policies_eq(policy, NULL)); + tt_assert(addr_policies_eq(policy2, policy2)); + tt_assert(addr_policies_eq(NULL, NULL)); + + tt_assert(!policy_is_reject_star(policy2, AF_INET, 1)); + tt_assert(policy_is_reject_star(policy, AF_INET, 1)); + tt_assert(policy_is_reject_star(policy10, AF_INET, 1)); + tt_assert(!policy_is_reject_star(policy10, AF_INET6, 1)); + tt_assert(policy_is_reject_star(policy11, AF_INET, 1)); + tt_assert(policy_is_reject_star(policy11, AF_INET6, 1)); + tt_assert(policy_is_reject_star(NULL, AF_INET, 1)); + tt_assert(policy_is_reject_star(NULL, AF_INET6, 1)); + tt_assert(!policy_is_reject_star(NULL, AF_INET, 0)); + tt_assert(!policy_is_reject_star(NULL, AF_INET6, 0)); addr_policy_list_free(policy); policy = NULL; @@ -392,13 +445,14 @@ test_policies_general(void *arg) "reject 14.0.0.0/9:80," "reject 15.0.0.0:81," "accept *:*", "accept 1-65535"); - test_policy_summary_helper("reject 11.0.0.0/9:80," - "reject 12.0.0.0/9:80," - "reject 13.0.0.0/9:80," - "reject 14.0.0.0/9:80," - "reject 15.0.0.0:80," - "accept *:*", - "reject 80"); + test_policy_summary_helper6("reject 11.0.0.0/9:80," + "reject 12.0.0.0/9:80," + "reject 13.0.0.0/9:80," + "reject 14.0.0.0/9:80," + "reject 15.0.0.0:80," + "accept *:*", + "reject 80", + "accept 1-65535"); /* no exits */ test_policy_summary_helper("accept 11.0.0.0/9:80," "reject *:*", @@ -429,6 +483,458 @@ test_policies_general(void *arg) "reject *:7," "accept *:*", "reject 1,3,5,7"); + /* long policies */ + /* standard long policy on many exits */ + test_policy_summary_helper("accept *:20-23," + "accept *:43," + "accept *:53," + "accept *:79-81," + "accept *:88," + "accept *:110," + "accept *:143," + "accept *:194," + "accept *:220," + "accept *:389," + "accept *:443," + "accept *:464," + "accept *:531," + "accept *:543-544," + "accept *:554," + "accept *:563," + "accept *:636," + "accept *:706," + "accept *:749," + "accept *:873," + "accept *:902-904," + "accept *:981," + "accept *:989-995," + "accept *:1194," + "accept *:1220," + "accept *:1293," + "accept *:1500," + "accept *:1533," + "accept *:1677," + "accept *:1723," + "accept *:1755," + "accept *:1863," + "accept *:2082," + "accept *:2083," + "accept *:2086-2087," + "accept *:2095-2096," + "accept *:2102-2104," + "accept *:3128," + "accept *:3389," + "accept *:3690," + "accept *:4321," + "accept *:4643," + "accept *:5050," + "accept *:5190," + "accept *:5222-5223," + "accept *:5228," + "accept *:5900," + "accept *:6660-6669," + "accept *:6679," + "accept *:6697," + "accept *:8000," + "accept *:8008," + "accept *:8074," + "accept *:8080," + "accept *:8087-8088," + "accept *:8332-8333," + "accept *:8443," + "accept *:8888," + "accept *:9418," + "accept *:9999," + "accept *:10000," + "accept *:11371," + "accept *:12350," + "accept *:19294," + "accept *:19638," + "accept *:23456," + "accept *:33033," + "accept *:64738," + "reject *:*", + "accept 20-23,43,53,79-81,88,110,143,194,220,389," + "443,464,531,543-544,554,563,636,706,749,873," + "902-904,981,989-995,1194,1220,1293,1500,1533," + "1677,1723,1755,1863,2082-2083,2086-2087," + "2095-2096,2102-2104,3128,3389,3690,4321,4643," + "5050,5190,5222-5223,5228,5900,6660-6669,6679," + "6697,8000,8008,8074,8080,8087-8088,8332-8333," + "8443,8888,9418,9999-10000,11371,12350,19294," + "19638,23456,33033,64738"); + /* short policy with configured addresses */ + test_policy_summary_helper("reject 149.56.1.1:*," + "reject [2607:5300:1:1::1:0]:*," + "accept *:80," + "accept *:443," + "reject *:*", + "accept 80,443"); + /* short policy with configured and local interface addresses */ + test_policy_summary_helper("reject 149.56.1.0:*," + "reject 149.56.1.1:*," + "reject 149.56.1.2:*," + "reject 149.56.1.3:*," + "reject 149.56.1.4:*," + "reject 149.56.1.5:*," + "reject 149.56.1.6:*," + "reject 149.56.1.7:*," + "reject [2607:5300:1:1::1:0]:*," + "reject [2607:5300:1:1::1:1]:*," + "reject [2607:5300:1:1::1:2]:*," + "reject [2607:5300:1:1::1:3]:*," + "reject [2607:5300:1:1::2:0]:*," + "reject [2607:5300:1:1::2:1]:*," + "reject [2607:5300:1:1::2:2]:*," + "reject [2607:5300:1:1::2:3]:*," + "accept *:80," + "accept *:443," + "reject *:*", + "accept 80,443"); + /* short policy with configured netblocks */ + test_policy_summary_helper("reject 149.56.0.0/16," + "reject6 2607:5300::/32," + "reject6 2608:5300::/64," + "reject6 2609:5300::/96," + "accept *:80," + "accept *:443," + "reject *:*", + "accept 80,443"); + /* short policy with large netblocks that do not count as a rejection */ + test_policy_summary_helper("reject 148.0.0.0/7," + "reject6 2600::/16," + "accept *:80," + "accept *:443," + "reject *:*", + "accept 80,443"); + /* short policy with large netblocks that count as a rejection */ + test_policy_summary_helper("reject 148.0.0.0/6," + "reject6 2600::/15," + "accept *:80," + "accept *:443," + "reject *:*", + "reject 1-65535"); + /* short policy with huge netblocks that count as a rejection */ + test_policy_summary_helper("reject 128.0.0.0/1," + "reject6 8000::/1," + "accept *:80," + "accept *:443," + "reject *:*", + "reject 1-65535"); + /* short policy which blocks everything using netblocks */ + test_policy_summary_helper("reject 0.0.0.0/0," + "reject6 ::/0," + "accept *:80," + "accept *:443," + "reject *:*", + "reject 1-65535"); + /* short policy which has repeated redundant netblocks */ + test_policy_summary_helper("reject 0.0.0.0/0," + "reject 0.0.0.0/0," + "reject 0.0.0.0/0," + "reject 0.0.0.0/0," + "reject 0.0.0.0/0," + "reject6 ::/0," + "reject6 ::/0," + "reject6 ::/0," + "reject6 ::/0," + "reject6 ::/0," + "accept *:80," + "accept *:443," + "reject *:*", + "reject 1-65535"); + + /* longest possible policy + * (1-2,4-5,... is longer, but gets reduced to 3,6,... ) + * Going all the way to 65535 is incredibly slow, so we just go slightly + * more than the expected length */ + test_policy_summary_helper("accept *:1," + "accept *:3," + "accept *:5," + "accept *:7," + "accept *:9," + "accept *:11," + "accept *:13," + "accept *:15," + "accept *:17," + "accept *:19," + "accept *:21," + "accept *:23," + "accept *:25," + "accept *:27," + "accept *:29," + "accept *:31," + "accept *:33," + "accept *:35," + "accept *:37," + "accept *:39," + "accept *:41," + "accept *:43," + "accept *:45," + "accept *:47," + "accept *:49," + "accept *:51," + "accept *:53," + "accept *:55," + "accept *:57," + "accept *:59," + "accept *:61," + "accept *:63," + "accept *:65," + "accept *:67," + "accept *:69," + "accept *:71," + "accept *:73," + "accept *:75," + "accept *:77," + "accept *:79," + "accept *:81," + "accept *:83," + "accept *:85," + "accept *:87," + "accept *:89," + "accept *:91," + "accept *:93," + "accept *:95," + "accept *:97," + "accept *:99," + "accept *:101," + "accept *:103," + "accept *:105," + "accept *:107," + "accept *:109," + "accept *:111," + "accept *:113," + "accept *:115," + "accept *:117," + "accept *:119," + "accept *:121," + "accept *:123," + "accept *:125," + "accept *:127," + "accept *:129," + "accept *:131," + "accept *:133," + "accept *:135," + "accept *:137," + "accept *:139," + "accept *:141," + "accept *:143," + "accept *:145," + "accept *:147," + "accept *:149," + "accept *:151," + "accept *:153," + "accept *:155," + "accept *:157," + "accept *:159," + "accept *:161," + "accept *:163," + "accept *:165," + "accept *:167," + "accept *:169," + "accept *:171," + "accept *:173," + "accept *:175," + "accept *:177," + "accept *:179," + "accept *:181," + "accept *:183," + "accept *:185," + "accept *:187," + "accept *:189," + "accept *:191," + "accept *:193," + "accept *:195," + "accept *:197," + "accept *:199," + "accept *:201," + "accept *:203," + "accept *:205," + "accept *:207," + "accept *:209," + "accept *:211," + "accept *:213," + "accept *:215," + "accept *:217," + "accept *:219," + "accept *:221," + "accept *:223," + "accept *:225," + "accept *:227," + "accept *:229," + "accept *:231," + "accept *:233," + "accept *:235," + "accept *:237," + "accept *:239," + "accept *:241," + "accept *:243," + "accept *:245," + "accept *:247," + "accept *:249," + "accept *:251," + "accept *:253," + "accept *:255," + "accept *:257," + "accept *:259," + "accept *:261," + "accept *:263," + "accept *:265," + "accept *:267," + "accept *:269," + "accept *:271," + "accept *:273," + "accept *:275," + "accept *:277," + "accept *:279," + "accept *:281," + "accept *:283," + "accept *:285," + "accept *:287," + "accept *:289," + "accept *:291," + "accept *:293," + "accept *:295," + "accept *:297," + "accept *:299," + "accept *:301," + "accept *:303," + "accept *:305," + "accept *:307," + "accept *:309," + "accept *:311," + "accept *:313," + "accept *:315," + "accept *:317," + "accept *:319," + "accept *:321," + "accept *:323," + "accept *:325," + "accept *:327," + "accept *:329," + "accept *:331," + "accept *:333," + "accept *:335," + "accept *:337," + "accept *:339," + "accept *:341," + "accept *:343," + "accept *:345," + "accept *:347," + "accept *:349," + "accept *:351," + "accept *:353," + "accept *:355," + "accept *:357," + "accept *:359," + "accept *:361," + "accept *:363," + "accept *:365," + "accept *:367," + "accept *:369," + "accept *:371," + "accept *:373," + "accept *:375," + "accept *:377," + "accept *:379," + "accept *:381," + "accept *:383," + "accept *:385," + "accept *:387," + "accept *:389," + "accept *:391," + "accept *:393," + "accept *:395," + "accept *:397," + "accept *:399," + "accept *:401," + "accept *:403," + "accept *:405," + "accept *:407," + "accept *:409," + "accept *:411," + "accept *:413," + "accept *:415," + "accept *:417," + "accept *:419," + "accept *:421," + "accept *:423," + "accept *:425," + "accept *:427," + "accept *:429," + "accept *:431," + "accept *:433," + "accept *:435," + "accept *:437," + "accept *:439," + "accept *:441," + "accept *:443," + "accept *:445," + "accept *:447," + "accept *:449," + "accept *:451," + "accept *:453," + "accept *:455," + "accept *:457," + "accept *:459," + "accept *:461," + "accept *:463," + "accept *:465," + "accept *:467," + "accept *:469," + "accept *:471," + "accept *:473," + "accept *:475," + "accept *:477," + "accept *:479," + "accept *:481," + "accept *:483," + "accept *:485," + "accept *:487," + "accept *:489," + "accept *:491," + "accept *:493," + "accept *:495," + "accept *:497," + "accept *:499," + "accept *:501," + "accept *:503," + "accept *:505," + "accept *:507," + "accept *:509," + "accept *:511," + "accept *:513," + "accept *:515," + "accept *:517," + "accept *:519," + "accept *:521," + "accept *:523," + "accept *:525," + "accept *:527," + "accept *:529," + "reject *:*", + "accept 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29," + "31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61," + "63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93," + "95,97,99,101,103,105,107,109,111,113,115,117," + "119,121,123,125,127,129,131,133,135,137,139,141," + "143,145,147,149,151,153,155,157,159,161,163,165," + "167,169,171,173,175,177,179,181,183,185,187,189," + "191,193,195,197,199,201,203,205,207,209,211,213," + "215,217,219,221,223,225,227,229,231,233,235,237," + "239,241,243,245,247,249,251,253,255,257,259,261," + "263,265,267,269,271,273,275,277,279,281,283,285," + "287,289,291,293,295,297,299,301,303,305,307,309," + "311,313,315,317,319,321,323,325,327,329,331,333," + "335,337,339,341,343,345,347,349,351,353,355,357," + "359,361,363,365,367,369,371,373,375,377,379,381," + "383,385,387,389,391,393,395,397,399,401,403,405," + "407,409,411,413,415,417,419,421,423,425,427,429," + "431,433,435,437,439,441,443,445,447,449,451,453," + "455,457,459,461,463,465,467,469,471,473,475,477," + "479,481,483,485,487,489,491,493,495,497,499,501," + "503,505,507,509,511,513,515,517,519,521,523"); /* Short policies with unrecognized formats should get accepted. */ test_short_policy_parse("accept fred,2,3-5", "accept 2,3-5"); @@ -523,18 +1029,17 @@ test_policies_general(void *arg) /* Test a too-long policy. */ { - int i; - char *policy = NULL; + char *policy_strng = NULL; smartlist_t *chunks = smartlist_new(); smartlist_add(chunks, tor_strdup("accept ")); for (i=1; i<10000; ++i) smartlist_add_asprintf(chunks, "%d,", i); smartlist_add(chunks, tor_strdup("20000")); - policy = smartlist_join_strings(chunks, "", 0, NULL); + policy_strng = smartlist_join_strings(chunks, "", 0, NULL); SMARTLIST_FOREACH(chunks, char *, ch, tor_free(ch)); smartlist_free(chunks); - short_parsed = parse_short_policy(policy);/* shouldn't be accepted */ - tor_free(policy); + short_parsed = parse_short_policy(policy_strng);/* shouldn't be accepted */ + tor_free(policy_strng); tt_ptr_op(NULL, OP_EQ, short_parsed); } @@ -778,8 +1283,8 @@ test_policies_reject_port_address(void *arg) UNMOCK(get_configured_ports); } -smartlist_t *mock_ipv4_addrs = NULL; -smartlist_t *mock_ipv6_addrs = NULL; +static smartlist_t *mock_ipv4_addrs = NULL; +static smartlist_t *mock_ipv6_addrs = NULL; /* mock get_interface_address6_list, returning a deep copy of the template * address list ipv4_interface_address_list or ipv6_interface_address_list */ @@ -804,7 +1309,7 @@ mock_get_interface_address6_list(int severity, tt_assert(template_list); SMARTLIST_FOREACH_BEGIN(template_list, tor_addr_t *, src_addr) { - tor_addr_t *dest_addr = malloc(sizeof(tor_addr_t)); + tor_addr_t *dest_addr = tor_malloc(sizeof(tor_addr_t)); memset(dest_addr, 0, sizeof(*dest_addr)); tor_addr_copy_tight(dest_addr, src_addr); smartlist_add(clone_list, dest_addr); @@ -1082,10 +1587,32 @@ test_policies_getinfo_helper_policies(void *arg) append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*"); mock_options.IPv6Exit = 1; - mock_options.ExitPolicyRejectPrivate = 1; tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR); tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR); + mock_options.ExitPolicyRejectPrivate = 1; + mock_options.ExitPolicyRejectLocalInterfaces = 1; + + rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay", + &answer, &errmsg); + tt_assert(rv == 0); + tt_assert(answer != NULL); + tt_assert(strlen(answer) > 0); + tor_free(answer); + + mock_options.ExitPolicyRejectPrivate = 1; + mock_options.ExitPolicyRejectLocalInterfaces = 0; + + rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay", + &answer, &errmsg); + tt_assert(rv == 0); + tt_assert(answer != NULL); + tt_assert(strlen(answer) > 0); + tor_free(answer); + + mock_options.ExitPolicyRejectPrivate = 0; + mock_options.ExitPolicyRejectLocalInterfaces = 1; + rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay", &answer, &errmsg); tt_assert(rv == 0); @@ -1093,6 +1620,16 @@ test_policies_getinfo_helper_policies(void *arg) tt_assert(strlen(answer) > 0); tor_free(answer); + mock_options.ExitPolicyRejectPrivate = 0; + mock_options.ExitPolicyRejectLocalInterfaces = 0; + + rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay", + &answer, &errmsg); + tt_assert(rv == 0); + tt_assert(answer != NULL); + tt_assert(strlen(answer) == 0); + tor_free(answer); + rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer, &errmsg); tt_assert(rv == 0); diff --git a/src/test/test_protover.c b/src/test/test_protover.c new file mode 100644 index 0000000000..f00955d1b4 --- /dev/null +++ b/src/test/test_protover.c @@ -0,0 +1,195 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define PROTOVER_PRIVATE + +#include "orconfig.h" +#include "test.h" + +#include "protover.h" + +static void +test_protover_parse(void *arg) +{ + (void) arg; + char *re_encoded = NULL; + + const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900"; + smartlist_t *elts = parse_protocol_list(orig); + + tt_assert(elts); + tt_int_op(smartlist_len(elts), OP_EQ, 4); + + const proto_entry_t *e; + const proto_range_t *r; + e = smartlist_get(elts, 0); + tt_str_op(e->name, OP_EQ, "Foo"); + tt_int_op(smartlist_len(e->ranges), OP_EQ, 2); + { + r = smartlist_get(e->ranges, 0); + tt_int_op(r->low, OP_EQ, 1); + tt_int_op(r->high, OP_EQ, 1); + + r = smartlist_get(e->ranges, 1); + tt_int_op(r->low, OP_EQ, 3); + tt_int_op(r->high, OP_EQ, 3); + } + + e = smartlist_get(elts, 1); + tt_str_op(e->name, OP_EQ, "Bar"); + tt_int_op(smartlist_len(e->ranges), OP_EQ, 1); + { + r = smartlist_get(e->ranges, 0); + tt_int_op(r->low, OP_EQ, 3); + tt_int_op(r->high, OP_EQ, 3); + } + + e = smartlist_get(elts, 2); + tt_str_op(e->name, OP_EQ, "Baz"); + tt_int_op(smartlist_len(e->ranges), OP_EQ, 0); + + e = smartlist_get(elts, 3); + tt_str_op(e->name, OP_EQ, "Quux"); + tt_int_op(smartlist_len(e->ranges), OP_EQ, 4); + { + r = smartlist_get(e->ranges, 0); + tt_int_op(r->low, OP_EQ, 9); + tt_int_op(r->high, OP_EQ, 12); + + r = smartlist_get(e->ranges, 1); + tt_int_op(r->low, OP_EQ, 14); + tt_int_op(r->high, OP_EQ, 14); + + r = smartlist_get(e->ranges, 2); + tt_int_op(r->low, OP_EQ, 15); + tt_int_op(r->high, OP_EQ, 16); + + r = smartlist_get(e->ranges, 3); + tt_int_op(r->low, OP_EQ, 900); + tt_int_op(r->high, OP_EQ, 900); + } + + re_encoded = encode_protocol_list(elts); + tt_assert(re_encoded); + tt_str_op(re_encoded, OP_EQ, orig); + + done: + if (elts) + SMARTLIST_FOREACH(elts, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(elts); + tor_free(re_encoded); +} + +static void +test_protover_parse_fail(void *arg) +{ + (void)arg; + smartlist_t *elts; + + /* random junk */ + elts = parse_protocol_list("!!3@*"); + tt_assert(elts == NULL); + + /* Missing equals sign in an entry */ + elts = parse_protocol_list("Link=4 Haprauxymatyve Desc=9"); + tt_assert(elts == NULL); + + /* Missing word. */ + elts = parse_protocol_list("Link=4 =3 Desc=9"); + tt_assert(elts == NULL); + + /* Broken numbers */ + elts = parse_protocol_list("Link=fred"); + tt_assert(elts == NULL); + elts = parse_protocol_list("Link=1,fred"); + tt_assert(elts == NULL); + elts = parse_protocol_list("Link=1,fred,3"); + tt_assert(elts == NULL); + + /* Broken range */ + elts = parse_protocol_list("Link=1,9-8,3"); + tt_assert(elts == NULL); + + done: + ; +} + +static void +test_protover_vote(void *arg) +{ + (void) arg; + + smartlist_t *lst = smartlist_new(); + char *result = protover_compute_vote(lst, 1); + + tt_str_op(result, OP_EQ, ""); + tor_free(result); + + smartlist_add(lst, (void*) "Foo=1-10,500 Bar=1,3-7,8"); + result = protover_compute_vote(lst, 1); + tt_str_op(result, OP_EQ, "Bar=1,3-8 Foo=1-10,500"); + tor_free(result); + + smartlist_add(lst, (void*) "Quux=123-456,78 Bar=2-6,8 Foo=9"); + result = protover_compute_vote(lst, 1); + tt_str_op(result, OP_EQ, "Bar=1-8 Foo=1-10,500 Quux=78,123-456"); + tor_free(result); + + result = protover_compute_vote(lst, 2); + tt_str_op(result, OP_EQ, "Bar=3-6,8 Foo=9"); + tor_free(result); + + done: + tor_free(result); + smartlist_free(lst); +} + +static void +test_protover_all_supported(void *arg) +{ + (void)arg; + char *msg = NULL; + + tt_assert(protover_all_supported(NULL, &msg)); + tt_assert(msg == NULL); + + tt_assert(protover_all_supported("", &msg)); + tt_assert(msg == NULL); + + // Some things that we do support + tt_assert(protover_all_supported("Link=3-4", &msg)); + tt_assert(msg == NULL); + tt_assert(protover_all_supported("Link=3-4 Desc=2", &msg)); + tt_assert(msg == NULL); + + // Some things we don't support + tt_assert(! protover_all_supported("Wombat=9", &msg)); + tt_str_op(msg, OP_EQ, "Wombat=9"); + tor_free(msg); + tt_assert(! protover_all_supported("Link=999", &msg)); + tt_str_op(msg, OP_EQ, "Link=999"); + tor_free(msg); + + // Mix of things we support and things we don't + tt_assert(! protover_all_supported("Link=3-4 Wombat=9", &msg)); + tt_str_op(msg, OP_EQ, "Wombat=9"); + tor_free(msg); + tt_assert(! protover_all_supported("Link=3-999", &msg)); + tt_str_op(msg, OP_EQ, "Link=3-999"); + tor_free(msg); + + done: + tor_free(msg); +} + +#define PV_TEST(name, flags) \ + { #name, test_protover_ ##name, (flags), NULL, NULL } + +struct testcase_t protover_tests[] = { + PV_TEST(parse, 0), + PV_TEST(parse_fail, 0), + PV_TEST(vote, 0), + PV_TEST(all_supported, 0), + END_OF_TESTCASES +}; + diff --git a/src/test/test_pt.c b/src/test/test_pt.c index ab8447dcd7..e5cdc5f3cd 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -107,12 +107,12 @@ test_pt_parsing(void *arg) tt_assert(parse_smethod_line(line, mp) == 0); tt_int_op(1, OP_EQ, smartlist_len(mp->transports)); { - const transport_t *transport = smartlist_get(mp->transports, 0); - tt_assert(transport); - tt_str_op(transport->name, OP_EQ, "trebuchet"); - tt_int_op(transport->port, OP_EQ, 9999); - tt_str_op(fmt_addr(&transport->addr), OP_EQ, "127.0.0.1"); - tt_str_op(transport->extra_info_args, OP_EQ, + const transport_t *transport_ = smartlist_get(mp->transports, 0); + tt_assert(transport_); + tt_str_op(transport_->name, OP_EQ, "trebuchet"); + tt_int_op(transport_->port, OP_EQ, 9999); + tt_str_op(fmt_addr(&transport_->addr), OP_EQ, "127.0.0.1"); + tt_str_op(transport_->extra_info_args, OP_EQ, "counterweight=3,sling=snappy"); } reset_mp(mp); diff --git a/src/test/test_pubsub.c b/src/test/test_pubsub.c new file mode 100644 index 0000000000..547d6c6b32 --- /dev/null +++ b/src/test/test_pubsub.c @@ -0,0 +1,85 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_pubsub.c + * \brief Unit tests for publish-subscribe abstraction. + **/ + +#include "or.h" +#include "test.h" +#include "pubsub.h" + +DECLARE_PUBSUB_STRUCT_TYPES(foobar) +DECLARE_PUBSUB_TOPIC(foobar) +DECLARE_NOTIFY_PUBSUB_TOPIC(static, foobar) +IMPLEMENT_PUBSUB_TOPIC(static, foobar) + +struct foobar_event_data_t { + unsigned u; + const char *s; +}; + +struct foobar_subscriber_data_t { + const char *name; + long l; +}; + +static int +foobar_sub1(foobar_event_data_t *ev, foobar_subscriber_data_t *mine) +{ + ev->u += 10; + mine->l += 100; + return 0; +} + +static int +foobar_sub2(foobar_event_data_t *ev, foobar_subscriber_data_t *mine) +{ + ev->u += 5; + mine->l += 50; + return 0; +} + +static void +test_pubsub_basic(void *arg) +{ + (void)arg; + foobar_subscriber_data_t subdata1 = { "hi", 0 }; + foobar_subscriber_data_t subdata2 = { "wow", 0 }; + const foobar_subscriber_t *sub1; + const foobar_subscriber_t *sub2; + foobar_event_data_t ed = { 0, "x" }; + foobar_event_data_t ed2 = { 0, "y" }; + sub1 = foobar_subscribe(foobar_sub1, &subdata1, SUBSCRIBE_ATSTART, 100); + tt_assert(sub1); + + foobar_notify(&ed, 0); + tt_int_op(subdata1.l, OP_EQ, 100); + tt_int_op(subdata2.l, OP_EQ, 0); + tt_int_op(ed.u, OP_EQ, 10); + + sub2 = foobar_subscribe(foobar_sub2, &subdata2, 0, 5); + tt_assert(sub2); + + foobar_notify(&ed2, 0); + tt_int_op(subdata1.l, OP_EQ, 200); + tt_int_op(subdata2.l, OP_EQ, 50); + tt_int_op(ed2.u, OP_EQ, 15); + + foobar_unsubscribe(sub1); + + foobar_notify(&ed, 0); + tt_int_op(subdata1.l, OP_EQ, 200); + tt_int_op(subdata2.l, OP_EQ, 100); + tt_int_op(ed.u, OP_EQ, 15); + + done: + foobar_clear(); +} + +struct testcase_t pubsub_tests[] = { + { "pubsub_basic", test_pubsub_basic, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_relay.c b/src/test/test_relay.c index a7fcad5401..4713c79ea5 100644 --- a/src/test/test_relay.c +++ b/src/test/test_relay.c @@ -74,6 +74,10 @@ test_relay_append_cell_to_circuit_queue(void *arg) /* Make a fake orcirc */ orcirc = new_fake_orcirc(nchan, pchan); tt_assert(orcirc); + circuitmux_attach_circuit(nchan->cmux, TO_CIRCUIT(orcirc), + CELL_DIRECTION_OUT); + circuitmux_attach_circuit(pchan->cmux, TO_CIRCUIT(orcirc), + CELL_DIRECTION_IN); /* Make a cell */ cell = tor_malloc_zero(sizeof(cell_t)); @@ -109,8 +113,12 @@ test_relay_append_cell_to_circuit_queue(void *arg) done: tor_free(cell); - cell_queue_clear(&orcirc->base_.n_chan_cells); - cell_queue_clear(&orcirc->p_chan_cells); + if (orcirc) { + circuitmux_detach_circuit(nchan->cmux, TO_CIRCUIT(orcirc)); + circuitmux_detach_circuit(pchan->cmux, TO_CIRCUIT(orcirc)); + cell_queue_clear(&orcirc->base_.n_chan_cells); + cell_queue_clear(&orcirc->p_chan_cells); + } tor_free(orcirc); free_fake_channel(nchan); free_fake_channel(pchan); diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c index 1cd9ff064b..fb6748965a 100644 --- a/src/test/test_relaycell.c +++ b/src/test/test_relaycell.c @@ -95,7 +95,7 @@ test_relaycell_resolved(void *arg) tt_int_op(srm_ncalls, OP_EQ, 1); \ tt_ptr_op(srm_conn, OP_EQ, entryconn); \ tt_int_op(srm_atype, OP_EQ, (atype)); \ - if (answer) { \ + if ((answer) != NULL) { \ tt_int_op(srm_alen, OP_EQ, sizeof(answer)-1); \ tt_int_op(srm_alen, OP_LT, 512); \ tt_int_op(srm_answer_is_set, OP_EQ, 1); \ diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c index d1b52649b2..a5d3f351f8 100644 --- a/src/test/test_rendcache.c +++ b/src/test/test_rendcache.c @@ -12,18 +12,14 @@ #include "config.h" #include <openssl/rsa.h> #include "rend_test_helpers.h" +#include "log_test_helpers.h" #define NS_MODULE rend_cache static const int RECENT_TIME = -10; static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + \ - REND_CACHE_MAX_SKEW + 10); -static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 10; - -extern strmap_t *rend_cache; -extern digestmap_t *rend_cache_v2_dir; -extern strmap_t *rend_cache_failure; -extern size_t rend_cache_total_allocation; + REND_CACHE_MAX_SKEW + 60); +static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 60; static rend_data_t * mock_rend_data(const char *onion_address) @@ -660,15 +656,19 @@ test_rend_cache_decrement_allocation(void *data) // Test when there are not enough allocations rend_cache_total_allocation = 1; + setup_full_capture_of_logs(LOG_WARN); rend_cache_decrement_allocation(2); tt_int_op(rend_cache_total_allocation, OP_EQ, 0); + expect_single_log_msg_containing( + "Underflow in rend_cache_decrement_allocation"); + teardown_capture_of_logs(); // And again rend_cache_decrement_allocation(2); tt_int_op(rend_cache_total_allocation, OP_EQ, 0); done: - (void)0; + teardown_capture_of_logs(); } static void @@ -683,15 +683,19 @@ test_rend_cache_increment_allocation(void *data) // Test when there are too many allocations rend_cache_total_allocation = SIZE_MAX-1; + setup_full_capture_of_logs(LOG_WARN); rend_cache_increment_allocation(2); tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX); + expect_single_log_msg_containing( + "Overflow in rend_cache_increment_allocation"); + teardown_capture_of_logs(); // And again rend_cache_increment_allocation(2); tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX); done: - (void)0; + teardown_capture_of_logs(); } static void @@ -976,7 +980,7 @@ test_rend_cache_entry_free(void *data) // Handles non-NULL descriptor correctly e = tor_malloc_zero(sizeof(rend_cache_entry_t)); - e->desc = (char *)malloc(10); + e->desc = tor_malloc(10); rend_cache_entry_free(e); /* done: */ diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index 2cffa6e801..088bd257c3 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -19,20 +19,24 @@ #include "networkstatus.h" #include "nodelist.h" #include "policies.h" +#include "router.h" #include "routerlist.h" #include "routerparse.h" +#include "shared_random.h" #include "test.h" #include "test_dir_common.h" -extern const char AUTHORITY_CERT_1[]; -extern const char AUTHORITY_SIGNKEY_1[]; -extern const char AUTHORITY_CERT_2[]; -extern const char AUTHORITY_SIGNKEY_2[]; -extern const char AUTHORITY_CERT_3[]; -extern const char AUTHORITY_SIGNKEY_3[]; - void construct_consensus(char **consensus_text_md); +static authority_cert_t *mock_cert; + +static authority_cert_t * +get_my_v3_authority_cert_m(void) +{ + tor_assert(mock_cert); + return mock_cert; +} + /* 4 digests + 3 sep + pre + post + NULL */ static char output[4*BASE64_DIGEST256_LEN+3+2+2+1]; @@ -234,6 +238,12 @@ test_router_pick_directory_server_impl(void *arg) tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60)); tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60)); + /* Init SR subsystem. */ + MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); + mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); + sr_init(0); + UNMOCK(get_my_v3_authority_cert); + /* No consensus available, fail early */ rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL); tt_assert(rs == NULL); @@ -423,7 +433,7 @@ test_router_pick_directory_server_impl(void *arg) networkstatus_vote_free(con_md); } -connection_t *mocked_connection = NULL; +static connection_t *mocked_connection = NULL; /* Mock connection_get_by_type_addr_port_purpose by returning * mocked_connection. */ diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c index 74b39c0486..1b526d430b 100644 --- a/src/test/test_routerset.c +++ b/src/test/test_routerset.c @@ -432,7 +432,7 @@ NS(test_main)(void *arg) NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string, (const char *s, int assume_action, int *malformed_list)); -addr_policy_t *NS(mock_addr_policy); +static addr_policy_t *NS(mock_addr_policy); static void NS(test_main)(void *arg) @@ -480,7 +480,7 @@ NS(router_parse_addr_policy_item_from_string)(const char *s, NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string, (const char *s, int assume_action, int *bogus)); -addr_policy_t *NS(mock_addr_policy); +static addr_policy_t *NS(mock_addr_policy); static void NS(test_main)(void *arg) @@ -527,7 +527,7 @@ NS(router_parse_addr_policy_item_from_string)(const char *s, int assume_action, NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string, (const char *s, int assume_action, int *bad)); -addr_policy_t *NS(mock_addr_policy); +static addr_policy_t *NS(mock_addr_policy); static void NS(test_main)(void *arg) @@ -1477,7 +1477,7 @@ NS(test_main)(void *arg) * routerset or routerinfo. */ -node_t NS(mock_node); +static node_t NS(mock_node); static void NS(test_main)(void *arg) @@ -1504,7 +1504,7 @@ NS(test_main)(void *arg) * routerset and no routerinfo. */ -node_t NS(mock_node); +static node_t NS(mock_node); static void NS(test_main)(void *arg) @@ -1603,7 +1603,7 @@ NS(test_main)(void *arg) NS_DECL(const node_t *, node_get_by_nickname, (const char *nickname, int warn_if_unused)); -const char *NS(mock_nickname); +static const char *NS(mock_nickname); static void NS(test_main)(void *arg) @@ -1652,8 +1652,8 @@ NS(node_get_by_nickname)(const char *nickname, int warn_if_unused) NS_DECL(const node_t *, node_get_by_nickname, (const char *nickname, int warn_if_unused)); -const char *NS(mock_nickname); -node_t NS(mock_node); +static const char *NS(mock_nickname); +static node_t NS(mock_node); static void NS(test_main)(void *arg) @@ -1702,8 +1702,8 @@ NS(node_get_by_nickname)(const char *nickname, int warn_if_unused) NS_DECL(const node_t *, node_get_by_nickname, (const char *nickname, int warn_if_unused)); -char *NS(mock_nickname); -node_t NS(mock_node); +static char *NS(mock_nickname); +static node_t NS(mock_node); static void NS(test_main)(void *arg) @@ -1754,7 +1754,7 @@ NS(node_get_by_nickname)(const char *nickname, int warn_if_unused) NS_DECL(smartlist_t *, nodelist_get_list, (void)); -smartlist_t *NS(mock_smartlist); +static smartlist_t *NS(mock_smartlist); static void NS(test_main)(void *arg) @@ -1800,8 +1800,8 @@ NS(nodelist_get_list)(void) NS_DECL(smartlist_t *, nodelist_get_list, (void)); -smartlist_t *NS(mock_smartlist); -node_t NS(mock_node); +static smartlist_t *NS(mock_smartlist); +static node_t NS(mock_node); static void NS(test_main)(void *arg) diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c index 6e9889b48b..05ea8e86e8 100644 --- a/src/test/test_scheduler.c +++ b/src/test/test_scheduler.c @@ -1,16 +1,10 @@ /* Copyright (c) 2014-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#include <math.h> - #include "orconfig.h" -/* Libevent stuff */ -#ifdef HAVE_EVENT2_EVENT_H +#include <math.h> #include <event2/event.h> -#else -#include <event.h> -#endif #define TOR_CHANNEL_INTERNAL_ #define CHANNEL_PRIVATE_ @@ -24,12 +18,6 @@ #include "test.h" #include "fakechans.h" -/* Statics in scheduler.c exposed to the test suite */ -extern smartlist_t *channels_pending; -extern struct event *run_sched_ev; -extern uint64_t queue_heuristic; -extern time_t queue_heuristic_timestamp; - /* Event base for scheduelr tests */ static struct event_base *mock_event_base = NULL; @@ -96,9 +84,7 @@ mock_event_free_all(void) static void mock_event_init(void) { -#ifdef HAVE_EVENT2_EVENT_H struct event_config *cfg = NULL; -#endif tt_ptr_op(mock_event_base, ==, NULL); @@ -108,7 +94,6 @@ mock_event_init(void) */ if (!mock_event_base) { -#ifdef HAVE_EVENT2_EVENT_H cfg = event_config_new(); #if LIBEVENT_VERSION_NUMBER >= V(2,0,9) /* We can enable changelist support with epoll, since we don't give @@ -117,9 +102,6 @@ mock_event_init(void) #endif mock_event_base = event_base_new_with_config(cfg); event_config_free(cfg); -#else - mock_event_base = event_init(); -#endif } tt_assert(mock_event_base != NULL); @@ -156,7 +138,7 @@ channel_flush_some_cells_mock_free_all(void) static void channel_flush_some_cells_mock_set(channel_t *chan, ssize_t num_cells) { - flush_mock_channel_t *flush_mock_ch = NULL; + int found = 0; if (!chan) return; if (num_cells <= 0) return; @@ -172,6 +154,7 @@ channel_flush_some_cells_mock_set(channel_t *chan, ssize_t num_cells) if (flush_mock_ch->chan == chan) { /* Found it */ flush_mock_ch->cells = num_cells; + found = 1; break; } } else { @@ -181,8 +164,9 @@ channel_flush_some_cells_mock_set(channel_t *chan, ssize_t num_cells) } } SMARTLIST_FOREACH_END(flush_mock_ch); - if (!flush_mock_ch) { + if (! found) { /* The loop didn't find it */ + flush_mock_channel_t *flush_mock_ch; flush_mock_ch = tor_malloc_zero(sizeof(*flush_mock_ch)); flush_mock_ch->chan = chan; flush_mock_ch->cells = num_cells; diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c new file mode 100644 index 0000000000..056f199b94 --- /dev/null +++ b/src/test/test_shared_random.c @@ -0,0 +1,1290 @@ +#define SHARED_RANDOM_PRIVATE +#define SHARED_RANDOM_STATE_PRIVATE +#define CONFIG_PRIVATE +#define DIRVOTE_PRIVATE + +#include "or.h" +#include "test.h" +#include "config.h" +#include "dirvote.h" +#include "shared_random.h" +#include "shared_random_state.h" +#include "routerkeys.h" +#include "routerlist.h" +#include "router.h" +#include "routerparse.h" +#include "networkstatus.h" +#include "log_test_helpers.h" + +static authority_cert_t *mock_cert; + +static authority_cert_t * +get_my_v3_authority_cert_m(void) +{ + tor_assert(mock_cert); + return mock_cert; +} + +static dir_server_t ds; + +static dir_server_t * +trusteddirserver_get_by_v3_auth_digest_m(const char *digest) +{ + (void) digest; + /* The shared random code only need to know if a valid pointer to a dir + * server object has been found so this is safe because it won't use the + * pointer at all never. */ + return &ds; +} + +/* Setup a minimal dirauth environment by initializing the SR state and + * making sure the options are set to be an authority directory. */ +static void +init_authority_state(void) +{ + MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); + + or_options_t *options = get_options_mutable(); + 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))); + 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 + * timestamp. Delete it before we do any testing below. */ + sr_state_delete_commits(); + + done: + UNMOCK(get_my_v3_authority_cert); +} + +static void +test_get_sr_protocol_phase(void *arg) +{ + time_t the_time; + sr_phase_t phase; + int retval; + + (void) arg; + + /* Initialize SR state */ + init_authority_state(); + + { + retval = parse_rfc1123_time("Wed, 20 Apr 2015 23:59:00 UTC", &the_time); + tt_int_op(retval, ==, 0); + + phase = get_sr_protocol_phase(the_time); + tt_int_op(phase, ==, SR_PHASE_REVEAL); + } + + { + retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:00 UTC", &the_time); + tt_int_op(retval, ==, 0); + + phase = get_sr_protocol_phase(the_time); + tt_int_op(phase, ==, SR_PHASE_COMMIT); + } + + { + retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:01 UTC", &the_time); + tt_int_op(retval, ==, 0); + + phase = get_sr_protocol_phase(the_time); + tt_int_op(phase, ==, SR_PHASE_COMMIT); + } + + { + retval = parse_rfc1123_time("Wed, 20 Apr 2015 11:59:00 UTC", &the_time); + tt_int_op(retval, ==, 0); + + phase = get_sr_protocol_phase(the_time); + tt_int_op(phase, ==, SR_PHASE_COMMIT); + } + + { + retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:00 UTC", &the_time); + tt_int_op(retval, ==, 0); + + phase = get_sr_protocol_phase(the_time); + tt_int_op(phase, ==, SR_PHASE_REVEAL); + } + + { + retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:01 UTC", &the_time); + tt_int_op(retval, ==, 0); + + phase = get_sr_protocol_phase(the_time); + tt_int_op(phase, ==, SR_PHASE_REVEAL); + } + + { + retval = parse_rfc1123_time("Wed, 20 Apr 2015 13:00:00 UTC", &the_time); + tt_int_op(retval, ==, 0); + + phase = get_sr_protocol_phase(the_time); + tt_int_op(phase, ==, SR_PHASE_REVEAL); + } + + done: + ; +} + +static networkstatus_t *mock_consensus = NULL; + +static void +test_get_state_valid_until_time(void *arg) +{ + time_t current_time; + time_t valid_until_time; + char tbuf[ISO_TIME_LEN + 1]; + int retval; + + (void) arg; + + { + /* Get the valid until time if called at 00:00:01 */ + retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC", + ¤t_time); + tt_int_op(retval, ==, 0); + valid_until_time = get_state_valid_until_time(current_time); + + /* Compare it with the correct result */ + format_iso_time(tbuf, valid_until_time); + tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf); + } + + { + retval = parse_rfc1123_time("Mon, 20 Apr 2015 19:22:00 UTC", + ¤t_time); + tt_int_op(retval, ==, 0); + valid_until_time = get_state_valid_until_time(current_time); + + format_iso_time(tbuf, valid_until_time); + tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf); + } + + { + retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:00 UTC", + ¤t_time); + tt_int_op(retval, ==, 0); + valid_until_time = get_state_valid_until_time(current_time); + + format_iso_time(tbuf, valid_until_time); + tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf); + } + + { + retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC", + ¤t_time); + tt_int_op(retval, ==, 0); + valid_until_time = get_state_valid_until_time(current_time); + + format_iso_time(tbuf, valid_until_time); + tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf); + } + + done: + ; +} + +/* Mock function to immediately return our local 'mock_consensus'. */ +static networkstatus_t * +mock_networkstatus_get_live_consensus(time_t now) +{ + (void) now; + return mock_consensus; +} + +/** Test the get_next_valid_after_time() function. */ +static void +test_get_next_valid_after_time(void *arg) +{ + time_t current_time; + time_t valid_after_time; + char tbuf[ISO_TIME_LEN + 1]; + int retval; + + (void) arg; + + { + /* Setup a fake consensus just to get the times out of it, since + get_next_valid_after_time() needs them. */ + mock_consensus = tor_malloc_zero(sizeof(networkstatus_t)); + + retval = parse_rfc1123_time("Mon, 13 Jan 2016 16:00:00 UTC", + &mock_consensus->fresh_until); + tt_int_op(retval, ==, 0); + + retval = parse_rfc1123_time("Mon, 13 Jan 2016 15:00:00 UTC", + &mock_consensus->valid_after); + tt_int_op(retval, ==, 0); + + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + } + + { + /* Get the valid after time if called at 00:00:00 */ + retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC", + ¤t_time); + tt_int_op(retval, ==, 0); + valid_after_time = get_next_valid_after_time(current_time); + + /* Compare it with the correct result */ + format_iso_time(tbuf, valid_after_time); + tt_str_op("2015-04-20 01:00:00", OP_EQ, tbuf); + } + + { + /* Get the valid until time if called at 00:00:01 */ + retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC", + ¤t_time); + tt_int_op(retval, ==, 0); + valid_after_time = get_next_valid_after_time(current_time); + + /* Compare it with the correct result */ + format_iso_time(tbuf, valid_after_time); + tt_str_op("2015-04-20 01:00:00", OP_EQ, tbuf); + } + + { + retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:30:01 UTC", + ¤t_time); + tt_int_op(retval, ==, 0); + valid_after_time = get_next_valid_after_time(current_time); + + /* Compare it with the correct result */ + format_iso_time(tbuf, valid_after_time); + tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf); + } + + done: + networkstatus_vote_free(mock_consensus); +} + +/* In this test we are going to generate a sr_commit_t object and validate + * it. We first generate our values, and then we parse them as if they were + * received from the network. After we parse both the commit and the reveal, + * we verify that they indeed match. */ +static void +test_sr_commit(void *arg) +{ + authority_cert_t *auth_cert = NULL; + time_t now = time(NULL); + sr_commit_t *our_commit = NULL; + smartlist_t *args = smartlist_new(); + sr_commit_t *parsed_commit = NULL; + + (void) arg; + + { /* Setup a minimal dirauth environment for this test */ + or_options_t *options = get_options_mutable(); + + auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); + tt_assert(auth_cert); + + options->AuthoritativeDir = 1; + tt_int_op(0, ==, load_ed_keys(options, now)); + } + + /* Generate our commit object and validate it has the appropriate field + * that we can then use to build a representation that we'll find in a + * vote coming from the network. */ + { + sr_commit_t test_commit; + our_commit = sr_generate_our_commit(now, auth_cert); + tt_assert(our_commit); + /* Default and only supported algorithm for now. */ + tt_assert(our_commit->alg == DIGEST_SHA3_256); + /* We should have a reveal value. */ + tt_assert(commit_has_reveal_value(our_commit)); + /* We should have a random value. */ + tt_assert(!tor_mem_is_zero((char *) our_commit->random_number, + sizeof(our_commit->random_number))); + /* Commit and reveal timestamp should be the same. */ + tt_u64_op(our_commit->commit_ts, ==, our_commit->reveal_ts); + /* We should have a hashed reveal. */ + tt_assert(!tor_mem_is_zero(our_commit->hashed_reveal, + sizeof(our_commit->hashed_reveal))); + /* Do we have a valid encoded commit and reveal. Note the following only + * tests if the generated values are correct. Their could be a bug in + * the decode function but we test them seperately. */ + tt_int_op(0, ==, reveal_decode(our_commit->encoded_reveal, + &test_commit)); + tt_int_op(0, ==, commit_decode(our_commit->encoded_commit, + &test_commit)); + tt_int_op(0, ==, verify_commit_and_reveal(our_commit)); + } + + /* Let's make sure our verify commit and reveal function works. We'll + * make it fail a bit with known failure case. */ + { + /* Copy our commit so we don't alter it for the rest of testing. */ + sr_commit_t test_commit; + memcpy(&test_commit, our_commit, sizeof(test_commit)); + + /* Timestamp MUST match. */ + test_commit.commit_ts = test_commit.reveal_ts - 42; + setup_full_capture_of_logs(LOG_WARN); + tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit)); + expect_log_msg_containing("doesn't match reveal timestamp"); + teardown_capture_of_logs(); + memcpy(&test_commit, our_commit, sizeof(test_commit)); + tt_int_op(0, ==, verify_commit_and_reveal(&test_commit)); + + /* Hashed reveal must match the H(encoded_reveal). */ + memset(test_commit.hashed_reveal, 'X', + sizeof(test_commit.hashed_reveal)); + setup_full_capture_of_logs(LOG_WARN); + tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit)); + expect_single_log_msg_containing("doesn't match the commit value"); + teardown_capture_of_logs(); + memcpy(&test_commit, our_commit, sizeof(test_commit)); + tt_int_op(0, ==, verify_commit_and_reveal(&test_commit)); + } + + /* 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)); + parsed_commit = sr_parse_commit(args); + tt_assert(parsed_commit); + /* That parsed commit should be _EXACTLY_ like our original commit (we + * have to explicitly set the valid flag though). */ + parsed_commit->valid = 1; + tt_mem_op(parsed_commit, OP_EQ, our_commit, sizeof(*parsed_commit)); + /* Cleanup */ + } + + done: + teardown_capture_of_logs(); + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); + smartlist_free(args); + sr_commit_free(our_commit); + sr_commit_free(parsed_commit); + authority_cert_free(auth_cert); +} + +/* Test the encoding and decoding function for commit and reveal values. */ +static void +test_encoding(void *arg) +{ + (void) arg; + int ret; + /* Random number is 32 bytes. */ + char raw_rand[32]; + time_t ts = 1454333590; + char hashed_rand[DIGEST256_LEN], hashed_reveal[DIGEST256_LEN]; + sr_commit_t parsed_commit; + + /* Those values were generated by sr_commit_calc_ref.py where the random + * value is 32 'A' and timestamp is the one in ts. */ + static const char *encoded_reveal = + "AAAAAFavXpZJxbwTupvaJCTeIUCQmOPxAMblc7ChL5H2nZKuGchdaA=="; + static const char *encoded_commit = + "AAAAAFavXpbkBMzMQG7aNoaGLFNpm2Wkk1ozXhuWWqL//GynltxVAg=="; + + /* Set up our raw random bytes array. */ + memset(raw_rand, 'A', sizeof(raw_rand)); + /* Hash random number because we don't expose bytes of the RNG. */ + ret = crypto_digest256(hashed_rand, raw_rand, + sizeof(raw_rand), SR_DIGEST_ALG); + tt_int_op(0, ==, ret); + /* Hash reveal value. */ + tt_int_op(SR_REVEAL_BASE64_LEN, ==, strlen(encoded_reveal)); + ret = crypto_digest256(hashed_reveal, encoded_reveal, + strlen(encoded_reveal), SR_DIGEST_ALG); + tt_int_op(0, ==, ret); + tt_int_op(SR_COMMIT_BASE64_LEN, ==, strlen(encoded_commit)); + + /* Test our commit/reveal decode functions. */ + { + /* Test the reveal encoded value. */ + tt_int_op(0, ==, reveal_decode(encoded_reveal, &parsed_commit)); + tt_u64_op(ts, ==, parsed_commit.reveal_ts); + tt_mem_op(hashed_rand, OP_EQ, parsed_commit.random_number, + sizeof(hashed_rand)); + + /* Test the commit encoded value. */ + memset(&parsed_commit, 0, sizeof(parsed_commit)); + tt_int_op(0, ==, commit_decode(encoded_commit, &parsed_commit)); + tt_u64_op(ts, ==, parsed_commit.commit_ts); + tt_mem_op(encoded_commit, OP_EQ, parsed_commit.encoded_commit, + sizeof(parsed_commit.encoded_commit)); + tt_mem_op(hashed_reveal, OP_EQ, parsed_commit.hashed_reveal, + sizeof(hashed_reveal)); + } + + /* Test our commit/reveal encode functions. */ + { + /* Test the reveal encode. */ + char encoded[SR_REVEAL_BASE64_LEN + 1]; + parsed_commit.reveal_ts = ts; + memcpy(parsed_commit.random_number, hashed_rand, + sizeof(parsed_commit.random_number)); + ret = reveal_encode(&parsed_commit, encoded, sizeof(encoded)); + tt_int_op(SR_REVEAL_BASE64_LEN, ==, ret); + tt_mem_op(encoded_reveal, OP_EQ, encoded, strlen(encoded_reveal)); + } + + { + /* Test the commit encode. */ + char encoded[SR_COMMIT_BASE64_LEN + 1]; + parsed_commit.commit_ts = ts; + memcpy(parsed_commit.hashed_reveal, hashed_reveal, + sizeof(parsed_commit.hashed_reveal)); + ret = commit_encode(&parsed_commit, encoded, sizeof(encoded)); + tt_int_op(SR_COMMIT_BASE64_LEN, ==, ret); + tt_mem_op(encoded_commit, OP_EQ, encoded, strlen(encoded_commit)); + } + + done: + ; +} + +/** Setup some SRVs in our SR state. If <b>also_current</b> is set, then set + * both current and previous SRVs. + * Helper of test_vote() and test_sr_compute_srv(). */ +static void +test_sr_setup_srv(int also_current) +{ + sr_srv_t *srv = tor_malloc_zero(sizeof(sr_srv_t)); + srv->num_reveals = 42; + memcpy(srv->value, + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", + sizeof(srv->value)); + + sr_state_set_previous_srv(srv); + + if (also_current) { + srv = tor_malloc_zero(sizeof(sr_srv_t)); + srv->num_reveals = 128; + memcpy(srv->value, + "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", + sizeof(srv->value)); + + sr_state_set_current_srv(srv); + } +} + +/* Test anything that has to do with SR protocol and vote. */ +static void +test_vote(void *arg) +{ + int ret; + time_t now = time(NULL); + sr_commit_t *our_commit = NULL; + + (void) arg; + + MOCK(trusteddirserver_get_by_v3_auth_digest, + trusteddirserver_get_by_v3_auth_digest_m); + + { /* Setup a minimal dirauth environment for this test */ + init_authority_state(); + /* Set ourself in reveal phase so we can parse the reveal value in the + * vote as well. */ + set_sr_phase(SR_PHASE_REVEAL); + } + + /* Generate our commit object and validate it has the appropriate field + * that we can then use to build a representation that we'll find in a + * vote coming from the network. */ + { + sr_commit_t *saved_commit; + our_commit = sr_generate_our_commit(now, mock_cert); + tt_assert(our_commit); + sr_state_add_commit(our_commit); + /* Make sure it's there. */ + saved_commit = sr_state_get_commit(our_commit->rsa_identity); + tt_assert(saved_commit); + } + + /* Also setup the SRVs */ + test_sr_setup_srv(1); + + { /* Now test the vote generation */ + smartlist_t *chunks = smartlist_new(); + smartlist_t *tokens = smartlist_new(); + /* Get our vote line and validate it. */ + char *lines = sr_get_string_for_vote(); + tt_assert(lines); + /* Split the lines. We expect 2 here. */ + ret = smartlist_split_string(chunks, lines, "\n", SPLIT_IGNORE_BLANK, 0); + tt_int_op(ret, ==, 4); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "shared-rand-participate"); + /* Get our commitment line and will validate it agains our commit. The + * format is as follow: + * "shared-rand-commitment" SP version SP algname SP identity + * SP COMMIT [SP REVEAL] NL + */ + char *commit_line = smartlist_get(chunks, 1); + tt_assert(commit_line); + ret = smartlist_split_string(tokens, commit_line, " ", 0, 0); + tt_int_op(ret, ==, 6); + tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-commit"); + tt_str_op(smartlist_get(tokens, 1), OP_EQ, "1"); + tt_str_op(smartlist_get(tokens, 2), OP_EQ, + crypto_digest_algorithm_get_name(DIGEST_SHA3_256)); + char digest[DIGEST_LEN]; + base16_decode(digest, sizeof(digest), smartlist_get(tokens, 3), + HEX_DIGEST_LEN); + tt_mem_op(digest, ==, our_commit->rsa_identity, sizeof(digest)); + tt_str_op(smartlist_get(tokens, 4), OP_EQ, our_commit->encoded_commit); + tt_str_op(smartlist_get(tokens, 5), OP_EQ, our_commit->encoded_reveal) +; + /* Finally, does this vote line creates a valid commit object? */ + smartlist_t *args = smartlist_new(); + smartlist_add(args, smartlist_get(tokens, 1)); + smartlist_add(args, smartlist_get(tokens, 2)); + smartlist_add(args, smartlist_get(tokens, 3)); + smartlist_add(args, smartlist_get(tokens, 4)); + smartlist_add(args, smartlist_get(tokens, 5)); + sr_commit_t *parsed_commit = sr_parse_commit(args); + tt_assert(parsed_commit); + /* Set valid flag explicitly here to compare since it's not set by + * simply parsing the commit. */ + parsed_commit->valid = 1; + tt_mem_op(parsed_commit, ==, our_commit, sizeof(*our_commit)); + + /* minor cleanup */ + SMARTLIST_FOREACH(tokens, char *, s, tor_free(s)); + smartlist_clear(tokens); + + /* Now test the previous SRV */ + char *prev_srv_line = smartlist_get(chunks, 2); + tt_assert(prev_srv_line); + ret = smartlist_split_string(tokens, prev_srv_line, " ", 0, 0); + tt_int_op(ret, ==, 3); + tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-previous-value"); + tt_str_op(smartlist_get(tokens, 1), OP_EQ, "42"); + tt_str_op(smartlist_get(tokens, 2), OP_EQ, + "WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlo="); + + /* minor cleanup */ + SMARTLIST_FOREACH(tokens, char *, s, tor_free(s)); + smartlist_clear(tokens); + + /* Now test the current SRV */ + char *current_srv_line = smartlist_get(chunks, 3); + tt_assert(current_srv_line); + ret = smartlist_split_string(tokens, current_srv_line, " ", 0, 0); + tt_int_op(ret, ==, 3); + tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-current-value"); + tt_str_op(smartlist_get(tokens, 1), OP_EQ, "128"); + tt_str_op(smartlist_get(tokens, 2), OP_EQ, + "Tk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk4="); + + /* Clean up */ + sr_commit_free(parsed_commit); + SMARTLIST_FOREACH(chunks, char *, s, tor_free(s)); + smartlist_free(chunks); + SMARTLIST_FOREACH(tokens, char *, s, tor_free(s)); + smartlist_free(tokens); + smartlist_clear(args); + smartlist_free(args); + tor_free(lines); + } + + done: + sr_commit_free(our_commit); + UNMOCK(trusteddirserver_get_by_v3_auth_digest); +} + +static const char *sr_state_str = "Version 1\n" + "TorVersion 0.2.9.0-alpha-dev\n" + "ValidAfter 2037-04-19 07:16:00\n" + "ValidUntil 2037-04-20 07:16:00\n" + "Commit 1 sha3-256 FA3CEC2C99DC68D3166B9B6E4FA21A4026C2AB1C " + "7M8GdubCAAdh7WUG0DiwRyxTYRKji7HATa7LLJEZ/UAAAAAAVmfUSg== " + "AAAAAFZn1EojfIheIw42bjK3VqkpYyjsQFSbv/dxNna3Q8hUEPKpOw==\n" + "Commit 1 sha3-256 41E89EDFBFBA44983E21F18F2230A4ECB5BFB543 " + "17aUsYuMeRjd2N1r8yNyg7aHqRa6gf4z7QPoxxAZbp0AAAAAVmfUSg==\n" + "Commit 1 sha3-256 36637026573A04110CF3E6B1D201FB9A98B88734 " + "DDDYtripvdOU+XPEUm5xpU64d9IURSds1xSwQsgeB8oAAAAAVmfUSg==\n" + "SharedRandPreviousValue 4 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=\n" + "SharedRandCurrentValue 3 8dWeW12KEzTGEiLGgO1UVJ7Z91CekoRcxt6Q9KhnOFI=\n"; + +/** Create an SR disk state, parse it and validate that the parsing went + * well. Yes! */ +static void +test_state_load_from_disk(void *arg) +{ + int ret; + char *dir = tor_strdup(get_fname("test_sr_state")); + char *sr_state_path = tor_strdup(get_fname("test_sr_state/sr_state")); + sr_state_t *the_sr_state = NULL; + + (void) arg; + + MOCK(trusteddirserver_get_by_v3_auth_digest, + trusteddirserver_get_by_v3_auth_digest_m); + + /* First try with a nonexistent path. */ + ret = disk_state_load_from_disk_impl("NONEXISTENTNONEXISTENT"); + tt_assert(ret == -ENOENT); + + /* Now create a mock state directory and state file */ +#ifdef _WIN32 + ret = mkdir(dir); +#else + ret = mkdir(dir, 0700); +#endif + tt_assert(ret == 0); + ret = write_str_to_file(sr_state_path, sr_state_str, 0); + tt_assert(ret == 0); + + /* Try to load the directory itself. Should fail. */ + ret = disk_state_load_from_disk_impl(dir); + tt_int_op(ret, OP_LT, 0); + + /* State should be non-existent at this point. */ + the_sr_state = get_sr_state(); + tt_assert(!the_sr_state); + + /* Now try to load the correct file! */ + ret = disk_state_load_from_disk_impl(sr_state_path); + tt_assert(ret == 0); + + /* Check the content of the state */ + /* XXX check more deeply!!! */ + the_sr_state = get_sr_state(); + tt_assert(the_sr_state); + tt_assert(the_sr_state->version == 1); + tt_assert(digestmap_size(the_sr_state->commits) == 3); + tt_assert(the_sr_state->current_srv); + tt_assert(the_sr_state->current_srv->num_reveals == 3); + tt_assert(the_sr_state->previous_srv); + + /* XXX Now also try loading corrupted state files and make sure parsing + fails */ + + done: + tor_free(dir); + tor_free(sr_state_path); + UNMOCK(trusteddirserver_get_by_v3_auth_digest); +} + +/** Generate three specially crafted commits (based on the test + * vector at sr_srv_calc_ref.py). Helper of test_sr_compute_srv(). */ +static void +test_sr_setup_commits(void) +{ + time_t now = time(NULL); + sr_commit_t *commit_a, *commit_b, *commit_c, *commit_d; + sr_commit_t *place_holder = tor_malloc_zero(sizeof(*place_holder)); + authority_cert_t *auth_cert = NULL; + + { /* Setup a minimal dirauth environment for this test */ + or_options_t *options = get_options_mutable(); + + auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); + tt_assert(auth_cert); + + options->AuthoritativeDir = 1; + tt_int_op(0, ==, load_ed_keys(options, now)); + } + + /* Generate three dummy commits according to sr_srv_calc_ref.py . Then + register them to the SR state. Also register a fourth commit 'd' with no + reveal info, to make sure that it will get ignored during SRV + calculation. */ + + { /* Commit from auth 'a' */ + commit_a = sr_generate_our_commit(now, auth_cert); + tt_assert(commit_a); + + /* Do some surgery on the commit */ + memset(commit_a->rsa_identity, 'A', sizeof(commit_a->rsa_identity)); + base16_encode(commit_a->rsa_identity_hex, + sizeof(commit_a->rsa_identity_hex), commit_a->rsa_identity, + sizeof(commit_a->rsa_identity)); + strlcpy(commit_a->encoded_reveal, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + sizeof(commit_a->encoded_reveal)); + memcpy(commit_a->hashed_reveal, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + sizeof(commit_a->hashed_reveal)); + } + + { /* Commit from auth 'b' */ + commit_b = sr_generate_our_commit(now, auth_cert); + tt_assert(commit_b); + + /* Do some surgery on the commit */ + memset(commit_b->rsa_identity, 'B', sizeof(commit_b->rsa_identity)); + base16_encode(commit_b->rsa_identity_hex, + sizeof(commit_b->rsa_identity_hex), commit_b->rsa_identity, + sizeof(commit_b->rsa_identity)); + strlcpy(commit_b->encoded_reveal, + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", + sizeof(commit_b->encoded_reveal)); + memcpy(commit_b->hashed_reveal, + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", + sizeof(commit_b->hashed_reveal)); + } + + { /* Commit from auth 'c' */ + commit_c = sr_generate_our_commit(now, auth_cert); + tt_assert(commit_c); + + /* Do some surgery on the commit */ + memset(commit_c->rsa_identity, 'C', sizeof(commit_c->rsa_identity)); + base16_encode(commit_c->rsa_identity_hex, + sizeof(commit_c->rsa_identity_hex), commit_c->rsa_identity, + sizeof(commit_c->rsa_identity)); + strlcpy(commit_c->encoded_reveal, + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + sizeof(commit_c->encoded_reveal)); + memcpy(commit_c->hashed_reveal, + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + sizeof(commit_c->hashed_reveal)); + } + + { /* Commit from auth 'd' */ + commit_d = sr_generate_our_commit(now, auth_cert); + tt_assert(commit_d); + + /* Do some surgery on the commit */ + memset(commit_d->rsa_identity, 'D', sizeof(commit_d->rsa_identity)); + base16_encode(commit_d->rsa_identity_hex, + sizeof(commit_d->rsa_identity_hex), commit_d->rsa_identity, + sizeof(commit_d->rsa_identity)); + strlcpy(commit_d->encoded_reveal, + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD", + sizeof(commit_d->encoded_reveal)); + memcpy(commit_d->hashed_reveal, + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD", + sizeof(commit_d->hashed_reveal)); + /* Clean up its reveal info */ + memcpy(place_holder, commit_d, sizeof(*place_holder)); + memset(commit_d->encoded_reveal, 0, sizeof(commit_d->encoded_reveal)); + tt_assert(!commit_has_reveal_value(commit_d)); + } + + /* Register commits to state (during commit phase) */ + set_sr_phase(SR_PHASE_COMMIT); + save_commit_to_state(commit_a); + save_commit_to_state(commit_b); + save_commit_to_state(commit_c); + save_commit_to_state(commit_d); + tt_int_op(digestmap_size(get_sr_state()->commits), ==, 4); + + /* Now during REVEAL phase save commit D by restoring its reveal. */ + set_sr_phase(SR_PHASE_REVEAL); + save_commit_to_state(place_holder); + tt_str_op(commit_d->encoded_reveal, OP_EQ, + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"); + /* Go back to an empty encoded reveal value. */ + memset(commit_d->encoded_reveal, 0, sizeof(commit_d->encoded_reveal)); + memset(commit_d->random_number, 0, sizeof(commit_d->random_number)); + tt_assert(!commit_has_reveal_value(commit_d)); + + done: + authority_cert_free(auth_cert); +} + +/** Verify that the SRV generation procedure is proper by testing it against + * the test vector from ./sr_srv_calc_ref.py. */ +static void +test_sr_compute_srv(void *arg) +{ + (void) arg; + const sr_srv_t *current_srv = NULL; + +#define SRV_TEST_VECTOR \ + "2A9B1D6237DAB312A40F575DA85C147663E7ED3F80E9555395F15B515C74253D" + + MOCK(trusteddirserver_get_by_v3_auth_digest, + trusteddirserver_get_by_v3_auth_digest_m); + + init_authority_state(); + + /* Setup the commits for this unittest */ + test_sr_setup_commits(); + test_sr_setup_srv(0); + + /* Now switch to reveal phase */ + set_sr_phase(SR_PHASE_REVEAL); + + /* Compute the SRV */ + sr_compute_srv(); + + /* Check the result against the test vector */ + current_srv = sr_state_get_current_srv(); + tt_assert(current_srv); + tt_u64_op(current_srv->num_reveals, ==, 3); + tt_str_op(hex_str((char*)current_srv->value, 32), + ==, + SRV_TEST_VECTOR); + + done: + UNMOCK(trusteddirserver_get_by_v3_auth_digest); +} + +/** Return a minimal vote document with a current SRV value set to + * <b>srv</b>. */ +static networkstatus_t * +get_test_vote_with_curr_srv(const char *srv) +{ + networkstatus_t *vote = tor_malloc_zero(sizeof(networkstatus_t)); + + vote->type = NS_TYPE_VOTE; + vote->sr_info.participate = 1; + vote->sr_info.current_srv = tor_malloc_zero(sizeof(sr_srv_t)); + vote->sr_info.current_srv->num_reveals = 42; + memcpy(vote->sr_info.current_srv->value, + srv, + sizeof(vote->sr_info.current_srv->value)); + + return vote; +} + +/* Test the function that picks the right SRV given a bunch of votes. Make sure + * that the function returns an SRV iff the majority/agreement requirements are + * met. */ +static void +test_sr_get_majority_srv_from_votes(void *arg) +{ + sr_srv_t *chosen_srv; + smartlist_t *votes = smartlist_new(); + +#define SRV_1 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +#define SRV_2 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + + (void) arg; + + init_authority_state(); + /* Make sure our SRV is fresh so we can consider the super majority with + * the consensus params of number of agreements needed. */ + sr_state_set_fresh_srv(); + + /* The test relies on the dirauth list being initialized. */ + clear_dir_servers(); + add_default_trusted_dir_authorities(V3_DIRINFO); + + { /* Prepare voting environment with just a single vote. */ + networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1); + smartlist_add(votes, vote); + } + + /* Since it's only one vote with an SRV, it should not achieve majority and + hence no SRV will be returned. */ + chosen_srv = get_majority_srv_from_votes(votes, 1); + tt_assert(!chosen_srv); + + { /* Now put in 8 more votes. Let SRV_1 have majority. */ + int i; + /* Now 7 votes believe in SRV_1 */ + for (i = 0; i < 3; i++) { + networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1); + smartlist_add(votes, vote); + } + /* and 2 votes believe in SRV_2 */ + for (i = 0; i < 2; i++) { + networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_2); + smartlist_add(votes, vote); + } + for (i = 0; i < 3; i++) { + networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1); + smartlist_add(votes, vote); + } + + tt_int_op(smartlist_len(votes), ==, 9); + } + + /* Now we achieve majority for SRV_1, but not the AuthDirNumSRVAgreements + requirement. So still not picking an SRV. */ + set_num_srv_agreements(8); + chosen_srv = get_majority_srv_from_votes(votes, 1); + tt_assert(!chosen_srv); + + /* We will now lower the AuthDirNumSRVAgreements requirement by tweaking the + * consensus parameter and we will try again. This time it should work. */ + set_num_srv_agreements(7); + chosen_srv = get_majority_srv_from_votes(votes, 1); + tt_assert(chosen_srv); + tt_u64_op(chosen_srv->num_reveals, ==, 42); + tt_mem_op(chosen_srv->value, OP_EQ, SRV_1, sizeof(chosen_srv->value)); + + done: + SMARTLIST_FOREACH(votes, networkstatus_t *, vote, + networkstatus_vote_free(vote)); + smartlist_free(votes); +} + +static void +test_utils(void *arg) +{ + (void) arg; + + /* Testing srv_dup(). */ + { + sr_srv_t *srv = NULL, *dup_srv = NULL; + const char *srv_value = + "1BDB7C3E973936E4D13A49F37C859B3DC69C429334CF9412E3FEF6399C52D47A"; + srv = tor_malloc_zero(sizeof(*srv)); + srv->num_reveals = 42; + memcpy(srv->value, srv_value, sizeof(srv->value)); + dup_srv = srv_dup(srv); + tt_assert(dup_srv); + tt_u64_op(dup_srv->num_reveals, ==, srv->num_reveals); + tt_mem_op(dup_srv->value, OP_EQ, srv->value, sizeof(srv->value)); + tor_free(srv); + tor_free(dup_srv); + } + + /* Testing commitments_are_the_same(). Currently, the check is to test the + * value of the encoded commit so let's make sure that actually works. */ + { + /* Payload of 57 bytes that is the length of sr_commit_t->encoded_commit. + * 56 bytes of payload and a NUL terminated byte at the end ('\x00') + * which comes down to SR_COMMIT_BASE64_LEN + 1. */ + const char *payload = + "\x5d\xb9\x60\xb6\xcc\x51\x68\x52\x31\xd9\x88\x88\x71\x71\xe0\x30" + "\x59\x55\x7f\xcd\x61\xc0\x4b\x05\xb8\xcd\xc1\x48\xe9\xcd\x16\x1f" + "\x70\x15\x0c\xfc\xd3\x1a\x75\xd0\x93\x6c\xc4\xe0\x5c\xbe\xe2\x18" + "\xc7\xaf\x72\xb6\x7c\x9b\x52\x00"; + sr_commit_t commit1, commit2; + memcpy(commit1.encoded_commit, payload, sizeof(commit1.encoded_commit)); + memcpy(commit2.encoded_commit, payload, sizeof(commit2.encoded_commit)); + tt_int_op(commitments_are_the_same(&commit1, &commit2), ==, 1); + /* Let's corrupt one of them. */ + memset(commit1.encoded_commit, 'A', sizeof(commit1.encoded_commit)); + tt_int_op(commitments_are_the_same(&commit1, &commit2), ==, 0); + } + + /* Testing commit_is_authoritative(). */ + { + crypto_pk_t *k = crypto_pk_new(); + char digest[DIGEST_LEN]; + sr_commit_t commit; + + tt_assert(!crypto_pk_generate_key(k)); + + tt_int_op(0, ==, crypto_pk_get_digest(k, digest)); + memcpy(commit.rsa_identity, digest, sizeof(commit.rsa_identity)); + tt_int_op(commit_is_authoritative(&commit, digest), ==, 1); + /* Change the pubkey. */ + memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity)); + tt_int_op(commit_is_authoritative(&commit, digest), ==, 0); + crypto_pk_free(k); + } + + /* Testing get_phase_str(). */ + { + tt_str_op(get_phase_str(SR_PHASE_REVEAL), ==, "reveal"); + tt_str_op(get_phase_str(SR_PHASE_COMMIT), ==, "commit"); + } + + /* Testing phase transition */ + { + init_authority_state(); + set_sr_phase(SR_PHASE_COMMIT); + tt_int_op(is_phase_transition(SR_PHASE_REVEAL), ==, 1); + tt_int_op(is_phase_transition(SR_PHASE_COMMIT), ==, 0); + set_sr_phase(SR_PHASE_REVEAL); + tt_int_op(is_phase_transition(SR_PHASE_REVEAL), ==, 0); + tt_int_op(is_phase_transition(SR_PHASE_COMMIT), ==, 1); + /* Junk. */ + tt_int_op(is_phase_transition(42), ==, 1); + } + + done: + return; +} + +static void +test_state_transition(void *arg) +{ + sr_state_t *state = NULL; + time_t now = time(NULL); + + (void) arg; + + { /* Setup a minimal dirauth environment for this test */ + init_authority_state(); + state = get_sr_state(); + tt_assert(state); + } + + /* Test our state reset for a new protocol run. */ + { + /* Add a commit to the state so we can test if the reset cleans the + * commits. Also, change all params that we expect to be updated. */ + sr_commit_t *commit = sr_generate_our_commit(now, mock_cert); + tt_assert(commit); + sr_state_add_commit(commit); + tt_int_op(digestmap_size(state->commits), ==, 1); + /* Let's test our delete feature. */ + sr_state_delete_commits(); + tt_int_op(digestmap_size(state->commits), ==, 0); + /* Add it back so we can continue the rest of the test because after + * deletiong our commit will be freed so generate a new one. */ + commit = sr_generate_our_commit(now, mock_cert); + tt_assert(commit); + sr_state_add_commit(commit); + tt_int_op(digestmap_size(state->commits), ==, 1); + state->n_reveal_rounds = 42; + state->n_commit_rounds = 43; + state->n_protocol_runs = 44; + reset_state_for_new_protocol_run(now); + tt_int_op(state->n_reveal_rounds, ==, 0); + tt_int_op(state->n_commit_rounds, ==, 0); + tt_u64_op(state->n_protocol_runs, ==, 45); + tt_int_op(digestmap_size(state->commits), ==, 0); + } + + /* Test SRV rotation in our state. */ + { + const sr_srv_t *cur, *prev; + test_sr_setup_srv(1); + cur = sr_state_get_current_srv(); + tt_assert(cur); + /* After, current srv should be the previous and then set to NULL. */ + state_rotate_srv(); + prev = sr_state_get_previous_srv(); + tt_assert(prev == cur); + tt_assert(!sr_state_get_current_srv()); + sr_state_clean_srvs(); + } + + /* New protocol run. */ + { + const sr_srv_t *cur; + /* Setup some new SRVs so we can confirm that a new protocol run + * actually makes them rotate and compute new ones. */ + test_sr_setup_srv(1); + cur = sr_state_get_current_srv(); + tt_assert(cur); + set_sr_phase(SR_PHASE_REVEAL); + MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); + new_protocol_run(now); + UNMOCK(get_my_v3_authority_cert); + /* Rotation happened. */ + tt_assert(sr_state_get_previous_srv() == cur); + /* We are going into COMMIT phase so we had to rotate our SRVs. Usually + * our current SRV would be NULL but a new protocol run should make us + * compute a new SRV. */ + tt_assert(sr_state_get_current_srv()); + /* Also, make sure we did change the current. */ + tt_assert(sr_state_get_current_srv() != cur); + /* We should have our commitment alone. */ + tt_int_op(digestmap_size(state->commits), ==, 1); + tt_int_op(state->n_reveal_rounds, ==, 0); + tt_int_op(state->n_commit_rounds, ==, 0); + /* 46 here since we were at 45 just before. */ + tt_u64_op(state->n_protocol_runs, ==, 46); + } + + /* Cleanup of SRVs. */ + { + sr_state_clean_srvs(); + tt_assert(!sr_state_get_current_srv()); + tt_assert(!sr_state_get_previous_srv()); + } + + done: + return; +} + +static void +test_keep_commit(void *arg) +{ + char fp[FINGERPRINT_LEN + 1]; + sr_commit_t *commit = NULL, *dup_commit = NULL; + sr_state_t *state; + time_t now = time(NULL); + crypto_pk_t *k = NULL; + + (void) arg; + + MOCK(trusteddirserver_get_by_v3_auth_digest, + trusteddirserver_get_by_v3_auth_digest_m); + + { + k = pk_generate(1); + /* Setup a minimal dirauth environment for this test */ + /* Have a key that is not the one from our commit. */ + init_authority_state(); + state = get_sr_state(); + } + + /* Test this very important function that tells us if we should keep a + * commit or not in our state. Most of it depends on the phase and what's + * in the commit so we'll change the commit as we go. */ + commit = sr_generate_our_commit(now, mock_cert); + tt_assert(commit); + /* Set us in COMMIT phase for starter. */ + set_sr_phase(SR_PHASE_COMMIT); + /* We should never keep a commit from a non authoritative authority. */ + tt_int_op(should_keep_commit(commit, fp, SR_PHASE_COMMIT), ==, 0); + /* This should NOT be kept because it has a reveal value in it. */ + tt_assert(commit_has_reveal_value(commit)); + tt_int_op(should_keep_commit(commit, commit->rsa_identity, + SR_PHASE_COMMIT), ==, 0); + /* Add it to the state which should return to not keep it. */ + sr_state_add_commit(commit); + tt_int_op(should_keep_commit(commit, commit->rsa_identity, + SR_PHASE_COMMIT), ==, 0); + /* Remove it from state so we can continue our testing. */ + digestmap_remove(state->commits, commit->rsa_identity); + /* Let's remove our reveal value which should make it OK to keep it. */ + memset(commit->encoded_reveal, 0, sizeof(commit->encoded_reveal)); + tt_int_op(should_keep_commit(commit, commit->rsa_identity, + SR_PHASE_COMMIT), ==, 1); + + /* Let's reset our commit and go into REVEAL phase. */ + sr_commit_free(commit); + commit = sr_generate_our_commit(now, mock_cert); + tt_assert(commit); + /* Dup the commit so we have one with and one without a reveal value. */ + dup_commit = tor_malloc_zero(sizeof(*dup_commit)); + memcpy(dup_commit, commit, sizeof(*dup_commit)); + memset(dup_commit->encoded_reveal, 0, sizeof(dup_commit->encoded_reveal)); + set_sr_phase(SR_PHASE_REVEAL); + /* We should never keep a commit from a non authoritative authority. */ + tt_int_op(should_keep_commit(commit, fp, SR_PHASE_REVEAL), ==, 0); + /* We shouldn't accept a commit that is not in our state. */ + tt_int_op(should_keep_commit(commit, commit->rsa_identity, + SR_PHASE_REVEAL), ==, 0); + /* Important to add the commit _without_ the reveal here. */ + sr_state_add_commit(dup_commit); + tt_int_op(digestmap_size(state->commits), ==, 1); + /* Our commit should be valid that is authoritative, contains a reveal, be + * in the state and commitment and reveal values match. */ + tt_int_op(should_keep_commit(commit, commit->rsa_identity, + SR_PHASE_REVEAL), ==, 1); + /* The commit shouldn't be kept if it's not verified that is no matchin + * hashed reveal. */ + { + /* Let's save the hash reveal so we can restore it. */ + sr_commit_t place_holder; + memcpy(place_holder.hashed_reveal, commit->hashed_reveal, + sizeof(place_holder.hashed_reveal)); + memset(commit->hashed_reveal, 0, sizeof(commit->hashed_reveal)); + setup_full_capture_of_logs(LOG_WARN); + tt_int_op(should_keep_commit(commit, commit->rsa_identity, + SR_PHASE_REVEAL), ==, 0); + expect_log_msg_containing("doesn't match the commit value."); + expect_log_msg_containing("has an invalid reveal value."); + assert_log_predicate(mock_saved_log_n_entries() == 2, + "expected 2 log entries"); + teardown_capture_of_logs(); + memcpy(commit->hashed_reveal, place_holder.hashed_reveal, + sizeof(commit->hashed_reveal)); + } + /* We shouldn't keep a commit that has no reveal. */ + tt_int_op(should_keep_commit(dup_commit, dup_commit->rsa_identity, + SR_PHASE_REVEAL), ==, 0); + /* We must not keep a commit that is not the same from the commit phase. */ + memset(commit->encoded_commit, 0, sizeof(commit->encoded_commit)); + tt_int_op(should_keep_commit(commit, commit->rsa_identity, + SR_PHASE_REVEAL), ==, 0); + + done: + teardown_capture_of_logs(); + sr_commit_free(commit); + sr_commit_free(dup_commit); + crypto_pk_free(k); + UNMOCK(trusteddirserver_get_by_v3_auth_digest); +} + +static void +test_state_update(void *arg) +{ + time_t commit_phase_time = 1452076000; + time_t reveal_phase_time = 1452086800; + sr_state_t *state; + + (void) arg; + + { + init_authority_state(); + state = get_sr_state(); + set_sr_phase(SR_PHASE_COMMIT); + /* We'll cheat a bit here and reset the creation time of the state which + * will avoid us to compute a valid_after time that fits the commit + * phase. */ + state->valid_after = 0; + state->n_reveal_rounds = 0; + state->n_commit_rounds = 0; + state->n_protocol_runs = 0; + } + + /* We need to mock for the state update function call. */ + MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); + + /* We are in COMMIT phase here and we'll trigger a state update but no + * transition. */ + sr_state_update(commit_phase_time); + tt_int_op(state->valid_after, ==, commit_phase_time); + tt_int_op(state->n_commit_rounds, ==, 1); + tt_int_op(state->phase, ==, SR_PHASE_COMMIT); + tt_int_op(digestmap_size(state->commits), ==, 1); + + /* We are still in the COMMIT phase here but we'll trigger a state + * transition to the REVEAL phase. */ + sr_state_update(reveal_phase_time); + tt_int_op(state->phase, ==, SR_PHASE_REVEAL); + tt_int_op(state->valid_after, ==, reveal_phase_time); + /* Only our commit should be in there. */ + tt_int_op(digestmap_size(state->commits), ==, 1); + tt_int_op(state->n_reveal_rounds, ==, 1); + + /* We can't update a state with a valid after _lower_ than the creation + * time so here it is. */ + sr_state_update(commit_phase_time); + tt_int_op(state->valid_after, ==, reveal_phase_time); + + /* Finally, let's go back in COMMIT phase so we can test the state update + * of a new protocol run. */ + state->valid_after = 0; + sr_state_update(commit_phase_time); + tt_int_op(state->valid_after, ==, commit_phase_time); + tt_int_op(state->n_commit_rounds, ==, 1); + tt_int_op(state->n_reveal_rounds, ==, 0); + tt_u64_op(state->n_protocol_runs, ==, 1); + tt_int_op(state->phase, ==, SR_PHASE_COMMIT); + tt_int_op(digestmap_size(state->commits), ==, 1); + tt_assert(state->current_srv); + + done: + sr_state_free(); + UNMOCK(get_my_v3_authority_cert); +} + +struct testcase_t sr_tests[] = { + { "get_sr_protocol_phase", test_get_sr_protocol_phase, TT_FORK, + NULL, NULL }, + { "sr_commit", test_sr_commit, TT_FORK, + NULL, NULL }, + { "keep_commit", test_keep_commit, TT_FORK, + NULL, NULL }, + { "encoding", test_encoding, TT_FORK, + NULL, NULL }, + { "get_next_valid_after_time", test_get_next_valid_after_time, TT_FORK, + NULL, NULL }, + { "get_state_valid_until_time", test_get_state_valid_until_time, TT_FORK, + NULL, NULL }, + { "vote", test_vote, TT_FORK, + NULL, NULL }, + { "state_load_from_disk", test_state_load_from_disk, TT_FORK, + NULL, NULL }, + { "sr_compute_srv", test_sr_compute_srv, TT_FORK, NULL, NULL }, + { "sr_get_majority_srv_from_votes", test_sr_get_majority_srv_from_votes, + TT_FORK, NULL, NULL }, + { "utils", test_utils, TT_FORK, NULL, NULL }, + { "state_transition", test_state_transition, TT_FORK, NULL, NULL }, + { "state_update", test_state_update, TT_FORK, + NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_slow.c b/src/test/test_slow.c index c1d2e81914..7c9f0b1cc2 100644 --- a/src/test/test_slow.c +++ b/src/test/test_slow.c @@ -18,9 +18,6 @@ #include "or.h" #include "test.h" -extern struct testcase_t slow_crypto_tests[]; -extern struct testcase_t slow_util_tests[]; - struct testgroup_t testgroups[] = { { "slow/crypto/", slow_crypto_tests }, { "slow/util/", slow_util_tests }, diff --git a/src/test/test_socks.c b/src/test/test_socks.c index 6da09fd653..62ff12fe15 100644 --- a/src/test/test_socks.c +++ b/src/test/test_socks.c @@ -34,7 +34,7 @@ socks_test_cleanup(const struct testcase_t *testcase, void *ptr) return 1; } -const struct testcase_setup_t socks_setup = { +static const struct testcase_setup_t socks_setup = { socks_test_setup, socks_test_cleanup }; diff --git a/src/test/test_status.c b/src/test/test_status.c index 84a0f6c024..a3b1a2af87 100644 --- a/src/test/test_status.c +++ b/src/test/test_status.c @@ -3,6 +3,8 @@ #define LOG_PRIVATE #define REPHIST_PRIVATE +#include "orconfig.h" + #include <float.h> #include <math.h> @@ -310,8 +312,6 @@ NS_DECL(void, logv, (int severity, log_domain_mask_t domain, NS_DECL(int, server_mode, (const or_options_t *options)); static routerinfo_t *mock_routerinfo; -extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1]; -extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1]; static void NS(test_main)(void *arg) diff --git a/src/test/test_switch_id.sh b/src/test/test_switch_id.sh index 1b4e0998b5..79c44f2eb1 100755 --- a/src/test/test_switch_id.sh +++ b/src/test/test_switch_id.sh @@ -10,6 +10,10 @@ if test "`id -u nobody`" = ""; then exit 1 fi +if test "$OVERRIDE_GCDA_PERMISSIONS_HACK" = "yes"; then + find src -type f -name '*gcda' -print0 | xargs -0 chmod 0666 +fi + "${builddir:-.}/src/test/test-switch-id" nobody setuid || exit 1 "${builddir:-.}/src/test/test-switch-id" nobody root-bind-low || exit 1 "${builddir:-.}/src/test/test-switch-id" nobody setuid-strict || exit 1 @@ -19,6 +23,9 @@ fi "${builddir:-.}/src/test/test-switch-id" nobody have-caps || exit 1 "${builddir:-.}/src/test/test-switch-id" nobody setuid-keepcaps || exit 1 +if test "$OVERRIDE_GCDA_PERMISSIONS_HACK" = "yes"; then + find src -type f -name '*gcda' -print0 | xargs -0 chmod 0644 +fi echo "All okay" diff --git a/src/test/test_threads.c b/src/test/test_threads.c index 1bbe6f5508..ebbc95c7ca 100644 --- a/src/test/test_threads.c +++ b/src/test/test_threads.c @@ -87,11 +87,6 @@ test_threads_basic(void *arg) char *s1 = NULL, *s2 = NULL; int done = 0, timedout = 0; time_t started; -#ifndef _WIN32 - struct timeval tv; - tv.tv_sec=0; - tv.tv_usec=100*1000; -#endif (void) arg; tt_int_op(tor_threadlocal_init(&count), OP_EQ, 0); @@ -120,10 +115,8 @@ test_threads_basic(void *arg) timedout = done = 1; } tor_mutex_release(thread_test_mutex_); -#ifndef _WIN32 /* Prevent the main thread from starving the worker threads. */ - select(0, NULL, NULL, NULL, &tv); -#endif + tor_sleep_msec(10); } tor_mutex_acquire(thread_test_start1_); tor_mutex_release(thread_test_start1_); @@ -286,16 +279,7 @@ test_threads_conditionvar(void *arg) if (!timeout) { tt_int_op(ti->n_shutdown, ==, 4); } else { -#ifdef _WIN32 - Sleep(500); /* msec */ -#elif defined(HAVE_USLEEP) - usleep(500*1000); /* usec */ -#else - { - struct tv = { 0, 500*1000 }; - select(0, NULL, NULL, NULL, &tv); - } -#endif + tor_sleep_msec(200); tor_mutex_acquire(ti->mutex); tt_int_op(ti->n_shutdown, ==, 2); tt_int_op(ti->n_timeouts, ==, 2); diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index b9b74a1e96..47455cff83 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -8,19 +8,13 @@ #ifdef _WIN32 #include <winsock2.h> #endif +#include <math.h> -#ifdef __GNUC__ -#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -#endif +#include "compat.h" -#if __GNUC__ && GCC_VERSION >= 402 -#if GCC_VERSION >= 406 -#pragma GCC diagnostic push -#endif /* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ -#pragma GCC diagnostic ignored "-Wredundant-decls" -#endif +DISABLE_GCC_WARNING(redundant-decls) #include <openssl/opensslv.h> @@ -33,13 +27,7 @@ #include <openssl/evp.h> #include <openssl/bn.h> -#if __GNUC__ && GCC_VERSION >= 402 -#if GCC_VERSION >= 406 -#pragma GCC diagnostic pop -#else -#pragma GCC diagnostic warning "-Wredundant-decls" -#endif -#endif +ENABLE_GCC_WARNING(redundant-decls) #include "or.h" #include "torlog.h" @@ -50,12 +38,11 @@ #include "log_test_helpers.h" #define NS_MODULE tortls -extern tor_tls_context_t *server_tls_context; -extern tor_tls_context_t *client_tls_context; - -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) \ - && !defined(LIBRESSL_VERSION_NUMBER) +#ifndef HAVE_SSL_STATE #define OPENSSL_OPAQUE +#endif + +#if defined(OPENSSL_OPAQUE) && !defined(LIBRESSL_VERSION_NUMBER) #define SSL_STATE_STR "before SSL initialization" #else #define SSL_STATE_STR "before/accept initialization" @@ -277,8 +264,6 @@ test_tortls_get_state_description(void *ignored) tor_free(tls); } -extern int tor_tls_object_ex_data_index; - static void test_tortls_get_by_ssl(void *ignored) { @@ -341,7 +326,7 @@ test_tortls_log_one_error(void *ignored) ctx = SSL_CTX_new(SSLv23_method()); tls = tor_malloc_zero(sizeof(tor_tls_t)); - int previous_log = setup_capture_of_logs(LOG_INFO); + setup_capture_of_logs(LOG_INFO); tor_tls_log_one_error(NULL, 0, LOG_WARN, 0, "something"); expect_log_msg("TLS error while something: " @@ -410,7 +395,7 @@ test_tortls_log_one_error(void *ignored) " (in (null):(null):" SSL_STATE_STR ")\n"); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); SSL_free(ssl); SSL_CTX_free(ctx); if (tls && tls->ssl) @@ -433,7 +418,7 @@ test_tortls_get_error(void *ignored) SSL_load_error_strings(); ctx = SSL_CTX_new(SSLv23_method()); - int previous_log = setup_capture_of_logs(LOG_INFO); + setup_capture_of_logs(LOG_INFO); tls = tor_malloc_zero(sizeof(tor_tls_t)); tls->ssl = SSL_new(ctx); SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL); @@ -499,7 +484,7 @@ test_tortls_get_error(void *ignored) "connect:before/accept initialization)\n"); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); SSL_free(tls->ssl); tor_free(tls); SSL_CTX_free(ctx); @@ -740,6 +725,26 @@ test_tortls_get_my_certs(void *ignored) (void)1; } +#ifndef HAVE_SSL_GET_CLIENT_CIPHERS +static SSL_CIPHER * +get_cipher_by_name(const char *name) +{ + int i; + const SSL_METHOD *method = SSLv23_method(); + int num = method->num_ciphers(); + + for (i = 0; i < num; ++i) { + const SSL_CIPHER *cipher = method->get_cipher(i); + const char *ciphername = SSL_CIPHER_get_name(cipher); + if (!strcmp(ciphername, name)) { + return (SSL_CIPHER *)cipher; + } + } + + return NULL; +} +#endif + #ifndef OPENSSL_OPAQUE static void test_tortls_get_ciphersuite_name(void *ignored) @@ -759,23 +764,6 @@ test_tortls_get_ciphersuite_name(void *ignored) } static SSL_CIPHER * -get_cipher_by_name(const char *name) -{ - int i; - const SSL_METHOD *method = SSLv23_method(); - int num = method->num_ciphers(); - for (i = 0; i < num; ++i) { - const SSL_CIPHER *cipher = method->get_cipher(i); - const char *ciphername = SSL_CIPHER_get_name(cipher); - if (!strcmp(ciphername, name)) { - return (SSL_CIPHER *)cipher; - } - } - - return NULL; -} - -static SSL_CIPHER * get_cipher_by_id(uint16_t id) { int i; @@ -791,8 +779,6 @@ get_cipher_by_id(uint16_t id) return NULL; } -extern uint16_t v2_cipher_list[]; - static void test_tortls_classify_client_ciphers(void *ignored) { @@ -853,9 +839,9 @@ test_tortls_classify_client_ciphers(void *ignored) sk_SSL_CIPHER_zero(ciphers); - one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384"); + one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384"); one->id = 0x00ff; - two = get_cipher_by_name("ECDH-RSA-AES128-GCM-SHA256"); + two = get_cipher_by_name("ECDHE-RSA-AES128-GCM-SHA256"); two->id = 0x0000; sk_SSL_CIPHER_push(ciphers, one); tls->client_cipher_list_type = 0; @@ -925,7 +911,7 @@ test_tortls_client_is_using_v2_ciphers(void *ignored) tt_int_op(ret, OP_EQ, 0); ciphers = sk_SSL_CIPHER_new_null(); - SSL_CIPHER *one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384"); + SSL_CIPHER *one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384"); one->id = 0x00ff; sk_SSL_CIPHER_push(ciphers, one); sess->ciphers = ciphers; @@ -1185,9 +1171,6 @@ test_tortls_get_forced_write_size(void *ignored) tor_free(tls); } -extern uint64_t total_bytes_written_over_tls; -extern uint64_t total_bytes_written_by_tls; - static void test_tortls_get_write_overhead_ratio(void *ignored) { @@ -1196,17 +1179,17 @@ test_tortls_get_write_overhead_ratio(void *ignored) total_bytes_written_over_tls = 0; ret = tls_get_write_overhead_ratio(); - tt_int_op(ret, OP_EQ, 1.0); + tt_double_op(fabs(ret - 1.0), OP_LT, 1E-12); total_bytes_written_by_tls = 10; total_bytes_written_over_tls = 1; ret = tls_get_write_overhead_ratio(); - tt_int_op(ret, OP_EQ, 10.0); + tt_double_op(fabs(ret - 10.0), OP_LT, 1E-12); total_bytes_written_by_tls = 10; total_bytes_written_over_tls = 2; ret = tls_get_write_overhead_ratio(); - tt_int_op(ret, OP_EQ, 5.0); + tt_double_op(fabs(ret - 5.0), OP_LT, 1E-12); done: (void)0; @@ -1573,7 +1556,7 @@ test_tortls_session_secret_cb(void *ignored) tor_tls_session_secret_cb(tls->ssl, NULL, NULL, NULL, NULL, NULL); tt_assert(!tls->ssl->tls_session_secret_cb); - one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384"); + one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384"); one->id = 0x00ff; ciphers = sk_SSL_CIPHER_new_null(); sk_SSL_CIPHER_push(ciphers, one); @@ -1812,7 +1795,7 @@ test_tortls_debug_state_callback(void *ignored) char *buf = tor_malloc_zero(1000); int n; - int previous_log = setup_capture_of_logs(LOG_DEBUG); + setup_capture_of_logs(LOG_DEBUG); ssl = tor_malloc_zero(sizeof(SSL)); @@ -1825,7 +1808,7 @@ test_tortls_debug_state_callback(void *ignored) expect_log_msg(buf); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); tor_free(buf); tor_free(ssl); } @@ -1839,7 +1822,6 @@ test_tortls_server_info_callback(void *ignored) tor_tls_t *tls; SSL_CTX *ctx; SSL *ssl; - int previous_log = setup_capture_of_logs(LOG_WARN); SSL_library_init(); SSL_load_error_strings(); @@ -1853,22 +1835,22 @@ test_tortls_server_info_callback(void *ignored) tls->magic = TOR_TLS_MAGIC; tls->ssl = ssl; - tor_tls_server_info_callback(NULL, 0, 0); - + setup_full_capture_of_logs(LOG_WARN); SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A); mock_clean_saved_logs(); tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); - expect_log_msg("Couldn't look up the tls for an SSL*. How odd!\n"); + expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n"); SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B); mock_clean_saved_logs(); tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); - expect_log_msg("Couldn't look up the tls for an SSL*. How odd!\n"); + expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n"); SSL_set_state(ssl, 99); mock_clean_saved_logs(); tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); expect_no_log_entry(); + teardown_capture_of_logs(); SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls); SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B); @@ -1889,7 +1871,7 @@ test_tortls_server_info_callback(void *ignored) tt_int_op(tls->wasV2Handshake, OP_EQ, 0); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); SSL_free(ssl); SSL_CTX_free(ctx); tor_free(tls); @@ -1951,7 +1933,7 @@ test_tortls_shutdown(void *ignored) int ret; tor_tls_t *tls; SSL_METHOD *method = give_me_a_test_method(); - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); tls = tor_malloc_zero(sizeof(tor_tls_t)); tls->ssl = tor_malloc_zero(sizeof(SSL)); @@ -2034,7 +2016,7 @@ test_tortls_shutdown(void *ignored) #endif done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); tor_free(method); tor_free(tls->ssl); tor_free(tls); @@ -2058,7 +2040,7 @@ test_tortls_read(void *ignored) tor_tls_t *tls; char buf[100]; SSL_METHOD *method = give_me_a_test_method(); - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); tls = tor_malloc_zero(sizeof(tor_tls_t)); tls->ssl = tor_malloc_zero(sizeof(SSL)); @@ -2106,7 +2088,7 @@ test_tortls_read(void *ignored) // TODO: fill up done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); tor_free(tls->ssl); tor_free(tls); tor_free(method); @@ -2131,7 +2113,7 @@ test_tortls_write(void *ignored) tor_tls_t *tls; SSL_METHOD *method = give_me_a_test_method(); char buf[100]; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); tls = tor_malloc_zero(sizeof(tor_tls_t)); tls->ssl = tor_malloc_zero(sizeof(SSL)); @@ -2171,7 +2153,7 @@ test_tortls_write(void *ignored) tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); BIO_free(tls->ssl->rbio); tor_free(tls->ssl); tor_free(tls); @@ -2216,7 +2198,7 @@ test_tortls_handshake(void *ignored) tor_tls_t *tls; SSL_CTX *ctx; SSL_METHOD *method = give_me_a_test_method(); - int previous_log = setup_capture_of_logs(LOG_INFO); + setup_capture_of_logs(LOG_INFO); SSL_library_init(); SSL_load_error_strings(); @@ -2279,7 +2261,7 @@ test_tortls_handshake(void *ignored) expect_log_severity(LOG_WARN); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); SSL_free(tls->ssl); SSL_CTX_free(ctx); tor_free(tls); @@ -2313,9 +2295,13 @@ test_tortls_finish_handshake(void *ignored) tls->isServer = 1; tls->wasV2Handshake = 0; + setup_full_capture_of_logs(LOG_WARN); ret = tor_tls_finish_handshake(tls); tt_int_op(ret, OP_EQ, 0); tt_int_op(tls->wasV2Handshake, OP_EQ, 1); + expect_single_log_msg_containing("For some reason, wasV2Handshake didn't " + "get set."); + teardown_capture_of_logs(); tls->wasV2Handshake = 1; ret = tor_tls_finish_handshake(tls); @@ -2354,6 +2340,7 @@ test_tortls_finish_handshake(void *ignored) tor_free(tls); SSL_CTX_free(ctx); tor_free(method); + teardown_capture_of_logs(); } #endif @@ -2440,6 +2427,8 @@ test_tortls_context_new(void *ignored) ret = tor_tls_context_new(NULL, 0, 0, 0); tt_assert(!ret); + /* note: we already override this in testing_common.c, so we + * run this unit test in a subprocess. */ MOCK(crypto_pk_generate_key_with_bits, fixed_crypto_pk_generate_key_with_bits); fixed_crypto_pk_new_result_index = 0; @@ -2826,7 +2815,7 @@ struct testcase_t tortls_tests[] = { INTRUSIVE_TEST_CASE(find_cipher_by_id, 0), INTRUSIVE_TEST_CASE(session_secret_cb, 0), INTRUSIVE_TEST_CASE(debug_state_callback, 0), - INTRUSIVE_TEST_CASE(context_new, 0), + INTRUSIVE_TEST_CASE(context_new, TT_FORK /* redundant */), LOCAL_TEST_CASE(create_certificate, 0), LOCAL_TEST_CASE(cert_new, 0), LOCAL_TEST_CASE(cert_is_valid, 0), diff --git a/src/test/test_util.c b/src/test/test_util.c index d534cc0b52..fcda564569 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -5,6 +5,7 @@ #include "orconfig.h" #define COMPAT_PRIVATE +#define COMPAT_TIME_PRIVATE #define CONTROL_PRIVATE #define UTIL_PRIVATE #include "or.h" @@ -13,6 +14,7 @@ #include "test.h" #include "memarea.h" #include "util_process.h" +#include "log_test_helpers.h" #ifdef HAVE_PWD_H #include <pwd.h> @@ -30,6 +32,9 @@ #include <ctype.h> #include <float.h> +#define INFINITY_DBL ((double)INFINITY) +#define NAN_DBL ((double)NAN) + /* XXXX this is a minimal wrapper to make the unit tests compile with the * changed tor_timegm interface. */ static time_t @@ -258,7 +263,7 @@ test_util_time(void *arg) int i; struct timeval tv; - /* Test tv_udiff */ + /* Test tv_udiff and tv_mdiff */ (void)arg; start.tv_sec = 5; @@ -268,22 +273,312 @@ test_util_time(void *arg) end.tv_usec = 5000; tt_int_op(0L,OP_EQ, tv_udiff(&start, &end)); + tt_int_op(0L,OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(0L,OP_EQ, tv_udiff(&end, &start)); + tt_int_op(0L,OP_EQ, tv_mdiff(&end, &start)); end.tv_usec = 7000; tt_int_op(2000L,OP_EQ, tv_udiff(&start, &end)); + tt_int_op(2L,OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(-2000L,OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-2L,OP_EQ, tv_mdiff(&end, &start)); end.tv_sec = 6; tt_int_op(1002000L,OP_EQ, tv_udiff(&start, &end)); + tt_int_op(1002L,OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(-1002000L,OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-1002L,OP_EQ, tv_mdiff(&end, &start)); end.tv_usec = 0; tt_int_op(995000L,OP_EQ, tv_udiff(&start, &end)); + tt_int_op(995L,OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(-995000L,OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-995L,OP_EQ, tv_mdiff(&end, &start)); end.tv_sec = 4; tt_int_op(-1005000L,OP_EQ, tv_udiff(&start, &end)); + tt_int_op(-1005L,OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(1005000L,OP_EQ, tv_udiff(&end, &start)); + tt_int_op(1005L,OP_EQ, tv_mdiff(&end, &start)); + + /* Negative tv_sec values, these will break on platforms where tv_sec is + * unsigned */ + + end.tv_sec = -10; + + tt_int_op(-15005000L,OP_EQ, tv_udiff(&start, &end)); + tt_int_op(-15005L,OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(15005000L,OP_EQ, tv_udiff(&end, &start)); + tt_int_op(15005L,OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = -100; + + tt_int_op(89995000L,OP_EQ, tv_udiff(&start, &end)); + tt_int_op(89995L,OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(-89995000L,OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-89995L,OP_EQ, tv_mdiff(&end, &start)); + + /* Test that tv_usec values round away from zero when converted to msec */ + start.tv_sec = 0; + start.tv_usec = 0; + end.tv_sec = 10; + end.tv_usec = 499; + + tt_int_op(10000499L, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(10000L, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(-10000499L, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-10000L, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = 0; + start.tv_usec = 0; + end.tv_sec = 10; + end.tv_usec = 500; + + tt_int_op(10000500L, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(10001L, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(-10000500L, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-10000L, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = 0; + start.tv_usec = 0; + end.tv_sec = 10; + end.tv_usec = 501; + + tt_int_op(10000501L, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(10001L, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(-10000501L, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-10001L, OP_EQ, tv_mdiff(&end, &start)); + + /* Overflow conditions */ + +#ifdef _WIN32 + /* Would you believe that tv_sec is a long on windows? Of course you would.*/ +#define TV_SEC_MAX LONG_MAX +#define TV_SEC_MIN LONG_MIN +#else + /* Some BSDs have struct timeval.tv_sec 64-bit, but time_t (and long) 32-bit + * Which means TIME_MAX is not actually the maximum value of tv_sec. + * But that's ok for the moment, because the code correctly performs 64-bit + * calculations internally, then catches the overflow. */ +#define TV_SEC_MAX TIME_MAX +#define TV_SEC_MIN TIME_MIN +#endif + +/* Assume tv_usec is an unsigned integer until proven otherwise */ +#define TV_USEC_MAX UINT_MAX +#define TOR_USEC_PER_SEC 1000000 + + /* Overflows in the result type */ + + /* All comparisons work */ + start.tv_sec = 0; + start.tv_usec = 0; + end.tv_sec = LONG_MAX/1000 - 2; + end.tv_usec = 0; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(end.tv_sec*1000L, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-end.tv_sec*1000L, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = 0; + start.tv_usec = 0; + end.tv_sec = LONG_MAX/1000000 - 1; + end.tv_usec = 0; + + tt_int_op(end.tv_sec*1000000L, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(end.tv_sec*1000L, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(-end.tv_sec*1000000L, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-end.tv_sec*1000L, OP_EQ, tv_mdiff(&end, &start)); + + /* No comparisons work */ + start.tv_sec = 0; + start.tv_usec = 0; + end.tv_sec = LONG_MAX/1000 + 1; + end.tv_usec = 0; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = 0; + start.tv_usec = 0; + end.tv_sec = LONG_MAX/1000000 + 1; + end.tv_usec = 0; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(end.tv_sec*1000L, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-end.tv_sec*1000L, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = 0; + start.tv_usec = 0; + end.tv_sec = LONG_MAX/1000; + end.tv_usec = TOR_USEC_PER_SEC; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = 0; + start.tv_usec = 0; + end.tv_sec = LONG_MAX/1000000; + end.tv_usec = TOR_USEC_PER_SEC; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op((end.tv_sec + 1)*1000L, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(-(end.tv_sec + 1)*1000L, OP_EQ, tv_mdiff(&end, &start)); + + /* Overflows on comparison to zero */ + + start.tv_sec = 0; + start.tv_usec = 0; + + end.tv_sec = TV_SEC_MAX; + end.tv_usec = 0; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + end.tv_sec = TV_SEC_MAX; + end.tv_usec = TOR_USEC_PER_SEC; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + end.tv_sec = 0; + end.tv_usec = TV_USEC_MAX; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + end.tv_sec = TV_SEC_MAX; + end.tv_usec = TV_USEC_MAX; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + end.tv_sec = 0; + end.tv_usec = 0; + + start.tv_sec = TV_SEC_MIN; + start.tv_usec = 0; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = TV_SEC_MIN; + start.tv_usec = TOR_USEC_PER_SEC; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = TV_SEC_MIN; + start.tv_usec = TV_USEC_MAX; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + /* overflows on comparison to maxima / minima */ + + start.tv_sec = TV_SEC_MIN; + start.tv_usec = 0; + + end.tv_sec = TV_SEC_MAX; + end.tv_usec = 0; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + end.tv_sec = TV_SEC_MAX; + end.tv_usec = TOR_USEC_PER_SEC; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + end.tv_sec = TV_SEC_MAX; + end.tv_usec = 0; + + start.tv_sec = TV_SEC_MIN; + start.tv_usec = 0; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = TV_SEC_MIN; + start.tv_usec = TOR_USEC_PER_SEC; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + /* overflows on comparison to maxima / minima with extra usec */ + + start.tv_sec = TV_SEC_MIN; + start.tv_usec = TOR_USEC_PER_SEC; + + end.tv_sec = TV_SEC_MAX; + end.tv_usec = 0; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + end.tv_sec = TV_SEC_MAX; + end.tv_usec = TOR_USEC_PER_SEC; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + end.tv_sec = TV_SEC_MAX; + end.tv_usec = TOR_USEC_PER_SEC; + + start.tv_sec = TV_SEC_MIN; + start.tv_usec = 0; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); + + start.tv_sec = TV_SEC_MIN; + start.tv_usec = TOR_USEC_PER_SEC; + + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end)); + tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start)); + tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start)); /* Test tor_timegm & tor_gmtime_r */ @@ -349,31 +644,57 @@ test_util_time(void *arg) /* Test tor_timegm out of range */ + /* The below tests will all cause a BUG message, so we capture, suppress, + * and detect. */ +#define CAPTURE() do { \ + setup_full_capture_of_logs(LOG_WARN); \ + } while (0) +#define CHECK_TIMEGM_WARNING(msg) do { \ + expect_log_msg_containing(msg); \ + tt_int_op(1, OP_EQ, smartlist_len(mock_saved_logs())); \ + teardown_capture_of_logs(); \ + } while (0) + +#define CHECK_TIMEGM_ARG_OUT_OF_RANGE(msg) \ + CHECK_TIMEGM_WARNING("Out-of-range argument to tor_timegm") + /* year */ /* Wrong year < 1970 */ a_time.tm_year = 1969-1900; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_year = -1-1900; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); #if SIZEOF_INT == 4 || SIZEOF_INT == 8 a_time.tm_year = -1*(1 << 16); + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); /* one of the smallest tm_year values my 64 bit system supports: * t_res = -9223372036854775LL without clamping */ a_time.tm_year = -292275055-1900; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_year = INT32_MIN; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); #endif #if SIZEOF_INT == 8 a_time.tm_year = -1*(1 << 48); + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); /* while unlikely, the system's gmtime(_r) could return * a "correct" retrospective gregorian negative year value, @@ -381,25 +702,35 @@ test_util_time(void *arg) * -1*(2^63)/60/60/24*2000/730485 + 1970 = -292277022657 * 730485 is the number of days in two millenia, including leap days */ a_time.tm_year = -292277022657-1900; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_year = INT64_MIN; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); #endif /* Wrong year >= INT32_MAX - 1900 */ #if SIZEOF_INT == 4 || SIZEOF_INT == 8 a_time.tm_year = INT32_MAX-1900; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_year = INT32_MAX; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); #endif #if SIZEOF_INT == 8 /* one of the largest tm_year values my 64 bit system supports */ a_time.tm_year = 292278994-1900; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); /* while unlikely, the system's gmtime(_r) could return * a "correct" proleptic gregorian year value, @@ -407,72 +738,104 @@ test_util_time(void *arg) * (2^63-1)/60/60/24*2000/730485 + 1970 = 292277026596 * 730485 is the number of days in two millenia, including leap days */ a_time.tm_year = 292277026596-1900; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_year = INT64_MAX-1900; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_year = INT64_MAX; + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); #endif /* month */ a_time.tm_year = 2007-1900; /* restore valid year */ a_time.tm_mon = 12; /* Wrong month, it's 0-based */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_mon = -1; /* Wrong month */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); /* day */ a_time.tm_mon = 6; /* Try July */ a_time.tm_mday = 32; /* Wrong day */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_mon = 5; /* Try June */ a_time.tm_mday = 31; /* Wrong day */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_year = 2008-1900; /* Try a leap year */ a_time.tm_mon = 1; /* in feb. */ a_time.tm_mday = 30; /* Wrong day */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_year = 2011-1900; /* Try a non-leap year */ a_time.tm_mon = 1; /* in feb. */ a_time.tm_mday = 29; /* Wrong day */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_mday = 0; /* Wrong day, it's 1-based (to be different) */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); /* hour */ a_time.tm_mday = 3; /* restore valid month day */ a_time.tm_hour = 24; /* Wrong hour, it's 0-based */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_hour = -1; /* Wrong hour */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); /* minute */ a_time.tm_hour = 22; /* restore valid hour */ a_time.tm_min = 60; /* Wrong minute, it's 0-based */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_min = -1; /* Wrong minute */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); /* second */ a_time.tm_min = 37; /* restore valid minute */ a_time.tm_sec = 61; /* Wrong second: 0-based with leap seconds */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); a_time.tm_sec = -1; /* Wrong second */ + CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); + CHECK_TIMEGM_ARG_OUT_OF_RANGE(); /* Test tor_gmtime_r out of range */ @@ -512,9 +875,17 @@ test_util_time(void *arg) * 730485 is the number of days in two millenia, including leap days * (int64_t)b_time.tm_year == (-292277022657LL-1900LL) without clamping */ t_res = INT64_MIN; + CAPTURE(); tor_gmtime_r(&t_res, &b_time); - tt_assert(b_time.tm_year == (1970-1900) || - b_time.tm_year == (1-1900)); + if (! (b_time.tm_year == (1970-1900) || + b_time.tm_year == (1-1900))) { + tt_int_op(b_time.tm_year, OP_EQ, 1970-1900); + } + if (b_time.tm_year != 1970-1900) { + CHECK_TIMEGM_WARNING("Rounding up to "); + } else { + teardown_capture_of_logs(); + } } #endif @@ -550,7 +921,10 @@ test_util_time(void *arg) * 730485 is the number of days in two millenia, including leap days * (int64_t)b_time.tm_year == (292277026596L-1900L) without clamping */ t_res = INT64_MAX; + CAPTURE(); tor_gmtime_r(&t_res, &b_time); + CHECK_TIMEGM_WARNING("Rounding down to "); + tt_assert(b_time.tm_year == (2037-1900) || b_time.tm_year == (9999-1900)); } @@ -622,9 +996,16 @@ test_util_time(void *arg) parse_rfc1123_time("Wed, 30 Ene 2011 23:59:59 GMT", &t_res)); tt_int_op(-1,OP_EQ, parse_rfc1123_time("Wed, 30 Mar 2011 23:59:59 GM", &t_res)); + tt_int_op(-1,OP_EQ, + parse_rfc1123_time("Wed, 30 Mar 1900 23:59:59 GMT", &t_res)); + /* Leap year. */ tt_int_op(-1,OP_EQ, parse_rfc1123_time("Wed, 29 Feb 2011 16:00:00 GMT", &t_res)); + tt_int_op(0,OP_EQ, + parse_rfc1123_time("Wed, 29 Feb 2012 16:00:00 GMT", &t_res)); + + /* Leap second plus one */ tt_int_op(-1,OP_EQ, parse_rfc1123_time("Wed, 30 Mar 2011 23:59:61 GMT", &t_res)); @@ -732,8 +1113,11 @@ test_util_time(void *arg) #endif #endif +#undef CAPTURE +#undef CHECK_TIMEGM_ARG_OUT_OF_RANGE + done: - ; + teardown_capture_of_logs(); } static void @@ -865,106 +1249,106 @@ test_util_config_line(void *arg) , sizeof(buf)); str = buf; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k"); tt_str_op(v,OP_EQ, "v"); tor_free(k); tor_free(v); tt_assert(!strcmpstart(str, "key value with")); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "key"); tt_str_op(v,OP_EQ, "value with spaces"); tor_free(k); tor_free(v); tt_assert(!strcmpstart(str, "keykey")); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "keykey"); tt_str_op(v,OP_EQ, "val"); tor_free(k); tor_free(v); tt_assert(!strcmpstart(str, "k2\n")); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k2"); tt_str_op(v,OP_EQ, ""); tor_free(k); tor_free(v); tt_assert(!strcmpstart(str, "k3 \n")); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k3"); tt_str_op(v,OP_EQ, ""); tor_free(k); tor_free(v); tt_assert(!strcmpstart(str, "#comment")); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k4"); tt_str_op(v,OP_EQ, ""); tor_free(k); tor_free(v); tt_assert(!strcmpstart(str, "k5#abc")); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k5"); tt_str_op(v,OP_EQ, ""); tor_free(k); tor_free(v); tt_assert(!strcmpstart(str, "k6")); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k6"); tt_str_op(v,OP_EQ, "val"); tor_free(k); tor_free(v); tt_assert(!strcmpstart(str, "kseven")); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "kseven"); tt_str_op(v,OP_EQ, "a quoted \'string"); tor_free(k); tor_free(v); tt_assert(!strcmpstart(str, "k8 ")); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k8"); tt_str_op(v,OP_EQ, "a quoted\n\"str\\ing\t\x01\x01\x01\""); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k9"); tt_str_op(v,OP_EQ, "a line that spans two lines."); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k10"); tt_str_op(v,OP_EQ, "more than one continuation"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k11"); tt_str_op(v,OP_EQ, "continuation at the start"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k12"); tt_str_op(v,OP_EQ, "line with a embedded"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k13"); tt_str_op(v,OP_EQ, "continuation at the very start"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k14"); tt_str_op(v,OP_EQ, "a line that has a comment and" ); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k15"); tt_str_op(v,OP_EQ, "this should be the next new line"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k16"); tt_str_op(v,OP_EQ, "a line that has a comment and" ); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k17"); tt_str_op(v,OP_EQ, "this should be the next new line"); tor_free(k); tor_free(v); @@ -999,32 +1383,36 @@ test_util_config_line_quotes(void *arg) , sizeof(buf4)); str = buf1; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "kTrailingSpace"); tt_str_op(v,OP_EQ, "quoted value"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_ptr_op(str,OP_EQ, NULL); tor_free(k); tor_free(v); str = buf2; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_ptr_op(str,OP_EQ, NULL); tor_free(k); tor_free(v); str = buf3; - str = parse_config_line_from_str(str, &k, &v); + const char *err = NULL; + str = parse_config_line_from_str_verbose(str, &k, &v, &err); tt_ptr_op(str,OP_EQ, NULL); tor_free(k); tor_free(v); + tt_str_op(err, OP_EQ, "Invalid escape sequence in quoted string"); str = buf4; - str = parse_config_line_from_str(str, &k, &v); + err = NULL; + str = parse_config_line_from_str_verbose(str, &k, &v, &err); tt_ptr_op(str,OP_EQ, NULL); tor_free(k); tor_free(v); + tt_str_op(err, OP_EQ, "Invalid escape sequence in quoted string"); done: tor_free(k); @@ -1046,12 +1434,12 @@ test_util_config_line_comment_character(void *arg) , sizeof(buf)); str = buf; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k1"); tt_str_op(v,OP_EQ, "# in quotes"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "k2"); tt_str_op(v,OP_EQ, "some value"); tor_free(k); tor_free(v); @@ -1059,7 +1447,7 @@ test_util_config_line_comment_character(void *arg) tt_str_op(str,OP_EQ, "k3 /home/user/myTorNetwork#2\n"); #if 0 - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); test_streq(k, "k3"); test_streq(v, "/home/user/myTorNetwork#2"); tor_free(k); tor_free(v); @@ -1116,57 +1504,57 @@ test_util_config_line_escaped_content(void *arg) str = buf1; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "HexadecimalLower"); tt_str_op(v,OP_EQ, "*"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "HexadecimalUpper"); tt_str_op(v,OP_EQ, "*"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "HexadecimalUpperX"); tt_str_op(v,OP_EQ, "*"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "Octal"); tt_str_op(v,OP_EQ, "*"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "Newline"); tt_str_op(v,OP_EQ, "\n"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "Tab"); tt_str_op(v,OP_EQ, "\t"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "CarriageReturn"); tt_str_op(v,OP_EQ, "\r"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "DoubleQuote"); tt_str_op(v,OP_EQ, "\""); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "SimpleQuote"); tt_str_op(v,OP_EQ, "'"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "Backslash"); tt_str_op(v,OP_EQ, "\\"); tor_free(k); tor_free(v); - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_str_op(k,OP_EQ, "Mix"); tt_str_op(v,OP_EQ, "This is a \"star\":\t'*'\nAnd second line"); tor_free(k); tor_free(v); @@ -1174,41 +1562,115 @@ test_util_config_line_escaped_content(void *arg) str = buf2; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_ptr_op(str,OP_EQ, NULL); tor_free(k); tor_free(v); str = buf3; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_ptr_op(str,OP_EQ, NULL); tor_free(k); tor_free(v); str = buf4; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_ptr_op(str,OP_EQ, NULL); tor_free(k); tor_free(v); #if 0 str = buf5; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_ptr_op(str, OP_EQ, NULL); tor_free(k); tor_free(v); #endif str = buf6; - str = parse_config_line_from_str(str, &k, &v); + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); + tt_ptr_op(str,OP_EQ, NULL); + tor_free(k); tor_free(v); + + /* more things to try. */ + /* Bad hex: */ + strlcpy(buf1, "Foo \"\\x9g\"\n", sizeof(buf1)); + strlcpy(buf2, "Foo \"\\xg0\"\n", sizeof(buf2)); + strlcpy(buf3, "Foo \"\\xf\"\n", sizeof(buf3)); + /* bad escape */ + strlcpy(buf4, "Foo \"\\q\"\n", sizeof(buf4)); + /* missing endquote */ + strlcpy(buf5, "Foo \"hello\n", sizeof(buf5)); + /* extra stuff */ + strlcpy(buf6, "Foo \"hello\" world\n", sizeof(buf6)); + + str=buf1; + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); + tt_ptr_op(str,OP_EQ, NULL); + tor_free(k); tor_free(v); + + str=buf2; + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); tt_ptr_op(str,OP_EQ, NULL); tor_free(k); tor_free(v); + str=buf3; + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); + tt_ptr_op(str,OP_EQ, NULL); + tor_free(k); tor_free(v); + + str=buf4; + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); + tt_ptr_op(str,OP_EQ, NULL); + tor_free(k); tor_free(v); + + str=buf5; + + str = parse_config_line_from_str_verbose(str, &k, &v, NULL); + tt_ptr_op(str,OP_EQ, NULL); + tor_free(k); tor_free(v); + + str=buf6; + const char *err = NULL; + str = parse_config_line_from_str_verbose(str, &k, &v, &err); + tt_ptr_op(str,OP_EQ, NULL); + tor_free(k); tor_free(v); + tt_str_op(err,OP_EQ, "Excess data after quoted string"); + done: tor_free(k); tor_free(v); } +static void +test_util_config_line_crlf(void *arg) +{ + char *k=NULL, *v=NULL; + const char *err = NULL; + (void)arg; + const char *str = + "Hello world\r\n" + "Hello \"nice big world\"\r\n"; + + str = parse_config_line_from_str_verbose(str, &k, &v, &err); + tt_assert(str); + tt_str_op(k,OP_EQ,"Hello"); + tt_str_op(v,OP_EQ,"world"); + tt_assert(!err); + tor_free(k); tor_free(v); + + str = parse_config_line_from_str_verbose(str, &k, &v, &err); + tt_assert(str); + tt_str_op(k,OP_EQ,"Hello"); + tt_str_op(v,OP_EQ,"nice big world"); + tt_assert(!err); + tor_free(k); tor_free(v); + tt_str_op(str,OP_EQ, ""); + + done: + tor_free(k); tor_free(v); +} + #ifndef _WIN32 static void test_util_expand_filename(void *arg) @@ -1372,8 +1834,7 @@ static void test_util_strmisc(void *arg) { char buf[1024]; - int i; - char *cp, *cp_tmp = NULL; + char *cp_tmp = NULL; /* Test strl operations */ (void)arg; @@ -1398,117 +1859,6 @@ test_util_strmisc(void *arg) tor_strstrip(buf, "!? "); tt_str_op(buf,OP_EQ, "Testing123"); - /* Test parse_long */ - /* Empty/zero input */ - tt_int_op(0L,OP_EQ, tor_parse_long("",10,0,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - tt_int_op(0L,OP_EQ, tor_parse_long("0",10,0,100,&i,NULL)); - tt_int_op(1,OP_EQ, i); - /* Normal cases */ - tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,100,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,10,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(10L,OP_EQ, tor_parse_long("10",10,10,100,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,100,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,0,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-50,0,&i,NULL)); - tt_int_op(1,OP_EQ, i); - /* Extra garbage */ - tt_int_op(0L,OP_EQ, tor_parse_long("10m",10,0,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - tt_int_op(0L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - tt_int_op(10L,OP_EQ, tor_parse_long("10m",10,0,100,&i,&cp)); - tt_int_op(1,OP_EQ, i); - tt_str_op(cp,OP_EQ, "m"); - tt_int_op(-50L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,&cp)); - tt_int_op(1,OP_EQ, i); - tt_str_op(cp,OP_EQ, " plus garbage"); - /* Out of bounds */ - tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,0,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - /* Base different than 10 */ - tt_int_op(2L,OP_EQ, tor_parse_long("10",2,0,100,NULL,NULL)); - tt_int_op(0L,OP_EQ, tor_parse_long("2",2,0,100,NULL,NULL)); - tt_int_op(0L,OP_EQ, tor_parse_long("10",-2,0,100,NULL,NULL)); - tt_int_op(68284L,OP_EQ, tor_parse_long("10abc",16,0,70000,NULL,NULL)); - tt_int_op(68284L,OP_EQ, tor_parse_long("10ABC",16,0,70000,NULL,NULL)); - tt_int_op(0,OP_EQ, tor_parse_long("10ABC",-1,0,70000,&i,NULL)); - tt_int_op(i,OP_EQ, 0); - - /* Test parse_ulong */ - tt_int_op(0UL,OP_EQ, tor_parse_ulong("",10,0,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("0",10,0,100,NULL,NULL)); - tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("10",10,50,100,NULL,NULL)); - tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,10,NULL,NULL)); - tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,10,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("8",8,0,100,NULL,NULL)); - tt_int_op(50UL,OP_EQ, tor_parse_ulong("50",10,50,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("-50",10,-100,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("50",-1,50,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - - /* Test parse_uint64 */ - tt_assert(U64_LITERAL(10) == tor_parse_uint64("10 x",10,0,100, &i, &cp)); - tt_int_op(1,OP_EQ, i); - tt_str_op(cp,OP_EQ, " x"); - tt_assert(U64_LITERAL(12345678901) == - tor_parse_uint64("12345678901",10,0,UINT64_MAX, &i, &cp)); - tt_int_op(1,OP_EQ, i); - tt_str_op(cp,OP_EQ, ""); - tt_assert(U64_LITERAL(0) == - tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp)); - tt_int_op(0,OP_EQ, i); - tt_assert(U64_LITERAL(0) == - tor_parse_uint64("123",-1,0,INT32_MAX, &i, &cp)); - tt_int_op(0,OP_EQ, i); - - { - /* Test parse_double */ - double d = tor_parse_double("10", 0, UINT64_MAX,&i,NULL); - tt_int_op(1,OP_EQ, i); - tt_assert(DBL_TO_U64(d) == 10); - d = tor_parse_double("0", 0, UINT64_MAX,&i,NULL); - tt_int_op(1,OP_EQ, i); - tt_assert(DBL_TO_U64(d) == 0); - d = tor_parse_double(" ", 0, UINT64_MAX,&i,NULL); - tt_int_op(0,OP_EQ, i); - d = tor_parse_double(".0a", 0, UINT64_MAX,&i,NULL); - tt_int_op(0,OP_EQ, i); - d = tor_parse_double(".0a", 0, UINT64_MAX,&i,&cp); - tt_int_op(1,OP_EQ, i); - d = tor_parse_double("-.0", 0, UINT64_MAX,&i,NULL); - tt_int_op(1,OP_EQ, i); - tt_assert(DBL_TO_U64(d) == 0); - d = tor_parse_double("-10", -100.0, 100.0,&i,NULL); - tt_int_op(1,OP_EQ, i); - tt_int_op(-10.0,OP_EQ, d); - } - - { - /* Test tor_parse_* where we overflow/underflow the underlying type. */ - /* This string should overflow 64-bit ints. */ -#define TOOBIG "100000000000000000000000000" - tt_int_op(0L, OP_EQ, - tor_parse_long(TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL)); - tt_int_op(i,OP_EQ, 0); - tt_int_op(0L,OP_EQ, - tor_parse_long("-"TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL)); - tt_int_op(i,OP_EQ, 0); - tt_int_op(0UL,OP_EQ, tor_parse_ulong(TOOBIG, 10, 0, ULONG_MAX, &i, NULL)); - tt_int_op(i,OP_EQ, 0); - tt_u64_op(U64_LITERAL(0), OP_EQ, tor_parse_uint64(TOOBIG, 10, - 0, UINT64_MAX, &i, NULL)); - tt_int_op(i,OP_EQ, 0); - } - /* Test snprintf */ /* Returning -1 when there's not enough room in the output buffer */ tt_int_op(-1,OP_EQ, tor_snprintf(buf, 0, "Foo")); @@ -1583,6 +1933,17 @@ test_util_strmisc(void *arg) tt_str_op("\"z\\001abc\\277d\"",OP_EQ, escaped("z\001abc\277d")); tt_str_op("\"z\\336\\255 ;foo\"",OP_EQ, escaped("z\xde\xad\x20;foo")); + /* Other cases of esc_for_log{,_len} */ + cp_tmp = esc_for_log(NULL); + tt_str_op(cp_tmp, OP_EQ, "(null)"); + tor_free(cp_tmp); + cp_tmp = esc_for_log_len("abcdefg", 3); + tt_str_op(cp_tmp, OP_EQ, "\"abc\""); + tor_free(cp_tmp); + cp_tmp = esc_for_log_len("abcdefg", 100); + tt_str_op(cp_tmp, OP_EQ, "\"abcdefg\""); + tor_free(cp_tmp); + /* Test strndup and memdup */ { const char *s = "abcdefghijklmnopqrstuvwxyz"; @@ -1642,9 +2003,9 @@ test_util_strmisc(void *arg) /* Test hex_str */ { char binary_data[68]; - size_t i; - for (i = 0; i < sizeof(binary_data); ++i) - binary_data[i] = i; + size_t idx; + for (idx = 0; idx < sizeof(binary_data); ++idx) + binary_data[idx] = idx; tt_str_op(hex_str(binary_data, 0),OP_EQ, ""); tt_str_op(hex_str(binary_data, 1),OP_EQ, "00"); tt_str_op(hex_str(binary_data, 17),OP_EQ, @@ -1687,6 +2048,144 @@ test_util_strmisc(void *arg) } static void +test_util_parse_integer(void *arg) +{ + (void)arg; + int i; + char *cp; + + /* Test parse_long */ + /* Empty/zero input */ + tt_int_op(0L,OP_EQ, tor_parse_long("",10,0,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(0L,OP_EQ, tor_parse_long("0",10,0,100,&i,NULL)); + tt_int_op(1,OP_EQ, i); + /* Normal cases */ + tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,100,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,10,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(10L,OP_EQ, tor_parse_long("10",10,10,100,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,100,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,0,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-50,0,&i,NULL)); + tt_int_op(1,OP_EQ, i); + /* Extra garbage */ + tt_int_op(0L,OP_EQ, tor_parse_long("10m",10,0,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(0L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(10L,OP_EQ, tor_parse_long("10m",10,0,100,&i,&cp)); + tt_int_op(1,OP_EQ, i); + tt_str_op(cp,OP_EQ, "m"); + tt_int_op(-50L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,&cp)); + tt_int_op(1,OP_EQ, i); + tt_str_op(cp,OP_EQ, " plus garbage"); + /* Illogical min max */ + tor_capture_bugs_(1); + tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,4,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(1, OP_EQ, smartlist_len(tor_get_captured_bug_log_())); + tt_str_op("!(max < min)", OP_EQ, + smartlist_get(tor_get_captured_bug_log_(), 0)); + tor_end_capture_bugs_(); + tor_capture_bugs_(1); + tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,100,-100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(1, OP_EQ, smartlist_len(tor_get_captured_bug_log_())); + tt_str_op("!(max < min)", OP_EQ, + smartlist_get(tor_get_captured_bug_log_(), 0)); + tor_end_capture_bugs_(); + /* Out of bounds */ + tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,0,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + /* Base different than 10 */ + tt_int_op(2L,OP_EQ, tor_parse_long("10",2,0,100,NULL,NULL)); + tt_int_op(0L,OP_EQ, tor_parse_long("2",2,0,100,NULL,NULL)); + tt_int_op(0L,OP_EQ, tor_parse_long("10",-2,0,100,NULL,NULL)); + tt_int_op(68284L,OP_EQ, tor_parse_long("10abc",16,0,70000,NULL,NULL)); + tt_int_op(68284L,OP_EQ, tor_parse_long("10ABC",16,0,70000,NULL,NULL)); + tt_int_op(0,OP_EQ, tor_parse_long("10ABC",-1,0,70000,&i,NULL)); + tt_int_op(i,OP_EQ, 0); + + /* Test parse_ulong */ + tt_int_op(0UL,OP_EQ, tor_parse_ulong("",10,0,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("0",10,0,100,NULL,NULL)); + tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("10",10,50,100,NULL,NULL)); + tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,10,NULL,NULL)); + tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,10,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("8",8,0,100,NULL,NULL)); + tt_int_op(50UL,OP_EQ, tor_parse_ulong("50",10,50,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("-50",10,0,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("50",-1,50,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("-50",10,0,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + + /* Test parse_uint64 */ + tt_assert(U64_LITERAL(10) == tor_parse_uint64("10 x",10,0,100, &i, &cp)); + tt_int_op(1,OP_EQ, i); + tt_str_op(cp,OP_EQ, " x"); + tt_assert(U64_LITERAL(12345678901) == + tor_parse_uint64("12345678901",10,0,UINT64_MAX, &i, &cp)); + tt_int_op(1,OP_EQ, i); + tt_str_op(cp,OP_EQ, ""); + tt_assert(U64_LITERAL(0) == + tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp)); + tt_int_op(0,OP_EQ, i); + tt_assert(U64_LITERAL(0) == + tor_parse_uint64("123",-1,0,INT32_MAX, &i, &cp)); + tt_int_op(0,OP_EQ, i); + + { + /* Test parse_double */ + double d = tor_parse_double("10", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(1,OP_EQ, i); + tt_assert(DBL_TO_U64(d) == 10); + d = tor_parse_double("0", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(1,OP_EQ, i); + tt_assert(DBL_TO_U64(d) == 0); + d = tor_parse_double(" ", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(0,OP_EQ, i); + d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(0,OP_EQ, i); + d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,&cp); + tt_int_op(1,OP_EQ, i); + d = tor_parse_double("-.0", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(1,OP_EQ, i); + tt_assert(DBL_TO_U64(d) == 0); + d = tor_parse_double("-10", -100.0, 100.0,&i,NULL); + tt_int_op(1,OP_EQ, i); + tt_double_op(fabs(d - -10.0),OP_LT, 1E-12); + } + + { + /* Test tor_parse_* where we overflow/underflow the underlying type. */ + /* This string should overflow 64-bit ints. */ +#define TOOBIG "100000000000000000000000000" + tt_int_op(0L, OP_EQ, + tor_parse_long(TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL)); + tt_int_op(i,OP_EQ, 0); + tt_int_op(0L,OP_EQ, + tor_parse_long("-"TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL)); + tt_int_op(i,OP_EQ, 0); + tt_int_op(0UL,OP_EQ, tor_parse_ulong(TOOBIG, 10, 0, ULONG_MAX, &i, NULL)); + tt_int_op(i,OP_EQ, 0); + tt_u64_op(U64_LITERAL(0), OP_EQ, tor_parse_uint64(TOOBIG, 10, + 0, UINT64_MAX, &i, NULL)); + tt_int_op(i,OP_EQ, 0); + } + done: + tor_end_capture_bugs_(); +} + +static void test_util_pow2(void *arg) { /* Test tor_log2(). */ @@ -1737,22 +2236,21 @@ test_util_gzip(void *arg) (void)arg; buf1 = tor_strdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ"); tt_assert(detect_compression_method(buf1, strlen(buf1)) == UNKNOWN_METHOD); - if (is_gzip_supported()) { - 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_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); - - tor_free(buf2); - tor_free(buf3); - } + + 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_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); + + tor_free(buf2); + tor_free(buf3); tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1, ZLIB_METHOD)); @@ -1836,6 +2334,58 @@ test_util_gzip(void *arg) tor_free(buf1); } +static void +test_util_gzip_compression_bomb(void *arg) +{ + /* A 'compression bomb' is a very small object that uncompresses to a huge + * one. Most compression formats support them, but they can be a DOS vector. + * In Tor we try not to generate them, and we don't accept them. + */ + (void) arg; + size_t one_million = 1<<20; + char *one_mb = tor_malloc_zero(one_million); + char *result = NULL; + size_t result_len = 0; + tor_zlib_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)); + expect_single_log_msg_containing( + "We compressed something and got an insanely high " + "compression factor; other Tors would think this " + "was a zlib 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)); + + /* Now try streaming that. */ + state = tor_zlib_new(0, ZLIB_METHOD, HIGH_COMPRESSION); + tor_zlib_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); + tt_int_op(inlen, OP_NE, 0); + } while (r == TOR_ZLIB_BUF_FULL); + + tt_int_op(r, OP_EQ, TOR_ZLIB_ERR); + + done: + tor_free(one_mb); + tor_zlib_free(state); +} + /** Run unit tests for mmap() wrapper functionality. */ static void test_util_mmap(void *arg) @@ -2842,19 +3392,40 @@ test_util_memarea(void *arg) p1 = memarea_alloc(area, 1); tt_ptr_op(p1,OP_EQ, p1_orig); memarea_clear(area); + size_t total = 0, initial_allocation, allocation2, dummy; + memarea_get_stats(area, &initial_allocation, &dummy); /* Check for running over an area's size. */ - for (i = 0; i < 512; ++i) { - p1 = memarea_alloc(area, crypto_rand_int(5)+1); + for (i = 0; i < 4096; ++i) { + size_t n = crypto_rand_int(6); + p1 = memarea_alloc(area, n); + total += n; tt_assert(memarea_owns_ptr(area, p1)); } memarea_assert_ok(area); + memarea_get_stats(area, &allocation2, &dummy); /* Make sure we can allocate a too-big object. */ p1 = memarea_alloc_zero(area, 9000); p2 = memarea_alloc_zero(area, 16); + total += 9000; + total += 16; tt_assert(memarea_owns_ptr(area, p1)); tt_assert(memarea_owns_ptr(area, p2)); + /* Now test stats... */ + size_t allocated = 0, used = 0; + memarea_get_stats(area, &allocated, &used); + tt_int_op(used, OP_LE, allocated); + tt_int_op(used, OP_GE, total); /* not EQ, because of alignment and headers*/ + tt_int_op(allocated, OP_GT, allocation2); + + tt_int_op(allocation2, OP_GT, initial_allocation); + + memarea_clear(area); + memarea_get_stats(area, &allocated, &used); + tt_int_op(used, OP_LT, 128); /* Not 0, because of header */ + tt_int_op(allocated, OP_EQ, initial_allocation); + done: memarea_drop_all(area); tor_free(malloced_ptr); @@ -3244,6 +3815,21 @@ test_util_ftruncate(void *ptr) tor_free(buf); } +static void +test_util_num_cpus(void *arg) +{ + (void)arg; + int num = compute_num_cpus(); + if (num < 0) + tt_skip(); + + tt_int_op(num, OP_GE, 1); + tt_int_op(num, OP_LE, 16); + + done: + ; +} + #ifdef _WIN32 static void test_util_load_win_lib(void *ptr) @@ -4209,6 +4795,31 @@ test_util_mathlog(void *arg) } static void +test_util_fraction(void *arg) +{ + uint64_t a,b; + (void)arg; + + a = 99; b = 30; + simplify_fraction64(&a,&b); + tt_u64_op(a, OP_EQ, 33); + tt_u64_op(b, OP_EQ, 10); + + a = 3000000; b = 10000000; + simplify_fraction64(&a,&b); + tt_u64_op(a, OP_EQ, 3); + tt_u64_op(b, OP_EQ, 10); + + a = 0; b = 15; + simplify_fraction64(&a,&b); + tt_u64_op(a, OP_EQ, 0); + tt_u64_op(b, OP_EQ, 1); + + done: + ; +} + +static void test_util_round_to_next_multiple_of(void *arg) { (void)arg; @@ -4223,21 +4834,6 @@ test_util_round_to_next_multiple_of(void *arg) tt_u64_op(round_uint64_to_next_multiple_of(UINT64_MAX,2), ==, UINT64_MAX); - tt_i64_op(round_int64_to_next_multiple_of(0,1), ==, 0); - tt_i64_op(round_int64_to_next_multiple_of(0,7), ==, 0); - - tt_i64_op(round_int64_to_next_multiple_of(99,1), ==, 99); - tt_i64_op(round_int64_to_next_multiple_of(99,7), ==, 105); - tt_i64_op(round_int64_to_next_multiple_of(99,9), ==, 99); - - tt_i64_op(round_int64_to_next_multiple_of(-99,1), ==, -99); - tt_i64_op(round_int64_to_next_multiple_of(-99,7), ==, -98); - tt_i64_op(round_int64_to_next_multiple_of(-99,9), ==, -99); - - tt_i64_op(round_int64_to_next_multiple_of(INT64_MIN,2), ==, INT64_MIN); - tt_i64_op(round_int64_to_next_multiple_of(INT64_MAX,2), ==, - INT64_MAX); - tt_int_op(round_uint32_to_next_multiple_of(0,1), ==, 0); tt_int_op(round_uint32_to_next_multiple_of(0,7), ==, 0); @@ -4407,7 +5003,7 @@ test_util_clamp_double_to_int64(void *arg) { (void)arg; - tt_i64_op(INT64_MIN, ==, clamp_double_to_int64(-INFINITY)); + tt_i64_op(INT64_MIN, ==, clamp_double_to_int64(-INFINITY_DBL)); tt_i64_op(INT64_MIN, ==, clamp_double_to_int64(-1.0 * pow(2.0, 64.0) - 1.0)); tt_i64_op(INT64_MIN, ==, @@ -4420,7 +5016,7 @@ test_util_clamp_double_to_int64(void *arg) tt_i64_op(0, ==, clamp_double_to_int64(-0.9)); tt_i64_op(0, ==, clamp_double_to_int64(-0.1)); tt_i64_op(0, ==, clamp_double_to_int64(0.0)); - tt_i64_op(0, ==, clamp_double_to_int64(NAN)); + tt_i64_op(0, ==, clamp_double_to_int64(NAN_DBL)); tt_i64_op(0, ==, clamp_double_to_int64(0.1)); tt_i64_op(0, ==, clamp_double_to_int64(0.9)); tt_i64_op(1, ==, clamp_double_to_int64(1.0)); @@ -4432,7 +5028,7 @@ test_util_clamp_double_to_int64(void *arg) clamp_double_to_int64(pow(2.0, 63.0))); tt_i64_op(INT64_MAX, ==, clamp_double_to_int64(pow(2.0, 64.0))); - tt_i64_op(INT64_MAX, ==, clamp_double_to_int64(INFINITY)); + tt_i64_op(INT64_MAX, ==, clamp_double_to_int64(INFINITY_DBL)); done: ; @@ -4507,25 +5103,61 @@ test_util_socket(void *arg) tt_int_op(fd_is_nonblocking(fd4), OP_EQ, 1); #endif - tor_close_socket(fd1); - tor_close_socket(fd2); + tor_assert(tor_close_socket == tor_close_socket__real); + + /* we use close_socket__real here so that coverity can tell that we are + * really closing these sockets. */ + tor_close_socket__real(fd1); + tor_close_socket__real(fd2); fd1 = fd2 = TOR_INVALID_SOCKET; tt_int_op(get_n_open_sockets(), OP_EQ, n + 2); - tor_close_socket(fd3); - tor_close_socket(fd4); + tor_close_socket__real(fd3); + tor_close_socket__real(fd4); fd3 = fd4 = TOR_INVALID_SOCKET; tt_int_op(get_n_open_sockets(), OP_EQ, n); done: if (SOCKET_OK(fd1)) - tor_close_socket(fd1); + tor_close_socket__real(fd1); if (SOCKET_OK(fd2)) - tor_close_socket(fd2); + tor_close_socket__real(fd2); if (SOCKET_OK(fd3)) - tor_close_socket(fd3); + tor_close_socket__real(fd3); if (SOCKET_OK(fd4)) - tor_close_socket(fd4); + tor_close_socket__real(fd4); +} + +#if 0 +static int +is_there_a_localhost(int family) +{ + tor_socket_t s; + s = tor_open_socket(family, SOCK_STREAM, IPPROTO_TCP); + tor_assert(SOCKET_OK(s)); + + int result = 0; + if (family == AF_INET) { + struct sockaddr_in s_in; + memset(&s_in, 0, sizeof(s_in)); + s_in.sin_family = AF_INET; + s_in.sin_addr.s_addr = htonl(0x7f000001); + s_in.sin_port = 0; + + if (bind(s, (void*)&s_in, sizeof(s_in)) == 0) { + result = 1; + } + } else if (family == AF_INET6) { + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr.s6_addr[15] = 1; + sin6.sin6_port = 0; + } + tor_close_socket(s); + + return result; } +#endif /* Test for socketpair and ersatz_socketpair(). We test them both, since * the latter is a tolerably good way to exersize tor_accept_socket(). */ @@ -4541,15 +5173,18 @@ test_util_socketpair(void *arg) int socketpair_result = 0; socketpair_result = tor_socketpair_fn(family, SOCK_STREAM, 0, fds); - /* If there is no 127.0.0.1 or ::1, tor_ersatz_socketpair will and must fail. + +#ifdef __FreeBSD__ + /* If there is no 127.0.0.1, tor_ersatz_socketpair will and must fail. * Otherwise, we risk exposing a socketpair on a routable IP address. (Some * BSD jails use a routable address for localhost. Fortunately, they have * the real AF_UNIX socketpair.) */ - if (ersatz && ERRNO_IS_EPROTO(-socketpair_result)) { + if (ersatz && socketpair_result < 0) { /* In my testing, an IPv6-only FreeBSD jail without ::1 returned EINVAL. * Assume we're on a machine without 127.0.0.1 or ::1 and give up now. */ - goto done; + tt_skip(); } +#endif tt_int_op(0, OP_EQ, socketpair_result); tt_assert(SOCKET_OK(fds[0])); @@ -4780,12 +5415,203 @@ test_util_pwdb(void *arg) dir = get_user_homedir(name); tt_assert(dir != NULL); + /* Try failing cases. First find a user that doesn't exist by name */ + char randbytes[4]; + char badname[9]; + int i, found=0; + for (i = 0; i < 100; ++i) { + crypto_rand(randbytes, sizeof(randbytes)); + base16_encode(badname, sizeof(badname), randbytes, sizeof(randbytes)); + if (tor_getpwnam(badname) == NULL) { + found = 1; + break; + } + } + tt_assert(found); + tor_free(dir); + + /* We should do a LOG_ERR */ + setup_full_capture_of_logs(LOG_ERR); + dir = get_user_homedir(badname); + tt_assert(dir == NULL); + expect_log_msg_containing("not found"); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); + teardown_capture_of_logs(); + + /* Now try to find a user that doesn't exist by ID. */ + found = 0; + for (i = 0; i < 1000; ++i) { + uid_t u; + crypto_rand((char*)&u, sizeof(u)); + if (tor_getpwuid(u) == NULL) { + found = 1; + break; + } + } + tt_assert(found); + done: tor_free(name); tor_free(dir); + teardown_capture_of_logs(); } #endif +static void +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)); + + /* 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)); + 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)); + + /* 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)); + + done: + ; +} + +static void +test_util_monotonic_time(void *arg) +{ + (void)arg; + + monotime_t mt1, mt2; + monotime_coarse_t mtc1, mtc2; + uint64_t nsec1, nsec2, usec1, msec1; + uint64_t nsecc1, nsecc2, usecc1, msecc1; + + monotime_init(); + + monotime_get(&mt1); + monotime_coarse_get(&mtc1); + nsec1 = monotime_absolute_nsec(); + usec1 = monotime_absolute_usec(); + msec1 = monotime_absolute_msec(); + nsecc1 = monotime_coarse_absolute_nsec(); + usecc1 = monotime_coarse_absolute_usec(); + msecc1 = monotime_coarse_absolute_msec(); + + tor_sleep_msec(200); + + monotime_get(&mt2); + monotime_coarse_get(&mtc2); + nsec2 = monotime_absolute_nsec(); + nsecc2 = monotime_coarse_absolute_nsec(); + + /* We need to be a little careful here since we don't know the system load. + */ + tt_i64_op(monotime_diff_msec(&mt1, &mt2), OP_GE, 175); + tt_i64_op(monotime_diff_msec(&mt1, &mt2), OP_LT, 1000); + tt_i64_op(monotime_coarse_diff_msec(&mtc1, &mtc2), OP_GE, 125); + tt_i64_op(monotime_coarse_diff_msec(&mtc1, &mtc2), OP_LT, 1000); + tt_u64_op(nsec2-nsec1, OP_GE, 175000000); + tt_u64_op(nsec2-nsec1, OP_LT, 1000000000); + tt_u64_op(nsecc2-nsecc1, OP_GE, 125000000); + tt_u64_op(nsecc2-nsecc1, OP_LT, 1000000000); + + tt_u64_op(msec1, OP_GE, nsec1 / 1000000); + tt_u64_op(usec1, OP_GE, nsec1 / 1000); + tt_u64_op(msecc1, OP_GE, nsecc1 / 1000000); + tt_u64_op(usecc1, OP_GE, nsecc1 / 1000); + tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 1); + tt_u64_op(usec1, OP_LE, nsec1 / 1000 + 1000); + tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 1); + tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 1000); + + done: + ; +} + +static void +test_util_monotonic_time_ratchet(void *arg) +{ + (void)arg; + monotime_init(); + monotime_reset_ratchets_for_testing(); + + /* win32, performance counter ratchet. */ + tt_i64_op(100, OP_EQ, ratchet_performance_counter(100)); + tt_i64_op(101, OP_EQ, ratchet_performance_counter(101)); + tt_i64_op(2000, OP_EQ, ratchet_performance_counter(2000)); + tt_i64_op(2000, OP_EQ, ratchet_performance_counter(100)); + tt_i64_op(2005, OP_EQ, ratchet_performance_counter(105)); + tt_i64_op(3005, OP_EQ, ratchet_performance_counter(1105)); + tt_i64_op(3005, OP_EQ, ratchet_performance_counter(1000)); + tt_i64_op(3010, OP_EQ, ratchet_performance_counter(1005)); + + /* win32, GetTickCounts32 ratchet-and-rollover-detector. */ + const int64_t R = ((int64_t)1) << 32; + tt_i64_op(5, OP_EQ, ratchet_coarse_performance_counter(5)); + tt_i64_op(1000, OP_EQ, ratchet_coarse_performance_counter(1000)); + tt_i64_op(5+R, OP_EQ, ratchet_coarse_performance_counter(5)); + tt_i64_op(10+R, OP_EQ, ratchet_coarse_performance_counter(10)); + tt_i64_op(4+R*2, OP_EQ, ratchet_coarse_performance_counter(4)); + + /* gettimeofday regular ratchet. */ + struct timeval tv_in = {0,0}, tv_out; + tv_in.tv_usec = 9000; + + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 9000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 0); + + tv_in.tv_sec = 1337; + tv_in.tv_usec = 0; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 0); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1337); + + tv_in.tv_sec = 1336; + tv_in.tv_usec = 500000; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 0); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1337); + + tv_in.tv_sec = 1337; + tv_in.tv_usec = 0; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 500000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1337); + + tv_in.tv_sec = 1337; + tv_in.tv_usec = 600000; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 100000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1338); + + tv_in.tv_sec = 1000; + tv_in.tv_usec = 1000; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 100000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1338); + + tv_in.tv_sec = 2000; + tv_in.tv_usec = 2000; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 101000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 2338); + + done: + ; +} + #define UTIL_LEGACY(name) \ { #name, test_util_ ## name , 0, NULL, NULL } @@ -4809,17 +5635,20 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(config_line_quotes), UTIL_LEGACY(config_line_comment_character), UTIL_LEGACY(config_line_escaped_content), + UTIL_LEGACY(config_line_crlf), UTIL_LEGACY_NO_WIN(expand_filename), UTIL_LEGACY(escape_string_socks), UTIL_LEGACY(string_is_key_value), UTIL_LEGACY(strmisc), + UTIL_TEST(parse_integer, 0), UTIL_LEGACY(pow2), UTIL_LEGACY(gzip), + UTIL_TEST(gzip_compression_bomb, TT_FORK), UTIL_LEGACY(datadir), UTIL_LEGACY(memarea), UTIL_LEGACY(control_formats), UTIL_LEGACY(mmap), - UTIL_LEGACY(sscanf), + UTIL_TEST(sscanf, TT_FORK), UTIL_LEGACY(format_time_interval), UTIL_LEGACY(path_is_relative), UTIL_LEGACY(strtok), @@ -4834,6 +5663,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(listdir, 0), UTIL_TEST(parent_dir, 0), UTIL_TEST(ftruncate, 0), + 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), @@ -4855,6 +5685,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(read_file_eof_zero_bytes, 0), UTIL_TEST(write_chunks_to_file, 0), UTIL_TEST(mathlog, 0), + UTIL_TEST(fraction, 0), UTIL_TEST(weak_random, 0), { "socket_ipv4", test_util_socket, TT_FORK, &passthrough_setup, (void*)"4" }, @@ -4871,6 +5702,9 @@ struct testcase_t util_tests[] = { UTIL_TEST(get_avail_disk_space, 0), UTIL_TEST(touch_file, 0), UTIL_TEST_NO_WIN(pwdb, TT_FORK), + UTIL_TEST(calloc_check, 0), + UTIL_TEST(monotonic_time, 0), + UTIL_TEST(monotonic_time_ratchet, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c index 3d02930983..63a668238c 100644 --- a/src/test/test_util_format.c +++ b/src/test/test_util_format.c @@ -263,14 +263,14 @@ test_util_format_base16_decode(void *ignored) res = base16_decode(dst, 1, src, 10); tt_int_op(res, OP_EQ, -1); - res = base16_decode(dst, SIZE_T_CEILING+2, src, 10); + res = base16_decode(dst, ((size_t)INT_MAX)+1, src, 10); tt_int_op(res, OP_EQ, -1); res = base16_decode(dst, 1000, "", 0); tt_int_op(res, OP_EQ, 0); res = base16_decode(dst, 1000, "aabc", 4); - tt_int_op(res, OP_EQ, 0); + tt_int_op(res, OP_EQ, 2); tt_mem_op(dst, OP_EQ, "\xaa\xbc", 2); res = base16_decode(dst, 1000, "aabcd", 6); @@ -280,7 +280,7 @@ test_util_format_base16_decode(void *ignored) tt_int_op(res, OP_EQ, -1); res = base16_decode(real_dst, 10, real_src, 14); - tt_int_op(res, OP_EQ, 0); + tt_int_op(res, OP_EQ, 7); tt_mem_op(real_dst, OP_EQ, expected, 7); done: @@ -289,6 +289,95 @@ test_util_format_base16_decode(void *ignored) tor_free(real_dst); } +static void +test_util_format_base32_encode(void *arg) +{ + (void) arg; + size_t real_dstlen = 32; + char *dst = tor_malloc_zero(real_dstlen); + + /* Basic use case that doesn't require a source length correction. */ + { + /* Length of 10 bytes. */ + const char *src = "blahbleh12"; + size_t srclen = strlen(src); + /* Expected result encoded base32. This was created using python as + * such (and same goes for all test case.): + * + * b = bytes("blahbleh12", 'utf-8') + * base64.b32encode(b) + * (result in lower case) + */ + const char *expected = "mjwgc2dcnrswqmjs"; + + base32_encode(dst, base32_encoded_size(srclen), src, srclen); + tt_mem_op(expected, OP_EQ, dst, strlen(expected)); + /* Encode but to a larger size destination. */ + memset(dst, 0, real_dstlen); + base32_encode(dst, real_dstlen, src, srclen); + tt_mem_op(expected, OP_EQ, dst, strlen(expected)); + } + + /* Non multiple of 5 for the source buffer length. */ + { + /* Length of 8 bytes. */ + const char *expected = "mjwgc2dcnrswq"; + const char *src = "blahbleh"; + size_t srclen = strlen(src); + + memset(dst, 0, real_dstlen); + base32_encode(dst, base32_encoded_size(srclen), src, srclen); + tt_mem_op(expected, OP_EQ, dst, strlen(expected)); + } + + done: + tor_free(dst); +} + +static void +test_util_format_base32_decode(void *arg) +{ + (void) arg; + int ret; + size_t real_dstlen = 32; + char *dst = tor_malloc_zero(real_dstlen); + + /* Basic use case. */ + { + /* Length of 10 bytes. */ + const char *expected = "blahbleh12"; + /* Expected result encoded base32. */ + const char *src = "mjwgc2dcnrswqmjs"; + + ret = base32_decode(dst, strlen(expected), src, strlen(src)); + tt_int_op(ret, ==, 0); + tt_str_op(expected, OP_EQ, dst); + } + + /* Non multiple of 5 for the source buffer length. */ + { + /* Length of 8 bytes. */ + const char *expected = "blahbleh"; + const char *src = "mjwgc2dcnrswq"; + + ret = base32_decode(dst, strlen(expected), src, strlen(src)); + tt_int_op(ret, ==, 0); + tt_mem_op(expected, OP_EQ, dst, strlen(expected)); + } + + /* Invalid values. */ + { + /* Invalid character '#'. */ + ret = base32_decode(dst, real_dstlen, "#abcde", 6); + tt_int_op(ret, ==, -1); + /* Make sure the destination buffer has been zeroed even on error. */ + tt_int_op(tor_mem_is_zero(dst, real_dstlen), ==, 1); + } + + done: + tor_free(dst); +} + struct testcase_t util_format_tests[] = { { "unaligned_accessors", test_util_format_unaligned_accessors, 0, NULL, NULL }, @@ -297,6 +386,10 @@ struct testcase_t util_format_tests[] = { NULL, NULL }, { "base64_decode", test_util_format_base64_decode, 0, NULL, NULL }, { "base16_decode", test_util_format_base16_decode, 0, NULL, NULL }, + { "base32_encode", test_util_format_base32_encode, 0, + NULL, NULL }, + { "base32_decode", test_util_format_base32_decode, 0, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_util_process.c b/src/test/test_util_process.c index 45c22ef47f..4e75b97f3d 100644 --- a/src/test/test_util_process.c +++ b/src/test/test_util_process.c @@ -26,7 +26,7 @@ test_util_process_set_waitpid_callback(void *ignored) { (void)ignored; waitpid_callback_t *res1 = NULL, *res2 = NULL; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_full_capture_of_logs(LOG_WARN); pid_t pid = (pid_t)42; res1 = set_waitpid_callback(pid, temp_callback, NULL); @@ -34,11 +34,12 @@ test_util_process_set_waitpid_callback(void *ignored) res2 = set_waitpid_callback(pid, temp_callback, NULL); tt_assert(res2); - expect_log_msg("Replaced a waitpid monitor on pid 42. That should be " + expect_single_log_msg( + "Replaced a waitpid monitor on pid 42. That should be " "impossible.\n"); done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); clear_waitpid_callback(res1); clear_waitpid_callback(res2); } @@ -48,7 +49,7 @@ test_util_process_clear_waitpid_callback(void *ignored) { (void)ignored; waitpid_callback_t *res; - int previous_log = setup_capture_of_logs(LOG_WARN); + setup_capture_of_logs(LOG_WARN); pid_t pid = (pid_t)43; clear_waitpid_callback(NULL); @@ -64,7 +65,7 @@ test_util_process_clear_waitpid_callback(void *ignored) #endif done: - teardown_capture_of_logs(previous_log); + teardown_capture_of_logs(); } #endif /* _WIN32 */ diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c index cbcf596b22..ccb8d0c8ca 100644 --- a/src/test/test_workqueue.c +++ b/src/test/test_workqueue.c @@ -12,11 +12,7 @@ #include "compat_libevent.h" #include <stdio.h> -#ifdef HAVE_EVENT2_EVENT_H #include <event2/event.h> -#else -#include <event.h> -#endif #define MAX_INFLIGHT (1<<16) @@ -400,6 +396,9 @@ main(int argc, char **argv) } rq = replyqueue_new(as_flags); + if (as_flags && rq == NULL) + return 77; // 77 means "skipped". + tor_assert(rq); tp = threadpool_new(opt_n_threads, rq, new_state, free_state, NULL); diff --git a/src/test/test_workqueue_cancel.sh b/src/test/test_workqueue_cancel.sh new file mode 100755 index 0000000000..f7c663171e --- /dev/null +++ b/src/test/test_workqueue_cancel.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +${builddir:-.}/src/test/test_workqueue -C 1 + diff --git a/src/test/test_workqueue_efd.sh b/src/test/test_workqueue_efd.sh new file mode 100755 index 0000000000..4d89396819 --- /dev/null +++ b/src/test/test_workqueue_efd.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +${builddir:-.}/src/test/test_workqueue \ + --no-eventfd2 --no-pipe2 --no-pipe --no-socketpair diff --git a/src/test/test_workqueue_efd2.sh b/src/test/test_workqueue_efd2.sh new file mode 100755 index 0000000000..7cfff45ff3 --- /dev/null +++ b/src/test/test_workqueue_efd2.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +${builddir:-.}/src/test/test_workqueue \ + --no-eventfd --no-pipe2 --no-pipe --no-socketpair diff --git a/src/test/test_workqueue_pipe.sh b/src/test/test_workqueue_pipe.sh new file mode 100755 index 0000000000..afcef87853 --- /dev/null +++ b/src/test/test_workqueue_pipe.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +${builddir:-.}/src/test/test_workqueue \ + --no-eventfd2 --no-eventfd --no-pipe2 --no-socketpair diff --git a/src/test/test_workqueue_pipe2.sh b/src/test/test_workqueue_pipe2.sh new file mode 100755 index 0000000000..a20a1427e0 --- /dev/null +++ b/src/test/test_workqueue_pipe2.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +${builddir:-.}/src/test/test_workqueue \ + --no-eventfd2 --no-eventfd --no-pipe --no-socketpair diff --git a/src/test/test_workqueue_socketpair.sh b/src/test/test_workqueue_socketpair.sh new file mode 100755 index 0000000000..76af79746d --- /dev/null +++ b/src/test/test_workqueue_socketpair.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +${builddir:-.}/src/test/test_workqueue \ + --no-eventfd2 --no-eventfd --no-pipe2 --no-pipe diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 39c3d02ab1..9c6580f788 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -3,6 +3,8 @@ * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +extern const char tor_git_revision[]; + /* Ordinarily defined in tor_main.c; this bit is just here to provide one * since we're not linking to tor_main.c */ const char tor_git_revision[] = ""; @@ -100,18 +102,41 @@ setup_directory(void) temp_dir_setup_in_pid = getpid(); } -/** Return a filename relative to our testing temporary directory */ -const char * -get_fname(const char *name) +/** Return a filename relative to our testing temporary directory, based on + * name and suffix. If name is NULL, return the name of the testing temporary + * directory. */ +static const char * +get_fname_suffix(const char *name, const char *suffix) { static char buf[1024]; setup_directory(); if (!name) return temp_dir; - tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name); + tor_snprintf(buf,sizeof(buf),"%s/%s%s%s",temp_dir,name,suffix ? "_" : "", + suffix ? suffix : ""); return buf; } +/** Return a filename relative to our testing temporary directory. If name is + * NULL, return the name of the testing temporary directory. */ +const char * +get_fname(const char *name) +{ + return get_fname_suffix(name, NULL); +} + +/** Return a filename with a random suffix, relative to our testing temporary + * directory. If name is NULL, return the name of the testing temporary + * directory, without any suffix. */ +const char * +get_fname_rnd(const char *name) +{ + char rnd[256], rnd32[256]; + crypto_rand(rnd, RAND_PATH_BYTES); + base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES); + return get_fname_suffix(name, rnd32); +} + /* Remove a directory and all of its subdirectories */ static void rm_rf(const char *dir) @@ -154,37 +179,51 @@ remove_directory(void) } /** Define this if unit tests spend too much time generating public keys*/ -#undef CACHE_GENERATED_KEYS +#define CACHE_GENERATED_KEYS -static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL}; -#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_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: we only guarantee that - * keys made with distinct values for <b>idx</b> are different. The value of - * <b>idx</b> must be at least 0, and less than N_PREGEN_KEYS. */ + * 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) { - int res; + (void) idx; #ifdef CACHE_GENERATED_KEYS - tor_assert(idx < N_PREGEN_KEYS); - if (! pregen_keys[idx]) { - pregen_keys[idx] = crypto_pk_new(); - res = crypto_pk_generate_key(pregen_keys[idx]); - tor_assert(!res); - } - return crypto_pk_dup_key(pregen_keys[idx]); + /* 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; - (void) idx; + int res; result = crypto_pk_new(); - res = crypto_pk_generate_key(result); + 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) @@ -201,6 +240,9 @@ free_pregenerated_keys(void) static void * passthrough_test_setup(const struct testcase_t *testcase) { + /* Make sure the passthrough doesn't unintentionally fail or skip tests */ + tor_assert(testcase->setup_data); + tor_assert(testcase->setup_data != (void*)TT_SKIP); return testcase->setup_data; } static int @@ -211,11 +253,33 @@ passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr) return 1; } +static void * +ed25519_testcase_setup(const struct testcase_t *testcase) +{ + crypto_ed25519_testing_force_impl(testcase->setup_data); + return testcase->setup_data; +} +static int +ed25519_testcase_cleanup(const struct testcase_t *testcase, void *ptr) +{ + (void)testcase; + (void)ptr; + crypto_ed25519_testing_restore_impl(); + return 1; +} +const struct testcase_setup_t ed25519_test_setup = { + ed25519_testcase_setup, ed25519_testcase_cleanup +}; + const struct testcase_setup_t passthrough_setup = { passthrough_test_setup, passthrough_test_cleanup }; -extern struct testgroup_t testgroups[]; +static void +an_assertion_failed(void) +{ + tinytest_set_test_failed_(); +} /** Main entry point for unit test code: parse the command line, and run * some unit tests. */ @@ -244,6 +308,8 @@ main(int c, const char **v) network_init(); + monotime_init(); + struct tor_libevent_cfg cfg; memset(&cfg, 0, sizeof(cfg)); tor_libevent_initialize(&cfg); @@ -272,6 +338,8 @@ main(int c, const char **v) 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)); } @@ -295,6 +363,17 @@ 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 atexit(remove_directory); diff --git a/src/test/vote_descriptors.inc b/src/test/vote_descriptors.inc index c5ce21f744..895dc6c65c 100644 --- a/src/test/vote_descriptors.inc +++ b/src/test/vote_descriptors.inc @@ -1,4 +1,4 @@ -const char* VOTE_BODY_V3 = +static const char* VOTE_BODY_V3 = "network-status-version 3\n" "vote-status vote\n" "consensus-methods 13 14 15 16 17 18 19 20 21\n" |