diff options
Diffstat (limited to 'src/test')
53 files changed, 5641 insertions, 434 deletions
diff --git a/src/test/bench.c b/src/test/bench.c index 5aefda5ff2..5595988f31 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[] = ""; 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..d0bc808877 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,7 +22,9 @@ 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 @@ -40,7 +48,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 +95,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 \ @@ -97,6 +107,7 @@ src_test_test_SOURCES = \ src/test/test_policy.c \ src/test/test_procmon.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 +116,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 +139,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 +161,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 +171,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 +184,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 +205,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 +240,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 +250,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 +262,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/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-memwipe.c b/src/test/test-memwipe.c index 5d4fcec664..c28d5054a2 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -6,9 +6,6 @@ #include "crypto.h" #include "compat.h" -#undef MIN -#define MIN(a,b) ( ((a)<(b)) ? (a) : (b) ) - static unsigned fill_a_buffer_memset(void) __attribute__((noinline)); static unsigned fill_a_buffer_memwipe(void) __attribute__((noinline)); static unsigned fill_a_buffer_nothing(void) __attribute__((noinline)); @@ -17,6 +14,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 diff --git a/src/test/test-timers.c b/src/test/test-timers.c new file mode 100644 index 0000000000..0196ec1fef --- /dev/null +++ b/src/test/test-timers.c @@ -0,0 +1,134 @@ +/* Copyright 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#include <math.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_EVENT2_EVENT_H +#include <event2/event.h> +#else +#include <event.h> +#endif + +#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 void +timer_cb(tor_timer_t *t, void *arg, const struct timeval *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]); + ++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); + 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); + 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; + total_difference += diff; + total_square_difference += diff*diff; + } + const int64_t mean_diff = total_difference / n_active_timers; + printf("mean difference: "U64_FORMAT" usec\n", + U64_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..3a1054decf 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1124,60 +1124,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 }, @@ -1224,13 +1170,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..6744d255f1 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -73,7 +73,7 @@ {print_ = (I64_PRINTF_TYPE) value_;}, {}, TT_EXIT_TEST_FUNCTION) const char *get_fname(const char *name); -crypto_pk_t *pk_generate(int idx); +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 +163,87 @@ 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 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 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_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..95584d54a8 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -695,9 +695,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) 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..394612f155 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -185,7 +185,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 +206,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_controller.c b/src/test/test_controller.c index 7f9db4312f..0dea8473b9 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 @@ -154,10 +157,1139 @@ test_rend_service_parse_port_config(void *arg) 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..ba2fb86246 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) @@ -312,6 +510,43 @@ test_crypto_aes(void *arg) tor_free(data3); } +static void +test_crypto_aes_ctr_testvec(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp=NULL; + + /* from NIST SP800-38a, section F.5 */ + const char key16[] = "2b7e151628aed2a6abf7158809cf4f3c"; + const char ctr16[] = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + const char plaintext16[] = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710"; + const char ciphertext16[] = + "874d6191b620e3261bef6864990db6ce" + "9806f66b7970fdff8617187bb9fffdff" + "5ae4df3edbd5d35e5b4f09020db03eab" + "1e031dda2fbe03d1792170a0f3009cee"; + + char key[16]; + char iv[16]; + char plaintext[16*4]; + base16_decode(key, sizeof(key), key16, strlen(key16)); + base16_decode(iv, sizeof(iv), ctr16, strlen(ctr16)); + base16_decode(plaintext, sizeof(plaintext), + plaintext16, strlen(plaintext16)); + + crypto_cipher_t *c = crypto_cipher_new_with_iv(key, iv); + 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) @@ -1084,6 +1319,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 +1372,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 +1521,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 +1792,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 +1975,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; @@ -1896,7 +2307,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 +2537,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]); @@ -2183,6 +2655,54 @@ test_crypto_ed25519_fuzz_donna(void *arg) } static void +test_crypto_ed25519_storage(void *arg) +{ + (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; + + 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(-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_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); + + /* 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 test_crypto_siphash(void *arg) { /* From the reference implementation, taking @@ -2398,13 +2918,23 @@ 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 }, + { "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_aes, TT_FORK, &passthrough_setup, (void*)"aes" }, { "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" }, + { "aes_ctr_testvec", test_crypto_aes_ctr_testvec, 0, NULL, NULL }, 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 +2945,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 }, @@ -2429,6 +2961,7 @@ struct testcase_t crypto_tests[] = { 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_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..8889ccc41b 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,6 +31,7 @@ #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" @@ -192,7 +194,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 */ @@ -580,7 +582,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) @@ -1435,6 +1437,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 +1560,44 @@ 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. */ + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "moomin", -100)); + /* fred and jack are truncated */ + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "fred", -100)); + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "jack", -100)); + /* electricity and opa aren't integers. */ + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "electricity", -100)); + tt_int_op(-100, OP_EQ, + dirvote_get_intermediate_param_value(lst, "opa", -100)); + + done: + SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp)); + smartlist_free(lst); +} + +#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. */ @@ -1788,6 +1842,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 +1894,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)); @@ -2196,56 +2279,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 +2340,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 +2350,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 +2382,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 +2931,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 +2949,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 +3137,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 +3146,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 @@ -3332,13 +3416,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 +3441,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 +3450,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 +3459,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 +3468,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 +3477,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 +3486,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 +3499,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 +3508,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 +3521,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 +3530,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 +3543,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 +3552,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 +3564,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 double, and maybe add one */ + tt_int_op(increment, OP_LE, 2 * 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; @@ -4042,6 +4184,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 +5232,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,7 +5341,7 @@ 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; } @@ -4230,6 +5369,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 +5382,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,6 +5391,9 @@ 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"), diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c index 0b446c2dfd..2448d307b2 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. */ diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 05657ca452..3282037243 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 @@ -224,51 +232,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) @@ -438,7 +401,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data) 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 +416,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)); @@ -504,7 +467,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; } @@ -565,7 +528,7 @@ test_dir_handle_get_micro_d(void *data) /* Make the request */ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); - 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, @@ -635,7 +598,7 @@ test_dir_handle_get_micro_d_server_busy(void *data) /* Make the request */ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); - 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, @@ -997,7 +960,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? @@ -1056,8 +1020,9 @@ test_dir_handle_get_server_descriptors_d(void* data) conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); - 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? @@ -1125,8 +1090,9 @@ test_dir_handle_get_server_descriptors_busy(void* data) #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, @@ -1204,8 +1170,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,7 +1201,7 @@ 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)); @@ -1396,11 +1360,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)); 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, @@ -1468,11 +1433,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)); 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, @@ -1550,13 +1516,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)); 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 +1573,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); @@ -1617,7 +1584,7 @@ test_dir_handle_get_server_keys_busy(void* data) conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR)); 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, @@ -2344,7 +2311,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; @@ -2423,7 +2390,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; @@ -2484,7 +2451,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_guardfraction.c b/src/test/test_guardfraction.c index 300590a3d9..130aff11aa 100644 --- a/src/test/test_guardfraction.c +++ b/src/test/test_guardfraction.c @@ -40,7 +40,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); } 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..1daa1552e9 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -435,6 +435,67 @@ 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; +} + struct testcase_t hs_tests[] = { { "hs_rend_data", test_hs_rend_data, TT_FORK, NULL, NULL }, @@ -445,6 +506,8 @@ 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 }, 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..4038783459 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -16,7 +16,7 @@ #include "test.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) 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..2afbdde88a 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. */ @@ -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_options.c b/src/test/test_options.c index 4f24757a85..8d1d6f901e 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" @@ -513,8 +513,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); @@ -571,8 +572,6 @@ test_options_validate__contactinfo(void *ignored) tor_free(msg); } -extern int quiet_level; - static void test_options_validate__logs(void *ignored) { diff --git a/src/test/test_policy.c b/src/test/test_policy.c index a939ebf54f..14182af86b 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -778,8 +778,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 +804,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); 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_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..e210e053b6 100644 --- a/src/test/test_rendcache.c +++ b/src/test/test_rendcache.c @@ -17,13 +17,8 @@ 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) @@ -976,7 +971,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..15fbb2d186 100644 --- a/src/test/test_scheduler.c +++ b/src/test/test_scheduler.c @@ -24,12 +24,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; diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c new file mode 100644 index 0000000000..886cee3a33 --- /dev/null +++ b/src/test/test_shared_random.c @@ -0,0 +1,1280 @@ +#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" + +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(); + + (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; + tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit)); + 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)); + tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit)); + 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. */ + { + sr_commit_t *parsed_commit; + 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, our_commit->encoded_commit); + smartlist_add(args, our_commit->encoded_reveal); + parsed_commit = sr_parse_commit(args); + tt_assert(parsed_commit); + /* That parsed commit should be _EXACTLY_ like our original commit (we + * 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 */ + tor_free(smartlist_get(args, 0)); /* strdup here. */ + tor_free(smartlist_get(args, 1)); /* strdup here. */ + smartlist_clear(args); + sr_commit_free(parsed_commit); + } + + done: + smartlist_free(args); + sr_commit_free(our_commit); +} + +/* Test the encoding and decoding function for commit and reveal values. */ +static void +test_encoding(void *arg) +{ + (void) arg; + int ret, duper_rand = 42; + /* 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; + + /* Encoded commit is: base64-encode( 1454333590 || H(H(42)) ). Remember + * that we do no expose the raw bytes of our PRNG to the network thus + * explaining the double H(). */ + static const char *encoded_commit = + "AAAAAFavXpZbx2LRneYFSLPCP8DLp9BXfeH5FXzbkxM4iRXKGeA54g=="; + /* Encoded reveal is: base64-encode( 1454333590 || H(42) ). */ + static const char *encoded_reveal = + "AAAAAFavXpYk9x9kTjiQWUqjHwSAEOdPAfCaurXgjPy173SzYjeC2g=="; + + /* Set up our raw random bytes array. */ + memset(raw_rand, 0, sizeof(raw_rand)); + memcpy(raw_rand, &duper_rand, sizeof(duper_rand)); + /* Hash random number. */ + 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); + } + + 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); +#ifdef _WIN32 + tt_int_op(ret, OP_EQ, -EACCES); +#else + tt_int_op(ret, OP_EQ, -EISDIR); +#endif + + /* 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: + return; +} + +/** 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); + tt_int_op(get_n_authorities(V3_DIRINFO), ==, 9); + + { /* 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); + } + + /* 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()); + } + + /* 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); + + (void) arg; + + MOCK(trusteddirserver_get_by_v3_auth_digest, + trusteddirserver_get_by_v3_auth_digest_m); + + { /* Setup a minimal dirauth environment for this test */ + crypto_pk_t *k = crypto_pk_new(); + /* Have a key that is not the one from our commit. */ + tt_int_op(0, ==, crypto_pk_generate_key(k)); + tt_int_op(0, ==, crypto_pk_get_fingerprint(k, fp, 0)); + 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)); + tt_int_op(should_keep_commit(commit, commit->rsa_identity, + SR_PHASE_REVEAL), ==, 0); + 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: + sr_commit_free(commit); + sr_commit_free(dup_commit); + 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..b4438aabe9 100644 --- a/src/test/test_status.c +++ b/src/test/test_status.c @@ -310,8 +310,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_tortls.c b/src/test/test_tortls.c index b9b74a1e96..3a048fb1f0 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,9 +38,6 @@ #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) #define OPENSSL_OPAQUE @@ -277,8 +262,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) { @@ -791,8 +774,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) { @@ -1185,9 +1166,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 +1174,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; diff --git a/src/test/test_util.c b/src/test/test_util.c index d534cc0b52..c331a662a5 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -30,6 +30,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 +261,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 +271,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 */ @@ -622,9 +915,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)); @@ -865,106 +1165,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 +1299,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 +1350,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 +1363,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 +1420,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,36 +1478,81 @@ 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); @@ -1428,6 +1777,11 @@ test_util_strmisc(void *arg) 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 */ + 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(0L,OP_EQ, tor_parse_long("-50",10,100,-100,&i,NULL)); + tt_int_op(0,OP_EQ, i); /* 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); @@ -1472,24 +1826,24 @@ test_util_strmisc(void *arg) { /* Test parse_double */ - double d = tor_parse_double("10", 0, UINT64_MAX,&i,NULL); + 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, UINT64_MAX,&i,NULL); + 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, UINT64_MAX,&i,NULL); + d = tor_parse_double(" ", 0, (double)UINT64_MAX,&i,NULL); tt_int_op(0,OP_EQ, i); - d = tor_parse_double(".0a", 0, UINT64_MAX,&i,NULL); + d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,NULL); tt_int_op(0,OP_EQ, i); - d = tor_parse_double(".0a", 0, UINT64_MAX,&i,&cp); + d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,&cp); tt_int_op(1,OP_EQ, i); - d = tor_parse_double("-.0", 0, UINT64_MAX,&i,NULL); + 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_int_op(-10.0,OP_EQ, d); + tt_double_op(fabs(d - -10.0),OP_LT, 1E-12); } { @@ -1583,6 +1937,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"; @@ -1737,22 +2102,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 +2200,52 @@ 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 */ + tt_int_op(-1, OP_EQ, tor_gzip_compress(&result, &result_len, + one_mb, one_million, + ZLIB_METHOD)); + + /* 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 +3252,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 +3675,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) @@ -4223,21 +4669,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 +4838,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 +4851,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 +4863,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: ; @@ -4780,12 +5211,71 @@ 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 rand[4]; + char badname[9]; + int i, found=0; + for (i = 0; i < 100; ++i) { + crypto_rand(rand, sizeof(rand)); + base16_encode(badname, sizeof(badname), rand, sizeof(rand)); + if (tor_getpwnam(badname) == NULL) { + found = 1; + break; + } + } + tt_assert(found); + tor_free(dir); + dir = get_user_homedir(badname); + tt_assert(dir == NULL); + + /* 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); } #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: + ; +} + #define UTIL_LEGACY(name) \ { #name, test_util_ ## name , 0, NULL, NULL } @@ -4815,11 +5305,12 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(strmisc), UTIL_LEGACY(pow2), UTIL_LEGACY(gzip), + UTIL_LEGACY(gzip_compression_bomb), 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 +5325,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), @@ -4871,6 +5363,7 @@ 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), END_OF_TESTCASES }; diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c index a25054cd0a..f3dcd1184c 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_workqueue.c b/src/test/test_workqueue.c index cbcf596b22..75d68a7ce0 100644 --- a/src/test/test_workqueue.c +++ b/src/test/test_workqueue.c @@ -400,6 +400,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..ea9366305c 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[] = ""; @@ -215,8 +217,6 @@ const struct testcase_setup_t passthrough_setup = { passthrough_test_setup, passthrough_test_cleanup }; -extern struct testgroup_t testgroups[]; - /** Main entry point for unit test code: parse the command line, and run * some unit tests. */ int 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" |