aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/Makefile.nmake8
-rw-r--r--src/test/bench.c119
-rw-r--r--src/test/conf_examples/badnick_1/error1
-rw-r--r--src/test/conf_examples/badnick_1/torrc2
-rw-r--r--src/test/conf_examples/badnick_2/error1
-rw-r--r--src/test/conf_examples/badnick_2/torrc2
-rw-r--r--src/test/conf_examples/contactinfo_notutf8/error1
-rw-r--r--src/test/conf_examples/contactinfo_notutf8/torrc1
-rw-r--r--src/test/conf_examples/example_1/expected2
-rw-r--r--src/test/conf_examples/example_1/torrc5
-rw-r--r--src/test/conf_examples/example_2/error1
-rw-r--r--src/test/conf_examples/example_2/torrc1
-rw-r--r--src/test/conf_examples/example_3/cmdline1
-rw-r--r--src/test/conf_examples/example_3/expected1
-rw-r--r--src/test/conf_examples/example_3/torrc0
-rw-r--r--src/test/conf_examples/include_1/expected3
-rw-r--r--src/test/conf_examples/include_1/included.inc4
-rw-r--r--src/test/conf_examples/include_1/nested.inc2
-rw-r--r--src/test/conf_examples/include_1/torrc4
-rw-r--r--src/test/conf_examples/include_bug_31408/expected2
-rw-r--r--src/test/conf_examples/include_bug_31408/included/01_nickname.inc1
-rw-r--r--src/test/conf_examples/include_bug_31408/included/02_no_configs.inc3
-rw-r--r--src/test/conf_examples/include_bug_31408/torrc2
-rw-r--r--src/test/conf_examples/large_1/expected159
-rw-r--r--src/test/conf_examples/large_1/torrc167
-rw-r--r--src/test/conf_examples/obsolete_1/expected0
-rw-r--r--src/test/conf_examples/obsolete_1/torrc68
-rw-r--r--src/test/conf_examples/obsolete_2/expected0
-rw-r--r--src/test/conf_examples/obsolete_2/torrc2
-rw-r--r--src/test/conf_examples/ops_1/cmdline1
-rw-r--r--src/test/conf_examples/ops_1/expected2
-rw-r--r--src/test/conf_examples/ops_1/torrc3
-rw-r--r--src/test/conf_examples/ops_2/cmdline1
-rw-r--r--src/test/conf_examples/ops_2/expected0
-rw-r--r--src/test/conf_examples/ops_2/torrc3
-rw-r--r--src/test/conf_examples/ops_3/cmdline1
-rw-r--r--src/test/conf_examples/ops_3/expected3
-rw-r--r--src/test/conf_examples/ops_3/torrc3
-rw-r--r--src/test/conf_examples/ops_4/expected2
-rw-r--r--src/test/conf_examples/ops_4/torrc3
-rw-r--r--src/test/conf_examples/ops_4/torrc.defaults1
-rw-r--r--src/test/conf_examples/ops_5/expected3
-rw-r--r--src/test/conf_examples/ops_5/torrc3
-rw-r--r--src/test/conf_examples/ops_5/torrc.defaults1
-rw-r--r--src/test/conf_examples/ops_6/expected0
-rw-r--r--src/test/conf_examples/ops_6/torrc3
-rw-r--r--src/test/conf_examples/ops_6/torrc.defaults1
-rw-r--r--src/test/conf_examples/relpath_rad/error1
-rw-r--r--src/test/conf_examples/relpath_rad/torrc4
-rwxr-xr-xsrc/test/fuzz/fixup_filenames.sh6
-rw-r--r--src/test/fuzz/fuzz_consensus.c6
-rw-r--r--src/test/fuzz/fuzz_diff.c32
-rw-r--r--src/test/fuzz/fuzz_diff_apply.c13
-rw-r--r--src/test/fuzz/fuzz_hsdescv3.c11
-rw-r--r--src/test/fuzz/fuzz_http.c2
-rw-r--r--src/test/fuzz/fuzz_http_connect.c2
-rwxr-xr-xsrc/test/fuzz/fuzz_multi.sh6
-rw-r--r--src/test/fuzz/fuzz_socks.c2
-rw-r--r--src/test/fuzz/fuzz_strops.c253
-rw-r--r--src/test/fuzz/fuzz_vrs.c24
-rw-r--r--src/test/fuzz/fuzzing.h2
-rw-r--r--src/test/fuzz/fuzzing_common.c21
-rw-r--r--src/test/fuzz/include.am29
-rwxr-xr-xsrc/test/fuzz/minimize.sh2
-rwxr-xr-xsrc/test/fuzz_static_testcases.sh2
-rw-r--r--src/test/hs_test_helpers.c86
-rw-r--r--src/test/include.am68
-rw-r--r--src/test/ope_ref.py2
-rw-r--r--src/test/prob_distr_mpfr_ref.c64
-rw-r--r--src/test/ptr_helpers.c50
-rw-r--r--src/test/ptr_helpers.h23
-rw-r--r--src/test/resolve_test_helpers.c85
-rw-r--r--src/test/resolve_test_helpers.h18
-rw-r--r--src/test/rng_test_helpers.c259
-rw-r--r--src/test/rng_test_helpers.h25
-rw-r--r--src/test/test-child.c61
-rw-r--r--src/test/test-memwipe.c2
-rwxr-xr-xsrc/test/test-network.sh48
-rw-r--r--src/test/test-process.c85
-rw-r--r--src/test/test.c62
-rw-r--r--src/test/test.h54
-rw-r--r--src/test/test_addr.c660
-rw-r--r--src/test/test_address.c166
-rw-r--r--src/test/test_address_set.c15
-rwxr-xr-xsrc/test/test_bt.sh2
-rw-r--r--src/test/test_bt_cl.c10
-rw-r--r--src/test/test_btrack.c129
-rw-r--r--src/test/test_buffers.c2
-rw-r--r--src/test/test_bwmgt.c209
-rw-r--r--src/test/test_channel.c6
-rw-r--r--src/test/test_channelpadding.c9
-rw-r--r--src/test/test_channeltls.c2
-rw-r--r--src/test/test_circuitbuild.c7
-rw-r--r--src/test/test_circuitpadding.c3205
-rw-r--r--src/test/test_circuitstats.c18
-rwxr-xr-xsrc/test/test_cmdline.sh48
-rw-r--r--src/test/test_compat_libevent.c3
-rw-r--r--src/test/test_config.c265
-rw-r--r--src/test/test_confmgr.c325
-rw-r--r--src/test/test_confparse.c1086
-rw-r--r--src/test/test_connection.h4
-rw-r--r--src/test/test_consdiff.c94
-rw-r--r--src/test/test_consdiffmgr.c41
-rw-r--r--src/test/test_containers.c90
-rw-r--r--src/test/test_controller.c322
-rw-r--r--src/test/test_controller_events.c218
-rw-r--r--src/test/test_crypto.c209
-rw-r--r--src/test/test_crypto_rng.c332
-rw-r--r--src/test/test_crypto_slow.c2
-rw-r--r--src/test/test_dir.c1461
-rw-r--r--src/test/test_dir_common.c17
-rw-r--r--src/test/test_dir_common.h4
-rw-r--r--src/test/test_dir_handle_get.c127
-rw-r--r--src/test/test_dispatch.c278
-rw-r--r--src/test/test_dns.c66
-rw-r--r--src/test/test_dos.c2
-rw-r--r--src/test/test_entryconn.c2
-rw-r--r--src/test/test_entrynodes.c67
-rw-r--r--src/test/test_extorport.c35
-rw-r--r--src/test/test_helpers.c64
-rw-r--r--src/test/test_helpers.h6
-rw-r--r--src/test/test_hs.c16
-rw-r--r--src/test/test_hs_cache.c30
-rw-r--r--src/test/test_hs_cell.c100
-rw-r--r--src/test/test_hs_client.c135
-rw-r--r--src/test/test_hs_common.c9
-rw-r--r--src/test/test_hs_config.c117
-rw-r--r--src/test/test_hs_control.c7
-rw-r--r--src/test/test_hs_descriptor.c132
-rw-r--r--src/test/test_hs_dos.c176
-rw-r--r--src/test/test_hs_intropoint.c226
-rw-r--r--src/test/test_hs_service.c169
-rw-r--r--src/test/test_introduce.c3
-rwxr-xr-xsrc/test/test_key_expiration.sh14
-rwxr-xr-xsrc/test/test_keygen.sh46
-rw-r--r--src/test/test_link_handshake.c8
-rw-r--r--src/test/test_logging.c26
-rw-r--r--src/test/test_mainloop.c229
-rw-r--r--src/test/test_microdesc.c169
-rw-r--r--src/test/test_namemap.c174
-rw-r--r--src/test/test_netinfo.c48
-rw-r--r--src/test/test_nodelist.c1030
-rw-r--r--src/test/test_oom.c2
-rw-r--r--src/test/test_options.c69
-rw-r--r--src/test/test_parsecommon.c594
-rwxr-xr-xsrc/test/test_parseconf.sh204
-rw-r--r--src/test/test_periodic_event.c102
-rw-r--r--src/test/test_policy.c285
-rw-r--r--src/test/test_prob_distr.c1402
-rw-r--r--src/test/test_process.c669
-rw-r--r--src/test/test_process_descs.c67
-rw-r--r--src/test/test_process_slow.c365
-rw-r--r--src/test/test_proto_http.c2
-rw-r--r--src/test/test_proto_misc.c2
-rw-r--r--src/test/test_protover.c10
-rw-r--r--src/test/test_pt.c129
-rw-r--r--src/test/test_ptr_slow.c106
-rw-r--r--src/test/test_pubsub_build.c578
-rw-r--r--src/test/test_pubsub_msg.c305
-rw-r--r--src/test/test_rebind.py5
-rwxr-xr-xsrc/test/test_rebind.sh13
-rw-r--r--src/test/test_relaycell.c7
-rw-r--r--src/test/test_relaycrypt.c10
-rw-r--r--src/test/test_rng.c59
-rw-r--r--src/test/test_router.c255
-rw-r--r--src/test/test_routerkeys.c12
-rw-r--r--src/test/test_routerlist.c24
-rw-r--r--src/test/test_routerset.c8
-rwxr-xr-xsrc/test/test_rust.sh5
-rw-r--r--src/test/test_sendme.c365
-rw-r--r--src/test/test_shared_random.c378
-rw-r--r--src/test/test_slow.c4
-rw-r--r--src/test/test_socks.c2
-rw-r--r--src/test/test_status.c24
-rw-r--r--src/test/test_switch_id.c2
-rwxr-xr-xsrc/test/test_switch_id.sh4
-rw-r--r--src/test/test_token_bucket.c152
-rw-r--r--src/test/test_tortls.c6
-rw-r--r--src/test/test_tortls.h2
-rw-r--r--src/test/test_tortls_openssl.c4
-rw-r--r--src/test/test_util.c534
-rw-r--r--src/test/test_util_format.c10
-rw-r--r--src/test/test_util_slow.c396
-rw-r--r--src/test/test_voting_flags.c191
-rw-r--r--src/test/test_workqueue.c2
-rwxr-xr-xsrc/test/test_workqueue_cancel.sh2
-rwxr-xr-xsrc/test/test_workqueue_efd.sh2
-rwxr-xr-xsrc/test/test_workqueue_efd2.sh2
-rwxr-xr-xsrc/test/test_workqueue_pipe.sh2
-rwxr-xr-xsrc/test/test_workqueue_pipe2.sh2
-rwxr-xr-xsrc/test/test_workqueue_socketpair.sh2
-rw-r--r--src/test/testing_common.c29
-rw-r--r--src/test/testing_rsakeys.c5
-rwxr-xr-xsrc/test/zero_length_keys.sh6
194 files changed, 19541 insertions, 2355 deletions
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake
index cfbe281b94..ca6a84cf8a 100644
--- a/src/test/Makefile.nmake
+++ b/src/test/Makefile.nmake
@@ -1,4 +1,4 @@
-all: test.exe test-child.exe bench.exe
+all: test.exe bench.exe
CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common /I ..\or \
/I ..\ext
@@ -19,6 +19,7 @@ TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \
test_cell_formats.obj test_relay.obj test_replay.obj \
test_channelpadding.obj \
test_circuitstats.obj \
+ test_circuitpadding.obj \
test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj
tinytest.obj: ..\ext\tinytest.c
@@ -30,8 +31,5 @@ test.exe: $(TEST_OBJECTS)
bench.exe: bench.obj
$(CC) $(CFLAGS) bench.obj $(LIBS) ..\common\*.lib /Fe$@
-test-child.exe: test-child.obj
- $(CC) $(CFLAGS) test-child.obj /Fe$@
-
clean:
- del *.obj *.lib test.exe bench.exe test-child.exe
+ del *.obj *.lib test.exe bench.exe
diff --git a/src/test/bench.c b/src/test/bench.c
index 06c616c3b0..cf732df593 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -14,16 +14,19 @@
#include "core/crypto/onion_tap.h"
#include "core/crypto/relay_crypto.h"
+#include "lib/intmath/weakrng.h"
+
#ifdef ENABLE_OPENSSL
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/obj_mac.h>
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
#include "core/or/circuitlist.h"
#include "app/config/config.h"
+#include "app/main/subsysmgr.h"
#include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_dh.h"
#include "core/crypto/onion_ntor.h"
@@ -38,6 +41,9 @@
#include "lib/crypt_ops/digestset.h"
#include "lib/crypt_ops/crypto_init.h"
+#include "feature/dirparse/microdesc_parse.h"
+#include "feature/nodelist/microdesc.h"
+
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
static uint64_t nanostart;
static inline uint64_t
@@ -332,6 +338,65 @@ bench_ed25519(void)
}
static void
+bench_rand_len(int len)
+{
+ const int N = 100000;
+ int i;
+ char *buf = tor_malloc(len);
+ uint64_t start,end;
+
+ start = perftime();
+ for (i = 0; i < N; ++i) {
+ crypto_rand(buf, len);
+ }
+ end = perftime();
+ printf("crypto_rand(%d): %f nsec.\n", len, NANOCOUNT(start,end,N));
+
+ crypto_fast_rng_t *fr = crypto_fast_rng_new();
+ start = perftime();
+ for (i = 0; i < N; ++i) {
+ crypto_fast_rng_getbytes(fr,(uint8_t*)buf,len);
+ }
+ end = perftime();
+ printf("crypto_fast_rng_getbytes(%d): %f nsec.\n", len,
+ NANOCOUNT(start,end,N));
+ crypto_fast_rng_free(fr);
+
+ if (len <= 32) {
+ start = perftime();
+ for (i = 0; i < N; ++i) {
+ crypto_strongest_rand((uint8_t*)buf, len);
+ }
+ end = perftime();
+ printf("crypto_strongest_rand(%d): %f nsec.\n", len,
+ NANOCOUNT(start,end,N));
+ }
+
+ if (len == 4) {
+ tor_weak_rng_t weak;
+ tor_init_weak_random(&weak, 1337);
+
+ start = perftime();
+ uint32_t t=0;
+ for (i = 0; i < N; ++i) {
+ t += tor_weak_random(&weak);
+ }
+ end = perftime();
+ printf("weak_rand(4): %f nsec.\n", NANOCOUNT(start,end,N));
+ }
+
+ tor_free(buf);
+}
+
+static void
+bench_rand(void)
+{
+ bench_rand_len(4);
+ bench_rand_len(16);
+ bench_rand_len(128);
+}
+
+static void
bench_cell_aes(void)
{
uint64_t start, end;
@@ -636,7 +701,42 @@ bench_ecdh_p224(void)
{
bench_ecdh_impl(NID_secp224r1, "P-224");
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
+
+static void
+bench_md_parse(void)
+{
+ uint64_t start, end;
+ const int N = 100000;
+ // selected arbitrarily
+ const char md_text[] =
+ "@last-listed 2018-12-14 18:14:14\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMHkZeXNDX/49JqM2BVLmh1Fnb5iMVnatvZZTLJyedqDLkbXZ1WKP5oh\n"
+ "7ec14dj/k3ntpwHD4s2o3Lb6nfagWbug4+F/rNJ7JuFru/PSyOvDyHGNAuegOXph\n"
+ "3gTGjdDpv/yPoiadGebbVe8E7n6hO+XxM2W/4dqheKimF0/s9B7HAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key QgF/EjqlNG1wRHLIop/nCekEH+ETGZSgYOhu26eiTF4=\n"
+ "family $00E9A86E7733240E60D8435A7BBD634A23894098 "
+ "$329BD7545DEEEBBDC8C4285F243916F248972102 "
+ "$69E06EBB2573A4F89330BDF8BC869794A3E10E4D "
+ "$DCA2A3FAE50B3729DAA15BC95FB21AF03389818B\n"
+ "p accept 53,80,443,5222-5223,25565\n"
+ "id ed25519 BzffzY99z6Q8KltcFlUTLWjNTBU7yKK+uQhyi1Ivb3A\n";
+
+ reset_perftime();
+ start = perftime();
+ for (int i = 0; i < N; ++i) {
+ smartlist_t *s = microdescs_parse_from_string(md_text, NULL, 1,
+ SAVED_IN_CACHE, NULL);
+ SMARTLIST_FOREACH(s, microdesc_t *, md, microdesc_free(md));
+ smartlist_free(s);
+ }
+
+ end = perftime();
+ printf("Microdesc parse: %f nsec\n", NANOCOUNT(start, end, N));
+}
typedef void (*bench_fn)(void);
@@ -656,6 +756,7 @@ static struct benchmark_t benchmarks[] = {
ENT(onion_TAP),
ENT(onion_ntor),
ENT(ed25519),
+ ENT(rand),
ENT(cell_aes),
ENT(cell_ops),
@@ -665,6 +766,8 @@ static struct benchmark_t benchmarks[] = {
ENT(ecdh_p256),
ENT(ecdh_p224),
#endif
+
+ ENT(md_parse),
{NULL,NULL,0}
};
@@ -690,9 +793,10 @@ main(int argc, const char **argv)
char *errmsg;
or_options_t *options;
- tor_threads_init();
+ subsystems_init_upto(SUBSYS_LEVEL_LIBS);
+ flush_log_messages_from_startup();
+
tor_compress_init();
- init_logging(1);
if (argc == 4 && !strcmp(argv[1], "diff")) {
const int N = 200;
@@ -702,11 +806,13 @@ main(int argc, const char **argv)
perror("X");
return 1;
}
+ size_t f1len = strlen(f1);
+ size_t f2len = strlen(f2);
for (i = 0; i < N; ++i) {
- char *diff = consensus_diff_generate(f1, f2);
+ char *diff = consensus_diff_generate(f1, f1len, f2, f2len);
tor_free(diff);
}
- char *diff = consensus_diff_generate(f1, f2);
+ char *diff = consensus_diff_generate(f1, f1len, f2, f2len);
printf("%s", diff);
tor_free(f1);
tor_free(f2);
@@ -737,7 +843,6 @@ main(int argc, const char **argv)
init_protocol_warning_severity_level();
options = options_new();
- init_logging(1);
options->command = CMD_RUN_UNITTESTS;
options->DataDirectory = tor_strdup("");
options->KeyDirectory = tor_strdup("");
diff --git a/src/test/conf_examples/badnick_1/error b/src/test/conf_examples/badnick_1/error
new file mode 100644
index 0000000000..3e92ddc832
--- /dev/null
+++ b/src/test/conf_examples/badnick_1/error
@@ -0,0 +1 @@
+nicknames must be between 1 and 19 characters inclusive
diff --git a/src/test/conf_examples/badnick_1/torrc b/src/test/conf_examples/badnick_1/torrc
new file mode 100644
index 0000000000..41ee4894f1
--- /dev/null
+++ b/src/test/conf_examples/badnick_1/torrc
@@ -0,0 +1,2 @@
+# This nickname is too long; we won't accept it.
+Nickname TooManyCharactersInThisNickname
diff --git a/src/test/conf_examples/badnick_2/error b/src/test/conf_examples/badnick_2/error
new file mode 100644
index 0000000000..ceac99f012
--- /dev/null
+++ b/src/test/conf_examples/badnick_2/error
@@ -0,0 +1 @@
+must contain only the characters \[a-zA-Z0-9\]
diff --git a/src/test/conf_examples/badnick_2/torrc b/src/test/conf_examples/badnick_2/torrc
new file mode 100644
index 0000000000..07acc61698
--- /dev/null
+++ b/src/test/conf_examples/badnick_2/torrc
@@ -0,0 +1,2 @@
+# this nickname has spaces in it and won't work.
+Nickname has a space
diff --git a/src/test/conf_examples/contactinfo_notutf8/error b/src/test/conf_examples/contactinfo_notutf8/error
new file mode 100644
index 0000000000..6d165152ce
--- /dev/null
+++ b/src/test/conf_examples/contactinfo_notutf8/error
@@ -0,0 +1 @@
+ContactInfo config option must be UTF-8
diff --git a/src/test/conf_examples/contactinfo_notutf8/torrc b/src/test/conf_examples/contactinfo_notutf8/torrc
new file mode 100644
index 0000000000..2ee4d093c5
--- /dev/null
+++ b/src/test/conf_examples/contactinfo_notutf8/torrc
@@ -0,0 +1 @@
+ContactInfo ÄëÄëÄë@example.com
diff --git a/src/test/conf_examples/example_1/expected b/src/test/conf_examples/example_1/expected
new file mode 100644
index 0000000000..9d6688a565
--- /dev/null
+++ b/src/test/conf_examples/example_1/expected
@@ -0,0 +1,2 @@
+ContactInfo tor_tellini@example.com
+SocksPort 80
diff --git a/src/test/conf_examples/example_1/torrc b/src/test/conf_examples/example_1/torrc
new file mode 100644
index 0000000000..bff7fa0aa2
--- /dev/null
+++ b/src/test/conf_examples/example_1/torrc
@@ -0,0 +1,5 @@
+
+# Here is a simple example torrc.
+ SocksPort 80
+
+ContactInfo "tor_tellini@example.com"
diff --git a/src/test/conf_examples/example_2/error b/src/test/conf_examples/example_2/error
new file mode 100644
index 0000000000..ce18b68db4
--- /dev/null
+++ b/src/test/conf_examples/example_2/error
@@ -0,0 +1 @@
+Unknown option 'JumpingJellyjars'
diff --git a/src/test/conf_examples/example_2/torrc b/src/test/conf_examples/example_2/torrc
new file mode 100644
index 0000000000..8ec8133b24
--- /dev/null
+++ b/src/test/conf_examples/example_2/torrc
@@ -0,0 +1 @@
+JumpingJellyjars 1
diff --git a/src/test/conf_examples/example_3/cmdline b/src/test/conf_examples/example_3/cmdline
new file mode 100644
index 0000000000..5b2fadcebb
--- /dev/null
+++ b/src/test/conf_examples/example_3/cmdline
@@ -0,0 +1 @@
+--socksport 99
diff --git a/src/test/conf_examples/example_3/expected b/src/test/conf_examples/example_3/expected
new file mode 100644
index 0000000000..867fb8bcc8
--- /dev/null
+++ b/src/test/conf_examples/example_3/expected
@@ -0,0 +1 @@
+SocksPort 99
diff --git a/src/test/conf_examples/example_3/torrc b/src/test/conf_examples/example_3/torrc
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/test/conf_examples/example_3/torrc
diff --git a/src/test/conf_examples/include_1/expected b/src/test/conf_examples/include_1/expected
new file mode 100644
index 0000000000..4bbf52ce9f
--- /dev/null
+++ b/src/test/conf_examples/include_1/expected
@@ -0,0 +1,3 @@
+ContactInfo includefile@example.com
+Nickname nested
+ORPort 8008
diff --git a/src/test/conf_examples/include_1/included.inc b/src/test/conf_examples/include_1/included.inc
new file mode 100644
index 0000000000..8d1834345d
--- /dev/null
+++ b/src/test/conf_examples/include_1/included.inc
@@ -0,0 +1,4 @@
+
+ContactInfo includefile@example.com
+
+%include "nested.inc" \ No newline at end of file
diff --git a/src/test/conf_examples/include_1/nested.inc b/src/test/conf_examples/include_1/nested.inc
new file mode 100644
index 0000000000..789b044a2b
--- /dev/null
+++ b/src/test/conf_examples/include_1/nested.inc
@@ -0,0 +1,2 @@
+
+Nickname nested \ No newline at end of file
diff --git a/src/test/conf_examples/include_1/torrc b/src/test/conf_examples/include_1/torrc
new file mode 100644
index 0000000000..2ed4074f6e
--- /dev/null
+++ b/src/test/conf_examples/include_1/torrc
@@ -0,0 +1,4 @@
+
+%include "included.inc"
+
+ORPort 8008
diff --git a/src/test/conf_examples/include_bug_31408/expected b/src/test/conf_examples/include_bug_31408/expected
new file mode 100644
index 0000000000..2e822f1a78
--- /dev/null
+++ b/src/test/conf_examples/include_bug_31408/expected
@@ -0,0 +1,2 @@
+Nickname test31408
+ORPort 31408
diff --git a/src/test/conf_examples/include_bug_31408/included/01_nickname.inc b/src/test/conf_examples/include_bug_31408/included/01_nickname.inc
new file mode 100644
index 0000000000..508dd89a35
--- /dev/null
+++ b/src/test/conf_examples/include_bug_31408/included/01_nickname.inc
@@ -0,0 +1 @@
+Nickname test31408
diff --git a/src/test/conf_examples/include_bug_31408/included/02_no_configs.inc b/src/test/conf_examples/include_bug_31408/included/02_no_configs.inc
new file mode 100644
index 0000000000..140e927f19
--- /dev/null
+++ b/src/test/conf_examples/include_bug_31408/included/02_no_configs.inc
@@ -0,0 +1,3 @@
+# Bug 31048 is triggered when the last file in a config directory:
+# * contains no configuration options,
+# * but is non-empty: that is, it contains comments or whitespace.
diff --git a/src/test/conf_examples/include_bug_31408/torrc b/src/test/conf_examples/include_bug_31408/torrc
new file mode 100644
index 0000000000..a42685e93c
--- /dev/null
+++ b/src/test/conf_examples/include_bug_31408/torrc
@@ -0,0 +1,2 @@
+%include "included"
+ORPort 31408
diff --git a/src/test/conf_examples/large_1/expected b/src/test/conf_examples/large_1/expected
new file mode 100644
index 0000000000..5866f5823e
--- /dev/null
+++ b/src/test/conf_examples/large_1/expected
@@ -0,0 +1,159 @@
+AccountingMax 10737418240
+AccountingRule sum
+AccountingStart day 05:15
+Address 128.66.8.8
+AllowNonRFC953Hostnames 1
+AndroidIdentityTag droidy
+AutomapHostsOnResolve 1
+AutomapHostsSuffixes .onions
+AvoidDiskWrites 1
+BandwidthBurst 2147483647
+BandwidthRate 1610612736
+Bridge 128.66.1.10:80
+CacheDirectory /this-is-a-cache
+CellStatistics 1
+CircuitBuildTimeout 200
+CircuitsAvailableTimeout 10
+CircuitStreamTimeout 20
+ClientAutoIPv6ORPort 1
+ClientOnly 1
+ClientPreferIPv6DirPort 1
+ClientPreferIPv6ORPort 1
+ClientRejectInternalAddresses 0
+ClientUseIPv4 0
+ClientUseIPv6 1
+ConnDirectionStatistics 1
+ConnectionPadding 1
+ConnLimit 64
+ConsensusParams wombat=7
+ConstrainedSockets 1
+ConstrainedSockSize 10240
+ContactInfo long_config@example.com
+ControlPortFileGroupReadable 1
+ControlPort 9058
+CookieAuthentication 1
+CookieAuthFile /control/cookie
+CookieAuthFileGroupReadable 1
+CountPrivateBandwidth 1
+DataDirectory /data/dir
+DirAllowPrivateAddresses 1
+DirPolicy reject 128.66.1.1/32, accept *:*
+DirPortFrontPage /dirport/frontpage
+DirPort 99
+DirReqStatistics 0
+DisableDebuggerAttachment 0
+DisableNetwork 1
+DisableOOSCheck 0
+DNSPort 53535
+DormantCanceledByStartup 1
+DormantClientTimeout 1260
+DormantOnFirstStartup 1
+DormantTimeoutDisabledByIdleStreams 0
+DoSCircuitCreationBurst 1000
+DoSCircuitCreationDefenseTimePeriod 300
+DoSCircuitCreationDefenseType 2
+DoSCircuitCreationEnabled 1
+DoSCircuitCreationMinConnections 10
+DoSCircuitCreationRate 100
+DoSConnectionDefenseType 2
+DoSConnectionEnabled 1
+DoSConnectionMaxConcurrentCount 6
+DoSRefuseSingleHopClientRendezvous 0
+DownloadExtraInfo 1
+EnforceDistinctSubnets 0
+EntryNodes potrzebie,triffid,cromulent
+EntryStatistics 1
+ExcludeExitNodes blaznort,kriffid,zeppelin
+ExcludeNodes 128.66.7.6
+ExitNodes 128.66.7.7,128.66.128.0/17,exitexit
+ExitPolicy accept *:80,reject *:*
+ExitPolicyRejectLocalInterfaces 1
+ExitPolicyRejectPrivate 0
+ExitPortStatistics 1
+ExitRelay 1
+ExtendAllowPrivateAddresses 1
+ExtendByEd25519ID 1
+ExtORPortCookieAuthFile /foobar
+ExtORPort 99
+FascistFirewall 1
+FetchDirInfoEarly 1
+FetchDirInfoExtraEarly 1
+FetchUselessDescriptors 1
+FirewallPorts 80,443,999
+GeoIPExcludeUnknown 1
+GeoIPFile /geoip
+GuardfractionFile /gff
+GuardLifetime 691200
+HeartbeatPeriod 2700
+IPv6Exit 1
+KeepalivePeriod 540
+KeyDirectory /keyz
+KISTSchedRunInterval 1
+Log notice file /logfile
+Log info file /logfile-verbose
+LogTimeGranularity 60000
+LongLivedPorts 9090
+MainloopStats 1
+MapAddress www.example.com:10.0.0.6
+MaxAdvertisedBandwidth 100
+MaxCircuitDirtiness 3600
+MaxClientCircuitsPending 127
+MaxConsensusAgeForDiffs 2629728
+MaxMemInQueues 314572800
+MaxOnionQueueDelay 60000
+MaxUnparseableDescSizeToLog 1048576
+MiddleNodes grommit,truffle,parcheesi
+MyFamily $ffffffffffffffffffffffffffffffffffffffff
+NewCircuitPeriod 7200
+Nickname nickname
+NodeFamily $ffffffffffffffffffffffffffffffffffffffff,$dddddddddddddddddddddddddddddddddddddddd
+NumCPUs 3
+NumDirectoryGuards 4
+NumEntryGuards 5
+NumPrimaryGuards 8
+OfflineMasterKey 1
+OptimisticData 1
+ORPort 2222
+OutboundBindAddress 10.0.0.7
+OutboundBindAddressExit 10.0.0.8
+OutboundBindAddressOR 10.0.0.9
+PerConnBWBurst 10485760
+PerConnBWRate 102400
+PidFile /piddy
+ProtocolWarnings 1
+PublishHidServDescriptors 0
+PublishServerDescriptor 0
+ReachableAddresses 0.0.0.0, *:*
+ReachableDirAddresses 128.0.0.0/1
+ReachableORAddresses 128.0.0.0/8
+RejectPlaintextPorts 23
+RelayBandwidthBurst 10000
+RelayBandwidthRate 1000
+RendPostPeriod 600
+RephistTrackTime 600
+SafeLogging 0
+Schedulers Vanilla,KISTLite,Kist
+ShutdownWaitLength 10
+SigningKeyLifetime 4838400
+Socks5Proxy 128.66.99.99:99
+Socks5ProxyPassword flynn
+Socks5ProxyUsername spaceparanoids
+SocksPolicy accept 127.0.0.0/24, reject *:*
+SocksPort 9099
+SocksTimeout 600
+SSLKeyLifetime 86400
+StrictNodes 1
+SyslogIdentityTag tortor
+TestSocks 1
+TokenBucketRefillInterval 1000
+TrackHostExits www.example.com
+TrackHostExitsExpire 3600
+TruncateLogFile 1
+UnixSocksGroupWritable 1
+UpdateBridgesFromAuthority 1
+UseDefaultFallbackDirs 0
+UseGuardFraction 1
+UseMicrodescriptors 0
+VirtualAddrNetworkIPv4 18.66.0.0/16
+VirtualAddrNetworkIPv6 [ff00::]/16
+WarnPlaintextPorts 7,11,23,1001
diff --git a/src/test/conf_examples/large_1/torrc b/src/test/conf_examples/large_1/torrc
new file mode 100644
index 0000000000..e99acd9fb7
--- /dev/null
+++ b/src/test/conf_examples/large_1/torrc
@@ -0,0 +1,167 @@
+AccountingMax 10 GB
+AccountingRule sum
+AccountingStart day 05:15
+Address 128.66.8.8
+AllowNonRFC953Hostnames 1
+AndroidIdentityTag droidy
+AutomapHostsOnResolve 1
+AutomapHostsSuffixes .onions
+AvoidDiskWrites 1
+BandwidthBurst 2 GB
+BandwidthRate 1.5 GB
+Bridge 128.66.1.10:80
+CacheDirectory /this-is-a-cache
+CellStatistics 1
+CircuitBuildTimeout 200
+CircuitPadding 1
+CircuitsAvailableTimeout 10
+CircuitStreamTimeout 20
+ClientAutoIPv6ORPort 1
+ClientOnly 1
+ClientPreferIPv6DirPort 1
+ClientPreferIPv6ORPort 1
+ClientRejectInternalAddresses 0
+ClientUseIPv4 0
+ClientUseIPv6 1
+ConnDirectionStatistics 1
+ConnectionPadding 1
+ConnLimit 64
+ConsensusParams wombat=7
+ConstrainedSockets 1
+ConstrainedSockSize 10240
+ContactInfo long_config@example.com
+ControlPortFileGroupReadable 1
+ControlPort 9058
+CookieAuthentication 1
+CookieAuthFile /control/cookie
+CookieAuthFileGroupReadable 1
+CountPrivateBandwidth 1
+DataDirectory /data/dir
+DirAllowPrivateAddresses 1
+DirPolicy reject 128.66.1.1/32, accept *:*
+DirReqStatistics 0
+DirPort 99
+DirPortFrontPage /dirport/frontpage
+DisableDebuggerAttachment 0
+DisableNetwork 1
+DisableOOSCheck 0
+DNSPort 53535
+DormantCanceledByStartup 1
+DormantClientTimeout 21 minutes
+DormantOnFirstStartup 1
+DormantTimeoutDisabledByIdleStreams 0
+DoSCircuitCreationBurst 1000
+DoSCircuitCreationDefenseTimePeriod 5 minutes
+DoSCircuitCreationDefenseType 2
+DoSCircuitCreationEnabled 1
+DoSCircuitCreationMinConnections 10
+DoSCircuitCreationRate 100
+DoSConnectionDefenseType 2
+DoSConnectionEnabled 1
+DoSConnectionMaxConcurrentCount 6
+DoSRefuseSingleHopClientRendezvous 0
+DownloadExtraInfo 1
+EnforceDistinctSubnets 0
+EntryNodes potrzebie,triffid,cromulent
+EntryStatistics 1
+ExcludeExitNodes blaznort,kriffid,zeppelin
+ExcludeNodes 128.66.7.6
+ExitNodes 128.66.7.7,128.66.128.0/17,exitexit
+ExitPolicy accept *:80,reject *:*
+ExitPolicyRejectLocalInterfaces 1
+ExitPolicyRejectPrivate 0
+ExitPortStatistics 1
+ExitRelay 1
+ExtendAllowPrivateAddresses 1
+ExtendByEd25519ID 1
+ExtORPort 99
+ExtORPortCookieAuthFile /foobar
+ExtraInfoStatistics 1
+FascistFirewall 1
+FetchDirInfoEarly 1
+FetchDirInfoExtraEarly 1
+FetchHidServDescriptors 1
+FetchServerDescriptors 1
+FetchUselessDescriptors 1
+FirewallPorts 80,443,999
+GeoIPExcludeUnknown 1
+GeoIPFile /geoip
+GuardfractionFile /gff
+GuardLifetime 8 days
+HeartbeatPeriod 45 minutes
+IPv6Exit 1
+KeepalivePeriod 9 minutes
+KeyDirectory /keyz
+KISTSchedRunInterval 1 msec
+LearnCircuitBuildTimeout 1
+Log notice file /logfile
+Log info file /logfile-verbose
+LogTimeGranularity 1 minute
+LongLivedPorts 9090
+MainloopStats 1
+MapAddress www.example.com:10.0.0.6
+MaxAdvertisedBandwidth 100
+MaxCircuitDirtiness 1 hour
+MaxClientCircuitsPending 127
+MaxConsensusAgeForDiffs 1 month
+MaxMemInQueues 300 MB
+MaxOnionQueueDelay 60 seconds
+MaxUnparseableDescSizeToLog 1 MB
+MiddleNodes grommit, truffle, parcheesi
+MyFamily $ffffffffffffffffffffffffffffffffffffffff
+NewCircuitPeriod 2 hours
+Nickname nickname
+NodeFamily $ffffffffffffffffffffffffffffffffffffffff,$dddddddddddddddddddddddddddddddddddddddd
+NumCPUs 3
+NumDirectoryGuards 4
+NumEntryGuards 5
+NumPrimaryGuards 8
+OfflineMasterKey 1
+OptimisticData 1
+ORPort 2222
+OutboundBindAddress 10.0.0.7
+OutboundBindAddressExit 10.0.0.8
+OutboundBindAddressOR 10.0.0.9
+PaddingStatistics 1
+PerConnBWBurst 10 MB
+PerConnBWRate 100 kb
+PidFile /piddy
+ProtocolWarnings 1
+PublishHidServDescriptors 0
+PublishServerDescriptor 0
+ReachableAddresses 0.0.0.0, *:*
+ReachableDirAddresses 128.0.0.0/1
+ReachableORAddresses 128.0.0.0/8
+RejectPlaintextPorts 23
+RelayBandwidthBurst 10000
+RelayBandwidthRate 1000
+RendPostPeriod 10 minutes
+RephistTrackTime 10 minutes
+SafeLogging 0
+SafeSocks 0
+Schedulers Vanilla,KISTLite,Kist
+ShutdownWaitLength 10 seconds
+SigningKeyLifetime 8 weeks
+Socks5Proxy 128.66.99.99:99
+Socks5ProxyPassword flynn
+Socks5ProxyUsername spaceparanoids
+SocksPolicy accept 127.0.0.0/24, reject *:*
+SocksPort 9099
+SocksTimeout 10 minutes
+SSLKeyLifetime 1 day
+StrictNodes 1
+SyslogIdentityTag tortor
+TestSocks 1
+TokenBucketRefillInterval 1 second
+TrackHostExits www.example.com
+TrackHostExitsExpire 1 hour
+TruncateLogFile 1
+UnixSocksGroupWritable 1
+UpdateBridgesFromAuthority 1
+UseDefaultFallbackDirs 0
+UseEntryGuards 1
+UseGuardFraction 1
+UseMicrodescriptors 0
+VirtualAddrNetworkIPv4 18.66.0.0/16
+VirtualAddrNetworkIPv6 [ff00::]/16
+WarnPlaintextPorts 7,11,23,1001
diff --git a/src/test/conf_examples/obsolete_1/expected b/src/test/conf_examples/obsolete_1/expected
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/test/conf_examples/obsolete_1/expected
diff --git a/src/test/conf_examples/obsolete_1/torrc b/src/test/conf_examples/obsolete_1/torrc
new file mode 100644
index 0000000000..3cd9a6d777
--- /dev/null
+++ b/src/test/conf_examples/obsolete_1/torrc
@@ -0,0 +1,68 @@
+# These options are obsolete as of 0.4.2
+AllowDotExit
+AllowInvalidNodes
+AllowSingleHopCircuits
+AllowSingleHopExits
+AlternateHSAuthority
+AuthDirBadDir
+AuthDirBadDirCCs
+AuthDirRejectUnlisted
+AuthDirListBadDirs
+AuthDirMaxServersPerAuthAddr
+CircuitIdleTimeout
+ControlListenAddress
+DirListenAddress
+DisableIOCP
+DisableV2DirectoryInfo_
+DynamicDHGroups
+DNSListenAddress
+TestingEnableTbEmptyEvent
+ExcludeSingleHopRelays
+FallbackNetworkstatusFile
+FastFirstHopPK
+FetchV2Networkstatus
+Group
+HidServDirectoryV2
+CloseHSClientCircuitsImmediatelyOnTimeout
+CloseHSServiceRendCircuitsImmediatelyOnTimeout
+MaxOnionsPending
+NamingAuthoritativeDirectory
+NATDListenAddress
+PredictedPortsRelevanceTime
+WarnUnsafeSocks
+ORListenAddress
+PathBiasDisableRate
+PathBiasScaleFactor
+PathBiasMultFactor
+PathBiasUseCloseCounts
+PortForwarding
+PortForwardingHelper
+PreferTunneledDirConns
+RecommendedPackages
+RunTesting
+SchedulerLowWaterMark__
+SchedulerHighWaterMark__
+SchedulerMaxFlushCells__
+SocksListenAddress
+StrictEntryNodes
+StrictExitNodes
+Support022HiddenServices
+Tor2webMode
+Tor2webRendezvousPoints
+TLSECGroup
+TransListenAddress
+TunnelDirConns
+UseEntryGuardsAsDirGuards
+UseNTorHandshake
+UserspaceIOCPBuffers
+V1AuthoritativeDirectory
+V2AuthoritativeDirectory
+VoteOnHidServDirectoriesV2
+UseFilteringSSLBufferevents
+__UseFilteringSSLBufferevents
+TestingConsensusMaxDownloadTries
+ClientBootstrapConsensusMaxDownloadTries
+ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries
+TestingDescriptorMaxDownloadTries
+TestingMicrodescMaxDownloadTries
+TestingCertMaxDownloadTries
diff --git a/src/test/conf_examples/obsolete_2/expected b/src/test/conf_examples/obsolete_2/expected
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/test/conf_examples/obsolete_2/expected
diff --git a/src/test/conf_examples/obsolete_2/torrc b/src/test/conf_examples/obsolete_2/torrc
new file mode 100644
index 0000000000..4f78d47625
--- /dev/null
+++ b/src/test/conf_examples/obsolete_2/torrc
@@ -0,0 +1,2 @@
+# This option has been obsolete for some time
+AllowDotExit
diff --git a/src/test/conf_examples/ops_1/cmdline b/src/test/conf_examples/ops_1/cmdline
new file mode 100644
index 0000000000..2bb9bfa132
--- /dev/null
+++ b/src/test/conf_examples/ops_1/cmdline
@@ -0,0 +1 @@
+ORPort 1000
diff --git a/src/test/conf_examples/ops_1/expected b/src/test/conf_examples/ops_1/expected
new file mode 100644
index 0000000000..84be6a70e2
--- /dev/null
+++ b/src/test/conf_examples/ops_1/expected
@@ -0,0 +1,2 @@
+Nickname Unnamed
+ORPort 1000
diff --git a/src/test/conf_examples/ops_1/torrc b/src/test/conf_examples/ops_1/torrc
new file mode 100644
index 0000000000..daf8ae60fe
--- /dev/null
+++ b/src/test/conf_examples/ops_1/torrc
@@ -0,0 +1,3 @@
+# We'll replace this option on the command line.
+
+ORPort 9999
diff --git a/src/test/conf_examples/ops_2/cmdline b/src/test/conf_examples/ops_2/cmdline
new file mode 100644
index 0000000000..fdd48a045c
--- /dev/null
+++ b/src/test/conf_examples/ops_2/cmdline
@@ -0,0 +1 @@
+/ORPort
diff --git a/src/test/conf_examples/ops_2/expected b/src/test/conf_examples/ops_2/expected
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/test/conf_examples/ops_2/expected
diff --git a/src/test/conf_examples/ops_2/torrc b/src/test/conf_examples/ops_2/torrc
new file mode 100644
index 0000000000..21fcc93f9a
--- /dev/null
+++ b/src/test/conf_examples/ops_2/torrc
@@ -0,0 +1,3 @@
+# We'll remove this option on the command line, and not replace it.
+
+ORPort 9999
diff --git a/src/test/conf_examples/ops_3/cmdline b/src/test/conf_examples/ops_3/cmdline
new file mode 100644
index 0000000000..e4965d26f8
--- /dev/null
+++ b/src/test/conf_examples/ops_3/cmdline
@@ -0,0 +1 @@
++ORPort 1000
diff --git a/src/test/conf_examples/ops_3/expected b/src/test/conf_examples/ops_3/expected
new file mode 100644
index 0000000000..569d26b577
--- /dev/null
+++ b/src/test/conf_examples/ops_3/expected
@@ -0,0 +1,3 @@
+Nickname Unnamed
+ORPort 9999
+ORPort 1000
diff --git a/src/test/conf_examples/ops_3/torrc b/src/test/conf_examples/ops_3/torrc
new file mode 100644
index 0000000000..14adf87d7f
--- /dev/null
+++ b/src/test/conf_examples/ops_3/torrc
@@ -0,0 +1,3 @@
+# We will extend this option on the command line
+
+ORPort 9999
diff --git a/src/test/conf_examples/ops_4/expected b/src/test/conf_examples/ops_4/expected
new file mode 100644
index 0000000000..bf52f6a330
--- /dev/null
+++ b/src/test/conf_examples/ops_4/expected
@@ -0,0 +1,2 @@
+Nickname Unnamed
+ORPort 9099
diff --git a/src/test/conf_examples/ops_4/torrc b/src/test/conf_examples/ops_4/torrc
new file mode 100644
index 0000000000..dcec2aa95d
--- /dev/null
+++ b/src/test/conf_examples/ops_4/torrc
@@ -0,0 +1,3 @@
+# This value is unadorned, so replaces the one from defaults.torrc.
+
+ORPort 9099
diff --git a/src/test/conf_examples/ops_4/torrc.defaults b/src/test/conf_examples/ops_4/torrc.defaults
new file mode 100644
index 0000000000..04cd0393c6
--- /dev/null
+++ b/src/test/conf_examples/ops_4/torrc.defaults
@@ -0,0 +1 @@
+ORPort 9000
diff --git a/src/test/conf_examples/ops_5/expected b/src/test/conf_examples/ops_5/expected
new file mode 100644
index 0000000000..288721da53
--- /dev/null
+++ b/src/test/conf_examples/ops_5/expected
@@ -0,0 +1,3 @@
+Nickname Unnamed
+ORPort 9000
+ORPort 9099
diff --git a/src/test/conf_examples/ops_5/torrc b/src/test/conf_examples/ops_5/torrc
new file mode 100644
index 0000000000..3284fc1c55
--- /dev/null
+++ b/src/test/conf_examples/ops_5/torrc
@@ -0,0 +1,3 @@
+# This value has a plus, and so extends the one from defaults.torrc.
+
++ORPort 9099
diff --git a/src/test/conf_examples/ops_5/torrc.defaults b/src/test/conf_examples/ops_5/torrc.defaults
new file mode 100644
index 0000000000..04cd0393c6
--- /dev/null
+++ b/src/test/conf_examples/ops_5/torrc.defaults
@@ -0,0 +1 @@
+ORPort 9000
diff --git a/src/test/conf_examples/ops_6/expected b/src/test/conf_examples/ops_6/expected
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/test/conf_examples/ops_6/expected
diff --git a/src/test/conf_examples/ops_6/torrc b/src/test/conf_examples/ops_6/torrc
new file mode 100644
index 0000000000..4d51caaff7
--- /dev/null
+++ b/src/test/conf_examples/ops_6/torrc
@@ -0,0 +1,3 @@
+# This value has a slash, and so clears the one from defaults.torrc.
+
+/ORPort
diff --git a/src/test/conf_examples/ops_6/torrc.defaults b/src/test/conf_examples/ops_6/torrc.defaults
new file mode 100644
index 0000000000..04cd0393c6
--- /dev/null
+++ b/src/test/conf_examples/ops_6/torrc.defaults
@@ -0,0 +1 @@
+ORPort 9000
diff --git a/src/test/conf_examples/relpath_rad/error b/src/test/conf_examples/relpath_rad/error
new file mode 100644
index 0000000000..e131744475
--- /dev/null
+++ b/src/test/conf_examples/relpath_rad/error
@@ -0,0 +1 @@
+RunAsDaemon is not compatible with relative paths.
diff --git a/src/test/conf_examples/relpath_rad/torrc b/src/test/conf_examples/relpath_rad/torrc
new file mode 100644
index 0000000000..fe02441c3f
--- /dev/null
+++ b/src/test/conf_examples/relpath_rad/torrc
@@ -0,0 +1,4 @@
+
+# Relative-path data directories are incompatible with RunAsDaemon
+DataDirectory ./datadir
+RunAsDaemon 1
diff --git a/src/test/fuzz/fixup_filenames.sh b/src/test/fuzz/fixup_filenames.sh
index 68efc1abc5..f730d532a5 100755
--- a/src/test/fuzz/fixup_filenames.sh
+++ b/src/test/fuzz/fixup_filenames.sh
@@ -8,9 +8,9 @@ if [ ! -d "$1" ] ; then
fi
for fn in "$1"/* ; do
- prev=`basename "$fn"`
- post=`sha256sum "$fn" | sed -e 's/ .*//;'`
- if [ "$prev" == "$post" ] ; then
+ prev=$(basename "$fn")
+ post=$(sha256sum "$fn" | sed -e 's/ .*//;')
+ if [ "$prev" = "$post" ] ; then
echo "OK $prev"
else
echo "mv $prev $post"
diff --git a/src/test/fuzz/fuzz_consensus.c b/src/test/fuzz/fuzz_consensus.c
index 5947a3f48c..656ef0bdb2 100644
--- a/src/test/fuzz/fuzz_consensus.c
+++ b/src/test/fuzz/fuzz_consensus.c
@@ -61,13 +61,13 @@ int
fuzz_main(const uint8_t *data, size_t sz)
{
networkstatus_t *ns;
- char *str = tor_memdup_nulterm(data, sz);
const char *eos = NULL;
networkstatus_type_t tp = NS_TYPE_CONSENSUS;
if (tor_memstr(data, MIN(sz, 1024), "tus vote"))
tp = NS_TYPE_VOTE;
const char *what = (tp == NS_TYPE_CONSENSUS) ? "consensus" : "vote";
- ns = networkstatus_parse_vote_from_string(str,
+ ns = networkstatus_parse_vote_from_string((const char *)data,
+ sz,
&eos,
tp);
if (ns) {
@@ -76,6 +76,6 @@ fuzz_main(const uint8_t *data, size_t sz)
} else {
log_debug(LD_GENERAL, "Parsing as %s failed", what);
}
- tor_free(str);
+
return 0;
}
diff --git a/src/test/fuzz/fuzz_diff.c b/src/test/fuzz/fuzz_diff.c
index 1bc60e50ee..a31445666c 100644
--- a/src/test/fuzz/fuzz_diff.c
+++ b/src/test/fuzz/fuzz_diff.c
@@ -10,9 +10,11 @@
#include "test/fuzz/fuzzing.h"
static int
-mock_consensus_compute_digest_(const char *c, consensus_digest_t *d)
+mock_consensus_compute_digest_(const char *c, size_t len,
+ consensus_digest_t *d)
{
(void)c;
+ (void)len;
memset(d->sha3_256, 3, sizeof(d->sha3_256));
return 0;
}
@@ -42,28 +44,34 @@ fuzz_main(const uint8_t *stdin_buf, size_t data_size)
if (! separator)
return 0;
size_t c1_len = separator - stdin_buf;
- char *c1 = tor_memdup_nulterm(stdin_buf, c1_len);
+ const char *c1 = (const char *)stdin_buf;
size_t c2_len = data_size - c1_len - SEPLEN;
- char *c2 = tor_memdup_nulterm(separator + SEPLEN, c2_len);
+ const char *c2 = (const char *)separator + SEPLEN;
- char *c3 = consensus_diff_generate(c1, c2);
+ const char *cp = memchr(c1, 0, c1_len);
+ if (cp)
+ c1_len = cp - c1;
+
+ cp = memchr(c2, 0, c2_len);
+ if (cp)
+ c2_len = cp - c2;
+
+ char *c3 = consensus_diff_generate(c1, c1_len, c2, c2_len);
if (c3) {
- char *c4 = consensus_diff_apply(c1, c3);
+ char *c4 = consensus_diff_apply(c1, c1_len, c3, strlen(c3));
tor_assert(c4);
- if (strcmp(c2, c4)) {
- printf("%s\n", escaped(c1));
- printf("%s\n", escaped(c2));
+ int equal = (c2_len == strlen(c4)) && fast_memeq(c2, c4, c2_len);
+ if (! equal) {
+ //printf("%s\n", escaped(c1));
+ //printf("%s\n", escaped(c2));
printf("%s\n", escaped(c3));
printf("%s\n", escaped(c4));
}
- tor_assert(! strcmp(c2, c4));
+ tor_assert(equal);
tor_free(c3);
tor_free(c4);
}
- tor_free(c1);
- tor_free(c2);
return 0;
}
-
diff --git a/src/test/fuzz/fuzz_diff_apply.c b/src/test/fuzz/fuzz_diff_apply.c
index 9bd3cb0bf8..d8a0f9e590 100644
--- a/src/test/fuzz/fuzz_diff_apply.c
+++ b/src/test/fuzz/fuzz_diff_apply.c
@@ -10,9 +10,11 @@
#include "test/fuzz/fuzzing.h"
static int
-mock_consensus_compute_digest_(const char *c, consensus_digest_t *d)
+mock_consensus_compute_digest_(const char *c, size_t len,
+ consensus_digest_t *d)
{
(void)c;
+ (void)len;
memset(d->sha3_256, 3, sizeof(d->sha3_256));
return 0;
}
@@ -50,16 +52,13 @@ fuzz_main(const uint8_t *stdin_buf, size_t data_size)
if (! separator)
return 0;
size_t c1_len = separator - stdin_buf;
- char *c1 = tor_memdup_nulterm(stdin_buf, c1_len);
+ const char *c1 = (const char *)stdin_buf;
size_t c2_len = data_size - c1_len - SEPLEN;
- char *c2 = tor_memdup_nulterm(separator + SEPLEN, c2_len);
+ const char *c2 = (const char *)separator + SEPLEN;
- char *c3 = consensus_diff_apply(c1, c2);
+ char *c3 = consensus_diff_apply(c1, c1_len, c2, c2_len);
- tor_free(c1);
- tor_free(c2);
tor_free(c3);
return 0;
}
-
diff --git a/src/test/fuzz/fuzz_hsdescv3.c b/src/test/fuzz/fuzz_hsdescv3.c
index 2cbd655898..9d4a6dbb55 100644
--- a/src/test/fuzz/fuzz_hsdescv3.c
+++ b/src/test/fuzz/fuzz_hsdescv3.c
@@ -35,16 +35,21 @@ mock_rsa_ed25519_crosscert_check(const uint8_t *crosscert,
static size_t
mock_decrypt_desc_layer(const hs_descriptor_t *desc,
- const uint8_t *encrypted_blob,
- size_t encrypted_blob_size,
const uint8_t *descriptor_cookie,
- int is_superencrypted_layer,
+ bool is_superencrypted_layer,
char **decrypted_out)
{
(void)is_superencrypted_layer;
(void)desc;
(void)descriptor_cookie;
const size_t overhead = HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN;
+ const uint8_t *encrypted_blob = (is_superencrypted_layer)
+ ? desc->plaintext_data.superencrypted_blob
+ : desc->superencrypted_data.encrypted_blob;
+ size_t encrypted_blob_size = (is_superencrypted_layer)
+ ? desc->plaintext_data.superencrypted_blob_size
+ : desc->superencrypted_data.encrypted_blob_size;
+
if (encrypted_blob_size < overhead)
return 0;
*decrypted_out = tor_memdup_nulterm(
diff --git a/src/test/fuzz/fuzz_http.c b/src/test/fuzz/fuzz_http.c
index 2798c47d23..44393b3a10 100644
--- a/src/test/fuzz/fuzz_http.c
+++ b/src/test/fuzz/fuzz_http.c
@@ -8,7 +8,7 @@
#include "core/or/or.h"
#include "lib/err/backtrace.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "feature/dircache/dircache.h"
diff --git a/src/test/fuzz/fuzz_http_connect.c b/src/test/fuzz/fuzz_http_connect.c
index a60fc36804..2a597cae74 100644
--- a/src/test/fuzz/fuzz_http_connect.c
+++ b/src/test/fuzz/fuzz_http_connect.c
@@ -8,7 +8,7 @@
#include "core/or/or.h"
#include "lib/err/backtrace.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
diff --git a/src/test/fuzz/fuzz_multi.sh b/src/test/fuzz/fuzz_multi.sh
index b4a17ed8cb..406ab498d9 100755
--- a/src/test/fuzz/fuzz_multi.sh
+++ b/src/test/fuzz/fuzz_multi.sh
@@ -1,3 +1,5 @@
+#!/bin/sh
+
MEMLIMIT_BYTES=21990500990976
N_CPUS=1
@@ -6,9 +8,9 @@ if [ $# -ge 1 ]; then
shift
fi
-FILTER=echo
+FILTER="echo"
-for i in `seq -w "$N_CPUS"`; do
+for i in $(seq -w "$N_CPUS"); do
if [ "$i" -eq 1 ]; then
if [ "$N_CPUS" -eq 1 ]; then
INSTANCE=""
diff --git a/src/test/fuzz/fuzz_socks.c b/src/test/fuzz/fuzz_socks.c
index 06cb08391e..d6c416a0f9 100644
--- a/src/test/fuzz/fuzz_socks.c
+++ b/src/test/fuzz/fuzz_socks.c
@@ -6,7 +6,7 @@
#define BUFFERS_PRIVATE
#include "core/or/or.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "lib/err/backtrace.h"
#include "lib/log/log.h"
#include "core/proto/proto_socks.h"
diff --git a/src/test/fuzz/fuzz_strops.c b/src/test/fuzz/fuzz_strops.c
new file mode 100644
index 0000000000..459b4e21aa
--- /dev/null
+++ b/src/test/fuzz/fuzz_strops.c
@@ -0,0 +1,253 @@
+/* Copyright (c) 2018-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file fuzz_strops.c
+ * \brief Fuzzers for various string encoding/decoding operations
+ **/
+
+#include "orconfig.h"
+
+#include "lib/cc/torint.h"
+#include "lib/ctime/di_ops.h"
+#include "lib/encoding/binascii.h"
+#include "lib/encoding/cstring.h"
+#include "lib/encoding/kvline.h"
+#include "lib/encoding/confline.h"
+#include "lib/malloc/malloc.h"
+#include "lib/log/escape.h"
+#include "lib/log/util_bug.h"
+#include "lib/intmath/muldiv.h"
+
+#include "test/fuzz/fuzzing.h"
+
+#include <stdio.h>
+#include <string.h>
+
+int
+fuzz_init(void)
+{
+ return 0;
+}
+
+int
+fuzz_cleanup(void)
+{
+ return 0;
+}
+
+typedef struct chunk_t {
+ uint8_t *buf;
+ size_t len;
+} chunk_t;
+
+#define chunk_free(ch) \
+ FREE_AND_NULL(chunk_t, chunk_free_, (ch))
+
+static chunk_t *
+chunk_new(size_t len)
+{
+ chunk_t *ch = tor_malloc(sizeof(chunk_t));
+ ch->buf = tor_malloc(len);
+ ch->len = len;
+ return ch;
+}
+static void
+chunk_free_(chunk_t *ch)
+{
+ if (!ch)
+ return;
+ tor_free(ch->buf);
+ tor_free(ch);
+}
+static bool
+chunk_eq(const chunk_t *a, const chunk_t *b)
+{
+ return a->len == b->len && fast_memeq(a->buf, b->buf, a->len);
+}
+
+static chunk_t *
+b16_dec(const chunk_t *inp)
+{
+ chunk_t *ch = chunk_new(CEIL_DIV(inp->len, 2));
+ int r = base16_decode((char *)ch->buf, ch->len, (char *)inp->buf, inp->len);
+ if (r >= 0) {
+ ch->len = r;
+ } else {
+ chunk_free(ch);
+ }
+ return ch;
+}
+static chunk_t *
+b16_enc(const chunk_t *inp)
+{
+ chunk_t *ch = chunk_new(inp->len * 2 + 1);
+ base16_encode((char *)ch->buf, ch->len, (char*)inp->buf, inp->len);
+ return ch;
+}
+
+static chunk_t *
+b32_dec(const chunk_t *inp)
+{
+ chunk_t *ch = chunk_new(inp->len);//XXXX
+ int r = base32_decode((char *)ch->buf, ch->len, (char *)inp->buf, inp->len);
+ if (r >= 0) {
+ ch->len = r;
+ } else {
+ chunk_free(ch);
+ }
+ return ch;
+}
+static chunk_t *
+b32_enc(const chunk_t *inp)
+{
+ chunk_t *ch = chunk_new(base32_encoded_size(inp->len));
+ base32_encode((char *)ch->buf, ch->len, (char*)inp->buf, inp->len);
+ ch->len = strlen((char *) ch->buf);
+ return ch;
+}
+
+static chunk_t *
+b64_dec(const chunk_t *inp)
+{
+ chunk_t *ch = chunk_new(inp->len);//XXXX This could be shorter.
+ int r = base64_decode((char *)ch->buf, ch->len, (char *)inp->buf, inp->len);
+ if (r >= 0) {
+ ch->len = r;
+ } else {
+ chunk_free(ch);
+ }
+ return ch;
+}
+static chunk_t *
+b64_enc(const chunk_t *inp)
+{
+ chunk_t *ch = chunk_new(BASE64_BUFSIZE(inp->len));
+ base64_encode((char *)ch->buf, ch->len, (char *)inp->buf, inp->len, 0);
+ ch->len = strlen((char *) ch->buf);
+ return ch;
+}
+
+static chunk_t *
+c_dec(const chunk_t *inp)
+{
+ char *s = tor_memdup_nulterm(inp->buf, inp->len);
+ chunk_t *ch = tor_malloc(sizeof(chunk_t));
+ char *r = NULL;
+ (void) unescape_string(s, &r, &ch->len);
+ tor_free(s);
+ ch->buf = (uint8_t*) r;
+ if (!ch->buf) {
+ tor_free(ch);
+ }
+ return ch;
+}
+static chunk_t *
+c_enc(const chunk_t *inp)
+{
+ char *s = tor_memdup_nulterm(inp->buf, inp->len);
+ chunk_t *ch = tor_malloc(sizeof(chunk_t));
+ ch->buf = (uint8_t*)esc_for_log(s);
+ tor_free(s);
+ ch->len = strlen((char*)ch->buf);
+ return ch;
+}
+
+static int kv_flags = 0;
+static config_line_t *
+kv_dec(const chunk_t *inp)
+{
+ char *s = tor_memdup_nulterm(inp->buf, inp->len);
+ config_line_t *res = kvline_parse(s, kv_flags);
+ tor_free(s);
+ return res;
+}
+static chunk_t *
+kv_enc(const config_line_t *inp)
+{
+ char *s = kvline_encode(inp, kv_flags);
+ if (!s)
+ return NULL;
+ chunk_t *res = tor_malloc(sizeof(chunk_t));
+ res->buf = (uint8_t*)s;
+ res->len = strlen(s);
+ return res;
+}
+
+/* Given an encoder function, a decoder function, and a function to free
+ * the decoded object, check whether any string that successfully decoded
+ * will then survive an encode-decode-encode round-trip unchanged.
+ */
+#define ENCODE_ROUNDTRIP(E,D,FREE) \
+ STMT_BEGIN { \
+ bool err = false; \
+ a = D(&inp); \
+ if (!a) \
+ return 0; \
+ b = E(a); \
+ tor_assert(b); \
+ c = D(b); \
+ tor_assert(c); \
+ d = E(c); \
+ tor_assert(d); \
+ if (!chunk_eq(b,d)) { \
+ printf("Unequal chunks: %s\n", \
+ hex_str((char*)b->buf, b->len)); \
+ printf(" vs %s\n", \
+ hex_str((char*)d->buf, d->len)); \
+ err = true; \
+ } \
+ FREE(a); \
+ chunk_free(b); \
+ FREE(c); \
+ chunk_free(d); \
+ tor_assert(!err); \
+ } STMT_END
+
+int
+fuzz_main(const uint8_t *stdin_buf, size_t data_size)
+{
+ if (!data_size)
+ return 0;
+
+ chunk_t inp = { (uint8_t*)stdin_buf, data_size };
+ chunk_t *b=NULL,*d=NULL;
+ void *a=NULL,*c=NULL;
+
+ switch (stdin_buf[0]) {
+ case 0:
+ ENCODE_ROUNDTRIP(b16_enc, b16_dec, chunk_free_);
+ break;
+ case 1:
+ ENCODE_ROUNDTRIP(b32_enc, b32_dec, chunk_free_);
+ break;
+ case 2:
+ ENCODE_ROUNDTRIP(b64_enc, b64_dec, chunk_free_);
+ break;
+ case 3:
+ ENCODE_ROUNDTRIP(c_enc, c_dec, chunk_free_);
+ break;
+ case 5:
+ kv_flags = KV_QUOTED|KV_OMIT_KEYS;
+ ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
+ break;
+ case 6:
+ kv_flags = 0;
+ ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
+ break;
+ case 7:
+ kv_flags = KV_OMIT_VALS;
+ ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
+ break;
+ case 8:
+ kv_flags = KV_QUOTED;
+ ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
+ break;
+ case 9:
+ kv_flags = KV_QUOTED|KV_OMIT_VALS;
+ ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
+ break;
+ }
+
+ return 0;
+}
diff --git a/src/test/fuzz/fuzz_vrs.c b/src/test/fuzz/fuzz_vrs.c
index 967397d1af..7b61b8df2d 100644
--- a/src/test/fuzz/fuzz_vrs.c
+++ b/src/test/fuzz/fuzz_vrs.c
@@ -3,6 +3,7 @@
#define NS_PARSE_PRIVATE
#define NETWORKSTATUS_PRIVATE
#include "core/or/or.h"
+#include "feature/dirauth/dirvote.h"
#include "feature/dirparse/ns_parse.h"
#include "feature/dirparse/unparseable.h"
#include "lib/memarea/memarea.h"
@@ -35,9 +36,12 @@ fuzz_init(void)
dummy_vote = tor_malloc_zero(sizeof(*dummy_vote));
dummy_vote->known_flags = smartlist_new();
smartlist_split_string(dummy_vote->known_flags,
- "Authority BadExit Exit Fast Guard HSDir "
- "NoEdConsensus Running Stable V2Dir Valid",
+ DIRVOTE_UNIVERSAL_FLAGS,
" ", 0, 0);
+ smartlist_split_string(dummy_vote->known_flags,
+ DIRVOTE_OPTIONAL_FLAGS,
+ " ", 0, 0);
+ smartlist_sort_strings(dummy_vote->known_flags);
return 0;
}
@@ -53,24 +57,24 @@ fuzz_cleanup(void)
int
fuzz_main(const uint8_t *data, size_t sz)
{
- char *str = tor_memdup_nulterm(data, sz);
const char *s;
routerstatus_t *rs_ns = NULL, *rs_md = NULL, *rs_vote = NULL;
vote_routerstatus_t *vrs = tor_malloc_zero(sizeof(*vrs));
smartlist_t *tokens = smartlist_new();
+ const char *eos = (const char *)data + sz;
- s = str;
- rs_ns = routerstatus_parse_entry_from_string(area, &s, tokens,
+ s = (const char *)data;
+ rs_ns = routerstatus_parse_entry_from_string(area, &s, eos, tokens,
NULL, NULL, 26, FLAV_NS);
tor_assert(smartlist_len(tokens) == 0);
- s = str;
- rs_md = routerstatus_parse_entry_from_string(area, &s, tokens,
+ s = (const char *)data;
+ rs_md = routerstatus_parse_entry_from_string(area, &s, eos, tokens,
NULL, NULL, 26, FLAV_MICRODESC);
tor_assert(smartlist_len(tokens) == 0);
- s = str;
- rs_vote = routerstatus_parse_entry_from_string(area, &s, tokens,
+ s = (const char *)data;
+ rs_vote = routerstatus_parse_entry_from_string(area, &s, eos, tokens,
dummy_vote, vrs, 26, FLAV_NS);
tor_assert(smartlist_len(tokens) == 0);
@@ -82,6 +86,6 @@ fuzz_main(const uint8_t *data, size_t sz)
vote_routerstatus_free(vrs);
memarea_clear(area);
smartlist_free(tokens);
- tor_free(str);
+
return 0;
}
diff --git a/src/test/fuzz/fuzzing.h b/src/test/fuzz/fuzzing.h
index 150ac4aa7d..2d278825ec 100644
--- a/src/test/fuzz/fuzzing.h
+++ b/src/test/fuzz/fuzzing.h
@@ -9,5 +9,5 @@ int fuzz_main(const uint8_t *data, size_t sz);
void disable_signature_checking(void);
-#endif /* FUZZING_H */
+#endif /* !defined(FUZZING_H) */
diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c
index 8ea4898522..604aba7a7f 100644
--- a/src/test/fuzz/fuzzing_common.c
+++ b/src/test/fuzz/fuzzing_common.c
@@ -1,14 +1,17 @@
/* Copyright (c) 2016-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define CRYPTO_ED25519_PRIVATE
+#define CONFIG_PRIVATE
#include "orconfig.h"
#include "core/or/or.h"
+#include "app/main/subsysmgr.h"
#include "lib/err/backtrace.h"
#include "app/config/config.h"
#include "test/fuzz/fuzzing.h"
#include "lib/compress/compress.h"
#include "lib/crypt_ops/crypto_ed25519.h"
#include "lib/crypt_ops/crypto_init.h"
+#include "lib/version/torversion.h"
static or_options_t *mock_options = NULL;
static const or_options_t *
@@ -94,12 +97,10 @@ disable_signature_checking(void)
static void
global_init(void)
{
- tor_threads_init();
- tor_compress_init();
+ subsystems_init_upto(SUBSYS_LEVEL_LIBS);
+ flush_log_messages_from_startup();
- /* Initialise logging first */
- init_logging(1);
- configure_backtrace_handler(get_version());
+ tor_compress_init();
if (crypto_global_init(0, NULL, NULL) < 0)
abort();
@@ -111,7 +112,7 @@ global_init(void)
}
/* set up the options. */
- mock_options = tor_malloc_zero(sizeof(or_options_t));
+ mock_options = options_new();
MOCK(get_options, mock_get_options);
/* Make BUG() and nonfatal asserts crash */
@@ -137,7 +138,7 @@ LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
return fuzz_main(Data, Size);
}
-#else /* Not LLVM_FUZZ, so AFL. */
+#else /* !defined(LLVM_FUZZ) */
int
main(int argc, char **argv)
@@ -166,7 +167,7 @@ main(int argc, char **argv)
memset(&s, 0, sizeof(s));
set_log_severity_config(loglevel, LOG_ERR, &s);
/* ALWAYS log bug warnings. */
- s.masks[LOG_WARN-LOG_ERR] |= LD_BUG;
+ s.masks[SEVERITY_MASK_IDX(LOG_WARN)] |= LD_BUG;
add_stream_log(&s, "", fileno(stdout));
}
@@ -189,9 +190,9 @@ main(int argc, char **argv)
if (fuzz_cleanup() < 0)
abort();
- tor_free(mock_options);
+ or_options_free(mock_options);
UNMOCK(get_options);
return 0;
}
-#endif
+#endif /* defined(LLVM_FUZZ) */
diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am
index 27eeced8c5..d0711f05d6 100644
--- a/src/test/fuzz/include.am
+++ b/src/test/fuzz/include.am
@@ -153,6 +153,16 @@ src_test_fuzz_fuzz_socks_LDADD = $(FUZZING_LIBS)
endif
if UNITTESTS_ENABLED
+src_test_fuzz_fuzz_strops_SOURCES = \
+ src/test/fuzz/fuzzing_common.c \
+ src/test/fuzz/fuzz_strops.c
+src_test_fuzz_fuzz_strops_CPPFLAGS = $(FUZZING_CPPFLAGS)
+src_test_fuzz_fuzz_strops_CFLAGS = $(FUZZING_CFLAGS)
+src_test_fuzz_fuzz_strops_LDFLAGS = $(FUZZING_LDFLAG)
+src_test_fuzz_fuzz_strops_LDADD = $(FUZZING_LIBS)
+endif
+
+if UNITTESTS_ENABLED
src_test_fuzz_fuzz_vrs_SOURCES = \
src/test/fuzz/fuzzing_common.c \
src/test/fuzz/fuzz_vrs.c
@@ -176,6 +186,7 @@ FUZZERS = \
src/test/fuzz/fuzz-iptsv2 \
src/test/fuzz/fuzz-microdesc \
src/test/fuzz/fuzz-socks \
+ src/test/fuzz/fuzz-strops \
src/test/fuzz/fuzz-vrs
endif
@@ -291,6 +302,15 @@ src_test_fuzz_lf_fuzz_socks_LDADD = $(LIBFUZZER_LIBS)
endif
if UNITTESTS_ENABLED
+src_test_fuzz_lf_fuzz_strops_SOURCES = \
+ $(src_test_fuzz_fuzz_strops_SOURCES)
+src_test_fuzz_lf_fuzz_strops_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
+src_test_fuzz_lf_fuzz_strops_CFLAGS = $(LIBFUZZER_CFLAGS)
+src_test_fuzz_lf_fuzz_strops_LDFLAGS = $(LIBFUZZER_LDFLAG)
+src_test_fuzz_lf_fuzz_strops_LDADD = $(LIBFUZZER_LIBS)
+endif
+
+if UNITTESTS_ENABLED
src_test_fuzz_lf_fuzz_vrs_SOURCES = \
$(src_test_fuzz_fuzz_vrs_SOURCES)
src_test_fuzz_lf_fuzz_vrs_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
@@ -312,6 +332,7 @@ LIBFUZZER_FUZZERS = \
src/test/fuzz/lf-fuzz-iptsv2 \
src/test/fuzz/lf-fuzz-microdesc \
src/test/fuzz/lf-fuzz-socks \
+ src/test/fuzz/lf-fuzz-strops \
src/test/fuzz/lf-fuzz-vrs
else
@@ -406,6 +427,13 @@ src_test_fuzz_liboss_fuzz_socks_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
endif
if UNITTESTS_ENABLED
+src_test_fuzz_liboss_fuzz_strops_a_SOURCES = \
+ $(src_test_fuzz_fuzz_strops_SOURCES)
+src_test_fuzz_liboss_fuzz_strops_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
+src_test_fuzz_liboss_fuzz_strops_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
+endif
+
+if UNITTESTS_ENABLED
src_test_fuzz_liboss_fuzz_vrs_a_SOURCES = \
$(src_test_fuzz_fuzz_vrs_SOURCES)
src_test_fuzz_liboss_fuzz_vrs_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
@@ -425,6 +453,7 @@ OSS_FUZZ_FUZZERS = \
src/test/fuzz/liboss-fuzz-iptsv2.a \
src/test/fuzz/liboss-fuzz-microdesc.a \
src/test/fuzz/liboss-fuzz-socks.a \
+ src/test/fuzz/liboss-fuzz-strops.a \
src/test/fuzz/liboss-fuzz-vrs.a
else
diff --git a/src/test/fuzz/minimize.sh b/src/test/fuzz/minimize.sh
index 87d3dda13c..ce43812bb8 100755
--- a/src/test/fuzz/minimize.sh
+++ b/src/test/fuzz/minimize.sh
@@ -7,7 +7,7 @@ if [ ! -d "$1" ] ; then
exit 1
fi
-which=`basename "$1"`
+which=$(basename "$1")
mkdir "$1.out"
afl-cmin -i "$1" -o "$1.out" -m none "./src/test/fuzz/fuzz-${which}"
diff --git a/src/test/fuzz_static_testcases.sh b/src/test/fuzz_static_testcases.sh
index f7b3adffb1..b883352402 100755
--- a/src/test/fuzz_static_testcases.sh
+++ b/src/test/fuzz_static_testcases.sh
@@ -14,7 +14,7 @@ fi
for fuzzer in "${builddir:-.}"/src/test/fuzz/fuzz-* ; do
- f=`basename $fuzzer`
+ f=$(basename "$fuzzer")
case="${f#fuzz-}"
if [ -d "${TOR_FUZZ_CORPORA}/${case}" ]; then
echo "Running tests for ${case}"
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index f2ae8398df..0a21fe576b 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -21,26 +21,35 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
/* For a usable intro point we need at least two link specifiers: One legacy
* keyid and one ipv4 */
{
- hs_desc_link_specifier_t *ls_legacy = tor_malloc_zero(sizeof(*ls_legacy));
- hs_desc_link_specifier_t *ls_v4 = tor_malloc_zero(sizeof(*ls_v4));
- ls_legacy->type = LS_LEGACY_ID;
- memcpy(ls_legacy->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8",
- DIGEST_LEN);
- ls_v4->u.ap.port = 9001;
- int family = tor_addr_parse(&ls_v4->u.ap.addr, addr);
+ tor_addr_t a;
+ tor_addr_make_unspec(&a);
+ link_specifier_t *ls_legacy = link_specifier_new();
+ link_specifier_t *ls_ip = link_specifier_new();
+ link_specifier_set_ls_type(ls_legacy, LS_LEGACY_ID);
+ memset(link_specifier_getarray_un_legacy_id(ls_legacy), 'C',
+ link_specifier_getlen_un_legacy_id(ls_legacy));
+ int family = tor_addr_parse(&a, addr);
switch (family) {
case AF_INET:
- ls_v4->type = LS_IPV4;
+ link_specifier_set_ls_type(ls_ip, LS_IPV4);
+ link_specifier_set_un_ipv4_addr(ls_ip, tor_addr_to_ipv4h(&a));
+ link_specifier_set_un_ipv4_port(ls_ip, 9001);
break;
case AF_INET6:
- ls_v4->type = LS_IPV6;
+ link_specifier_set_ls_type(ls_ip, LS_IPV6);
+ memcpy(link_specifier_getarray_un_ipv6_addr(ls_ip),
+ tor_addr_to_in6_addr8(&a),
+ link_specifier_getlen_un_ipv6_addr(ls_ip));
+ link_specifier_set_un_ipv6_port(ls_ip, 9001);
break;
default:
- /* Stop the test, not suppose to have an error. */
- tt_int_op(family, OP_EQ, AF_INET);
+ /* Stop the test, not supposed to have an error.
+ * Compare with -1 to show the actual family.
+ */
+ tt_int_op(family, OP_EQ, -1);
}
smartlist_add(ip->link_specifiers, ls_legacy);
- smartlist_add(ip->link_specifiers, ls_v4);
+ smartlist_add(ip->link_specifiers, ls_ip);
}
ret = ed25519_keypair_generate(&auth_kp, 0);
@@ -202,7 +211,6 @@ void
hs_helper_desc_equal(const hs_descriptor_t *desc1,
const hs_descriptor_t *desc2)
{
- char *addr1 = NULL, *addr2 = NULL;
/* Plaintext data section. */
tt_int_op(desc1->plaintext_data.version, OP_EQ,
desc2->plaintext_data.version);
@@ -291,35 +299,57 @@ hs_helper_desc_equal(const hs_descriptor_t *desc1,
tt_int_op(smartlist_len(ip1->link_specifiers), ==,
smartlist_len(ip2->link_specifiers));
for (int j = 0; j < smartlist_len(ip1->link_specifiers); j++) {
- hs_desc_link_specifier_t *ls1 = smartlist_get(ip1->link_specifiers, j),
- *ls2 = smartlist_get(ip2->link_specifiers, j);
- tt_int_op(ls1->type, ==, ls2->type);
- switch (ls1->type) {
+ link_specifier_t *ls1 = smartlist_get(ip1->link_specifiers, j),
+ *ls2 = smartlist_get(ip2->link_specifiers, j);
+ tt_int_op(link_specifier_get_ls_type(ls1), ==,
+ link_specifier_get_ls_type(ls2));
+ switch (link_specifier_get_ls_type(ls1)) {
case LS_IPV4:
+ {
+ uint32_t addr1 = link_specifier_get_un_ipv4_addr(ls1);
+ uint32_t addr2 = link_specifier_get_un_ipv4_addr(ls2);
+ tt_int_op(addr1, OP_EQ, addr2);
+ uint16_t port1 = link_specifier_get_un_ipv4_port(ls1);
+ uint16_t port2 = link_specifier_get_un_ipv4_port(ls2);
+ tt_int_op(port1, ==, port2);
+ }
+ break;
case LS_IPV6:
{
- addr1 = tor_addr_to_str_dup(&ls1->u.ap.addr);
- addr2 = tor_addr_to_str_dup(&ls2->u.ap.addr);
- tt_str_op(addr1, OP_EQ, addr2);
- tor_free(addr1);
- tor_free(addr2);
- tt_int_op(ls1->u.ap.port, ==, ls2->u.ap.port);
+ const uint8_t *addr1 =
+ link_specifier_getconstarray_un_ipv6_addr(ls1);
+ const uint8_t *addr2 =
+ link_specifier_getconstarray_un_ipv6_addr(ls2);
+ tt_int_op(link_specifier_getlen_un_ipv6_addr(ls1), OP_EQ,
+ link_specifier_getlen_un_ipv6_addr(ls2));
+ tt_mem_op(addr1, OP_EQ, addr2,
+ link_specifier_getlen_un_ipv6_addr(ls1));
+ uint16_t port1 = link_specifier_get_un_ipv6_port(ls1);
+ uint16_t port2 = link_specifier_get_un_ipv6_port(ls2);
+ tt_int_op(port1, ==, port2);
}
break;
case LS_LEGACY_ID:
- tt_mem_op(ls1->u.legacy_id, OP_EQ, ls2->u.legacy_id,
- sizeof(ls1->u.legacy_id));
+ {
+ const uint8_t *id1 =
+ link_specifier_getconstarray_un_legacy_id(ls1);
+ const uint8_t *id2 =
+ link_specifier_getconstarray_un_legacy_id(ls2);
+ tt_int_op(link_specifier_getlen_un_legacy_id(ls1), OP_EQ,
+ link_specifier_getlen_un_legacy_id(ls2));
+ tt_mem_op(id1, OP_EQ, id2,
+ link_specifier_getlen_un_legacy_id(ls1));
+ }
break;
default:
/* Unknown type, caught it and print its value. */
- tt_int_op(ls1->type, OP_EQ, -1);
+ tt_int_op(link_specifier_get_ls_type(ls1), OP_EQ, -1);
}
}
}
}
done:
- tor_free(addr1);
- tor_free(addr2);
+ ;
}
diff --git a/src/test/include.am b/src/test/include.am
index ecb7689579..d8e25dea9f 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -23,7 +23,9 @@ TESTSCRIPTS = \
src/test/test_workqueue_pipe.sh \
src/test/test_workqueue_pipe2.sh \
src/test/test_workqueue_socketpair.sh \
- src/test/test_switch_id.sh
+ src/test/test_switch_id.sh \
+ src/test/test_cmdline.sh \
+ src/test/test_parseconf.sh
if USE_RUST
TESTSCRIPTS += \
@@ -31,9 +33,20 @@ TESTSCRIPTS += \
endif
if USEPYTHON
-TESTSCRIPTS += src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh
+TESTSCRIPTS += \
+ src/test/test_ntor.sh \
+ src/test/test_hs_ntor.sh \
+ src/test/test_bt.sh \
+ scripts/maint/practracker/test_practracker.sh
+
+if COVERAGE_ENABLED
+# ...
+else
+# Only do this when coverage is not on, since it invokes lots of code
+# in a kind of unpredictable way.
TESTSCRIPTS += src/test/test_rebind.sh
endif
+endif
TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
src/test/test_workqueue \
@@ -46,10 +59,8 @@ TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-v2-min hs-v3-min \
single-onion-v23
# only run if we can ping6 ::1 (localhost)
-# IPv6-only v3 single onion services don't work yet, so we don't test the
-# single-onion-v23-ipv6-md flavor
TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-v23-ipv6-md \
- single-onion-ipv6-md
+ single-onion-v23-ipv6-md
# only run if we can find a stable (or simply another) version of tor
TEST_CHUTNEY_FLAVORS_MIXED = mixed+hs-v2
@@ -65,10 +76,11 @@ noinst_PROGRAMS+= \
src/test/test \
src/test/test-slow \
src/test/test-memwipe \
- src/test/test-child \
+ src/test/test-process \
src/test/test_workqueue \
src/test/test-switch-id \
- src/test/test-timers
+ src/test/test-timers \
+ src/test/test-rng
endif
src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
@@ -84,22 +96,28 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
src_test_test_SOURCES =
if UNITTESTS_ENABLED
+
+# ADD_C_FILE: INSERT SOURCES HERE.
src_test_test_SOURCES += \
src/test/log_test_helpers.c \
src/test/hs_test_helpers.c \
src/test/rend_test_helpers.c \
+ src/test/resolve_test_helpers.c \
+ src/test/rng_test_helpers.c \
src/test/test.c \
src/test/test_accounting.c \
src/test/test_addr.c \
src/test/test_address.c \
src/test/test_address_set.c \
src/test/test_bridges.c \
+ src/test/test_btrack.c \
src/test/test_buffers.c \
src/test/test_bwmgt.c \
src/test/test_cell_formats.c \
src/test/test_cell_queue.c \
src/test/test_channel.c \
src/test/test_channelpadding.c \
+ src/test/test_circuitpadding.c \
src/test/test_channeltls.c \
src/test/test_checkdir.c \
src/test/test_circuitlist.c \
@@ -109,6 +127,8 @@ src_test_test_SOURCES += \
src/test/test_circuitstats.c \
src/test/test_compat_libevent.c \
src/test/test_config.c \
+ src/test/test_confmgr.c \
+ src/test/test_confparse.c \
src/test/test_connection.c \
src/test/test_conscache.c \
src/test/test_consdiff.c \
@@ -118,10 +138,12 @@ src_test_test_SOURCES += \
src/test/test_controller_events.c \
src/test/test_crypto.c \
src/test/test_crypto_ope.c \
+ src/test/test_crypto_rng.c \
src/test/test_data.c \
src/test/test_dir.c \
src/test/test_dir_common.c \
src/test/test_dir_handle_get.c \
+ src/test/test_dispatch.c \
src/test/test_dos.c \
src/test/test_entryconn.c \
src/test/test_entrynodes.c \
@@ -140,12 +162,15 @@ src_test_test_SOURCES += \
src/test/test_handles.c \
src/test/test_hs_cache.c \
src/test/test_hs_descriptor.c \
+ src/test/test_hs_dos.c \
src/test/test_introduce.c \
src/test/test_keypin.c \
src/test/test_link_handshake.c \
src/test/test_logging.c \
src/test/test_mainloop.c \
src/test/test_microdesc.c \
+ src/test/test_namemap.c \
+ src/test/test_netinfo.c \
src/test/test_nodelist.c \
src/test/test_oom.c \
src/test/test_oos.c \
@@ -153,11 +178,16 @@ src_test_test_SOURCES += \
src/test/test_pem.c \
src/test/test_periodic_event.c \
src/test/test_policy.c \
+ src/test/test_process.c \
+ src/test/test_process_descs.c \
+ src/test/test_prob_distr.c \
src/test/test_procmon.c \
src/test/test_proto_http.c \
src/test/test_proto_misc.c \
src/test/test_protover.c \
src/test/test_pt.c \
+ src/test/test_pubsub_build.c \
+ src/test/test_pubsub_msg.c \
src/test/test_relay.c \
src/test/test_relaycell.c \
src/test/test_relaycrypt.c \
@@ -168,19 +198,23 @@ src_test_test_SOURCES += \
src/test/test_routerlist.c \
src/test/test_routerset.c \
src/test/test_scheduler.c \
+ src/test/test_sendme.c \
src/test/test_shared_random.c \
src/test/test_socks.c \
src/test/test_status.c \
src/test/test_storagedir.c \
src/test/test_threads.c \
+ src/test/test_token_bucket.c \
src/test/test_tortls.c \
src/test/test_util.c \
src/test/test_util_format.c \
src/test/test_util_process.c \
+ src/test/test_voting_flags.c \
src/test/test_voting_schedule.c \
src/test/test_x509.c \
src/test/test_helpers.c \
src/test/test_dns.c \
+ src/test/test_parsecommon.c \
src/test/testing_common.c \
src/test/testing_rsakeys.c \
src/ext/tinytest.c
@@ -198,9 +232,13 @@ endif
src_test_test_slow_SOURCES =
if UNITTESTS_ENABLED
src_test_test_slow_SOURCES += \
+ src/test/rng_test_helpers.c \
src/test/test_slow.c \
src/test/test_crypto_slow.c \
- src/test/test_util_slow.c \
+ src/test/test_process_slow.c \
+ src/test/test_prob_distr.c \
+ src/test/ptr_helpers.c \
+ src/test/test_ptr_slow.c \
src/test/testing_common.c \
src/test/testing_rsakeys.c \
src/ext/tinytest.c
@@ -251,6 +289,12 @@ src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS)
src_test_test_slow_LDADD = $(src_test_test_LDADD)
src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS)
+src_test_test_rng_CPPFLAGS = $(src_test_test_CPPFLAGS)
+src_test_test_rng_CFLAGS = $(src_test_test_CFLAGS)
+src_test_test_rng_SOURCES = src/test/test_rng.c
+src_test_test_rng_LDFLAGS = $(src_test_test_LDFLAGS)
+src_test_test_rng_LDADD = $(src_test_test_LDADD)
+
src_test_test_memwipe_CPPFLAGS = $(src_test_test_CPPFLAGS)
# Don't use bugtrap cflags here: memwipe tests require memory violations.
src_test_test_memwipe_CFLAGS = $(TEST_CFLAGS)
@@ -292,12 +336,16 @@ src_test_test_timers_LDADD = \
@TOR_LZMA_LIBS@
src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS+= \
src/test/fakechans.h \
src/test/hs_test_helpers.h \
src/test/log_test_helpers.h \
src/test/rend_test_helpers.h \
+ src/test/resolve_test_helpers.h \
+ src/test/rng_test_helpers.h \
src/test/test.h \
+ src/test/ptr_helpers.h \
src/test/test_helpers.h \
src/test/test_dir_common.h \
src/test/test_connection.h \
@@ -369,7 +417,9 @@ EXTRA_DIST += \
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_workqueue_socketpair.sh \
+ src/test/test_cmdline.sh \
+ src/test/test_parseconf.sh
test-rust:
$(TESTS_ENVIRONMENT) "$(abs_top_srcdir)/src/test/test_rust.sh"
diff --git a/src/test/ope_ref.py b/src/test/ope_ref.py
index f9bd97c546..b2f7012563 100644
--- a/src/test/ope_ref.py
+++ b/src/test/ope_ref.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
# Copyright 2018-2019, The Tor Project, Inc. See LICENSE for licensing info.
# Reference implementation for our rudimentary OPE code, used to
diff --git a/src/test/prob_distr_mpfr_ref.c b/src/test/prob_distr_mpfr_ref.c
new file mode 100644
index 0000000000..425733dc1b
--- /dev/null
+++ b/src/test/prob_distr_mpfr_ref.c
@@ -0,0 +1,64 @@
+/* Copyright 2012-2019, The Tor Project, Inc
+ * See LICENSE for licensing information */
+
+/** prob_distr_mpfr_ref.c
+ *
+ * Example reference file for GNU MPFR vectors tested in test_prob_distr.c .
+ * Code by Riastradh.
+ */
+
+#include <complex.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+/* Must come after <stdio.h> so we get mpfr_printf. */
+#include <mpfr.h>
+
+/* gcc -o mpfr prob_distr_mpfr_ref.c -lmpfr -lm */
+
+/* Computes logit(p) for p = .49999 */
+int
+main(void)
+{
+ mpfr_t p, q, r;
+ mpfr_init(p);
+ mpfr_set_prec(p, 200);
+ mpfr_init(q);
+ mpfr_set_prec(q, 200);
+ mpfr_init(r);
+ mpfr_set_prec(r, 200);
+ mpfr_set_d(p, .49999, MPFR_RNDN);
+ mpfr_set_d(q, 1, MPFR_RNDN);
+ /* r := q - p = 1 - p */
+ mpfr_sub(r, q, p, MPFR_RNDN);
+ /* q := p/r = p/(1 - p) */
+ mpfr_div(q, p, r, MPFR_RNDN);
+ /* r := log(q) = log(p/(1 - p)) */
+ mpfr_log(r, q, MPFR_RNDN);
+ mpfr_printf("mpfr 200-bit\t%.128Rg\n", r);
+
+ /*
+ * Print a double approximation to logit three different ways. All
+ * three agree bit for bit on the libms I tried, with the nextafter
+ * adjustment (which is well within the 10 eps relative error bound
+ * advertised). Apparently I must have used the Goldberg expression
+ * for what I wrote down in the test case.
+ */
+ printf("mpfr 53-bit\t%.17g\n", nextafter(mpfr_get_d(r, MPFR_RNDN), 0), 0);
+ volatile double p0 = .49999;
+ printf("log1p\t\t%.17g\n", nextafter(-log1p((1 - 2*p0)/p0), 0));
+ volatile double x = (1 - 2*p0)/p0;
+ volatile double xp1 = x + 1;
+ printf("Goldberg\t%.17g\n", -x*log(xp1)/(xp1 - 1));
+
+ /*
+ * Print a bad approximation, using the naive expression, to see a
+ * lot of wrong digits, far beyond the 10 eps relative error attained
+ * by -log1p((1 - 2*p)/p).
+ */
+ printf("naive\t\t%.17g\n", log(p0/(1 - p0)));
+
+ fflush(stdout);
+ return ferror(stdout);
+}
diff --git a/src/test/ptr_helpers.c b/src/test/ptr_helpers.c
new file mode 100644
index 0000000000..a55ab437fa
--- /dev/null
+++ b/src/test/ptr_helpers.c
@@ -0,0 +1,50 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "test/ptr_helpers.h"
+
+/**
+ * Cast <b> (inptr_t value) to a void pointer.
+ */
+void *
+cast_intptr_to_voidstar(intptr_t x)
+{
+ void *r = (void *)x;
+
+ return r;
+}
+
+/**
+ * Cast x (void pointer) to inptr_t value.
+ */
+intptr_t
+cast_voidstar_to_intptr(void *x)
+{
+ intptr_t r = (intptr_t)x;
+
+ return r;
+}
+
+/**
+ * Cast x (uinptr_t value) to void pointer.
+ */
+void *
+cast_uintptr_to_voidstar(uintptr_t x)
+{
+ void *r = (void *)x;
+
+ return r;
+}
+
+/**
+ * Cast x (void pointer) to uinptr_t value.
+ */
+uintptr_t
+cast_voidstar_to_uintptr(void *x)
+{
+ uintptr_t r = (uintptr_t)x;
+
+ return r;
+}
diff --git a/src/test/ptr_helpers.h b/src/test/ptr_helpers.h
new file mode 100644
index 0000000000..7349bddd51
--- /dev/null
+++ b/src/test/ptr_helpers.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PTR_HELPERS_H
+#define TOR_PTR_HELPERS_H
+
+#include <stdint.h>
+
+void *
+cast_intptr_to_voidstar(intptr_t x);
+
+intptr_t
+cast_voidstar_to_intptr(void *x);
+
+void *
+cast_uintptr_to_voidstar(uintptr_t x);
+
+uintptr_t
+cast_voidstar_to_uintptr(void *x);
+
+#endif /* !defined(TOR_PTR_HELPERS_H) */
diff --git a/src/test/resolve_test_helpers.c b/src/test/resolve_test_helpers.c
new file mode 100644
index 0000000000..73ea730149
--- /dev/null
+++ b/src/test/resolve_test_helpers.c
@@ -0,0 +1,85 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file resolve_test_helpers.c
+ * @brief Helper functions for mocking libc's blocking hostname lookup
+ * facilities.
+ **/
+
+#define RESOLVE_PRIVATE
+#include "orconfig.h"
+#include "test/resolve_test_helpers.h"
+#include "lib/net/address.h"
+#include "lib/net/resolve.h"
+#include "test/test.h"
+
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * Mock replacement for our getaddrinfo/gethostbyname wrapper.
+ **/
+static int
+replacement_host_lookup(const char *name, uint16_t family, tor_addr_t *addr)
+{
+ static const struct lookup_table_ent {
+ const char *name;
+ const char *ipv4;
+ const char *ipv6;
+ } entries[] = {
+ { "localhost", "127.0.0.1", "::1" },
+ { "torproject.org", "198.51.100.6", "2001:DB8::700" },
+ { NULL, NULL, NULL },
+ };
+
+ int r = -1;
+
+ for (unsigned i = 0; entries[i].name != NULL; ++i) {
+ if (!strcasecmp(name, entries[i].name)) {
+ if (family == AF_INET6) {
+ int s = tor_addr_parse(addr, entries[i].ipv6);
+ tt_int_op(s, OP_EQ, AF_INET6);
+ } else {
+ int s = tor_addr_parse(addr, entries[i].ipv4);
+ tt_int_op(s, OP_EQ, AF_INET);
+ }
+ r = 0;
+ break;
+ }
+ }
+
+ log_debug(LD_GENERAL, "resolve(%s,%d) => %s",
+ name, family, r == 0 ? fmt_addr(addr) : "-1");
+
+ return r;
+ done:
+ return -1;
+}
+
+/**
+ * Set up a mock replacement for our wrapper on libc's resolver code.
+ *
+ * According to our replacement, only "localhost" and "torproject.org"
+ * are real addresses; everything else doesn't exist.
+ *
+ * Use this function to avoid using the DNS resolver during unit tests;
+ * call unmock_hostname_resolver() when you're done.
+ **/
+void
+mock_hostname_resolver(void)
+{
+ MOCK(tor_addr_lookup_host_impl, replacement_host_lookup);
+}
+
+/**
+ * Unmock our wrappers for libc's blocking hostname resolver code.
+ **/
+void
+unmock_hostname_resolver(void)
+{
+ UNMOCK(tor_addr_lookup_host_impl);
+}
diff --git a/src/test/resolve_test_helpers.h b/src/test/resolve_test_helpers.h
new file mode 100644
index 0000000000..e7d2e29373
--- /dev/null
+++ b/src/test/resolve_test_helpers.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file resolve_test_helpers.h
+ * @brief Header for test/resolve_test_helpers.c
+ **/
+
+#ifndef TOR_TEST_RESOLVE_TEST_HELPERS_H
+#define TOR_TEST_RESOLVE_TEST_HELPERS_H
+
+void mock_hostname_resolver(void);
+void unmock_hostname_resolver(void);
+
+#endif /* !defined(TOR_TEST_RESOLVE_TEST_HELPERS_H) */
diff --git a/src/test/rng_test_helpers.c b/src/test/rng_test_helpers.c
new file mode 100644
index 0000000000..7024fb5793
--- /dev/null
+++ b/src/test/rng_test_helpers.c
@@ -0,0 +1,259 @@
+/* Copyright (c) 2018-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file rng_test_helpers.c
+ * \brief Helpers for overriding PRNGs during unit tests.
+ *
+ * We define two PRNG overrides: a "reproducible PRNG" where the seed is
+ * chosen randomly but the stream can be replayed later on in case a bug is
+ * found, and a "deterministic PRNG" where the seed is fixed in the unit
+ * tests.
+ *
+ * Obviously, this code is testing-only.
+ */
+
+#include "orconfig.h"
+#include "core/or/or.h"
+
+#include "lib/crypt_ops/crypto_rand.h"
+#include "ext/tinytest.h"
+
+#include "test/rng_test_helpers.h"
+
+#ifndef TOR_UNIT_TESTS
+#error "No. Never link this code into Tor proper."
+#endif
+
+/**
+ * True iff the RNG is currently replaced. Prevents double-replacement.
+ **/
+static bool rng_is_replaced = false;
+
+/**
+ * Mutex to protect deterministic prng.
+ *
+ * Note that if you actually _use_ the prng from two threads at the same time,
+ * the results will probably be nondeterministic anyway.
+ */
+static tor_mutex_t *rng_mutex = NULL;
+
+/**
+ * Cached old value for the thread prng.
+ **/
+static crypto_fast_rng_t *stored_fast_rng = NULL;
+
+/** replacement for crypto_strongest_rand that delegates to crypto_rand. */
+static void
+mock_crypto_strongest_rand(uint8_t *out, size_t len)
+{
+ crypto_rand((char *)out, len);
+}
+
+/* This is the seed of the deterministic randomness. */
+static uint8_t rng_seed[16];
+static crypto_xof_t *rng_xof = NULL;
+
+/**
+ * Print the seed for our PRNG to stdout. We use this when we're failed
+ * test that had a reproducible RNG set.
+ **/
+void
+testing_dump_reproducible_rng_seed(void)
+{
+ printf("\n"
+ "Seed: %s\n",
+ hex_str((const char*)rng_seed, sizeof(rng_seed)));
+}
+
+/** Produce deterministic randomness for the stochastic tests using the global
+ * rng_xof output.
+ *
+ * This function produces deterministic data over multiple calls iff it's
+ * called in the same call order with the same 'n' parameter.
+ * If not, outputs will deviate. */
+static void
+crypto_rand_deterministic(char *out, size_t n)
+{
+ tor_assert(rng_xof);
+ tor_mutex_acquire(rng_mutex);
+ crypto_xof_squeeze_bytes(rng_xof, (uint8_t*)out, n);
+ tor_mutex_release(rng_mutex);
+}
+
+/**
+ * Implementation helper: override our crypto_rand() PRNG with a given seed of
+ * length <b>seed_len</b>. Overlong seeds are truncated; short ones are
+ * padded.
+ **/
+static void
+enable_deterministic_rng_impl(const uint8_t *seed, size_t seed_len)
+{
+ tor_assert(!rng_is_replaced);
+ tor_assert(crypto_rand == crypto_rand__real);
+
+ memset(rng_seed, 0, sizeof(rng_seed));
+ memcpy(rng_seed, seed, MIN(seed_len, sizeof(rng_seed)));
+
+ rng_mutex = tor_mutex_new();
+
+ crypto_xof_free(rng_xof);
+ rng_xof = crypto_xof_new();
+ crypto_xof_add_bytes(rng_xof, rng_seed, sizeof(rng_seed));
+ MOCK(crypto_rand, crypto_rand_deterministic);
+ MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
+
+ uint8_t fast_rng_seed[CRYPTO_FAST_RNG_SEED_LEN];
+ memset(fast_rng_seed, 0xff, sizeof(fast_rng_seed));
+ memcpy(fast_rng_seed, rng_seed, MIN(sizeof(rng_seed),
+ sizeof(fast_rng_seed)));
+ crypto_fast_rng_t *fast_rng = crypto_fast_rng_new_from_seed(fast_rng_seed);
+ crypto_fast_rng_disable_reseed(fast_rng);
+ stored_fast_rng = crypto_replace_thread_fast_rng(fast_rng);
+
+ rng_is_replaced = true;
+}
+
+/**
+ * Replace our get_thread_fast_rng(), crypto_rand() and
+ * crypto_strongest_rand() prngs with a variant that generates all of its
+ * output deterministically from a randomly chosen seed. In the event of an
+ * error, you can log the seed later on with
+ * testing_dump_reproducible_rng_seed.
+ **/
+void
+testing_enable_reproducible_rng(void)
+{
+ const char *provided_seed = getenv("TOR_TEST_RNG_SEED");
+ if (provided_seed) {
+ size_t hexlen = strlen(provided_seed);
+ size_t seedlen = hexlen / 2;
+ uint8_t *seed = tor_malloc(hexlen / 2);
+ if (base16_decode((char*)seed, seedlen, provided_seed, hexlen) < 0) {
+ puts("Cannot decode value in TOR_TEST_RNG_SEED");
+ exit(1);
+ }
+ enable_deterministic_rng_impl(seed, seedlen);
+ tor_free(seed);
+ } else {
+ uint8_t seed[16];
+ crypto_rand((char*)seed, sizeof(seed));
+ enable_deterministic_rng_impl(seed, sizeof(seed));
+ }
+}
+
+/**
+ * Replace our get_thread_fast_rng(), crypto_rand() and
+ * crypto_strongest_rand() prngs with a variant that generates all of its
+ * output deterministically from a fixed seed. This variant is mainly useful
+ * for cases when we don't want coverage to change between runs.
+ *
+ * USAGE NOTE: Test correctness SHOULD NOT depend on the specific output of
+ * this "rng". If you need a specific output, use
+ * testing_enable_prefilled_rng() instead.
+ **/
+void
+testing_enable_deterministic_rng(void)
+{
+ static const uint8_t quotation[] =
+ "What will it be? A tree? A weed? "
+ "Each one is started from a seed."; // -- Mary Ann Hoberman
+ enable_deterministic_rng_impl(quotation, sizeof(quotation));
+}
+
+static uint8_t *prefilled_rng_buffer = NULL;
+static size_t prefilled_rng_buflen;
+static size_t prefilled_rng_idx;
+
+/**
+ * crypto_rand() replacement that returns canned data.
+ **/
+static void
+crypto_rand_prefilled(char *out, size_t n)
+{
+ tor_mutex_acquire(rng_mutex);
+ while (n) {
+ size_t n_to_copy = MIN(prefilled_rng_buflen - prefilled_rng_idx, n);
+ memcpy(out, prefilled_rng_buffer + prefilled_rng_idx, n_to_copy);
+ out += n_to_copy;
+ n -= n_to_copy;
+ prefilled_rng_idx += n_to_copy;
+
+ if (prefilled_rng_idx == prefilled_rng_buflen) {
+ prefilled_rng_idx = 0;
+ }
+ }
+ tor_mutex_release(rng_mutex);
+}
+
+/**
+ * Replace our crypto_rand() and crypto_strongest_rand() prngs with a variant
+ * that yields output from a buffer. If it reaches the end of the buffer, it
+ * starts over.
+ *
+ * Note: the get_thread_fast_rng() prng is not replaced by this; we'll need
+ * more code to support that.
+ **/
+void
+testing_enable_prefilled_rng(const void *buffer, size_t buflen)
+{
+ tor_assert(buflen > 0);
+ tor_assert(!rng_mutex);
+ rng_mutex = tor_mutex_new();
+
+ tor_mutex_acquire(rng_mutex);
+
+ prefilled_rng_buffer = tor_memdup(buffer, buflen);
+ prefilled_rng_buflen = buflen;
+ prefilled_rng_idx = 0;
+
+ tor_mutex_release(rng_mutex);
+
+ MOCK(crypto_rand, crypto_rand_prefilled);
+ MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
+}
+
+/**
+ * Reset the position in the prefilled RNG buffer to the start.
+ */
+void
+testing_prefilled_rng_reset(void)
+{
+ tor_mutex_acquire(rng_mutex);
+ prefilled_rng_idx = 0;
+ tor_mutex_release(rng_mutex);
+}
+
+/**
+ * Undo the overrides for our PRNG. To be used at the end of testing.
+ *
+ * Note that this function should be safe to call even if the rng has not
+ * yet been replaced.
+ **/
+void
+testing_disable_rng_override(void)
+{
+ crypto_xof_free(rng_xof);
+ tor_free(prefilled_rng_buffer);
+ UNMOCK(crypto_rand);
+ UNMOCK(crypto_strongest_rand_);
+ tor_mutex_free(rng_mutex);
+
+ crypto_fast_rng_t *rng = crypto_replace_thread_fast_rng(stored_fast_rng);
+ crypto_fast_rng_free(rng);
+
+ rng_is_replaced = false;
+}
+
+/**
+ * As testing_disable_rng_override(), but dump the seed if the current
+ * test has failed.
+ */
+void
+testing_disable_reproducible_rng(void)
+{
+ if (tinytest_cur_test_has_failed()) {
+ testing_dump_reproducible_rng_seed();
+ }
+ testing_disable_rng_override();
+}
diff --git a/src/test/rng_test_helpers.h b/src/test/rng_test_helpers.h
new file mode 100644
index 0000000000..d7925148ae
--- /dev/null
+++ b/src/test/rng_test_helpers.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2017-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_RNG_TEST_HELPERS_H
+#define TOR_RNG_TEST_HELPERS_H
+
+#include "core/or/or.h"
+
+void testing_enable_deterministic_rng(void);
+void testing_enable_reproducible_rng(void);
+void testing_enable_prefilled_rng(const void *buffer, size_t buflen);
+
+void testing_prefilled_rng_reset(void);
+
+void testing_disable_rng_override(void);
+
+void testing_disable_reproducible_rng(void);
+#define testing_disable_deterministic_rng() \
+ testing_disable_rng_override()
+#define testing_disable_prefilled_rng() \
+ testing_disable_rng_override()
+
+void testing_dump_reproducible_rng_seed(void);
+
+#endif /* !defined(TOR_RNG_TEST_HELPERS_H) */
diff --git a/src/test/test-child.c b/src/test/test-child.c
deleted file mode 100644
index 11a1695cad..0000000000
--- a/src/test/test-child.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Copyright (c) 2011-2019, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-#include "orconfig.h"
-#include <stdio.h>
-#ifdef _WIN32
-#define WINDOWS_LEAN_AND_MEAN
-#include <windows.h>
-#else
-#include <unistd.h>
-#endif /* defined(_WIN32) */
-#include <string.h>
-
-#ifdef _WIN32
-#define SLEEP(sec) Sleep((sec)*1000)
-#else
-#define SLEEP(sec) sleep(sec)
-#endif
-
-/** Trivial test program which prints out its command line arguments so we can
- * check if tor_spawn_background() works */
-int
-main(int argc, char **argv)
-{
- int i;
- int delay = 1;
- int fast = 0;
-
- if (argc > 1) {
- if (!strcmp(argv[1], "--hang")) {
- delay = 60;
- } else if (!strcmp(argv[1], "--fast")) {
- fast = 1;
- delay = 0;
- }
- }
-
- fprintf(stdout, "OUT\n");
- fprintf(stderr, "ERR\n");
- for (i = 1; i < argc; i++)
- fprintf(stdout, "%s\n", argv[i]);
- if (!fast)
- fprintf(stdout, "SLEEPING\n");
- /* We need to flush stdout so that test_util_spawn_background_partial_read()
- succeed. Otherwise ReadFile() will get the entire output in one */
- // XXX: Can we make stdio flush on newline?
- fflush(stdout);
- if (!fast)
- SLEEP(1);
- fprintf(stdout, "DONE\n");
- fflush(stdout);
- if (fast)
- return 0;
-
- while (--delay) {
- SLEEP(1);
- }
-
- return 0;
-}
-
diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c
index 43754ed1c2..3f952e484f 100644
--- a/src/test/test-memwipe.c
+++ b/src/test/test-memwipe.c
@@ -49,7 +49,7 @@ const char *s = NULL;
* us do bad things, such as access freed buffers, without crashing. */
extern const char *malloc_options;
const char *malloc_options = "sufjj";
-#endif
+#endif /* defined(OpenBSD) */
static unsigned
fill_a_buffer_memset(void)
diff --git a/src/test/test-network.sh b/src/test/test-network.sh
index b7a9f1b3c0..5ef995f1a4 100755
--- a/src/test/test-network.sh
+++ b/src/test/test-network.sh
@@ -5,7 +5,7 @@
# If we already know CHUTNEY_PATH, don't bother with argument parsing
TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh"
# Call the chutney version of this script, if it exists, and we can find it
-if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then
+if [ -d "$CHUTNEY_PATH" ] && [ -x "$TEST_NETWORK" ]; then
# we can't produce any output, because we might be --quiet
# this preserves arguments with spaces correctly
exec "$TEST_NETWORK" "$@"
@@ -16,34 +16,16 @@ fi
# Do we output anything at all?
ECHO="${ECHO:-echo}"
# Output is prefixed with the name of the script
-myname=$(basename $0)
-
-# Save the arguments before we destroy them
-# This might not preserve arguments with spaces in them
-ORIGINAL_ARGS="$@"
+myname=$(basename "$0")
# We need to find CHUTNEY_PATH, so that we can call the version of this script
# in chutney/tools with the same arguments. We also need to respect --quiet.
-until [ -z "$1" ]
-do
- case "$1" in
- --chutney-path)
- CHUTNEY_PATH="$2"
- shift
- ;;
- --tor-path)
- TOR_DIR="$2"
- shift
- ;;
- --quiet)
- ECHO=true
- ;;
- *)
- # maybe chutney's test-network.sh can handle it
- ;;
- esac
- shift
-done
+CHUTNEY_PATH=$(echo "$@" | awk -F '--chutney-path ' '{sub(" .*","",$2); print $2}')
+TOR_DIR=$(echo "$@" | awk -F '--tor-dir ' '{sub(" .*","",$2); print $2}')
+
+if echo "$@" | grep -e "--quiet" > /dev/null; then
+ ECHO=true
+fi
# optional: $TOR_DIR is the tor build directory
# it's used to find the location of tor binaries
@@ -52,12 +34,12 @@ done
# - if $PWD looks like a tor build directory, set it to $PWD, or
# - unset $TOR_DIR, and let chutney fall back to finding tor binaries in $PATH
if [ ! -d "$TOR_DIR" ]; then
- if [ -d "$BUILDDIR/src/core/or" -a -d "$BUILDDIR/src/tools" ]; then
+ if [ -d "$BUILDDIR/src/core/or" ] && [ -d "$BUILDDIR/src/tools" ]; then
# Choose the build directory
# But only if it looks like one
$ECHO "$myname: \$TOR_DIR not set, trying \$BUILDDIR"
TOR_DIR="$BUILDDIR"
- elif [ -d "$PWD/src/core/or" -a -d "$PWD/src/tools" ]; then
+ elif [ -d "$PWD/src/core/or" ] && [ -d "$PWD/src/tools" ]; then
# Guess the tor directory is the current directory
# But only if it looks like one
$ECHO "$myname: \$TOR_DIR not set, trying \$PWD"
@@ -73,12 +55,12 @@ fi
# - if $PWD looks like a chutney directory, set it to $PWD, or
# - set it based on $TOR_DIR, expecting chutney to be next to tor, or
# - fail and tell the user how to clone the chutney repository
-if [ ! -d "$CHUTNEY_PATH" -o ! -x "$CHUTNEY_PATH/chutney" ]; then
+if [ ! -d "$CHUTNEY_PATH" ] || [ ! -x "$CHUTNEY_PATH/chutney" ]; then
if [ -x "$PWD/chutney" ]; then
$ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$PWD"
CHUTNEY_PATH="$PWD"
- elif [ -d "$TOR_DIR" -a -d "$TOR_DIR/../chutney" -a \
- -x "$TOR_DIR/../chutney/chutney" ]; then
+ elif [ -d "$TOR_DIR" ] && [ -d "$TOR_DIR/../chutney" ] && \
+ [ -x "$TOR_DIR/../chutney/chutney" ]; then
$ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney"
CHUTNEY_PATH="$TOR_DIR/../chutney"
else
@@ -94,12 +76,12 @@ fi
TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh"
# Call the chutney version of this script, if it exists, and we can find it
-if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then
+if [ -d "$CHUTNEY_PATH" ] && [ -x "$TEST_NETWORK" ]; then
$ECHO "$myname: Calling newer chutney script $TEST_NETWORK"
# this may fail if some arguments have spaces in them
# if so, set CHUTNEY_PATH before calling test-network.sh, and spaces
# will be handled correctly
- exec "$TEST_NETWORK" $ORIGINAL_ARGS
+ exec "$TEST_NETWORK" "$@"
else
$ECHO "$myname: Could not find tools/test-network.sh in CHUTNEY_PATH."
$ECHO "$myname: Please update your chutney using 'git pull'."
diff --git a/src/test/test-process.c b/src/test/test-process.c
new file mode 100644
index 0000000000..eb28ad90e9
--- /dev/null
+++ b/src/test/test-process.c
@@ -0,0 +1,85 @@
+/* Copyright (c) 2011-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include <stdio.h>
+#ifdef _WIN32
+#define WINDOWS_LEAN_AND_MEAN
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif /* defined(_WIN32) */
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+#define SLEEP(sec) Sleep((sec)*1000)
+#else
+#define SLEEP(sec) sleep(sec)
+#endif
+
+/* Trivial test program to test process_t. */
+int
+main(int argc, char **argv)
+{
+ /* Does our process get the right arguments? */
+ for (int i = 0; i < argc; ++i) {
+ fprintf(stdout, "argv[%d] = '%s'\n", i, argv[i]);
+ fflush(stdout);
+ }
+
+ /* Make sure our process got our environment variable. */
+ fprintf(stdout, "Environment variable TOR_TEST_ENV = '%s'\n",
+ getenv("TOR_TEST_ENV"));
+ fflush(stdout);
+
+ /* Test line handling on stdout and stderr. */
+ fprintf(stdout, "Output on stdout\nThis is a new line\n");
+ fflush(stdout);
+
+ fprintf(stderr, "Output on stderr\nThis is a new line\n");
+ fflush(stderr);
+
+ fprintf(stdout, "Partial line on stdout ...");
+ fflush(stdout);
+
+ fprintf(stderr, "Partial line on stderr ...");
+ fflush(stderr);
+
+ SLEEP(2);
+
+ fprintf(stdout, "end of partial line on stdout\n");
+ fflush(stdout);
+ fprintf(stderr, "end of partial line on stderr\n");
+ fflush(stderr);
+
+ /* Echo input from stdin. */
+ char buffer[1024];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), stdin)) {
+ /* Strip the newline. */
+ size_t size = strlen(buffer);
+
+ if (size >= 1 && buffer[size - 1] == '\n') {
+ buffer[size - 1] = '\0';
+ --size;
+ }
+
+ if (size >= 1 && buffer[size - 1] == '\r') {
+ buffer[size - 1] = '\0';
+ --size;
+ }
+
+ fprintf(stdout, "Read line from stdin: '%s'\n", buffer);
+ fflush(stdout);
+
+ if (++count == 3)
+ break;
+ }
+
+ fprintf(stdout, "We are done for here, thank you!\n");
+
+ return 0;
+}
diff --git a/src/test/test.c b/src/test/test.c
index 58b468775c..6dbec26fa8 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -12,6 +12,7 @@
#include "lib/crypt_ops/crypto_dh.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "app/config/or_state_st.h"
+#include "test/rng_test_helpers.h"
#include <stdio.h>
#ifdef HAVE_FCNTL_H
@@ -37,7 +38,7 @@
#include "core/or/or.h"
#include "lib/err/backtrace.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "core/or/circuitlist.h"
#include "core/or/circuitstats.h"
#include "lib/compress/compress.h"
@@ -283,7 +284,7 @@ test_fast_handshake(void *arg)
/* First, test an entire handshake. */
memset(client_handshake, 0, sizeof(client_handshake));
tt_int_op(0, OP_EQ, fast_onionskin_create(&state, client_handshake));
- tt_assert(! tor_mem_is_zero((char*)client_handshake,
+ tt_assert(! fast_mem_is_zero((char*)client_handshake,
sizeof(client_handshake)));
tt_int_op(0, OP_EQ,
@@ -354,18 +355,6 @@ test_onion_queues(void *arg)
tor_free(onionskin);
}
-static crypto_cipher_t *crypto_rand_aes_cipher = NULL;
-
-// Mock replacement for crypto_rand: Generates bytes from a provided AES_CTR
-// cipher in <b>crypto_rand_aes_cipher</b>.
-static void
-crypto_rand_deterministic_aes(char *out, size_t n)
-{
- tor_assert(crypto_rand_aes_cipher);
- memset(out, 0, n);
- crypto_cipher_crypt_inplace(crypto_rand_aes_cipher, out, n);
-}
-
static void
test_circuit_timeout(void *arg)
{
@@ -397,8 +386,7 @@ test_circuit_timeout(void *arg)
// Use a deterministic RNG here, or else we'll get nondeterministic
// coverage in some of the circuitstats functions.
- MOCK(crypto_rand, crypto_rand_deterministic_aes);
- crypto_rand_aes_cipher = crypto_cipher_new("xyzzyplughplover");
+ testing_enable_deterministic_rng();
circuitbuild_running_unit_tests();
#define timeout0 (build_time_t)(30*1000.0)
@@ -534,8 +522,8 @@ test_circuit_timeout(void *arg)
circuit_build_times_free_timeouts(&final);
or_state_free(state);
teardown_periodic_events();
- UNMOCK(crypto_rand);
- crypto_cipher_free(crypto_rand_aes_cipher);
+
+ testing_disable_deterministic_rng();
}
/** Test encoding and parsing of rendezvous service descriptors. */
@@ -845,18 +833,23 @@ struct testgroup_t testgroups[] = {
{ "channeltls/", channeltls_tests },
{ "checkdir/", checkdir_tests },
{ "circuitbuild/", circuitbuild_tests },
+ { "circuitpadding/", circuitpadding_tests },
{ "circuitlist/", circuitlist_tests },
{ "circuitmux/", circuitmux_tests },
- { "circuituse/", circuituse_tests },
{ "circuitstats/", circuitstats_tests },
+ { "circuituse/", circuituse_tests },
{ "compat/libevent/", compat_libevent_tests },
{ "config/", config_tests },
+ { "config/mgr/", confmgr_tests },
+ { "config/parse/", confparse_tests },
{ "connection/", connection_tests },
{ "conscache/", conscache_tests },
{ "consdiff/", consdiff_tests },
{ "consdiffmgr/", consdiffmgr_tests },
{ "container/", container_tests },
+ { "container/namemap/", namemap_tests },
{ "control/", controller_tests },
+ { "control/btrack/", btrack_tests },
{ "control/event/", controller_event_tests },
{ "crypto/", crypto_tests },
{ "crypto/ope/", crypto_ope_tests },
@@ -864,42 +857,54 @@ struct testgroup_t testgroups[] = {
{ "crypto/openssl/", crypto_openssl_tests },
#endif
{ "crypto/pem/", pem_tests },
+ { "crypto/rng/", crypto_rng_tests },
{ "dir/", dir_tests },
- { "dir_handle_get/", dir_handle_get_tests },
+ { "dir/auth/process_descs/", process_descs_tests },
{ "dir/md/", microdesc_tests },
- { "dir/voting-schedule/", voting_schedule_tests },
+ { "dir/voting/flags/", voting_flags_tests },
+ { "dir/voting/schedule/", voting_schedule_tests },
+ { "dir_handle_get/", dir_handle_get_tests },
+ { "dispatch/", dispatch_tests, },
+ { "dns/", dns_tests },
{ "dos/", dos_tests },
{ "entryconn/", entryconn_tests },
{ "entrynodes/", entrynodes_tests },
- { "guardfraction/", guardfraction_tests },
{ "extorport/", extorport_tests },
{ "geoip/", geoip_tests },
- { "legacy_hs/", hs_tests },
+ { "guardfraction/", guardfraction_tests },
{ "hs_cache/", hs_cache },
{ "hs_cell/", hs_cell_tests },
+ { "hs_client/", hs_client_tests },
{ "hs_common/", hs_common_tests },
{ "hs_config/", hs_config_tests },
{ "hs_control/", hs_control_tests },
{ "hs_descriptor/", hs_descriptor },
+ { "hs_dos/", hs_dos_tests },
+ { "hs_intropoint/", hs_intropoint_tests },
{ "hs_ntor/", hs_ntor_tests },
{ "hs_service/", hs_service_tests },
- { "hs_client/", hs_client_tests },
- { "hs_intropoint/", hs_intropoint_tests },
{ "introduce/", introduce_tests },
{ "keypin/", keypin_tests },
+ { "legacy_hs/", hs_tests },
{ "link-handshake/", link_handshake_tests },
{ "mainloop/", mainloop_tests },
+ { "netinfo/", netinfo_tests },
{ "nodelist/", nodelist_tests },
{ "oom/", oom_tests },
{ "oos/", oos_tests },
{ "options/", options_tests },
+ { "parsecommon/", parsecommon_tests },
{ "periodic-event/" , periodic_event_tests },
{ "policy/" , policy_tests },
+ { "prob_distr/", prob_distr_tests },
{ "procmon/", procmon_tests },
+ { "process/", process_tests },
{ "proto/http/", proto_http_tests },
{ "proto/misc/", proto_misc_tests },
{ "protover/", protover_tests },
{ "pt/", pt_tests },
+ { "pubsub/build/", pubsub_build_tests },
+ { "pubsub/msg/", pubsub_msg_tests },
{ "relay/" , relay_tests },
{ "relaycell/", relaycell_tests },
{ "relaycrypt/", relaycrypt_tests },
@@ -910,10 +915,12 @@ struct testgroup_t testgroups[] = {
{ "routerlist/", routerlist_tests },
{ "routerset/" , routerset_tests },
{ "scheduler/", scheduler_tests },
- { "socks/", socks_tests },
+ { "sendme/", sendme_tests },
{ "shared-random/", sr_tests },
+ { "socks/", socks_tests },
{ "status/" , status_tests },
{ "storagedir/", storagedir_tests },
+ { "token_bucket/", token_bucket_tests },
{ "tortls/", tortls_tests },
#ifndef ENABLE_NSS
{ "tortls/openssl/", tortls_openssl_tests },
@@ -921,10 +928,9 @@ struct testgroup_t testgroups[] = {
{ "tortls/x509/", x509_tests },
{ "util/", util_tests },
{ "util/format/", util_format_tests },
+ { "util/handle/", handle_tests },
{ "util/logging/", logging_tests },
{ "util/process/", util_process_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 aacc9dba87..76c4c0ec75 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -177,71 +177,89 @@ extern const struct testcase_setup_t ed25519_test_setup;
extern struct testcase_t accounting_tests[];
extern struct testcase_t addr_tests[];
-extern struct testcase_t address_tests[];
extern struct testcase_t address_set_tests[];
+extern struct testcase_t address_tests[];
extern struct testcase_t bridges_tests[];
-extern struct testcase_t bwmgt_tests[];
+extern struct testcase_t btrack_tests[];
extern struct testcase_t buffer_tests[];
+extern struct testcase_t bwmgt_tests[];
extern struct testcase_t cell_format_tests[];
extern struct testcase_t cell_queue_tests[];
extern struct testcase_t channel_tests[];
extern struct testcase_t channelpadding_tests[];
+extern struct testcase_t circuitpadding_tests[];
extern struct testcase_t channeltls_tests[];
extern struct testcase_t checkdir_tests[];
extern struct testcase_t circuitbuild_tests[];
extern struct testcase_t circuitlist_tests[];
extern struct testcase_t circuitmux_tests[];
-extern struct testcase_t circuituse_tests[];
extern struct testcase_t circuitstats_tests[];
+extern struct testcase_t circuituse_tests[];
extern struct testcase_t compat_libevent_tests[];
extern struct testcase_t config_tests[];
+extern struct testcase_t confmgr_tests[];
+extern struct testcase_t confparse_tests[];
extern struct testcase_t connection_tests[];
extern struct testcase_t conscache_tests[];
extern struct testcase_t consdiff_tests[];
extern struct testcase_t consdiffmgr_tests[];
extern struct testcase_t container_tests[];
-extern struct testcase_t controller_tests[];
extern struct testcase_t controller_event_tests[];
-extern struct testcase_t crypto_tests[];
+extern struct testcase_t controller_tests[];
extern struct testcase_t crypto_ope_tests[];
extern struct testcase_t crypto_openssl_tests[];
-extern struct testcase_t dir_tests[];
+extern struct testcase_t crypto_rng_tests[];
+extern struct testcase_t crypto_tests[];
extern struct testcase_t dir_handle_get_tests[];
+extern struct testcase_t dir_tests[];
+extern struct testcase_t dispatch_tests[];
+extern struct testcase_t dns_tests[];
extern struct testcase_t dos_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 geoip_tests[];
-extern struct testcase_t hs_tests[];
+extern struct testcase_t guardfraction_tests[];
+extern struct testcase_t handle_tests[];
extern struct testcase_t hs_cache[];
extern struct testcase_t hs_cell_tests[];
+extern struct testcase_t hs_client_tests[];
extern struct testcase_t hs_common_tests[];
extern struct testcase_t hs_config_tests[];
extern struct testcase_t hs_control_tests[];
extern struct testcase_t hs_descriptor[];
+extern struct testcase_t hs_dos_tests[];
+extern struct testcase_t hs_intropoint_tests[];
extern struct testcase_t hs_ntor_tests[];
extern struct testcase_t hs_service_tests[];
-extern struct testcase_t hs_client_tests[];
-extern struct testcase_t hs_intropoint_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 mainloop_tests[];
extern struct testcase_t microdesc_tests[];
+extern struct testcase_t namemap_tests[];
+extern struct testcase_t netinfo_tests[];
extern struct testcase_t nodelist_tests[];
extern struct testcase_t oom_tests[];
extern struct testcase_t oos_tests[];
extern struct testcase_t options_tests[];
+extern struct testcase_t parsecommon_tests[];
extern struct testcase_t pem_tests[];
extern struct testcase_t periodic_event_tests[];
extern struct testcase_t policy_tests[];
+extern struct testcase_t prob_distr_tests[];
+extern struct testcase_t slow_stochastic_prob_distr_tests[];
extern struct testcase_t procmon_tests[];
+extern struct testcase_t process_tests[];
+extern struct testcase_t process_descs_tests[];
extern struct testcase_t proto_http_tests[];
extern struct testcase_t proto_misc_tests[];
extern struct testcase_t protover_tests[];
extern struct testcase_t pt_tests[];
+extern struct testcase_t pubsub_build_tests[];
+extern struct testcase_t pubsub_msg_tests[];
extern struct testcase_t relay_tests[];
extern struct testcase_t relaycell_tests[];
extern struct testcase_t relaycrypt_tests[];
@@ -252,23 +270,25 @@ 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 storagedir_tests[];
+extern struct testcase_t sendme_tests[];
extern struct testcase_t socks_tests[];
+extern struct testcase_t sr_tests[];
extern struct testcase_t status_tests[];
+extern struct testcase_t storagedir_tests[];
extern struct testcase_t thread_tests[];
-extern struct testcase_t tortls_tests[];
+extern struct testcase_t token_bucket_tests[];
extern struct testcase_t tortls_openssl_tests[];
-extern struct testcase_t util_tests[];
+extern struct testcase_t tortls_tests[];
extern struct testcase_t util_format_tests[];
extern struct testcase_t util_process_tests[];
+extern struct testcase_t util_tests[];
+extern struct testcase_t voting_flags_tests[];
extern struct testcase_t voting_schedule_tests[];
-extern struct testcase_t dns_tests[];
-extern struct testcase_t handle_tests[];
-extern struct testcase_t sr_tests[];
extern struct testcase_t x509_tests[];
extern struct testcase_t slow_crypto_tests[];
-extern struct testcase_t slow_util_tests[];
+extern struct testcase_t slow_process_tests[];
+extern struct testcase_t slow_ptr_tests[];
extern struct testgroup_t testgroups[];
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index 8868edce25..c89c6e78d4 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -11,6 +11,8 @@
#include "feature/client/addressmap.h"
#include "test/log_test_helpers.h"
#include "lib/net/resolve.h"
+#include "test/rng_test_helpers.h"
+#include "test/resolve_test_helpers.h"
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
@@ -239,7 +241,7 @@ test_addr_ip6_helpers(void *arg)
tt_int_op(0,OP_EQ, tor_addr_lookup("9000::5", AF_UNSPEC, &t1));
tt_int_op(AF_INET6,OP_EQ, tor_addr_family(&t1));
tt_int_op(0x90,OP_EQ, tor_addr_to_in6_addr8(&t1)[0]);
- tt_assert(tor_mem_is_zero((char*)tor_addr_to_in6_addr8(&t1)+1, 14));
+ tt_assert(fast_mem_is_zero((char*)tor_addr_to_in6_addr8(&t1)+1, 14));
tt_int_op(0x05,OP_EQ, tor_addr_to_in6_addr8(&t1)[15]);
/* === Test pton: valid af_inet6 */
@@ -696,7 +698,7 @@ test_addr_ip6_helpers(void *arg)
&t1,&mask,&port1,&port2);
tt_int_op(r,OP_EQ,AF_INET6);
tt_int_op(tor_addr_family(&t1),OP_EQ,AF_INET6);
- tt_assert(tor_mem_is_zero((const char*)tor_addr_to_in6_addr32(&t1), 16));
+ tt_assert(fast_mem_is_zero((const char*)tor_addr_to_in6_addr32(&t1), 16));
tt_int_op(mask,OP_EQ,0);
tt_int_op(port1,OP_EQ,1);
tt_int_op(port2,OP_EQ,65535);
@@ -723,104 +725,559 @@ test_addr_ip6_helpers(void *arg)
;
}
-/** Test tor_addr_port_parse(). */
+/* Test that addr_str successfully parses, and:
+ * - the address has family expect_family,
+ * - the fmt_decorated result of tor_addr_to_str() is expect_str.
+ */
+#define TEST_ADDR_PARSE_FMT(addr_str, expect_family, fmt_decorated, \
+ expect_str) \
+ STMT_BEGIN \
+ r = tor_addr_parse(&addr, addr_str); \
+ tt_int_op(r, OP_EQ, expect_family); \
+ sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \
+ tt_str_op(sv, OP_EQ, buf); \
+ tt_str_op(buf, OP_EQ, expect_str); \
+ STMT_END
+
+/* Test that addr_str fails to parse, and:
+ * - the returned address is null.
+ */
+#define TEST_ADDR_PARSE_XFAIL(addr_str) \
+ STMT_BEGIN \
+ r = tor_addr_parse(&addr, addr_str); \
+ tt_int_op(r, OP_EQ, -1); \
+ tt_assert(tor_addr_is_null(&addr)); \
+ STMT_END
+
+/* Test that addr_port_str and default_port successfully parse, and:
+ * - the address has family expect_family,
+ * - the fmt_decorated result of tor_addr_to_str() is expect_str,
+ * - the port is expect_port.
+ */
+#define TEST_ADDR_PORT_PARSE_FMT(addr_port_str, default_port, expect_family, \
+ fmt_decorated, expect_str, expect_port) \
+ STMT_BEGIN \
+ r = tor_addr_port_parse(LOG_DEBUG, addr_port_str, &addr, &port, \
+ default_port); \
+ tt_int_op(r, OP_EQ, 0); \
+ tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \
+ sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \
+ tt_str_op(sv, OP_EQ, buf); \
+ tt_str_op(buf, OP_EQ, expect_str); \
+ tt_int_op(port, OP_EQ, expect_port); \
+ STMT_END
+
+/* Test that addr_port_str and default_port fail to parse, and:
+ * - the returned address is null,
+ * - the returned port is 0.
+ */
+#define TEST_ADDR_PORT_PARSE_XFAIL(addr_port_str, default_port) \
+ STMT_BEGIN \
+ r = tor_addr_port_parse(LOG_DEBUG, addr_port_str, &addr, &port, \
+ default_port); \
+ tt_int_op(r, OP_EQ, -1); \
+ tt_assert(tor_addr_is_null(&addr)); \
+ tt_int_op(port, OP_EQ, 0); \
+ STMT_END
+
+/* Test that addr_str successfully parses as an IPv4 address using
+ * tor_lookup_hostname(), and:
+ * - the fmt_addr32() of the result is expect_str.
+ */
+#define TEST_ADDR_V4_LOOKUP_HOSTNAME(addr_str, expect_str) \
+ STMT_BEGIN \
+ r = tor_lookup_hostname(addr_str, &addr32h); \
+ tt_int_op(r, OP_EQ, 0); \
+ tt_str_op(fmt_addr32(addr32h), OP_EQ, expect_str); \
+ STMT_END
+
+/* Test that bad_str fails to parse using tor_lookup_hostname(), with a
+ * permanent failure, and:
+ * - the returned address is 0.
+ */
+#define TEST_ADDR_V4_LOOKUP_XFAIL(bad_str) \
+ STMT_BEGIN \
+ r = tor_lookup_hostname(bad_str, &addr32h); \
+ tt_int_op(r, OP_EQ, -1); \
+ tt_int_op(addr32h, OP_EQ, 0); \
+ STMT_END
+
+/* Test that looking up host_str as an IPv4 address using tor_lookup_hostname()
+ * does something sensible:
+ * - the result is -1, 0, or 1.
+ * - if the result is a failure, the returned address is 0.
+ * We can't rely on the result of this function, because it depends on the
+ * network.
+ */
+#define TEST_HOST_V4_LOOKUP(host_str) \
+ STMT_BEGIN \
+ r = tor_lookup_hostname(host_str, &addr32h); \
+ tt_int_op(r, OP_GE, -1); \
+ tt_int_op(r, OP_LE, 1); \
+ if (r != 0) \
+ tt_int_op(addr32h, OP_EQ, 0); \
+ STMT_END
+
+/* Test that addr_str successfully parses as a require_family IP address using
+ * tor_addr_lookup(), and:
+ * - the address has family expect_family,
+ * - the fmt_decorated result of tor_addr_to_str() is expect_str.
+ */
+#define TEST_ADDR_LOOKUP_FMT(addr_str, require_family, expect_family, \
+ fmt_decorated, expect_str) \
+ STMT_BEGIN \
+ r = tor_addr_lookup(addr_str, require_family, &addr); \
+ tt_int_op(r, OP_EQ, 0); \
+ tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \
+ sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \
+ tt_str_op(sv, OP_EQ, buf); \
+ tt_str_op(buf, OP_EQ, expect_str); \
+ STMT_END
+
+/* Test that bad_str fails to parse as a require_family IP address using
+ * tor_addr_lookup(), with a permanent failure, and:
+ * - the returned address is null.
+ */
+#define TEST_ADDR_LOOKUP_XFAIL(bad_str, require_family) \
+ STMT_BEGIN \
+ r = tor_addr_lookup(bad_str, require_family, &addr); \
+ tt_int_op(r, OP_EQ, -1); \
+ tt_assert(tor_addr_is_null(&addr)); \
+ STMT_END
+
+/* Test that looking up host_string as a require_family IP address using
+ * tor_addr_lookup(), does something sensible:
+ * - the result is -1, 0, or 1.
+ * - if the result is a failure, the returned address is null.
+ * We can't rely on the result of this function, because it depends on the
+ * network.
+ */
+#define TEST_HOST_LOOKUP(host_str, require_family) \
+ STMT_BEGIN \
+ r = tor_addr_lookup(host_str, require_family, &addr); \
+ tt_int_op(r, OP_GE, -1); \
+ tt_int_op(r, OP_LE, 1); \
+ if (r != 0) \
+ tt_assert(tor_addr_is_null(&addr)); \
+ STMT_END
+
+/* Test that addr_port_str successfully parses as an IP address and port
+ * using tor_addr_port_lookup(), and:
+ * - the address has family expect_family,
+ * - the fmt_decorated result of tor_addr_to_str() is expect_str,
+ * - the port is expect_port.
+ */
+#define TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, expect_family, \
+ fmt_decorated, expect_str, expect_port) \
+ STMT_BEGIN \
+ r = tor_addr_port_lookup(addr_port_str, &addr, &port); \
+ tt_int_op(r, OP_EQ, 0); \
+ tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \
+ sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \
+ tt_str_op(sv, OP_EQ, buf); \
+ tt_str_op(buf, OP_EQ, expect_str); \
+ tt_int_op(port, OP_EQ, expect_port); \
+ STMT_END
+
+/* Test that bad_str fails to parse as an IP address and port
+ * using tor_addr_port_lookup(), and:
+ * - the returned address is null,
+ * - the returned port is 0.
+ */
+#define TEST_ADDR_PORT_LOOKUP_XFAIL(bad_str) \
+ STMT_BEGIN \
+ r = tor_addr_port_lookup(bad_str, &addr, &port); \
+ tt_int_op(r, OP_EQ, -1); \
+ tt_assert(tor_addr_is_null(&addr)); \
+ tt_int_op(port, OP_EQ, 0); \
+ STMT_END
+
+/* Test that looking up host_port_str as an IP address using
+ * tor_addr_port_lookup(), does something sensible:
+ * - the result is -1 or 0.
+ * - if the result is a failure, the returned address is null, and the
+ * returned port is zero,
+ * - if the result is a success, the returned port is expect_success_port,
+ * and the returned family is AF_INET or AF_INET6.
+ * We can't rely on the result of this function, because it depends on the
+ * network.
+ */
+#define TEST_HOST_PORT_LOOKUP(host_port_str, expect_success_port) \
+ STMT_BEGIN \
+ r = tor_addr_port_lookup(host_port_str, &addr, &port); \
+ tt_int_op(r, OP_GE, -1); \
+ tt_int_op(r, OP_LE, 0); \
+ if (r == -1) { \
+ tt_assert(tor_addr_is_null(&addr)); \
+ tt_int_op(port, OP_EQ, 0); \
+ } else { \
+ tt_assert(tor_addr_family(&addr) == AF_INET || \
+ tor_addr_family(&addr) == AF_INET6); \
+ tt_int_op(port, OP_EQ, expect_success_port); \
+ } \
+ STMT_END
+
+/* Test that addr_str successfully parses as a canonical IPv4 address.
+ * Check for successful parsing using:
+ * - tor_addr_parse(),
+ * - tor_addr_port_parse() with a default port,
+ * - tor_lookup_hostname(),
+ * - tor_addr_lookup() with AF_INET,
+ * - tor_addr_lookup() with AF_UNSPEC,
+ * - tor_addr_port_lookup(), with a zero port.
+ * Check for failures using:
+ * - tor_addr_port_parse() without a default port, because there is no port,
+ * - tor_addr_lookup() with AF_INET6,
+ * - tor_addr_port_lookup(), because there is no port.
+ */
+#define TEST_ADDR_V4_PARSE_CANONICAL(addr_str) \
+ STMT_BEGIN \
+ TEST_ADDR_PARSE_FMT(addr_str, AF_INET, 0, addr_str); \
+ TEST_ADDR_PORT_PARSE_FMT(addr_str, 111, AF_INET, 0, \
+ addr_str, 111); \
+ TEST_ADDR_V4_LOOKUP_HOSTNAME(addr_str, addr_str); \
+ TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET, 0, addr_str, 0); \
+ TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET, AF_INET, 0, addr_str); \
+ TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET, 0, addr_str); \
+ TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \
+ TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET6); \
+ STMT_END
+
+/* Test that addr_str successfully parses as a canonical fmt_decorated
+ * IPv6 address.
+ * Check for successful parsing using:
+ * - tor_addr_parse(),
+ * - tor_addr_port_parse() with a default port,
+ * - tor_addr_lookup() with AF_INET6,
+ * - tor_addr_lookup() with AF_UNSPEC,
+ * - tor_addr_port_lookup(), with a zero port.
+ * Check for failures using:
+ * - tor_addr_port_parse() without a default port, because there is no port,
+ * - tor_lookup_hostname(), because it only supports IPv4,
+ * - tor_addr_lookup() with AF_INET.
+ */
+#define TEST_ADDR_V6_PARSE_CANONICAL(addr_str, fmt_decorated) \
+ STMT_BEGIN \
+ TEST_ADDR_PARSE_FMT(addr_str, AF_INET6, fmt_decorated, addr_str); \
+ TEST_ADDR_PORT_PARSE_FMT(addr_str, 222, AF_INET6, fmt_decorated, \
+ addr_str, 222); \
+ TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET6, AF_INET6, fmt_decorated, \
+ addr_str); \
+ TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET6, fmt_decorated, \
+ addr_str); \
+ TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET6, fmt_decorated, addr_str, \
+ 0); \
+ TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \
+ TEST_ADDR_V4_LOOKUP_XFAIL(addr_str); \
+ TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET); \
+ STMT_END
+
+/* Test that addr_str successfully parses, and the fmt_decorated canonical
+ * IPv6 string is expect_str.
+ * Check for successful parsing using:
+ * - tor_addr_parse(),
+ * - tor_addr_port_parse() with a default port,
+ * - tor_addr_lookup() with AF_INET6,
+ * - tor_addr_lookup() with AF_UNSPEC,
+ * - tor_addr_port_lookup(), with a zero port.
+ * Check for failures using:
+ * - tor_addr_port_parse() without a default port, because there is no port.
+ * - tor_lookup_hostname(), because it only supports IPv4,
+ * - tor_addr_lookup() with AF_INET.
+ */
+#define TEST_ADDR_V6_PARSE(addr_str, fmt_decorated, expect_str) \
+ STMT_BEGIN \
+ TEST_ADDR_PARSE_FMT(addr_str, AF_INET6, fmt_decorated, expect_str); \
+ TEST_ADDR_PORT_PARSE_FMT(addr_str, 333, AF_INET6, fmt_decorated, \
+ expect_str, 333); \
+ TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET6, AF_INET6, fmt_decorated, \
+ expect_str); \
+ TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET6, fmt_decorated, \
+ expect_str); \
+ TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET6, fmt_decorated, expect_str, \
+ 0); \
+ TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \
+ TEST_ADDR_V4_LOOKUP_XFAIL(addr_str); \
+ TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET); \
+ STMT_END
+
+/* Test that addr_port_str successfully parses to the canonical IPv4 address
+ * string expect_str, and port expect_port.
+ * Check for successful parsing using:
+ * - tor_addr_port_parse() without a default port,
+ * - tor_addr_port_parse() with a default port,
+ * - tor_addr_port_lookup().
+ * Check for failures using:
+ * - tor_addr_parse(), because there is a port,
+ * - tor_lookup_hostname(), because there is a port.
+ * - tor_addr_lookup(), regardless of the address family, because there is a
+ * port.
+ */
+#define TEST_ADDR_V4_PORT_PARSE(addr_port_str, expect_str, expect_port) \
+ STMT_BEGIN \
+ TEST_ADDR_PORT_PARSE_FMT(addr_port_str, -1, AF_INET, 0, expect_str, \
+ expect_port); \
+ TEST_ADDR_PORT_PARSE_FMT(addr_port_str, 444, AF_INET, 0, expect_str, \
+ expect_port); \
+ TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, AF_INET, 0, expect_str, \
+ expect_port); \
+ TEST_ADDR_PARSE_XFAIL(addr_port_str); \
+ TEST_ADDR_V4_LOOKUP_XFAIL(addr_port_str); \
+ TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET); \
+ TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_UNSPEC); \
+ TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET6); \
+ STMT_END
+
+/* Test that addr_port_str successfully parses to the canonical undecorated
+ * IPv6 address string expect_str, and port expect_port.
+ * Check for successful parsing using:
+ * - tor_addr_port_parse() without a default port,
+ * - tor_addr_port_parse() with a default port,
+ * - tor_addr_port_lookup().
+ * Check for failures using:
+ * - tor_addr_parse(), because there is a port,
+ * - tor_lookup_hostname(), because there is a port, and because it only
+ * supports IPv4,
+ * - tor_addr_lookup(), regardless of the address family, because there is a
+ * port.
+ */
+#define TEST_ADDR_V6_PORT_PARSE(addr_port_str, expect_str, expect_port) \
+ STMT_BEGIN \
+ TEST_ADDR_PORT_PARSE_FMT(addr_port_str, -1, AF_INET6, 0, expect_str, \
+ expect_port); \
+ TEST_ADDR_PORT_PARSE_FMT(addr_port_str, 555, AF_INET6, 0, expect_str, \
+ expect_port); \
+ TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, AF_INET6, 0, expect_str, \
+ expect_port); \
+ TEST_ADDR_PARSE_XFAIL(addr_port_str); \
+ TEST_ADDR_V4_LOOKUP_XFAIL(addr_port_str); \
+ TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET6); \
+ TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_UNSPEC); \
+ TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET); \
+ STMT_END
+
+/* Test that bad_str fails to parse due to a bad address or port.
+ * Check for failures using:
+ * - tor_addr_parse(),
+ * - tor_addr_port_parse() without a default port,
+ * - tor_addr_port_parse() with a default port,
+ * - tor_lookup_hostname(),
+ * - tor_addr_lookup(), regardless of the address family,
+ * - tor_addr_port_lookup().
+ */
+#define TEST_ADDR_PARSE_XFAIL_MALFORMED(bad_str) \
+ STMT_BEGIN \
+ TEST_ADDR_PARSE_XFAIL(bad_str); \
+ TEST_ADDR_PORT_PARSE_XFAIL(bad_str, -1); \
+ TEST_ADDR_PORT_PARSE_XFAIL(bad_str, 666); \
+ TEST_ADDR_V4_LOOKUP_XFAIL(bad_str); \
+ TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_UNSPEC); \
+ TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_INET); \
+ TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_INET6); \
+ TEST_ADDR_PORT_LOOKUP_XFAIL(bad_str); \
+ STMT_END
+
+/* Test that host_str is treated as a hostname, and not an address.
+ * Check for success or failure using the network-dependent functions:
+ * - tor_lookup_hostname(),
+ * - tor_addr_lookup(), regardless of the address family,
+ * - tor_addr_port_lookup(), expecting a zero port.
+ * Check for failures using:
+ * - tor_addr_parse(),
+ * - tor_addr_port_parse() without a default port,
+ * - tor_addr_port_parse() with a default port.
+ */
+#define TEST_HOSTNAME(host_str) \
+ STMT_BEGIN \
+ TEST_HOST_V4_LOOKUP(host_str); \
+ TEST_HOST_LOOKUP(host_str, AF_UNSPEC); \
+ TEST_HOST_LOOKUP(host_str, AF_INET); \
+ TEST_HOST_LOOKUP(host_str, AF_INET6); \
+ TEST_HOST_PORT_LOOKUP(host_str, 0); \
+ TEST_ADDR_PARSE_XFAIL(host_str); \
+ TEST_ADDR_PORT_PARSE_XFAIL(host_str, -1); \
+ TEST_ADDR_PORT_PARSE_XFAIL(host_str, 777); \
+ STMT_END
+
+/* Test that host_port_str is treated as a hostname and port, and not a
+ * hostname or an address.
+ * Check for success or failure using the network-dependent function:
+ * - tor_addr_port_lookup(), expecting expect_success_port if the lookup is
+ * successful.
+ * Check for failures using:
+ * - tor_addr_parse(),
+ * - tor_addr_port_parse() without a default port,
+ * - tor_addr_port_parse() with a default port,
+ * - tor_lookup_hostname(), because it doesn't support ports,
+ * - tor_addr_lookup(), regardless of the address family, because it doesn't
+ * support ports.
+ */
+#define TEST_HOSTNAME_PORT(host_port_str, expect_success_port) \
+ STMT_BEGIN \
+ TEST_HOST_PORT_LOOKUP(host_port_str, expect_success_port); \
+ TEST_ADDR_PARSE_XFAIL(host_port_str); \
+ TEST_ADDR_PORT_PARSE_XFAIL(host_port_str, -1); \
+ TEST_ADDR_PORT_PARSE_XFAIL(host_port_str, 888); \
+ TEST_ADDR_V4_LOOKUP_XFAIL(host_port_str); \
+ TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_UNSPEC); \
+ TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_INET); \
+ TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_INET6); \
+ STMT_END
+
+static void
+test_addr_parse_canonical(void *arg)
+{
+ int r;
+ tor_addr_t addr;
+ uint16_t port;
+ const char *sv;
+ uint32_t addr32h;
+ char buf[TOR_ADDR_BUF_LEN];
+
+ (void)arg;
+
+ /* Correct calls. */
+ TEST_ADDR_V4_PARSE_CANONICAL("192.0.2.1");
+ TEST_ADDR_V4_PARSE_CANONICAL("192.0.2.2");
+
+ TEST_ADDR_V6_PARSE_CANONICAL("[11:22::33:44]", 1);
+ TEST_ADDR_V6_PARSE_CANONICAL("[::1]", 1);
+ TEST_ADDR_V6_PARSE_CANONICAL("[::]", 1);
+ TEST_ADDR_V6_PARSE_CANONICAL("[2::]", 1);
+ TEST_ADDR_V6_PARSE_CANONICAL("[11:22:33:44:55:66:77:88]", 1);
+
+ /* Allow IPv6 without square brackets, when there is no port, but only if
+ * there is a default port */
+ TEST_ADDR_V6_PARSE_CANONICAL("11:22::33:44", 0);
+ TEST_ADDR_V6_PARSE_CANONICAL("::1", 0);
+ TEST_ADDR_V6_PARSE_CANONICAL("::", 0);
+ TEST_ADDR_V6_PARSE_CANONICAL("2::", 0);
+ TEST_ADDR_V6_PARSE_CANONICAL("11:22:33:44:55:66:77:88", 0);
+ done:
+ ;
+}
+
+/** Test tor_addr_parse() and tor_addr_port_parse(). */
static void
test_addr_parse(void *arg)
{
+
int r;
tor_addr_t addr;
+ uint16_t port;
+ const char *sv;
+ uint32_t addr32h;
char buf[TOR_ADDR_BUF_LEN];
- uint16_t port = 0;
- /* Correct call. */
(void)arg;
- r= tor_addr_port_parse(LOG_DEBUG,
- "192.0.2.1:1234",
- &addr, &port, -1);
- tt_int_op(r, OP_EQ, 0);
- tor_addr_to_str(buf, &addr, sizeof(buf), 0);
- tt_str_op(buf,OP_EQ, "192.0.2.1");
- tt_int_op(port,OP_EQ, 1234);
-
- r= tor_addr_port_parse(LOG_DEBUG,
- "[::1]:1234",
- &addr, &port, -1);
- tt_int_op(r, OP_EQ, 0);
- tor_addr_to_str(buf, &addr, sizeof(buf), 0);
- tt_str_op(buf,OP_EQ, "::1");
- tt_int_op(port,OP_EQ, 1234);
-
- /* Domain name. */
- r= tor_addr_port_parse(LOG_DEBUG,
- "torproject.org:1234",
- &addr, &port, -1);
- tt_int_op(r, OP_EQ, -1);
- /* Only IP. */
- r= tor_addr_port_parse(LOG_DEBUG,
- "192.0.2.2",
- &addr, &port, -1);
- tt_int_op(r, OP_EQ, -1);
+ mock_hostname_resolver();
- r= tor_addr_port_parse(LOG_DEBUG,
- "192.0.2.2",
- &addr, &port, 200);
- tt_int_op(r, OP_EQ, 0);
- tt_int_op(port,OP_EQ,200);
+ /* IPv6-mapped IPv4 addresses. Tor doesn't really use these. */
+ TEST_ADDR_V6_PARSE("11:22:33:44:55:66:1.2.3.4", 0,
+ "11:22:33:44:55:66:102:304");
- r= tor_addr_port_parse(LOG_DEBUG,
- "[::1]",
- &addr, &port, -1);
- tt_int_op(r, OP_EQ, -1);
+ TEST_ADDR_V6_PARSE("11:22::33:44:1.2.3.4", 0,
+ "11:22::33:44:102:304");
- r= tor_addr_port_parse(LOG_DEBUG,
- "[::1]",
- &addr, &port, 400);
- tt_int_op(r, OP_EQ, 0);
- tt_int_op(port,OP_EQ,400);
+ /* Ports. */
+ TEST_ADDR_V4_PORT_PARSE("192.0.2.1:1234", "192.0.2.1", 1234);
+ TEST_ADDR_V6_PORT_PARSE("[::1]:1234", "::1", 1234);
- /* Bad port. */
- r= tor_addr_port_parse(LOG_DEBUG,
- "192.0.2.2:66666",
- &addr, &port, -1);
- tt_int_op(r, OP_EQ, -1);
- r= tor_addr_port_parse(LOG_DEBUG,
- "192.0.2.2:66666",
- &addr, &port, 200);
- tt_int_op(r, OP_EQ, -1);
+ /* Host names. */
+ TEST_HOSTNAME("localhost");
+ TEST_HOSTNAME_PORT("localhost:1234", 1234);
+ TEST_HOSTNAME_PORT("localhost:0", 0);
- /* Only domain name */
- r= tor_addr_port_parse(LOG_DEBUG,
- "torproject.org",
- &addr, &port, -1);
- tt_int_op(r, OP_EQ, -1);
- r= tor_addr_port_parse(LOG_DEBUG,
- "torproject.org",
- &addr, &port, 200);
- tt_int_op(r, OP_EQ, -1);
+ TEST_HOSTNAME("torproject.org");
+ TEST_HOSTNAME_PORT("torproject.org:56", 56);
- /* Bad IP address */
- r= tor_addr_port_parse(LOG_DEBUG,
- "192.0.2:1234",
- &addr, &port, -1);
- tt_int_op(r, OP_EQ, -1);
+ TEST_HOSTNAME("probably-not-a-valid-dns.name-tld");
+ TEST_HOSTNAME_PORT("probably-not-a-valid-dns.name-tld:789", 789);
+
+ /* Malformed addresses. */
+ /* Empty string. */
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("");
+
+ /* Square brackets around IPv4 address. */
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("[192.0.2.1]");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("[192.0.2.3]:12345");
- /* Make sure that the default port has lower priority than the real
- one */
- r= tor_addr_port_parse(LOG_DEBUG,
- "192.0.2.2:1337",
- &addr, &port, 200);
- tt_int_op(r, OP_EQ, 0);
- tt_int_op(port,OP_EQ,1337);
+ /* Only left square bracket. */
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("[11:22::33:44");
- r= tor_addr_port_parse(LOG_DEBUG,
- "[::1]:1369",
- &addr, &port, 200);
- tt_int_op(r, OP_EQ, 0);
- tt_int_op(port,OP_EQ,1369);
+ /* Only right square bracket. */
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("11:22::33:44]");
+
+ /* Leading colon. */
+ TEST_ADDR_PARSE_XFAIL_MALFORMED(":11:22::33:44");
+
+ /* Trailing colon. */
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("11:22::33:44:");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:");
+
+ /* Bad port. */
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:66666");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:77777");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:88888");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:99999");
+
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:-1");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:-2");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:-3");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:-4");
+
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:1 bad");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:bad-port");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:bad-port-1");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:1-bad-port");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:1-bad-port");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:1-bad-port-1");
+
+ /* Bad hostname */
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("definitely invalid");
+ TEST_ADDR_PARSE_XFAIL_MALFORMED("definitely invalid:22222");
+
+ /* Ambiguous cases */
+ /* Too many hex words in IPv4-mapped IPv6 address.
+ * But some OS host lookup routines accept it as a hostname, or
+ * as an IP address?? (I assume they discard unused characters). */
+ TEST_HOSTNAME("11:22:33:44:55:66:77:88:1.2.3.4");
+
+ /* IPv6 address with port and no brackets
+ * We reject it, but some OS host lookup routines accept it as an
+ * IPv6 address:port ? */
+ TEST_HOSTNAME_PORT("11:22::33:44:12345", 12345);
+ /* Is it a port, or are there too many hex words?
+ * We reject it either way, but some OS host lookup routines accept it as an
+ * IPv6 address:port */
+ TEST_HOSTNAME_PORT("11:22:33:44:55:66:77:88:99", 99);
+ /* But we accept it if it has square brackets. */
+ TEST_ADDR_V6_PORT_PARSE("[11:22:33:44:55:66:77:88]:99",
+ "11:22:33:44:55:66:77:88",99);
+
+ /* Bad IPv4 address
+ * We reject it, but some OS host lookup routines accept it as an
+ * IPv4 address[:port], with a zero last octet */
+ TEST_HOSTNAME("192.0.1");
+ TEST_HOSTNAME_PORT("192.0.2:1234", 1234);
+
+ /* More bad IPv6 addresses and ports: no brackets
+ * We reject it, but some OS host lookup routines accept it as an
+ * IPv6 address[:port] */
+ TEST_HOSTNAME_PORT("::1:12345", 12345);
+ TEST_HOSTNAME_PORT("11:22::33:44:12345", 12345);
+
+ /* And this is an ambiguous case, which is interpreted as an IPv6 address. */
+ TEST_ADDR_V6_PARSE_CANONICAL("11:22::88:99", 0);
+ /* Use square brackets to resolve the ambiguity */
+ TEST_ADDR_V6_PARSE_CANONICAL("[11:22::88:99]", 1);
+ TEST_ADDR_V6_PORT_PARSE("[11:22::88]:99",
+ "11:22::88",99);
done:
- ;
+ unmock_hostname_resolver();
}
static void
@@ -891,27 +1348,6 @@ test_virtaddrmap(void *data)
;
}
-static const char *canned_data = NULL;
-static size_t canned_data_len = 0;
-
-/* Mock replacement for crypto_rand() that returns canned data from
- * canned_data above. */
-static void
-crypto_canned(char *ptr, size_t n)
-{
- if (canned_data_len) {
- size_t to_copy = MIN(n, canned_data_len);
- memcpy(ptr, canned_data, to_copy);
- canned_data += to_copy;
- canned_data_len -= to_copy;
- n -= to_copy;
- ptr += to_copy;
- }
- if (n) {
- crypto_rand_unmocked(ptr, n);
- }
-}
-
static void
test_virtaddrmap_persist(void *data)
{
@@ -919,6 +1355,8 @@ test_virtaddrmap_persist(void *data)
const char *a, *b, *c;
tor_addr_t addr;
char *ones = NULL;
+ const char *canned_data;
+ size_t canned_data_len;
addressmap_init();
@@ -937,7 +1375,7 @@ test_virtaddrmap_persist(void *data)
"1234567890" // the second call returns this.
"abcdefghij"; // the third call returns this.
canned_data_len = 30;
- MOCK(crypto_rand, crypto_canned);
+ testing_enable_prefilled_rng(canned_data, canned_data_len);
a = addressmap_register_virtual_address(RESOLVED_TYPE_HOSTNAME,
tor_strdup("quuxit.baz"));
@@ -947,9 +1385,9 @@ test_virtaddrmap_persist(void *data)
tt_assert(b);
tt_str_op(a, OP_EQ, "gezdgnbvgy3tqojq.virtual");
tt_str_op(b, OP_EQ, "mfrggzdfmztwq2lk.virtual");
+ testing_disable_prefilled_rng();
// Now try something to get us an ipv4 address
- UNMOCK(crypto_rand);
tt_int_op(0,OP_EQ, parse_virtual_addr_network("192.168.0.0/16",
AF_INET, 0, NULL));
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
@@ -966,22 +1404,23 @@ test_virtaddrmap_persist(void *data)
// Try some canned entropy and verify all the we discard duplicates,
// addresses that end with 0, and addresses that end with 255.
- MOCK(crypto_rand, crypto_canned);
canned_data = "\x01\x02\x03\x04" // okay
"\x01\x02\x03\x04" // duplicate
"\x03\x04\x00\x00" // bad ending 1
"\x05\x05\x00\xff" // bad ending 2
"\x05\x06\x07\xf0"; // okay
canned_data_len = 20;
+ testing_enable_prefilled_rng(canned_data, canned_data_len);
+
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
tor_strdup("wumble.onion"));
b = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
tor_strdup("wumpus.onion"));
tt_str_op(a, OP_EQ, "192.168.3.4");
tt_str_op(b, OP_EQ, "192.168.7.240");
+ testing_disable_prefilled_rng();
// Now try IPv6!
- UNMOCK(crypto_rand);
tt_int_op(0,OP_EQ, parse_virtual_addr_network("1010:F000::/20",
AF_INET6, 0, NULL));
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
@@ -997,7 +1436,7 @@ test_virtaddrmap_persist(void *data)
tt_assert(!strcmpstart(b, "[1010:f"));
// Try IPv6 with canned entropy, to make sure we detect duplicates.
- MOCK(crypto_rand, crypto_canned);
+
canned_data = "acanthopterygian" // okay
"cinematographist" // okay
"acanthopterygian" // duplicate
@@ -1006,6 +1445,8 @@ test_virtaddrmap_persist(void *data)
"cinematographist" // duplicate
"coadministration"; // okay
canned_data_len = 16 * 7;
+ testing_enable_prefilled_rng(canned_data, canned_data_len);
+
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
tor_strdup("wuffle.baz"));
b = addressmap_register_virtual_address(RESOLVED_TYPE_IPV6,
@@ -1018,9 +1459,11 @@ test_virtaddrmap_persist(void *data)
// Try address exhaustion: make sure we can actually fail if we
// get too many already-existing addresses.
+ testing_disable_prefilled_rng();
canned_data_len = 128*1024;
canned_data = ones = tor_malloc(canned_data_len);
memset(ones, 1, canned_data_len);
+ testing_enable_prefilled_rng(canned_data, canned_data_len);
// There is some chance this one will fail if a previous random
// allocation gave out the address already.
a = addressmap_register_virtual_address(RESOLVED_TYPE_IPV4,
@@ -1037,7 +1480,7 @@ test_virtaddrmap_persist(void *data)
expect_single_log_msg_containing("Ran out of virtual addresses!");
done:
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
tor_free(ones);
addressmap_free_all();
teardown_capture_of_logs();
@@ -1213,6 +1656,7 @@ struct testcase_t addr_tests[] = {
ADDR_LEGACY(basic),
ADDR_LEGACY(ip6_helpers),
ADDR_LEGACY(parse),
+ ADDR_LEGACY(parse_canonical),
{ "virtaddr", test_virtaddrmap, 0, NULL, NULL },
{ "virtaddr_persist", test_virtaddrmap_persist, TT_FORK, NULL, NULL },
{ "localname", test_addr_localname, 0, NULL, NULL },
diff --git a/src/test/test_address.c b/src/test/test_address.c
index c33c30aee5..32fb4aa232 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -24,6 +24,10 @@
#endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */
#include "core/or/or.h"
+#include "app/config/config.h"
+#include "feature/dirauth/process_descs.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/node_st.h"
#include "feature/nodelist/nodelist.h"
#include "lib/net/address.h"
#include "test/test.h"
@@ -1170,8 +1174,167 @@ test_address_tor_addr_in_same_network_family(void *ignored)
return;
}
+static node_t *
+helper_create_mock_node(char id_char)
+{
+ node_t *node = tor_malloc_zero(sizeof(node_t));
+ routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t));
+ tor_addr_make_null(&ri->ipv6_addr, AF_INET6);
+ node->ri = ri;
+ memset(node->identity, id_char, sizeof(node->identity));
+ return node;
+}
+
+static void
+helper_free_mock_node(node_t *node)
+{
+ if (!node)
+ return;
+ tor_free(node->ri);
+ tor_free(node);
+}
+
+#define NODE_SET_IPV4(node, ipv4_addr, ipv4_port) { \
+ tor_addr_t addr; \
+ tor_addr_parse(&addr, ipv4_addr); \
+ node->ri->addr = tor_addr_to_ipv4h(&addr); \
+ node->ri->or_port = ipv4_port; \
+ }
+
+#define NODE_CLEAR_IPV4(node) { \
+ node->ri->addr = 0; \
+ node->ri->or_port = 0; \
+ }
+
+#define NODE_SET_IPV6(node, ipv6_addr_str, ipv6_port) { \
+ tor_addr_parse(&node->ri->ipv6_addr, ipv6_addr_str); \
+ node->ri->ipv6_orport = ipv6_port; \
+ }
+
+static void
+test_address_tor_node_in_same_network_family(void *ignored)
+{
+ (void)ignored;
+ node_t *node_a = helper_create_mock_node('a');
+ node_t *node_b = helper_create_mock_node('b');
+
+ NODE_SET_IPV4(node_a, "8.8.8.8", 1);
+ NODE_SET_IPV4(node_b, "8.8.4.4", 1);
+
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 1);
+
+ NODE_SET_IPV4(node_a, "8.8.8.8", 1);
+ NODE_SET_IPV4(node_b, "1.1.1.1", 1);
+
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 0);
+
+ NODE_CLEAR_IPV4(node_a);
+ NODE_SET_IPV6(node_a, "2001:470:20::2", 1);
+
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 0);
+
+ NODE_CLEAR_IPV4(node_b);
+ NODE_SET_IPV6(node_b, "2606:4700:4700::1111", 1);
+
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 0);
+
+ NODE_SET_IPV6(node_a, "2606:4700:4700::1001", 1);
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 1);
+
+ done:
+ helper_free_mock_node(node_a);
+ helper_free_mock_node(node_b);
+}
+
+static or_options_t mock_options;
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mock_options;
+}
+
+/* Test dirserv_router_has_valid_address() on a stub routerinfo, with only its
+ * address fields set. Use IPv4 ipv4_addr_str and IPv6 ipv6_addr_str.
+ * Fail if it does not return rv. */
+#define TEST_ROUTER_VALID_ADDRESS_HELPER(ipv4_addr_str, ipv6_addr_str, rv) \
+ STMT_BEGIN \
+ ri = tor_malloc_zero(sizeof(routerinfo_t)); \
+ tor_addr_t addr; \
+ tor_addr_parse(&addr, (ipv4_addr_str)); \
+ ri->addr = tor_addr_to_ipv4h(&addr); \
+ tor_addr_parse(&ri->ipv6_addr, (ipv6_addr_str)); \
+ tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, (rv)); \
+ tor_free(ri); \
+ STMT_END
+
+/* Like TEST_ROUTER_VALID_ADDRESS_HELPER(), but always passes a null
+ * IPv6 address. */
+#define CHECK_RI_ADDR(ipv4_addr_str, rv) \
+ TEST_ROUTER_VALID_ADDRESS_HELPER(ipv4_addr_str, "::", rv)
+
+/* Like TEST_ROUTER_VALID_ADDRESS_HELPER(), but always passes a non-internal
+ * IPv4 address, so that the IPv6 check is reached. */
+#define CHECK_RI_ADDR6(ipv6_addr_str, rv) \
+ TEST_ROUTER_VALID_ADDRESS_HELPER("1.0.0.1", ipv6_addr_str, rv)
+
+static void
+test_address_dirserv_router_addr_private(void *opt_dir_allow_private)
+{
+ /* A stub routerinfo structure, with only its address fields set. */
+ routerinfo_t *ri = NULL;
+ /* The expected return value for private addresses.
+ * Modified if DirAllowPrivateAddresses is 1. */
+ int private_rv = -1;
+
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(get_options, mock_get_options);
+
+ if (opt_dir_allow_private) {
+ mock_options.DirAllowPrivateAddresses = 1;
+ private_rv = 0;
+ }
+
+ CHECK_RI_ADDR("1.0.0.1", 0);
+ CHECK_RI_ADDR("10.0.0.1", private_rv);
+
+ CHECK_RI_ADDR6("2600::1", 0);
+ CHECK_RI_ADDR6("fe80::1", private_rv);
+
+ /* Null addresses */
+ /* IPv4 null fails, regardless of IPv6 */
+ CHECK_RI_ADDR("0.0.0.0", private_rv);
+ TEST_ROUTER_VALID_ADDRESS_HELPER("0.0.0.0", "::", private_rv);
+
+ /* IPv6 null succeeds, because IPv4 is not null */
+ CHECK_RI_ADDR6("::", 0);
+
+ /* Byte-zeroed null addresses */
+ /* IPv4 null fails, regardless of IPv6 */
+ {
+ ri = tor_malloc_zero(sizeof(routerinfo_t));
+ tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, private_rv);
+ tor_free(ri);
+ }
+
+ /* IPv6 null succeeds, because IPv4 is not internal */
+ {
+ ri = tor_malloc_zero(sizeof(routerinfo_t));
+ ri->addr = 16777217; /* 1.0.0.1 */
+ tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, 0);
+ tor_free(ri);
+ }
+
+ done:
+ tor_free(ri);
+ UNMOCK(get_options);
+}
+
#define ADDRESS_TEST(name, flags) \
{ #name, test_address_ ## name, flags, NULL, NULL }
+#define ADDRESS_TEST_STR_ARG(name, flags, str_arg) \
+ { #name "/" str_arg, test_address_ ## name, flags, &passthrough_setup, \
+ (void *)(str_arg) }
struct testcase_t address_tests[] = {
ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK),
@@ -1202,5 +1365,8 @@ struct testcase_t address_tests[] = {
ADDRESS_TEST(tor_addr_to_mapped_ipv4h, 0),
ADDRESS_TEST(tor_addr_eq_ipv4h, 0),
ADDRESS_TEST(tor_addr_in_same_network_family, 0),
+ ADDRESS_TEST(tor_node_in_same_network_family, 0),
+ ADDRESS_TEST(dirserv_router_addr_private, 0),
+ ADDRESS_TEST_STR_ARG(dirserv_router_addr_private, 0, "allow_private"),
END_OF_TESTCASES
};
diff --git a/src/test/test_address_set.c b/src/test/test_address_set.c
index fb8408b3c3..6e299d779e 100644
--- a/src/test/test_address_set.c
+++ b/src/test/test_address_set.c
@@ -4,6 +4,7 @@
#include "core/or/or.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "core/or/address_set.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
@@ -31,6 +32,12 @@ mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
return dummy_ns;
}
+static void
+mock_dirlist_add_trusted_dir_addresses(void)
+{
+ return;
+}
+
/* Number of address a single node_t can have. Default to the production
* value. This is to control the size of the bloom filter. */
static int addr_per_node = 2;
@@ -98,6 +105,8 @@ test_nodelist(void *arg)
mock_networkstatus_get_latest_consensus_by_flavor);
MOCK(get_estimated_address_per_node,
mock_get_estimated_address_per_node);
+ MOCK(dirlist_add_trusted_dir_addresses,
+ mock_dirlist_add_trusted_dir_addresses);
dummy_ns = tor_malloc_zero(sizeof(*dummy_ns));
dummy_ns->flavor = FLAV_MICRODESC;
@@ -113,7 +122,10 @@ test_nodelist(void *arg)
* (the_nodelist->node_addrs) so we will fail the contain test rarely. */
addr_per_node = 1024;
- /* No node no nothing. The lookups should be empty. */
+ /* No node no nothing. The lookups should be empty. We've mocked the
+ * dirlist_add_trusted_dir_addresses in order for _no_ authorities to be
+ * added to the filter else it makes this test to trigger many false
+ * positive. */
nodelist_set_consensus(dummy_ns);
/* The address set should be empty. */
@@ -167,6 +179,7 @@ test_nodelist(void *arg)
UNMOCK(networkstatus_get_latest_consensus);
UNMOCK(networkstatus_get_latest_consensus_by_flavor);
UNMOCK(get_estimated_address_per_node);
+ UNMOCK(dirlist_add_trusted_dir_addresses);
}
struct testcase_t address_set_tests[] = {
diff --git a/src/test/test_bt.sh b/src/test/test_bt.sh
index df8bcb8eda..312905a4e2 100755
--- a/src/test/test_bt.sh
+++ b/src/test/test_bt.sh
@@ -3,8 +3,6 @@
exitcode=0
-ulimit -c 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 2>&1 | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?"
diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c
index 0c15a02ee4..b29c2c6cbc 100644
--- a/src/test/test_bt_cl.c
+++ b/src/test/test_bt_cl.c
@@ -4,6 +4,9 @@
#include "orconfig.h"
#include <stdio.h>
#include <stdlib.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
/* To prevent 'assert' from going away. */
#undef TOR_COVERAGE
@@ -43,7 +46,7 @@ crash(int x)
*(volatile int *)0 = 0;
#endif /* defined(__clang_analyzer__) || defined(__COVERITY__) */
} else if (crashtype == 1) {
- tor_assert(1 == 0);
+ tor_assertf(1 == 0, "%d != %d", 1, 0);
} else if (crashtype == -1) {
;
}
@@ -88,6 +91,11 @@ main(int argc, char **argv)
return 1;
}
+#ifdef HAVE_SYS_RESOURCE_H
+ struct rlimit rlim = { .rlim_cur = 0, .rlim_max = 0 };
+ setrlimit(RLIMIT_CORE, &rlim);
+#endif
+
#if !(defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION))
puts("Backtrace reporting is not supported on this platform");
diff --git a/src/test/test_btrack.c b/src/test/test_btrack.c
new file mode 100644
index 0000000000..80da7829ae
--- /dev/null
+++ b/src/test/test_btrack.c
@@ -0,0 +1,129 @@
+/* Copyright (c) 2013-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "core/or/or.h"
+
+#include "test/test.h"
+#include "test/test_helpers.h"
+#include "test/log_test_helpers.h"
+
+#define OCIRC_EVENT_PRIVATE
+#define ORCONN_EVENT_PRIVATE
+#include "core/or/ocirc_event.h"
+#include "core/or/orconn_event.h"
+
+static void
+send_state(const orconn_state_msg_t *msg_in)
+{
+ orconn_state_msg_t *msg = tor_malloc(sizeof(*msg));
+
+ *msg = *msg_in;
+ orconn_state_publish(msg);
+}
+
+static void
+send_status(const orconn_status_msg_t *msg_in)
+{
+ orconn_status_msg_t *msg = tor_malloc(sizeof(*msg));
+
+ *msg = *msg_in;
+ orconn_status_publish(msg);
+}
+
+static void
+send_chan(const ocirc_chan_msg_t *msg_in)
+{
+ ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg));
+
+ *msg = *msg_in;
+ ocirc_chan_publish(msg);
+}
+
+static void
+test_btrack_launch(void *arg)
+{
+ orconn_state_msg_t conn;
+ ocirc_chan_msg_t circ;
+ memset(&conn, 0, sizeof(conn));
+ memset(&circ, 0, sizeof(circ));
+
+ (void)arg;
+ conn.gid = 1;
+ conn.chan = 1;
+ conn.proxy_type = PROXY_NONE;
+ conn.state = OR_CONN_STATE_CONNECTING;
+
+ setup_full_capture_of_logs(LOG_DEBUG);
+ send_state(&conn);
+ expect_log_msg_containing("ORCONN gid=1 chan=1 proxy_type=0 state=1");
+ expect_no_log_msg_containing("ORCONN BEST_");
+ teardown_capture_of_logs();
+
+ circ.chan = 1;
+ circ.onehop = true;
+
+ setup_full_capture_of_logs(LOG_DEBUG);
+ send_chan(&circ);
+ expect_log_msg_containing("ORCONN LAUNCH chan=1 onehop=1");
+ expect_log_msg_containing("ORCONN BEST_ANY state -1->1 gid=1");
+ teardown_capture_of_logs();
+
+ conn.gid = 2;
+ conn.chan = 2;
+
+ setup_full_capture_of_logs(LOG_DEBUG);
+ send_state(&conn);
+ expect_log_msg_containing("ORCONN gid=2 chan=2 proxy_type=0 state=1");
+ expect_no_log_msg_containing("ORCONN BEST_");
+ teardown_capture_of_logs();
+
+ circ.chan = 2;
+ circ.onehop = false;
+
+ setup_full_capture_of_logs(LOG_DEBUG);
+ send_chan(&circ);
+ expect_log_msg_containing("ORCONN LAUNCH chan=2 onehop=0");
+ expect_log_msg_containing("ORCONN BEST_AP state -1->1 gid=2");
+ teardown_capture_of_logs();
+
+ done:
+ ;
+}
+
+static void
+test_btrack_delete(void *arg)
+{
+ orconn_state_msg_t state;
+ orconn_status_msg_t status;
+ memset(&state, 0, sizeof(state));
+ memset(&status, 0, sizeof(status));
+
+ (void)arg;
+ state.gid = 1;
+ state.chan = 1;
+ state.proxy_type = PROXY_NONE;
+ state.state = OR_CONN_STATE_CONNECTING;
+
+ setup_full_capture_of_logs(LOG_DEBUG);
+ send_state(&state);
+ expect_log_msg_containing("ORCONN gid=1 chan=1 proxy_type=0");
+ teardown_capture_of_logs();
+
+ status.gid = 1;
+ status.status = OR_CONN_EVENT_CLOSED;
+ status.reason = 0;
+
+ setup_full_capture_of_logs(LOG_DEBUG);
+ send_status(&status);
+ expect_log_msg_containing("ORCONN DELETE gid=1 status=3 reason=0");
+ teardown_capture_of_logs();
+
+ done:
+ ;
+}
+
+struct testcase_t btrack_tests[] = {
+ { "launch", test_btrack_launch, TT_FORK, &helper_pubsub_setup, NULL },
+ { "delete", test_btrack_delete, TT_FORK, &helper_pubsub_setup, NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c
index 2f9ad1fbe3..97311c85cc 100644
--- a/src/test/test_buffers.c
+++ b/src/test/test_buffers.c
@@ -6,7 +6,7 @@
#define BUFFERS_PRIVATE
#define PROTO_HTTP_PRIVATE
#include "core/or/or.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "lib/tls/buffers_tls.h"
#include "lib/tls/tortls.h"
#include "lib/compress/compress.h"
diff --git a/src/test/test_bwmgt.c b/src/test/test_bwmgt.c
index 5a013aa268..e6f028ed74 100644
--- a/src/test/test_bwmgt.c
+++ b/src/test/test_bwmgt.c
@@ -6,18 +6,67 @@
* \brief tests for bandwidth management / token bucket functions
*/
+#define CONFIG_PRIVATE
+#define CONNECTION_PRIVATE
#define TOKEN_BUCKET_PRIVATE
#include "core/or/or.h"
-#include "test/test.h"
+#include "app/config/config.h"
+#include "core/mainloop/connection.h"
+#include "feature/dircommon/directory.h"
+#include "feature/nodelist/microdesc.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "lib/crypt_ops/crypto_rand.h"
#include "lib/evloop/token_bucket.h"
+#include "test/test.h"
+#include "test/test_helpers.h"
+
+#include "app/config/or_options_st.h"
+#include "core/or/connection_st.h"
+#include "feature/nodelist/microdesc_st.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/routerstatus_st.h"
// an imaginary time, in timestamp units. Chosen so it will roll over.
static const uint32_t START_TS = UINT32_MAX-10;
static const int32_t KB = 1024;
static const uint32_t GB = (UINT64_C(1) << 30);
+static or_options_t mock_options;
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mock_options;
+}
+
+static networkstatus_t *dummy_ns = NULL;
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus(void)
+{
+ return dummy_ns;
+}
+
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
+{
+ tor_assert(f == FLAV_MICRODESC);
+ return dummy_ns;
+}
+
+/* Number of address a single node_t can have. Default to the production
+ * value. This is to control the size of the bloom filter. */
+static int addr_per_node = 2;
+static int
+mock_get_estimated_address_per_node(void)
+{
+ return addr_per_node;
+}
+
static void
test_bwmgt_token_buf_init(void *arg)
{
@@ -220,8 +269,162 @@ test_bwmgt_token_buf_helpers(void *arg)
;
}
+static void
+test_bwmgt_dir_conn_global_write_low(void *arg)
+{
+ bool ret;
+ int addr_family;
+ connection_t *conn = NULL;
+ routerstatus_t *rs = NULL; microdesc_t *md = NULL; routerinfo_t *ri = NULL;
+ tor_addr_t relay_addr;
+
+ (void) arg;
+
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus);
+ MOCK(networkstatus_get_latest_consensus_by_flavor,
+ mock_networkstatus_get_latest_consensus_by_flavor);
+ MOCK(get_estimated_address_per_node,
+ mock_get_estimated_address_per_node);
+
+ /*
+ * The following is rather complex but that is what it takes to add a dummy
+ * consensus with a valid routerlist which will populate our node address
+ * set that we need to lookup to test the known relay code path.
+ *
+ * We MUST do that before we MOCK(get_options) else it is another world of
+ * complexity.
+ */
+
+ /* This will be the address of our relay. */
+ tor_addr_parse(&relay_addr, "1.2.3.4");
+
+ /* We'll now add a relay into our routerlist and see if we let it. */
+ dummy_ns = tor_malloc_zero(sizeof(*dummy_ns));
+ dummy_ns->flavor = FLAV_MICRODESC;
+ dummy_ns->routerstatus_list = smartlist_new();
+
+ md = tor_malloc_zero(sizeof(*md));
+ ri = tor_malloc_zero(sizeof(*ri));
+ rs = tor_malloc_zero(sizeof(*rs));
+ crypto_rand(rs->identity_digest, sizeof(rs->identity_digest));
+ crypto_rand(md->digest, sizeof(md->digest));
+ memcpy(rs->descriptor_digest, md->digest, DIGEST256_LEN);
+
+ /* Set IP address. */
+ rs->addr = tor_addr_to_ipv4h(&relay_addr);
+ ri->addr = rs->addr;
+ /* Add the rs to the consensus becoming a node_t. */
+ smartlist_add(dummy_ns->routerstatus_list, rs);
+
+ /* Add all configured authorities (hardcoded) before we set the consensus so
+ * the address set exists. */
+ ret = consider_adding_dir_servers(&mock_options, &mock_options);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* This will make the nodelist bloom filter very large
+ * (the_nodelist->node_addrs) so we will fail the contain test rarely. */
+ addr_per_node = 1024;
+
+ nodelist_set_consensus(dummy_ns);
+
+ /* Ok, now time to control which options we use. */
+ MOCK(get_options, mock_get_options);
+
+ /* Set ourselves as an authoritative dir. */
+ mock_options.AuthoritativeDir = 1;
+ mock_options.V3AuthoritativeDir = 1;
+ mock_options.UseDefaultFallbackDirs = 0;
+
+ /* This will set our global bucket to 1 byte and thus we will hit the
+ * banwdith limit in our test. */
+ mock_options.BandwidthRate = 1;
+ mock_options.BandwidthBurst = 1;
+
+ /* Else an IPv4 address screams. */
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+
+ /* Initialize the global buckets. */
+ connection_bucket_init();
+
+ /* The address "127.0.0.1" is set with this helper. */
+ conn = test_conn_get_connection(DIR_CONN_STATE_MIN_, CONN_TYPE_DIR,
+ DIR_PURPOSE_MIN_);
+ tt_assert(conn);
+
+ /* First try a non authority non relay IP thus a client but we are not
+ * configured to reject requests under load so we should get a false value
+ * that our limit is _not_ low. */
+ addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Now, we will reject requests under load so try again a non authority non
+ * relay IP thus a client. We should get a warning that our limit is too
+ * low. */
+ mock_options.AuthDirRejectRequestsUnderLoad = 1;
+
+ addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 1);
+
+ /* Now, lets try with a connection address from moria1. It should always
+ * pass even though our limit is too low. */
+ addr_family = tor_addr_parse(&conn->addr, "128.31.0.39");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* IPv6 testing of gabelmoo. */
+ addr_family = tor_addr_parse(&conn->addr, "[2001:638:a000:4140::ffff:189]");
+ tt_int_op(addr_family, OP_EQ, AF_INET6);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Lets retry with a known relay address. It should pass. Possible due to
+ * our consensus setting above. */
+ memcpy(&conn->addr, &relay_addr, sizeof(tor_addr_t));
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Lets retry with a random IP that is not an authority nor a relay. */
+ addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Finally, just make sure it still denies an IP if we are _not_ a v3
+ * directory authority. */
+ mock_options.V3AuthoritativeDir = 0;
+ addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 1);
+
+ /* Random IPv6 should not be allowed. */
+ addr_family = tor_addr_parse(&conn->addr, "[CAFE::ACAB]");
+ tt_int_op(addr_family, OP_EQ, AF_INET6);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ connection_free_minimal(conn);
+ routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md);
+ smartlist_clear(dummy_ns->routerstatus_list);
+ networkstatus_vote_free(dummy_ns);
+
+ UNMOCK(get_estimated_address_per_node);
+ UNMOCK(networkstatus_get_latest_consensus);
+ UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+ UNMOCK(get_options);
+}
+
#define BWMGT(name) \
- { #name, test_bwmgt_ ## name , 0, NULL, NULL }
+ { #name, test_bwmgt_ ## name , TT_FORK, NULL, NULL }
struct testcase_t bwmgt_tests[] = {
BWMGT(token_buf_init),
@@ -229,5 +432,7 @@ struct testcase_t bwmgt_tests[] = {
BWMGT(token_buf_dec),
BWMGT(token_buf_refill),
BWMGT(token_buf_helpers),
+
+ BWMGT(dir_conn_global_write_low),
END_OF_TESTCASES
};
diff --git a/src/test/test_channel.c b/src/test/test_channel.c
index e55b9b0750..6a6bc9d810 100644
--- a/src/test/test_channel.c
+++ b/src/test/test_channel.c
@@ -598,7 +598,6 @@ test_channel_outbound_cell(void *arg)
circuit_set_n_circid_chan(TO_CIRCUIT(circ), 42, chan);
tt_int_op(channel_num_circuits(chan), OP_EQ, 1);
/* Test the cmux state. */
- tt_ptr_op(TO_CIRCUIT(circ)->n_mux, OP_EQ, chan->cmux);
tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)),
OP_EQ, 1);
@@ -1541,6 +1540,10 @@ test_channel_listener(void *arg)
channel_listener_dump_statistics(chan, LOG_INFO);
done:
+ if (chan) {
+ channel_listener_unregister(chan);
+ tor_free(chan);
+ }
channel_free_all();
}
@@ -1567,4 +1570,3 @@ struct testcase_t channel_tests[] = {
NULL, NULL },
END_OF_TESTCASES
};
-
diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c
index 5da2d81377..885246628e 100644
--- a/src/test/test_channelpadding.c
+++ b/src/test/test_channelpadding.c
@@ -21,7 +21,7 @@
#include "test/log_test_helpers.h"
#include "lib/tls/tortls.h"
#include "lib/evloop/timers.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "core/or/cell_st.h"
#include "feature/nodelist/networkstatus_st.h"
@@ -289,8 +289,6 @@ test_channelpadding_timers(void *arg)
channel_t *chans[CHANNELS_TO_TEST];
(void)arg;
- tor_libevent_postfork();
-
if (!connection_array)
connection_array = smartlist_new();
@@ -393,7 +391,6 @@ test_channelpadding_killonehop(void *arg)
channelpadding_decision_t decision;
int64_t new_time;
(void)arg;
- tor_libevent_postfork();
routerstatus_t *relay = tor_malloc_zero(sizeof(routerstatus_t));
monotime_init();
@@ -502,8 +499,6 @@ test_channelpadding_consensus(void *arg)
int64_t new_time;
(void)arg;
- tor_libevent_postfork();
-
/*
* Params tested:
* nf_pad_before_usage
@@ -898,8 +893,6 @@ test_channelpadding_decide_to_pad_channel(void *arg)
connection_array = smartlist_new();
(void)arg;
- tor_libevent_postfork();
-
monotime_init();
monotime_enable_test_mocking();
monotime_set_mock_time_nsec(1);
diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c
index 10513e451d..054d3910e4 100644
--- a/src/test/test_channeltls.c
+++ b/src/test/test_channeltls.c
@@ -8,7 +8,7 @@
#define TOR_CHANNEL_INTERNAL_
#include "core/or/or.h"
#include "lib/net/address.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "core/or/channel.h"
#include "core/or/channeltls.h"
#include "core/mainloop/connection.h"
diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c
index 538f20781f..7291e04d6a 100644
--- a/src/test/test_circuitbuild.c
+++ b/src/test/test_circuitbuild.c
@@ -27,11 +27,11 @@ static smartlist_t dummy_nodes;
static extend_info_t dummy_ei;
static int
-mock_count_acceptable_nodes(smartlist_t *nodes)
+mock_count_acceptable_nodes(const smartlist_t *nodes, int direct)
{
(void)nodes;
- return DEFAULT_ROUTE_LEN + 1;
+ return direct ? 1 : DEFAULT_ROUTE_LEN + 1;
}
/* Test route lengths when the caller of new_route_len() doesn't
@@ -167,6 +167,7 @@ test_upgrade_from_guard_wait(void *arg)
tt_assert(!list);
done:
+ smartlist_free(list);
circuit_free(circ);
entry_guard_free_(guard);
}
@@ -177,6 +178,6 @@ struct testcase_t circuitbuild_tests[] = {
{ "unsafe_exit", test_new_route_len_unsafe_exit, 0, NULL, NULL },
{ "unhandled_exit", test_new_route_len_unhandled_exit, 0, NULL, NULL },
{ "upgrade_from_guard_wait", test_upgrade_from_guard_wait, TT_FORK,
- NULL, NULL },
+ &helper_pubsub_setup, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_circuitpadding.c b/src/test/test_circuitpadding.c
new file mode 100644
index 0000000000..934ddb0208
--- /dev/null
+++ b/src/test/test_circuitpadding.c
@@ -0,0 +1,3205 @@
+#define TOR_CHANNEL_INTERNAL_
+#define TOR_TIMERS_PRIVATE
+#define CIRCUITPADDING_PRIVATE
+#define CIRCUITPADDING_MACHINES_PRIVATE
+#define NETWORKSTATUS_PRIVATE
+#define CRYPT_PATH_PRIVATE
+#define RELAY_PRIVATE
+
+#include "core/or/or.h"
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+#include "lib/testsupport/testsupport.h"
+#include "core/or/connection_or.h"
+#include "core/or/channel.h"
+#include "core/or/channeltls.h"
+#include "core/or/crypt_path.h"
+#include <event.h>
+#include "lib/evloop/compat_libevent.h"
+#include "lib/time/compat_time.h"
+#include "lib/defs/time.h"
+#include "core/or/relay.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuitbuild.h"
+#include "core/or/circuitpadding.h"
+#include "core/or/circuitpadding_machines.h"
+#include "core/mainloop/netstatus.h"
+#include "core/crypto/relay_crypto.h"
+#include "core/or/protover.h"
+#include "feature/nodelist/nodelist.h"
+#include "lib/evloop/compat_libevent.h"
+#include "app/config/config.h"
+
+#include "feature/nodelist/routerstatus_st.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "feature/nodelist/node_st.h"
+#include "core/or/cell_st.h"
+#include "core/or/crypt_path_st.h"
+#include "core/or/or_circuit_st.h"
+#include "core/or/origin_circuit_st.h"
+
+#include "test/rng_test_helpers.h"
+
+/* Start our monotime mocking at 1 second past whatever monotime_init()
+ * thought the actual wall clock time was, for platforms with bad resolution
+ * and weird timevalues during monotime_init() before mocking. */
+#define MONOTIME_MOCK_START (monotime_absolute_nsec()+\
+ TOR_NSEC_PER_USEC*TOR_USEC_PER_SEC)
+
+extern smartlist_t *connection_array;
+void circuit_expire_old_circuits_clientside(void);
+
+circid_t get_unique_circ_id_by_chan(channel_t *chan);
+void helper_create_basic_machine(void);
+static void helper_create_conditional_machines(void);
+
+static or_circuit_t * new_fake_orcirc(channel_t *nchan, channel_t *pchan);
+channel_t *new_fake_channel(void);
+void test_circuitpadding_negotiation(void *arg);
+void test_circuitpadding_wronghop(void *arg);
+void test_circuitpadding_conditions(void *arg);
+
+void test_circuitpadding_serialize(void *arg);
+void test_circuitpadding_rtt(void *arg);
+void test_circuitpadding_tokens(void *arg);
+void test_circuitpadding_state_length(void *arg);
+
+static void
+simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay,
+ int padding);
+void free_fake_orcirc(circuit_t *circ);
+void free_fake_origin_circuit(origin_circuit_t *circ);
+
+static int deliver_negotiated = 1;
+static int64_t curr_mocked_time;
+
+static node_t padding_node;
+static node_t non_padding_node;
+
+static channel_t dummy_channel;
+static circpad_machine_spec_t circ_client_machine;
+
+static void
+timers_advance_and_run(int64_t msec_update)
+{
+ curr_mocked_time += msec_update*TOR_NSEC_PER_MSEC;
+ monotime_coarse_set_mock_time_nsec(curr_mocked_time);
+ monotime_set_mock_time_nsec(curr_mocked_time);
+ timers_run_pending();
+}
+
+static void
+nodes_init(void)
+{
+ padding_node.rs = tor_malloc_zero(sizeof(routerstatus_t));
+ padding_node.rs->pv.supports_hs_setup_padding = 1;
+
+ non_padding_node.rs = tor_malloc_zero(sizeof(routerstatus_t));
+ non_padding_node.rs->pv.supports_hs_setup_padding = 0;
+}
+
+static void
+nodes_free(void)
+{
+ tor_free(padding_node.rs);
+
+ tor_free(non_padding_node.rs);
+}
+
+static const node_t *
+node_get_by_id_mock(const char *identity_digest)
+{
+ if (identity_digest[0] == 1) {
+ return &padding_node;
+ } else if (identity_digest[0] == 0) {
+ return &non_padding_node;
+ }
+
+ return NULL;
+}
+
+static const node_t *
+circuit_get_nth_node_mock(origin_circuit_t *circ, int hop)
+{
+ (void) circ;
+ (void) hop;
+
+ return &padding_node;
+}
+
+static or_circuit_t *
+new_fake_orcirc(channel_t *nchan, channel_t *pchan)
+{
+ or_circuit_t *orcirc = NULL;
+ circuit_t *circ = NULL;
+ crypt_path_t tmp_cpath;
+ char whatevs_key[CPATH_KEY_MATERIAL_LEN];
+
+ orcirc = tor_malloc_zero(sizeof(*orcirc));
+ circ = &(orcirc->base_);
+ circ->magic = OR_CIRCUIT_MAGIC;
+
+ //circ->n_chan = nchan;
+ circ->n_circ_id = get_unique_circ_id_by_chan(nchan);
+ cell_queue_init(&(circ->n_chan_cells));
+ circ->n_hop = NULL;
+ circ->streams_blocked_on_n_chan = 0;
+ circ->streams_blocked_on_p_chan = 0;
+ circ->n_delete_pending = 0;
+ circ->p_delete_pending = 0;
+ circ->received_destroy = 0;
+ circ->state = CIRCUIT_STATE_OPEN;
+ circ->purpose = CIRCUIT_PURPOSE_OR;
+ circ->package_window = CIRCWINDOW_START_MAX;
+ circ->deliver_window = CIRCWINDOW_START_MAX;
+ circ->n_chan_create_cell = NULL;
+
+ //orcirc->p_chan = pchan;
+ orcirc->p_circ_id = get_unique_circ_id_by_chan(pchan);
+ cell_queue_init(&(orcirc->p_chan_cells));
+
+ circuit_set_p_circid_chan(orcirc, orcirc->p_circ_id, pchan);
+ circuit_set_n_circid_chan(circ, circ->n_circ_id, nchan);
+
+ memset(&tmp_cpath, 0, sizeof(tmp_cpath));
+ if (cpath_init_circuit_crypto(&tmp_cpath, whatevs_key,
+ sizeof(whatevs_key), 0, 0)<0) {
+ log_warn(LD_BUG,"Circuit initialization failed");
+ return NULL;
+ }
+ orcirc->crypto = tmp_cpath.pvt_crypto;
+
+ return orcirc;
+}
+
+void
+free_fake_orcirc(circuit_t *circ)
+{
+ or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
+
+ relay_crypto_clear(&orcirc->crypto);
+
+ circpad_circuit_free_all_machineinfos(circ);
+ tor_free(circ);
+}
+
+void
+free_fake_origin_circuit(origin_circuit_t *circ)
+{
+ circpad_circuit_free_all_machineinfos(TO_CIRCUIT(circ));
+ circuit_clear_cpath(circ);
+ tor_free(circ);
+}
+
+void dummy_nop_timer(void);
+
+//static int dont_stop_libevent = 0;
+
+static circuit_t *client_side;
+static circuit_t *relay_side;
+
+static int n_client_cells = 0;
+static int n_relay_cells = 0;
+
+static int
+circuit_package_relay_cell_mock(cell_t *cell, circuit_t *circ,
+ cell_direction_t cell_direction,
+ crypt_path_t *layer_hint, streamid_t on_stream,
+ const char *filename, int lineno);
+
+static void
+circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ,
+ cell_direction_t direction);
+
+static void
+circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ,
+ cell_direction_t direction)
+{
+ (void)cmux;
+ (void)circ;
+ (void)direction;
+
+ return;
+}
+
+static int
+circuit_package_relay_cell_mock(cell_t *cell, circuit_t *circ,
+ cell_direction_t cell_direction,
+ crypt_path_t *layer_hint, streamid_t on_stream,
+ const char *filename, int lineno)
+{
+ (void)cell; (void)on_stream; (void)filename; (void)lineno;
+
+ if (circ == client_side) {
+ if (cell->payload[0] == RELAY_COMMAND_PADDING_NEGOTIATE) {
+ // Deliver to relay
+ circpad_handle_padding_negotiate(relay_side, cell);
+ } else {
+
+ int is_target_hop = circpad_padding_is_from_expected_hop(circ,
+ layer_hint);
+ tt_int_op(cell_direction, OP_EQ, CELL_DIRECTION_OUT);
+ tt_int_op(is_target_hop, OP_EQ, 1);
+
+ // No need to pretend a padding cell was sent: This event is
+ // now emitted internally when the circuitpadding code sends them.
+ //circpad_cell_event_padding_sent(client_side);
+
+ // Receive padding cell at middle
+ circpad_deliver_recognized_relay_cell_events(relay_side,
+ cell->payload[0], NULL);
+ }
+ n_client_cells++;
+ } else if (circ == relay_side) {
+ tt_int_op(cell_direction, OP_EQ, CELL_DIRECTION_IN);
+
+ if (cell->payload[0] == RELAY_COMMAND_PADDING_NEGOTIATED) {
+ // XXX: blah need right layer_hint..
+ if (deliver_negotiated)
+ circpad_handle_padding_negotiated(client_side, cell,
+ TO_ORIGIN_CIRCUIT(client_side)
+ ->cpath->next);
+ } else if (cell->payload[0] == RELAY_COMMAND_PADDING_NEGOTIATE) {
+ circpad_handle_padding_negotiate(client_side, cell);
+ } else {
+ // No need to pretend a padding cell was sent: This event is
+ // now emitted internally when the circuitpadding code sends them.
+ //circpad_cell_event_padding_sent(relay_side);
+
+ // Receive padding cell at client
+ circpad_deliver_recognized_relay_cell_events(client_side,
+ cell->payload[0],
+ TO_ORIGIN_CIRCUIT(client_side)->cpath->next);
+ }
+
+ n_relay_cells++;
+ }
+
+ done:
+ timers_advance_and_run(1);
+ return 0;
+}
+
+// Test reading and writing padding to strings (or options_t + consensus)
+void
+test_circuitpadding_serialize(void *arg)
+{
+ (void)arg;
+}
+
+static signed_error_t
+circpad_send_command_to_hop_mock(origin_circuit_t *circ, uint8_t hopnum,
+ uint8_t relay_command, const uint8_t *payload,
+ ssize_t payload_len)
+{
+ (void) circ;
+ (void) hopnum;
+ (void) relay_command;
+ (void) payload;
+ (void) payload_len;
+ return 0;
+}
+
+void
+test_circuitpadding_rtt(void *arg)
+{
+ /* Test Plan:
+ *
+ * 1. Test RTT measurement server side
+ * a. test usage of measured RTT
+ * 2. Test termination of RTT measurement
+ * a. test non-update of RTT
+ * 3. Test client side circuit and non-application of RTT..
+ */
+ circpad_delay_t rtt_estimate;
+ int64_t actual_mocked_monotime_start;
+ (void)arg;
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ MOCK(circpad_send_command_to_hop, circpad_send_command_to_hop_mock);
+ testing_enable_reproducible_rng();
+
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ timers_initialize();
+ circpad_machines_init();
+ helper_create_basic_machine();
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side,
+ 0);
+
+ relay_side->padding_machine[0] = &circ_client_machine;
+ relay_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side,0);
+
+ /* Test 1: Test measuring RTT */
+ circpad_cell_event_nonpadding_received(relay_side);
+ tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_NE, 0);
+
+ timers_advance_and_run(20);
+
+ circpad_cell_event_nonpadding_sent(relay_side);
+ tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_EQ, 0);
+
+ tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_GE, 19000);
+ tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_LE, 30000);
+ tt_int_op(circpad_histogram_bin_to_usec(relay_side->padding_info[0], 0),
+ OP_EQ,
+ relay_side->padding_info[0]->rtt_estimate_usec+
+ circpad_machine_current_state(
+ relay_side->padding_info[0])->histogram_edges[0]);
+
+ circpad_cell_event_nonpadding_received(relay_side);
+ circpad_cell_event_nonpadding_received(relay_side);
+ tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_NE, 0);
+ timers_advance_and_run(20);
+ circpad_cell_event_nonpadding_sent(relay_side);
+ circpad_cell_event_nonpadding_sent(relay_side);
+ tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_EQ, 0);
+
+ tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_GE, 20000);
+ tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_LE, 21000);
+ tt_int_op(circpad_histogram_bin_to_usec(relay_side->padding_info[0], 0),
+ OP_EQ,
+ relay_side->padding_info[0]->rtt_estimate_usec+
+ circpad_machine_current_state(
+ relay_side->padding_info[0])->histogram_edges[0]);
+
+ /* Test 2: Termination of RTT measurement (from the previous test) */
+ tt_int_op(relay_side->padding_info[0]->stop_rtt_update, OP_EQ, 1);
+ rtt_estimate = relay_side->padding_info[0]->rtt_estimate_usec;
+
+ circpad_cell_event_nonpadding_received(relay_side);
+ timers_advance_and_run(4);
+ circpad_cell_event_nonpadding_sent(relay_side);
+
+ tt_int_op(relay_side->padding_info[0]->rtt_estimate_usec, OP_EQ,
+ rtt_estimate);
+ tt_u64_op(relay_side->padding_info[0]->last_received_time_usec, OP_EQ, 0);
+ tt_int_op(relay_side->padding_info[0]->stop_rtt_update, OP_EQ, 1);
+ tt_int_op(circpad_histogram_bin_to_usec(relay_side->padding_info[0], 0),
+ OP_EQ,
+ relay_side->padding_info[0]->rtt_estimate_usec+
+ circpad_machine_current_state(
+ relay_side->padding_info[0])->histogram_edges[0]);
+
+ /* Test 3: Make sure client side machine properly ignores RTT */
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_u64_op(client_side->padding_info[0]->last_received_time_usec, OP_EQ, 0);
+
+ timers_advance_and_run(20);
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_u64_op(client_side->padding_info[0]->last_received_time_usec, OP_EQ, 0);
+
+ tt_int_op(client_side->padding_info[0]->rtt_estimate_usec, OP_EQ, 0);
+ tt_int_op(circpad_histogram_bin_to_usec(client_side->padding_info[0], 0),
+ OP_NE, client_side->padding_info[0]->rtt_estimate_usec);
+ tt_int_op(circpad_histogram_bin_to_usec(client_side->padding_info[0], 0),
+ OP_EQ,
+ circpad_machine_current_state(
+ client_side->padding_info[0])->histogram_edges[0]);
+ done:
+ free_fake_orcirc(relay_side);
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ timers_shutdown();
+ monotime_disable_test_mocking();
+ UNMOCK(circuit_package_relay_cell);
+ UNMOCK(circuitmux_attach_circuit);
+ tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
+
+ return;
+}
+
+void
+helper_create_basic_machine(void)
+{
+ /* Start, burst */
+ circpad_machine_states_init(&circ_client_machine, 2);
+
+ circ_client_machine.name = "basic";
+
+ circ_client_machine.states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_BURST;
+ circ_client_machine.states[CIRCPAD_STATE_START].use_rtt_estimate = 1;
+
+ circ_client_machine.states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_BURST;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_BURST;
+
+ circ_client_machine.states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_NONPADDING_SENT] = CIRCPAD_STATE_CANCEL;
+
+ circ_client_machine.states[CIRCPAD_STATE_BURST].token_removal =
+ CIRCPAD_TOKEN_REMOVAL_HIGHER;
+
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_len = 5;
+
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 500;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[1] = 2500;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[2] = 5000;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[3] = 10000;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[4] = 20000;
+
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[0] = 1;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[1] = 0;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[2] = 2;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[3] = 2;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram[4] = 2;
+
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_total_tokens = 7;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].use_rtt_estimate = 1;
+
+ return;
+}
+
+#define BIG_HISTOGRAM_LEN 10
+
+/** Setup a machine with a big histogram */
+static void
+helper_create_machine_with_big_histogram(circpad_removal_t removal_strategy)
+{
+ const int tokens_per_bin = 2;
+
+ /* Start, burst */
+ circpad_machine_states_init(&circ_client_machine, 2);
+
+ circpad_state_t *burst_state =
+ &circ_client_machine.states[CIRCPAD_STATE_BURST];
+
+ circ_client_machine.states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_BURST;
+
+ burst_state->next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_BURST;
+ burst_state->next_state[CIRCPAD_EVENT_NONPADDING_RECV] =CIRCPAD_STATE_BURST;
+
+ burst_state->next_state[CIRCPAD_EVENT_NONPADDING_SENT] =CIRCPAD_STATE_CANCEL;
+
+ burst_state->token_removal = CIRCPAD_TOKEN_REMOVAL_HIGHER;
+
+ burst_state->histogram_len = BIG_HISTOGRAM_LEN;
+
+ int n_tokens = 0;
+ int i;
+ for (i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ burst_state->histogram[i] = tokens_per_bin;
+ n_tokens += tokens_per_bin;
+ }
+
+ burst_state->histogram_edges[0] = 0;
+ burst_state->histogram_edges[1] = 1;
+ burst_state->histogram_edges[2] = 7;
+ burst_state->histogram_edges[3] = 15;
+ burst_state->histogram_edges[4] = 31;
+ burst_state->histogram_edges[5] = 62;
+ burst_state->histogram_edges[6] = 125;
+ burst_state->histogram_edges[7] = 250;
+ burst_state->histogram_edges[8] = 500;
+ burst_state->histogram_edges[9] = 1000;
+
+ burst_state->histogram_total_tokens = n_tokens;
+ burst_state->length_dist.type = CIRCPAD_DIST_UNIFORM;
+ burst_state->length_dist.param1 = n_tokens;
+ burst_state->length_dist.param2 = n_tokens;
+ burst_state->max_length = n_tokens;
+ burst_state->length_includes_nonpadding = 1;
+ burst_state->use_rtt_estimate = 0;
+ burst_state->token_removal = removal_strategy;
+}
+
+static circpad_decision_t
+circpad_machine_schedule_padding_mock(circpad_machine_runtime_t *mi)
+{
+ (void)mi;
+ return 0;
+}
+
+static uint64_t
+mock_monotime_absolute_usec(void)
+{
+ return 100;
+}
+
+/** Test higher token removal strategy by bin */
+static void
+test_circuitpadding_token_removal_higher(void *arg)
+{
+ circpad_machine_runtime_t *mi;
+ (void)arg;
+
+ /* Mock it up */
+ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
+ MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
+
+ /* Setup test environment (time etc.) */
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ monotime_enable_test_mocking();
+
+ /* Create test machine */
+ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_HIGHER);
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+
+ /* move the machine to the right state */
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+
+ /* Get the machine and setup tokens */
+ mi = client_side->padding_info[0];
+ tt_assert(mi);
+
+ /*************************************************************************/
+
+ uint64_t current_time = monotime_absolute_usec();
+
+ /* Test left boundaries of each histogram bin: */
+ const circpad_delay_t bin_left_bounds[] =
+ {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE};
+ for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) {
+ tt_uint_op(bin_left_bounds[i], OP_EQ,
+ circpad_histogram_bin_to_usec(mi, i));
+ }
+
+ /* Test right boundaries of each histogram bin: */
+ const circpad_delay_t bin_right_bounds[] =
+ {0, 6, 14, 30, 61, 124, 249, 499, 999, CIRCPAD_DELAY_INFINITE-1};
+ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ tt_uint_op(bin_right_bounds[i], OP_EQ,
+ histogram_get_bin_upper_bound(mi, i));
+ }
+
+ /* Check that all bins have two tokens right now */
+ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ tt_int_op(mi->histogram[i], OP_EQ, 2);
+ }
+
+ /* This is the right order to remove tokens from this histogram. That is, we
+ * first remove tokens from the 4th bin since 57 usec is nearest to the 4th
+ * bin midpoint (31 + (62-31)/2 == 46). Then we remove from the 3rd bin for
+ * the same reason, then from the 5th, etc. */
+ const int bin_removal_order[] = {4, 5, 6, 7, 8};
+ unsigned i;
+
+ /* Remove all tokens from all bins apart from the infinity bin */
+ for (i = 0; i < sizeof(bin_removal_order)/sizeof(int) ; i++) {
+ int bin_to_remove = bin_removal_order[i];
+ log_debug(LD_GENERAL, "Testing that %d attempt removes %d bin",
+ i, bin_to_remove);
+
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2);
+
+ mi->padding_scheduled_at_usec = current_time - 57;
+ circpad_cell_event_nonpadding_sent(client_side);
+
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1);
+
+ mi->padding_scheduled_at_usec = current_time - 57;
+ circpad_cell_event_nonpadding_sent(client_side);
+
+ /* Test that we cleaned out this bin. Don't do this in the case of the last
+ bin since the tokens will get refilled */
+ if (i != BIG_HISTOGRAM_LEN - 2) {
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 0);
+ }
+ }
+
+ /* Check that all lower bins are not touched */
+ for (i=0; i < 4 ; i++) {
+ tt_int_op(mi->histogram[i], OP_EQ, 2);
+ }
+
+ /* Test below the lowest bin, for coverage */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 100;
+ mi->padding_scheduled_at_usec = current_time;
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_int_op(mi->histogram[0], OP_EQ, 1);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ monotime_disable_test_mocking();
+ tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
+}
+
+/** Test lower token removal strategy by bin */
+static void
+test_circuitpadding_token_removal_lower(void *arg)
+{
+ circpad_machine_runtime_t *mi;
+ (void)arg;
+
+ /* Mock it up */
+ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
+ MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
+
+ /* Setup test environment (time etc.) */
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ monotime_enable_test_mocking();
+
+ /* Create test machine */
+ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_LOWER);
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+
+ /* move the machine to the right state */
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+
+ /* Get the machine and setup tokens */
+ mi = client_side->padding_info[0];
+ tt_assert(mi);
+
+ /*************************************************************************/
+
+ uint64_t current_time = monotime_absolute_usec();
+
+ /* Test left boundaries of each histogram bin: */
+ const circpad_delay_t bin_left_bounds[] =
+ {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE};
+ for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) {
+ tt_uint_op(bin_left_bounds[i], OP_EQ,
+ circpad_histogram_bin_to_usec(mi, i));
+ }
+
+ /* Check that all bins have two tokens right now */
+ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ tt_int_op(mi->histogram[i], OP_EQ, 2);
+ }
+
+ /* This is the right order to remove tokens from this histogram. That is, we
+ * first remove tokens from the 4th bin since 57 usec is nearest to the 4th
+ * bin midpoint (31 + (62-31)/2 == 46). Then we remove from the 3rd bin for
+ * the same reason, then from the 5th, etc. */
+ const int bin_removal_order[] = {4, 3, 2, 1, 0};
+ unsigned i;
+
+ /* Remove all tokens from all bins apart from the infinity bin */
+ for (i = 0; i < sizeof(bin_removal_order)/sizeof(int) ; i++) {
+ int bin_to_remove = bin_removal_order[i];
+ log_debug(LD_GENERAL, "Testing that %d attempt removes %d bin",
+ i, bin_to_remove);
+
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2);
+
+ mi->padding_scheduled_at_usec = current_time - 57;
+ circpad_cell_event_nonpadding_sent(client_side);
+
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1);
+
+ mi->padding_scheduled_at_usec = current_time - 57;
+ circpad_cell_event_nonpadding_sent(client_side);
+
+ /* Test that we cleaned out this bin. Don't do this in the case of the last
+ bin since the tokens will get refilled */
+ if (i != BIG_HISTOGRAM_LEN - 2) {
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 0);
+ }
+ }
+
+ /* Check that all higher bins are untouched */
+ for (i = 5; i < BIG_HISTOGRAM_LEN ; i++) {
+ tt_int_op(mi->histogram[i], OP_EQ, 2);
+ }
+
+ /* Test above the highest bin, for coverage */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ circ_client_machine.states[CIRCPAD_STATE_BURST].
+ histogram_edges[BIG_HISTOGRAM_LEN-2] = 100;
+ mi->padding_scheduled_at_usec = current_time - 29202;
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_int_op(mi->histogram[BIG_HISTOGRAM_LEN-2], OP_EQ, 1);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ monotime_disable_test_mocking();
+ tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
+}
+
+/** Test closest token removal strategy by bin */
+static void
+test_circuitpadding_closest_token_removal(void *arg)
+{
+ circpad_machine_runtime_t *mi;
+ (void)arg;
+
+ /* Mock it up */
+ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
+ MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
+
+ /* Setup test environment (time etc.) */
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ monotime_enable_test_mocking();
+
+ /* Create test machine */
+ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_CLOSEST);
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+
+ /* move the machine to the right state */
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+
+ /* Get the machine and setup tokens */
+ mi = client_side->padding_info[0];
+ tt_assert(mi);
+
+ /*************************************************************************/
+
+ uint64_t current_time = monotime_absolute_usec();
+
+ /* Test left boundaries of each histogram bin: */
+ const circpad_delay_t bin_left_bounds[] =
+ {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE};
+ for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) {
+ tt_uint_op(bin_left_bounds[i], OP_EQ,
+ circpad_histogram_bin_to_usec(mi, i));
+ }
+
+ /* Check that all bins have two tokens right now */
+ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ tt_int_op(mi->histogram[i], OP_EQ, 2);
+ }
+
+ /* This is the right order to remove tokens from this histogram. That is, we
+ * first remove tokens from the 4th bin since 57 usec is nearest to the 4th
+ * bin midpoint (31 + (62-31)/2 == 46). Then we remove from the 3rd bin for
+ * the same reason, then from the 5th, etc. */
+ const int bin_removal_order[] = {4, 3, 5, 2, 6, 1, 7, 0, 8, 9};
+
+ /* Remove all tokens from all bins apart from the infinity bin */
+ for (int i = 0; i < BIG_HISTOGRAM_LEN-1 ; i++) {
+ int bin_to_remove = bin_removal_order[i];
+ log_debug(LD_GENERAL, "Testing that %d attempt removes %d bin",
+ i, bin_to_remove);
+
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2);
+
+ mi->padding_scheduled_at_usec = current_time - 57;
+ circpad_cell_event_nonpadding_sent(client_side);
+
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1);
+
+ mi->padding_scheduled_at_usec = current_time - 57;
+ circpad_cell_event_nonpadding_sent(client_side);
+
+ /* Test that we cleaned out this bin. Don't do this in the case of the last
+ bin since the tokens will get refilled */
+ if (i != BIG_HISTOGRAM_LEN - 2) {
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 0);
+ }
+ }
+
+ /* Check that all bins have been refilled */
+ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ tt_int_op(mi->histogram[i], OP_EQ, 2);
+ }
+
+ /* Test below the lowest bin, for coverage */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 100;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[1] = 101;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[2] = 120;
+ mi->padding_scheduled_at_usec = current_time - 102;
+ mi->histogram[0] = 0;
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_int_op(mi->histogram[1], OP_EQ, 1);
+
+ /* Test above the highest bin, for coverage */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ mi->padding_scheduled_at_usec = current_time - 29202;
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_int_op(mi->histogram[BIG_HISTOGRAM_LEN-2], OP_EQ, 1);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ monotime_disable_test_mocking();
+ tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
+}
+
+/** Test closest token removal strategy with usec */
+static void
+test_circuitpadding_closest_token_removal_usec(void *arg)
+{
+ circpad_machine_runtime_t *mi;
+ (void)arg;
+
+ /* Mock it up */
+ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
+ MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
+
+ /* Setup test environment (time etc.) */
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ monotime_enable_test_mocking();
+
+ /* Create test machine */
+ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_CLOSEST_USEC);
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+
+ /* move the machine to the right state */
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+
+ /* Get the machine and setup tokens */
+ mi = client_side->padding_info[0];
+ tt_assert(mi);
+
+ /*************************************************************************/
+
+ uint64_t current_time = monotime_absolute_usec();
+
+ /* Test left boundaries of each histogram bin: */
+ const circpad_delay_t bin_left_bounds[] =
+ {0, 1, 7, 15, 31, 62, 125, 250, 500, 1000, CIRCPAD_DELAY_INFINITE};
+ for (int i = 0; i <= BIG_HISTOGRAM_LEN ; i++) {
+ tt_uint_op(bin_left_bounds[i], OP_EQ,
+ circpad_histogram_bin_to_usec(mi, i));
+ }
+
+ /* XXX we want to test remove_token_exact and
+ circpad_machine_remove_closest_token() with usec */
+
+ /* Check that all bins have two tokens right now */
+ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ tt_int_op(mi->histogram[i], OP_EQ, 2);
+ }
+
+ /* This is the right order to remove tokens from this histogram. That is, we
+ * first remove tokens from the 4th bin since 57 usec is nearest to the 4th
+ * bin midpoint (31 + (62-31)/2 == 46). Then we remove from the 3rd bin for
+ * the same reason, then from the 5th, etc. */
+ const int bin_removal_order[] = {4, 3, 5, 2, 1, 0, 6, 7, 8, 9};
+
+ /* Remove all tokens from all bins apart from the infinity bin */
+ for (int i = 0; i < BIG_HISTOGRAM_LEN-1 ; i++) {
+ int bin_to_remove = bin_removal_order[i];
+ log_debug(LD_GENERAL, "Testing that %d attempt removes %d bin",
+ i, bin_to_remove);
+
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 2);
+
+ mi->padding_scheduled_at_usec = current_time - 57;
+ circpad_cell_event_nonpadding_sent(client_side);
+
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 1);
+
+ mi->padding_scheduled_at_usec = current_time - 57;
+ circpad_cell_event_nonpadding_sent(client_side);
+
+ /* Test that we cleaned out this bin. Don't do this in the case of the last
+ bin since the tokens will get refilled */
+ if (i != BIG_HISTOGRAM_LEN - 2) {
+ tt_int_op(mi->histogram[bin_to_remove], OP_EQ, 0);
+ }
+ }
+
+ /* Check that all bins have been refilled */
+ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ tt_int_op(mi->histogram[i], OP_EQ, 2);
+ }
+
+ /* Test below the lowest bin, for coverage */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[0] = 100;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[1] = 101;
+ circ_client_machine.states[CIRCPAD_STATE_BURST].histogram_edges[2] = 120;
+ mi->padding_scheduled_at_usec = current_time - 102;
+ mi->histogram[0] = 0;
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_int_op(mi->histogram[1], OP_EQ, 1);
+
+ /* Test above the highest bin, for coverage */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ circ_client_machine.states[CIRCPAD_STATE_BURST].
+ histogram_edges[BIG_HISTOGRAM_LEN-2] = 100;
+ mi->padding_scheduled_at_usec = current_time - 29202;
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_int_op(mi->histogram[BIG_HISTOGRAM_LEN-2], OP_EQ, 1);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ monotime_disable_test_mocking();
+ tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
+}
+
+/** Test closest token removal strategy with usec */
+static void
+test_circuitpadding_token_removal_exact(void *arg)
+{
+ circpad_machine_runtime_t *mi;
+ (void)arg;
+
+ /* Mock it up */
+ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
+ MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
+
+ /* Setup test environment (time etc.) */
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ monotime_enable_test_mocking();
+
+ /* Create test machine */
+ helper_create_machine_with_big_histogram(CIRCPAD_TOKEN_REMOVAL_EXACT);
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+
+ /* move the machine to the right state */
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+
+ /* Get the machine and setup tokens */
+ mi = client_side->padding_info[0];
+ tt_assert(mi);
+
+ /**********************************************************************/
+ uint64_t current_time = monotime_absolute_usec();
+
+ /* Ensure that we will clear out bin #4 with this usec */
+ mi->padding_scheduled_at_usec = current_time - 57;
+ tt_int_op(mi->histogram[4], OP_EQ, 2);
+ circpad_cell_event_nonpadding_sent(client_side);
+ mi->padding_scheduled_at_usec = current_time - 57;
+ tt_int_op(mi->histogram[4], OP_EQ, 1);
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_int_op(mi->histogram[4], OP_EQ, 0);
+
+ /* Ensure that we will not remove any other tokens even tho we try to, since
+ * this is what the exact strategy dictates */
+ mi->padding_scheduled_at_usec = current_time - 57;
+ circpad_cell_event_nonpadding_sent(client_side);
+ for (int i = 0; i < BIG_HISTOGRAM_LEN ; i++) {
+ if (i != 4) {
+ tt_int_op(mi->histogram[i], OP_EQ, 2);
+ }
+ }
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ monotime_disable_test_mocking();
+ tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
+}
+
+#undef BIG_HISTOGRAM_LEN
+
+void
+test_circuitpadding_tokens(void *arg)
+{
+ const circpad_state_t *state;
+ circpad_machine_runtime_t *mi;
+ int64_t actual_mocked_monotime_start;
+ (void)arg;
+
+ testing_enable_reproducible_rng();
+
+ /** Test plan:
+ *
+ * 1. Test symmetry between bin_to_usec and usec_to_bin
+ * a. Test conversion
+ * b. Test edge transitions (lower, upper)
+ * 2. Test remove higher on an empty bin
+ * a. Normal bin
+ * b. Infinity bin
+ * c. Bin 0
+ * d. No higher
+ * 3. Test remove lower
+ * a. Normal bin
+ * b. Bin 0
+ * c. No lower
+ * 4. Test remove closest
+ * a. Closest lower
+ * b. Closest higher
+ * c. Closest 0
+ * d. Closest Infinity
+ */
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
+ timers_initialize();
+
+ helper_create_basic_machine();
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] = circpad_circuit_machineinfo_new(client_side,
+ 0);
+
+ mi = client_side->padding_info[0];
+
+ // Pretend a non-padding cell was sent
+ circpad_cell_event_nonpadding_received(client_side);
+ circpad_cell_event_nonpadding_sent(client_side);
+ /* We have to save the infinity bin because one inf delay
+ * could have been chosen when we transition to burst */
+ circpad_hist_token_t inf_bin = mi->histogram[4];
+
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+
+ state = circpad_machine_current_state(client_side->padding_info[0]);
+
+ // Test 0: convert bin->usec->bin
+ // Bin 0+1 have different semantics
+ for (int bin = 0; bin < 2; bin++) {
+ circpad_delay_t usec =
+ circpad_histogram_bin_to_usec(client_side->padding_info[0], bin);
+ int bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0],
+ usec);
+ tt_int_op(bin, OP_EQ, bin2);
+ }
+ for (int bin = 2; bin < state->histogram_len-1; bin++) {
+ circpad_delay_t usec =
+ circpad_histogram_bin_to_usec(client_side->padding_info[0], bin);
+ int bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0],
+ usec);
+ tt_int_op(bin, OP_EQ, bin2);
+ /* Verify we round down */
+ bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0],
+ usec+3);
+ tt_int_op(bin, OP_EQ, bin2);
+
+ bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0],
+ usec-1);
+ tt_int_op(bin, OP_EQ, bin2+1);
+ }
+
+ // Test 1: converting usec->bin->usec->bin
+ // Bin 0+1 have different semantics.
+ for (circpad_delay_t i = 0; i <= state->histogram_edges[0]; i++) {
+ int bin = circpad_histogram_usec_to_bin(client_side->padding_info[0],
+ i);
+ circpad_delay_t usec =
+ circpad_histogram_bin_to_usec(client_side->padding_info[0], bin);
+ int bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0],
+ usec);
+ tt_int_op(bin, OP_EQ, bin2);
+ tt_int_op(i, OP_LE, usec);
+ }
+ for (circpad_delay_t i = state->histogram_edges[0]+1;
+ i <= state->histogram_edges[0] +
+ state->histogram_edges[state->histogram_len-2]; i++) {
+ int bin = circpad_histogram_usec_to_bin(client_side->padding_info[0],
+ i);
+ circpad_delay_t usec =
+ circpad_histogram_bin_to_usec(client_side->padding_info[0], bin);
+ int bin2 = circpad_histogram_usec_to_bin(client_side->padding_info[0],
+ usec);
+ tt_int_op(bin, OP_EQ, bin2);
+ tt_int_op(i, OP_GE, usec);
+ }
+
+ /* 2.a. Normal higher bin */
+ {
+ tt_int_op(mi->histogram[2], OP_EQ, 2);
+ tt_int_op(mi->histogram[3], OP_EQ, 2);
+ circpad_machine_remove_higher_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1);
+ tt_int_op(mi->histogram[3], OP_EQ, 2);
+ tt_int_op(mi->histogram[2], OP_EQ, 1);
+
+ circpad_machine_remove_higher_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1);
+ tt_int_op(mi->histogram[2], OP_EQ, 0);
+
+ tt_int_op(mi->histogram[3], OP_EQ, 2);
+ circpad_machine_remove_higher_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1);
+ circpad_machine_remove_higher_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1);
+ tt_int_op(mi->histogram[3], OP_EQ, 0);
+ circpad_machine_remove_higher_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1);
+ tt_int_op(mi->histogram[3], OP_EQ, 0);
+ }
+
+ /* 2.b. Higher Infinity bin */
+ {
+ tt_int_op(mi->histogram[4], OP_EQ, inf_bin);
+ circpad_machine_remove_higher_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1);
+ tt_int_op(mi->histogram[4], OP_EQ, inf_bin);
+
+ /* Test past the infinity bin */
+ circpad_machine_remove_higher_token(mi,
+ circpad_histogram_bin_to_usec(mi, 5)+1000000);
+
+ tt_int_op(mi->histogram[4], OP_EQ, inf_bin);
+ }
+
+ /* 2.c. Bin 0 */
+ {
+ tt_int_op(mi->histogram[0], OP_EQ, 0);
+ mi->histogram[0] = 1;
+ circpad_machine_remove_higher_token(mi, state->histogram_edges[0]/2);
+ tt_int_op(mi->histogram[0], OP_EQ, 0);
+ }
+
+ /* Drain the infinity bin and cause a refill */
+ while (inf_bin != 0) {
+ tt_int_op(mi->histogram[4], OP_EQ, inf_bin);
+ circpad_cell_event_nonpadding_received(client_side);
+ inf_bin--;
+ }
+
+ circpad_cell_event_nonpadding_sent(client_side);
+
+ // We should have refilled here.
+ tt_int_op(mi->histogram[4], OP_EQ, 2);
+
+ /* 3.a. Bin 0 */
+ {
+ tt_int_op(mi->histogram[0], OP_EQ, 1);
+ circpad_machine_remove_higher_token(mi, state->histogram_edges[0]/2);
+ tt_int_op(mi->histogram[0], OP_EQ, 0);
+ }
+
+ /* 3.b. Test remove lower normal bin */
+ {
+ tt_int_op(mi->histogram[3], OP_EQ, 2);
+ circpad_machine_remove_lower_token(mi,
+ circpad_histogram_bin_to_usec(mi, 3)+1);
+ circpad_machine_remove_lower_token(mi,
+ circpad_histogram_bin_to_usec(mi, 3)+1);
+ tt_int_op(mi->histogram[3], OP_EQ, 0);
+ tt_int_op(mi->histogram[2], OP_EQ, 2);
+ circpad_machine_remove_lower_token(mi,
+ circpad_histogram_bin_to_usec(mi, 3)+1);
+ circpad_machine_remove_lower_token(mi,
+ circpad_histogram_bin_to_usec(mi, 3)+1);
+ /* 3.c. No lower */
+ circpad_machine_remove_lower_token(mi,
+ circpad_histogram_bin_to_usec(mi, 3)+1);
+ tt_int_op(mi->histogram[2], OP_EQ, 0);
+ }
+
+ /* 4. Test remove closest
+ * a. Closest lower
+ * b. Closest higher
+ * c. Closest 0
+ * d. Closest Infinity
+ */
+ circpad_machine_setup_tokens(mi);
+ tt_int_op(mi->histogram[2], OP_EQ, 2);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1, 0);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1, 0);
+ tt_int_op(mi->histogram[2], OP_EQ, 0);
+ tt_int_op(mi->histogram[3], OP_EQ, 2);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1, 0);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1, 0);
+ tt_int_op(mi->histogram[3], OP_EQ, 0);
+ tt_int_op(mi->histogram[0], OP_EQ, 1);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1, 0);
+ tt_int_op(mi->histogram[0], OP_EQ, 0);
+ tt_int_op(mi->histogram[4], OP_EQ, 2);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 2)+1, 0);
+ tt_int_op(mi->histogram[4], OP_EQ, 2);
+
+ /* 5. Test remove closest usec
+ * a. Closest 0
+ * b. Closest lower (below midpoint)
+ * c. Closest higher (above midpoint)
+ * d. Closest Infinity
+ */
+ circpad_machine_setup_tokens(mi);
+
+ tt_int_op(mi->histogram[0], OP_EQ, 1);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 0)/3, 1);
+ tt_int_op(mi->histogram[0], OP_EQ, 0);
+ tt_int_op(mi->histogram[2], OP_EQ, 2);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 0)/3, 1);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 0)/3, 1);
+ tt_int_op(mi->histogram[2], OP_EQ, 0);
+ tt_int_op(mi->histogram[3], OP_EQ, 2);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 4), 1);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 4), 1);
+ tt_int_op(mi->histogram[3], OP_EQ, 0);
+ tt_int_op(mi->histogram[4], OP_EQ, 2);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 4), 1);
+ circpad_machine_remove_closest_token(mi,
+ circpad_histogram_bin_to_usec(mi, 4), 1);
+ tt_int_op(mi->histogram[4], OP_EQ, 2);
+
+ // XXX: Need more coverage of the actual usec branches
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ monotime_disable_test_mocking();
+ tor_free(circ_client_machine.states);
+ testing_disable_reproducible_rng();
+}
+
+void
+test_circuitpadding_wronghop(void *arg)
+{
+ /**
+ * Test plan:
+ * 1. Padding sent from hop 1 and 3 to client
+ * 2. Send negotiated from hop 1 and 3 to client
+ * 3. Garbled negotiated cell
+ * 4. Padding negotiate sent to client
+ * 5. Send negotiate stop command for unknown machine
+ * 6. Send negotiated to relay
+ * 7. Garbled padding negotiate cell
+ */
+ (void)arg;
+ uint32_t read_bw = 0, overhead_bw = 0;
+ cell_t cell;
+ signed_error_t ret;
+ origin_circuit_t *orig_client;
+ int64_t actual_mocked_monotime_start;
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+
+ /* Mock this function so that our cell counting tests don't get confused by
+ * padding that gets sent by scheduled timers. */
+ MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
+
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
+ &dummy_channel));
+ orig_client = TO_ORIGIN_CIRCUIT(client_side);
+
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ nodes_init();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ timers_initialize();
+ circpad_machines_init();
+
+ MOCK(node_get_by_id,
+ node_get_by_id_mock);
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+
+ /* Build three hops */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* verify padding was negotiated */
+ tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL);
+ tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL);
+
+ /* verify echo was sent */
+ tt_int_op(n_relay_cells, OP_EQ, 1);
+ tt_int_op(n_client_cells, OP_EQ, 1);
+
+ read_bw = orig_client->n_delivered_read_circ_bw;
+ overhead_bw = orig_client->n_overhead_read_circ_bw;
+
+ /* 1. Test padding from first and third hop */
+ circpad_deliver_recognized_relay_cell_events(client_side,
+ RELAY_COMMAND_DROP,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath);
+ tt_int_op(read_bw, OP_EQ,
+ orig_client->n_delivered_read_circ_bw);
+ tt_int_op(overhead_bw, OP_EQ,
+ orig_client->n_overhead_read_circ_bw);
+
+ circpad_deliver_recognized_relay_cell_events(client_side,
+ RELAY_COMMAND_DROP,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath->next->next);
+ tt_int_op(read_bw, OP_EQ,
+ orig_client->n_delivered_read_circ_bw);
+ tt_int_op(overhead_bw, OP_EQ,
+ orig_client->n_overhead_read_circ_bw);
+
+ circpad_deliver_recognized_relay_cell_events(client_side,
+ RELAY_COMMAND_DROP,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath->next);
+ tt_int_op(read_bw, OP_EQ,
+ orig_client->n_delivered_read_circ_bw);
+ tt_int_op(overhead_bw, OP_LT,
+ orig_client->n_overhead_read_circ_bw);
+
+ /* 2. Test padding negotiated not handled from hops 1,3 */
+ ret = circpad_handle_padding_negotiated(client_side, &cell,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath);
+ tt_int_op(ret, OP_EQ, -1);
+
+ ret = circpad_handle_padding_negotiated(client_side, &cell,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath->next->next);
+ tt_int_op(ret, OP_EQ, -1);
+
+ /* 3. Garbled negotiated cell */
+ memset(&cell, 255, sizeof(cell));
+ ret = circpad_handle_padding_negotiated(client_side, &cell,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath->next);
+ tt_int_op(ret, OP_EQ, -1);
+
+ /* 4. Test that negotiate is dropped at origin */
+ read_bw = orig_client->n_delivered_read_circ_bw;
+ overhead_bw = orig_client->n_overhead_read_circ_bw;
+ relay_send_command_from_edge(0, relay_side,
+ RELAY_COMMAND_PADDING_NEGOTIATE,
+ (void*)cell.payload,
+ (size_t)3, NULL);
+ tt_int_op(read_bw, OP_EQ,
+ orig_client->n_delivered_read_circ_bw);
+ tt_int_op(overhead_bw, OP_EQ,
+ orig_client->n_overhead_read_circ_bw);
+
+ tt_int_op(n_relay_cells, OP_EQ, 2);
+ tt_int_op(n_client_cells, OP_EQ, 1);
+
+ /* 5. Test that asking to stop the wrong machine does nothing */
+ circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(client_side),
+ 255, 2, CIRCPAD_COMMAND_STOP);
+ tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL);
+ tt_ptr_op(client_side->padding_info[0], OP_NE, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL);
+ tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL);
+ tt_int_op(n_relay_cells, OP_EQ, 3);
+ tt_int_op(n_client_cells, OP_EQ, 2);
+
+ /* 6. Sending negotiated command to relay does nothing */
+ ret = circpad_handle_padding_negotiated(relay_side, &cell, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ /* 7. Test garbled negotated cell (bad command 255) */
+ memset(&cell, 0, sizeof(cell));
+ ret = circpad_handle_padding_negotiate(relay_side, &cell);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(n_client_cells, OP_EQ, 2);
+
+ /* Test 2: Test no padding */
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ free_fake_orcirc(relay_side);
+
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
+ &dummy_channel));
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 0);
+
+ /* verify no padding was negotiated */
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+ tt_int_op(n_relay_cells, OP_EQ, 3);
+ tt_int_op(n_client_cells, OP_EQ, 2);
+
+ /* verify no echo was sent */
+ tt_int_op(n_relay_cells, OP_EQ, 3);
+ tt_int_op(n_client_cells, OP_EQ, 2);
+
+ /* Finish circuit */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Spoof padding negotiated on circuit with no padding */
+ circpad_padding_negotiated(relay_side,
+ CIRCPAD_MACHINE_CIRC_SETUP,
+ CIRCPAD_COMMAND_START,
+ CIRCPAD_RESPONSE_OK);
+
+ /* verify no padding was negotiated */
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ circpad_padding_negotiated(relay_side,
+ CIRCPAD_MACHINE_CIRC_SETUP,
+ CIRCPAD_COMMAND_START,
+ CIRCPAD_RESPONSE_ERR);
+
+ /* verify no padding was negotiated */
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ free_fake_orcirc(relay_side);
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ monotime_disable_test_mocking();
+ UNMOCK(node_get_by_id);
+ UNMOCK(circuit_package_relay_cell);
+ UNMOCK(circuitmux_attach_circuit);
+ nodes_free();
+ testing_disable_reproducible_rng();
+}
+
+void
+test_circuitpadding_negotiation(void *arg)
+{
+ /**
+ * Test plan:
+ * 1. Test circuit where padding is supported by middle
+ * a. Make sure padding negotiation is sent
+ * b. Test padding negotiation delivery and parsing
+ * 2. Test circuit where padding is unsupported by middle
+ * a. Make sure padding negotiation is not sent
+ * 3. Test failure to negotiate a machine due to desync.
+ */
+ int64_t actual_mocked_monotime_start;
+ (void)arg;
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
+
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ nodes_init();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ timers_initialize();
+ circpad_machines_init();
+
+ MOCK(node_get_by_id,
+ node_get_by_id_mock);
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+
+ /* Build two hops */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* verify padding was negotiated */
+ tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL);
+ tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL);
+
+ /* verify echo was sent */
+ tt_int_op(n_relay_cells, OP_EQ, 1);
+ tt_int_op(n_client_cells, OP_EQ, 1);
+
+ /* Finish circuit */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Test 2: Test no padding */
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ free_fake_orcirc(relay_side);
+
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 0);
+
+ /* verify no padding was negotiated */
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+ tt_int_op(n_relay_cells, OP_EQ, 1);
+ tt_int_op(n_client_cells, OP_EQ, 1);
+
+ /* verify no echo was sent */
+ tt_int_op(n_relay_cells, OP_EQ, 1);
+ tt_int_op(n_client_cells, OP_EQ, 1);
+
+ /* Finish circuit */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Force negotiate padding. */
+ circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(client_side),
+ CIRCPAD_MACHINE_CIRC_SETUP,
+ 2, CIRCPAD_COMMAND_START);
+
+ /* verify no padding was negotiated */
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ /* verify no echo was sent */
+ tt_int_op(n_relay_cells, OP_EQ, 1);
+ tt_int_op(n_client_cells, OP_EQ, 1);
+
+ /* 3. Test failure to negotiate a machine due to desync */
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ free_fake_orcirc(relay_side);
+
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ SMARTLIST_FOREACH(relay_padding_machines,
+ circpad_machine_spec_t *,
+ m, tor_free(m->states); tor_free(m));
+ smartlist_free(relay_padding_machines);
+ relay_padding_machines = smartlist_new();
+
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* verify echo was sent */
+ tt_int_op(n_client_cells, OP_EQ, 2);
+ tt_int_op(n_relay_cells, OP_EQ, 2);
+
+ /* verify no padding was negotiated */
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ free_fake_orcirc(relay_side);
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ monotime_disable_test_mocking();
+ UNMOCK(node_get_by_id);
+ UNMOCK(circuit_package_relay_cell);
+ UNMOCK(circuitmux_attach_circuit);
+ nodes_free();
+}
+
+static void
+simulate_single_hop_extend(circuit_t *client, circuit_t *mid_relay,
+ int padding)
+{
+ char whatevs_key[CPATH_KEY_MATERIAL_LEN];
+ char digest[DIGEST_LEN];
+ tor_addr_t addr;
+
+ // Pretend a non-padding cell was sent
+ circpad_cell_event_nonpadding_sent(client);
+
+ // Receive extend cell at middle
+ circpad_cell_event_nonpadding_received(mid_relay);
+
+ // Advance time a tiny bit so we can calculate an RTT
+ curr_mocked_time += 10 * TOR_NSEC_PER_MSEC;
+ monotime_coarse_set_mock_time_nsec(curr_mocked_time);
+ monotime_set_mock_time_nsec(curr_mocked_time);
+
+ // Receive extended cell at middle
+ circpad_cell_event_nonpadding_sent(mid_relay);
+
+ // Receive extended cell at first hop
+ circpad_cell_event_nonpadding_received(client);
+
+ // Add a hop to cpath
+ crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t));
+ cpath_extend_linked_list(&TO_ORIGIN_CIRCUIT(client)->cpath, hop);
+
+ hop->magic = CRYPT_PATH_MAGIC;
+ hop->state = CPATH_STATE_OPEN;
+
+ // add an extend info to indicate if this node supports padding or not.
+ // (set the first byte of the digest for our mocked node_get_by_id)
+ digest[0] = padding;
+
+ hop->extend_info = extend_info_new(
+ padding ? "padding" : "non-padding",
+ digest, NULL, NULL, NULL,
+ &addr, padding);
+
+ cpath_init_circuit_crypto(hop, whatevs_key, sizeof(whatevs_key), 0, 0);
+
+ hop->package_window = circuit_initial_package_window();
+ hop->deliver_window = CIRCWINDOW_START;
+
+ // Signal that the hop was added
+ circpad_machine_event_circ_added_hop(TO_ORIGIN_CIRCUIT(client));
+}
+
+static circpad_machine_spec_t *
+helper_create_length_machine(void)
+{
+ circpad_machine_spec_t *ret =
+ tor_malloc_zero(sizeof(circpad_machine_spec_t));
+
+ /* Start, burst */
+ circpad_machine_states_init(ret, 2);
+
+ ret->states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST;
+
+ ret->states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST;
+
+ ret->states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END;
+
+ ret->states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_BINS_EMPTY] = CIRCPAD_STATE_END;
+
+ /* No token removal.. end via state_length only */
+ ret->states[CIRCPAD_STATE_BURST].token_removal =
+ CIRCPAD_TOKEN_REMOVAL_NONE;
+
+ /* Let's have this one end after 12 packets */
+ ret->states[CIRCPAD_STATE_BURST].length_dist.type = CIRCPAD_DIST_UNIFORM;
+ ret->states[CIRCPAD_STATE_BURST].length_dist.param1 = 12;
+ ret->states[CIRCPAD_STATE_BURST].length_dist.param2 = 13;
+ ret->states[CIRCPAD_STATE_BURST].max_length = 12;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_len = 4;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[0] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[1] = 1;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[2] = 1000000;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[3] = 10000000;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram[0] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram[1] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram[2] = 6;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_total_tokens = 6;
+ ret->states[CIRCPAD_STATE_BURST].use_rtt_estimate = 0;
+ ret->states[CIRCPAD_STATE_BURST].length_includes_nonpadding = 0;
+
+ return ret;
+}
+
+static circpad_machine_spec_t *
+helper_create_conditional_machine(void)
+{
+ circpad_machine_spec_t *ret =
+ tor_malloc_zero(sizeof(circpad_machine_spec_t));
+
+ /* Start, burst */
+ circpad_machine_states_init(ret, 2);
+
+ ret->states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST;
+
+ ret->states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_BURST;
+
+ ret->states[CIRCPAD_STATE_BURST].
+ next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END;
+
+ /* Use EXACT removal strategy, otherwise setup_tokens() does not work */
+ ret->states[CIRCPAD_STATE_BURST].token_removal =
+ CIRCPAD_TOKEN_REMOVAL_EXACT;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_len = 3;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[0] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[1] = 1;
+ ret->states[CIRCPAD_STATE_BURST].histogram_edges[2] = 1000000;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram[0] = 6;
+ ret->states[CIRCPAD_STATE_BURST].histogram[1] = 0;
+ ret->states[CIRCPAD_STATE_BURST].histogram[2] = 0;
+
+ ret->states[CIRCPAD_STATE_BURST].histogram_total_tokens = 6;
+ ret->states[CIRCPAD_STATE_BURST].use_rtt_estimate = 0;
+ ret->states[CIRCPAD_STATE_BURST].length_includes_nonpadding = 1;
+
+ return ret;
+}
+
+static void
+helper_create_conditional_machines(void)
+{
+ circpad_machine_spec_t *add = helper_create_conditional_machine();
+
+ if (!origin_padding_machines)
+ origin_padding_machines = smartlist_new();
+ if (!relay_padding_machines)
+ relay_padding_machines = smartlist_new();
+
+ add->machine_num = 2;
+ add->is_origin_side = 1;
+ add->should_negotiate_end = 1;
+ add->target_hopnum = 2;
+
+ /* Let's have this one end after 4 packets */
+ add->states[CIRCPAD_STATE_BURST].length_dist.type = CIRCPAD_DIST_UNIFORM;
+ add->states[CIRCPAD_STATE_BURST].length_dist.param1 = 4;
+ add->states[CIRCPAD_STATE_BURST].length_dist.param2 = 4;
+ add->states[CIRCPAD_STATE_BURST].max_length = 4;
+
+ add->conditions.requires_vanguards = 0;
+ add->conditions.min_hops = 2;
+ add->conditions.state_mask = CIRCPAD_CIRC_BUILDING|
+ CIRCPAD_CIRC_NO_STREAMS|CIRCPAD_CIRC_HAS_RELAY_EARLY;
+ add->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL;
+ circpad_register_padding_machine(add, origin_padding_machines);
+
+ add = helper_create_conditional_machine();
+ add->machine_num = 3;
+ add->is_origin_side = 1;
+ add->should_negotiate_end = 1;
+ add->target_hopnum = 2;
+
+ /* Let's have this one end after 4 packets */
+ add->states[CIRCPAD_STATE_BURST].length_dist.type = CIRCPAD_DIST_UNIFORM;
+ add->states[CIRCPAD_STATE_BURST].length_dist.param1 = 4;
+ add->states[CIRCPAD_STATE_BURST].length_dist.param2 = 4;
+ add->states[CIRCPAD_STATE_BURST].max_length = 4;
+
+ add->conditions.requires_vanguards = 1;
+ add->conditions.min_hops = 3;
+ add->conditions.state_mask = CIRCPAD_CIRC_OPENED|
+ CIRCPAD_CIRC_STREAMS|CIRCPAD_CIRC_HAS_NO_RELAY_EARLY;
+ add->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL;
+ circpad_register_padding_machine(add, origin_padding_machines);
+
+ add = helper_create_conditional_machine();
+ add->machine_num = 2;
+ circpad_register_padding_machine(add, relay_padding_machines);
+
+ add = helper_create_conditional_machine();
+ add->machine_num = 3;
+ circpad_register_padding_machine(add, relay_padding_machines);
+}
+
+void
+test_circuitpadding_state_length(void *arg)
+{
+ /**
+ * Test plan:
+ * * Explicitly test that with no token removal enabled, we hit
+ * the state length limit due to either padding, or non-padding.
+ * * Repeat test with an arbitrary token removal strategy, and
+ * verify that if we run out of tokens due to padding before we
+ * hit the state length, we still go to state end (all our
+ * token removal tests only test nonpadding token removal).
+ */
+ int64_t actual_mocked_monotime_start;
+ (void)arg;
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ MOCK(circpad_send_command_to_hop, circpad_send_command_to_hop_mock);
+
+ nodes_init();
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
+ &dummy_channel));
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
+ timers_initialize();
+ circpad_machine_spec_t *client_machine =
+ helper_create_length_machine();
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+ MOCK(node_get_by_id,
+ node_get_by_id_mock);
+
+ client_side->padding_machine[0] = client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+ circpad_machine_runtime_t *mi = client_side->padding_info[0];
+
+ circpad_cell_event_padding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, 12);
+ tt_ptr_op(mi->histogram, OP_EQ, NULL);
+
+ /* Verify that non-padding does not change our state length */
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, 12);
+
+ /* verify that sending padding changes our state length */
+ for (uint64_t i = mi->state_length-1; i > 0; i--) {
+ circpad_send_padding_cell_for_callback(mi);
+ tt_i64_op(mi->state_length, OP_EQ, i);
+ }
+ circpad_send_padding_cell_for_callback(mi);
+
+ tt_i64_op(mi->state_length, OP_EQ, -1);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ /* Restart machine */
+ mi->current_state = CIRCPAD_STATE_START;
+
+ /* Now, count nonpadding as part of the state length */
+ client_machine->states[CIRCPAD_STATE_BURST].length_includes_nonpadding = 1;
+
+ circpad_cell_event_padding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, 12);
+
+ /* Verify that non-padding does change our state length now */
+ for (uint64_t i = mi->state_length-1; i > 0; i--) {
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, i);
+ }
+
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, -1);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ /* Now, just test token removal when we send padding */
+ client_machine->states[CIRCPAD_STATE_BURST].token_removal =
+ CIRCPAD_TOKEN_REMOVAL_EXACT;
+
+ /* Restart machine */
+ mi->current_state = CIRCPAD_STATE_START;
+ circpad_cell_event_padding_sent(client_side);
+ tt_i64_op(mi->state_length, OP_EQ, 12);
+ tt_ptr_op(mi->histogram, OP_NE, NULL);
+ tt_int_op(mi->chosen_bin, OP_EQ, 2);
+
+ /* verify that sending padding changes our state length and
+ * our histogram now */
+ for (uint32_t i = mi->histogram[2]-1; i > 0; i--) {
+ circpad_send_padding_cell_for_callback(mi);
+ tt_int_op(mi->chosen_bin, OP_EQ, 2);
+ tt_int_op(mi->histogram[2], OP_EQ, i);
+ }
+
+ tt_i64_op(mi->state_length, OP_EQ, 7);
+ tt_int_op(mi->histogram[2], OP_EQ, 1);
+
+ circpad_send_padding_cell_for_callback(mi);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ done:
+ tor_free(client_machine->states);
+ tor_free(client_machine);
+
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ free_fake_orcirc(relay_side);
+
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ timers_shutdown();
+ monotime_disable_test_mocking();
+ UNMOCK(circuit_package_relay_cell);
+ UNMOCK(circuitmux_attach_circuit);
+ UNMOCK(node_get_by_id);
+
+ return;
+}
+
+void
+test_circuitpadding_conditions(void *arg)
+{
+ /**
+ * Test plan:
+ * 0. Make a few origin and client machines with diff conditions
+ * * vanguards, purposes, has_opened circs, no relay early
+ * * Client side should_negotiate_end
+ * * Length limits
+ * 1. Test STATE_END transitions
+ * 2. Test new machine after end with same conditions
+ * 3. Test new machine due to changed conditions
+ * * Esp: built event, no relay early, no streams
+ * XXX: Diff test:
+ * 1. Test STATE_END with pending timers
+ * 2. Test marking a circuit before padding callback fires
+ * 3. Test freeing a circuit before padding callback fires
+ */
+ int64_t actual_mocked_monotime_start;
+ (void)arg;
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ testing_enable_reproducible_rng();
+
+ nodes_init();
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel,
+ &dummy_channel));
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
+ timers_initialize();
+ helper_create_conditional_machines();
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+ MOCK(node_get_by_id,
+ node_get_by_id_mock);
+
+ /* Simulate extend. This should result in the original machine getting
+ * added, since the circuit is not built */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Verify that machine #2 is added */
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ /* Deliver a padding cell to the client, to trigger burst state */
+ circpad_cell_event_padding_sent(client_side);
+
+ /* This should have trigger length shutdown condition on client.. */
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ /* Verify machine is gone from both sides */
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ /* Send another event.. verify machine gets re-added properly
+ * (test race with shutdown) */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ TO_ORIGIN_CIRCUIT(client_side)->p_streams = 0;
+ circpad_machine_event_circ_has_no_streams(TO_ORIGIN_CIRCUIT(client_side));
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ /* Now make the circuit opened and send built event */
+ TO_ORIGIN_CIRCUIT(client_side)->has_opened = 1;
+ circpad_machine_event_circ_built(TO_ORIGIN_CIRCUIT(client_side));
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ TO_ORIGIN_CIRCUIT(client_side)->remaining_relay_early_cells = 0;
+ circpad_machine_event_circ_has_no_relay_early(
+ TO_ORIGIN_CIRCUIT(client_side));
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ get_options_mutable()->HSLayer2Nodes = (void*)1;
+ TO_ORIGIN_CIRCUIT(client_side)->p_streams = (void*)1;
+ circpad_machine_event_circ_has_streams(TO_ORIGIN_CIRCUIT(client_side));
+
+ /* Verify different machine is added */
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 3);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 3);
+
+ /* Hold off on negotiated */
+ deliver_negotiated = 0;
+
+ /* Deliver a padding cell to the client, to trigger burst state */
+ circpad_cell_event_padding_sent(client_side);
+
+ /* This should have trigger length shutdown condition on client
+ * but not the response for the padding machine */
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL);
+
+ /* Verify machine is gone from the relay (but negotiated not back yet */
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ /* Add another hop and verify it's back */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 3);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 3);
+
+ tt_ptr_op(client_side->padding_info[0], OP_NE, NULL);
+ tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL);
+
+ done:
+ /* XXX: Free everything */
+ testing_disable_reproducible_rng();
+ return;
+}
+
+/** Disabled unstable test until #29298 is implemented (see #29122) */
+#if 0
+void
+test_circuitpadding_circuitsetup_machine(void *arg)
+{
+ int64_t actual_mocked_monotime_start;
+ /**
+ * Test case plan:
+ *
+ * 1. Simulate a normal circuit setup pattern
+ * a. Application traffic
+ *
+ * FIXME: This should focus more on exercising the machine
+ * features rather than actual traffic patterns. For example,
+ * test cancellation and bins empty/refill
+ */
+ (void)arg;
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+
+ dummy_channel.cmux = circuitmux_alloc();
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
+
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ nodes_init();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+
+ timers_initialize();
+ circpad_machines_init();
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+ MOCK(node_get_by_id,
+ node_get_by_id_mock);
+
+ /* Test case #1: Build a 3 hop circuit, then wait and let pad */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ tt_int_op(n_client_cells, OP_EQ, 1);
+ tt_int_op(n_relay_cells, OP_EQ, 1);
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_int_op(relay_side->padding_info[0]->is_padding_timer_scheduled,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 2);
+ tt_int_op(n_relay_cells, OP_EQ, 1);
+
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_GAP);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 2);
+ tt_int_op(n_relay_cells, OP_EQ, 2);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 3);
+ tt_int_op(n_relay_cells, OP_EQ, 2);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 3);
+ tt_int_op(n_relay_cells, OP_EQ, 3);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 4);
+ tt_int_op(n_relay_cells, OP_EQ, 3);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 4);
+ tt_int_op(n_relay_cells, OP_EQ, 4);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 5);
+ tt_int_op(n_relay_cells, OP_EQ, 4);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 5);
+ tt_int_op(n_relay_cells, OP_EQ, 5);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ timers_advance_and_run(2000);
+ tt_int_op(n_client_cells, OP_EQ, 6);
+ tt_int_op(n_relay_cells, OP_EQ, 5);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ timers_advance_and_run(5000);
+ tt_int_op(n_client_cells, OP_EQ, 6);
+ tt_int_op(n_relay_cells, OP_EQ, 6);
+
+ tt_int_op(client_side->padding_info[0]->current_state,
+ OP_EQ, CIRCPAD_STATE_END);
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ tt_int_op(relay_side->padding_info[0]->current_state,
+ OP_EQ, CIRCPAD_STATE_GAP);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+
+ /* Verify we can't schedule padding in END state */
+ circpad_decision_t ret =
+ circpad_machine_schedule_padding(client_side->padding_info[0]);
+ tt_int_op(ret, OP_EQ, CIRCPAD_STATE_UNCHANGED);
+
+ /* Simulate application traffic */
+ circpad_cell_event_nonpadding_sent(client_side);
+ circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_OUT);
+ circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_IN);
+ circpad_deliver_recognized_relay_cell_events(client_side, RELAY_COMMAND_DATA,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath->next);
+
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+ tt_int_op(n_client_cells, OP_EQ, 6);
+ tt_int_op(n_relay_cells, OP_EQ, 7);
+
+ // Test timer cancellation
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ timers_advance_and_run(5000);
+ circpad_cell_event_padding_received(client_side);
+
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_GAP);
+
+ tt_int_op(n_client_cells, OP_EQ, 8);
+ tt_int_op(n_relay_cells, OP_EQ, 8);
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+
+ /* Test timer cancel due to state rules */
+ circpad_cell_event_nonpadding_sent(client_side);
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_EQ, 0);
+ circpad_cell_event_padding_received(client_side);
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+
+ /* Simulate application traffic to cancel timer */
+ circpad_cell_event_nonpadding_sent(client_side);
+ circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_OUT);
+ circpad_deliver_unrecognized_cell_events(relay_side, CELL_DIRECTION_IN);
+ circpad_deliver_recognized_relay_cell_events(client_side, RELAY_COMMAND_DATA,
+ TO_ORIGIN_CIRCUIT(client_side)->cpath->next);
+
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ /* No cells sent, except negotiate end from relay */
+ tt_int_op(n_client_cells, OP_EQ, 8);
+ tt_int_op(n_relay_cells, OP_EQ, 9);
+
+ /* Test mark for close and free */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ timers_advance_and_run(5000);
+ circpad_cell_event_padding_received(client_side);
+
+ tt_int_op(n_client_cells, OP_EQ, 10);
+ tt_int_op(n_relay_cells, OP_EQ, 10);
+
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_BURST);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_GAP);
+
+ tt_u64_op(client_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ tt_u64_op(relay_side->padding_info[0]->padding_scheduled_at_usec,
+ OP_NE, 0);
+ circuit_mark_for_close(client_side, END_CIRC_REASON_FLAG_REMOTE);
+ free_fake_orcirc(relay_side);
+ timers_advance_and_run(5000);
+
+ /* No cells sent */
+ tt_int_op(n_client_cells, OP_EQ, 10);
+ tt_int_op(n_relay_cells, OP_EQ, 10);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ timers_shutdown();
+ monotime_disable_test_mocking();
+ UNMOCK(circuit_package_relay_cell);
+ UNMOCK(circuitmux_attach_circuit);
+
+ return;
+}
+#endif /* 0 */
+
+/** Helper function: Initializes a padding machine where every state uses the
+ * uniform probability distribution. */
+static void
+helper_circpad_circ_distribution_machine_setup(int min, int max)
+{
+ circpad_machine_states_init(&circ_client_machine, 7);
+
+ circpad_state_t *zero_st = &circ_client_machine.states[0];
+ zero_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 1;
+ zero_st->iat_dist.type = CIRCPAD_DIST_UNIFORM;
+ /* param2 is upper bound, param1 is lower */
+ zero_st->iat_dist.param1 = min;
+ zero_st->iat_dist.param2 = max;
+ zero_st->dist_added_shift_usec = min;
+ zero_st->dist_max_sample_usec = max;
+
+ circpad_state_t *first_st = &circ_client_machine.states[1];
+ first_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 2;
+ first_st->iat_dist.type = CIRCPAD_DIST_LOGISTIC;
+ /* param1 is Mu, param2 is sigma. */
+ first_st->iat_dist.param1 = 9;
+ first_st->iat_dist.param2 = 3;
+ first_st->dist_added_shift_usec = min;
+ first_st->dist_max_sample_usec = max;
+
+ circpad_state_t *second_st = &circ_client_machine.states[2];
+ second_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 3;
+ second_st->iat_dist.type = CIRCPAD_DIST_LOG_LOGISTIC;
+ /* param1 is Alpha, param2 is 1.0/Beta */
+ second_st->iat_dist.param1 = 1;
+ second_st->iat_dist.param2 = 0.5;
+ second_st->dist_added_shift_usec = min;
+ second_st->dist_max_sample_usec = max;
+
+ circpad_state_t *third_st = &circ_client_machine.states[3];
+ third_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 4;
+ third_st->iat_dist.type = CIRCPAD_DIST_GEOMETRIC;
+ /* param1 is 'p' (success probability) */
+ third_st->iat_dist.param1 = 0.2;
+ third_st->dist_added_shift_usec = min;
+ third_st->dist_max_sample_usec = max;
+
+ circpad_state_t *fourth_st = &circ_client_machine.states[4];
+ fourth_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 5;
+ fourth_st->iat_dist.type = CIRCPAD_DIST_WEIBULL;
+ /* param1 is k, param2 is Lambda */
+ fourth_st->iat_dist.param1 = 1.5;
+ fourth_st->iat_dist.param2 = 1;
+ fourth_st->dist_added_shift_usec = min;
+ fourth_st->dist_max_sample_usec = max;
+
+ circpad_state_t *fifth_st = &circ_client_machine.states[5];
+ fifth_st->next_state[CIRCPAD_EVENT_NONPADDING_RECV] = 6;
+ fifth_st->iat_dist.type = CIRCPAD_DIST_PARETO;
+ /* param1 is sigma, param2 is xi */
+ fifth_st->iat_dist.param1 = 1;
+ fifth_st->iat_dist.param2 = 5;
+ fifth_st->dist_added_shift_usec = min;
+ fifth_st->dist_max_sample_usec = max;
+}
+
+/** Simple test that the padding delays sampled from a uniform distribution
+ * actually faill within the uniform distribution range. */
+static void
+test_circuitpadding_sample_distribution(void *arg)
+{
+ circpad_machine_runtime_t *mi;
+ int n_samples;
+ int n_states;
+
+ (void) arg;
+
+ /* mock this function so that we dont actually schedule any padding */
+ MOCK(circpad_machine_schedule_padding,
+ circpad_machine_schedule_padding_mock);
+ testing_enable_reproducible_rng();
+
+ /* Initialize a machine with multiple probability distributions */
+ circpad_machines_init();
+ helper_circpad_circ_distribution_machine_setup(0, 10);
+
+ /* Initialize machine and circuits */
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+ mi = client_side->padding_info[0];
+
+ /* For every state, sample a bunch of values from the distribution and ensure
+ * they fall within range. */
+ for (n_states = 0 ; n_states < 6; n_states++) {
+ /* Make sure we in the right state */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ, n_states);
+
+ for (n_samples = 0; n_samples < 100; n_samples++) {
+ circpad_delay_t delay = circpad_machine_sample_delay(mi);
+ tt_int_op(delay, OP_GE, 0);
+ tt_int_op(delay, OP_LE, 10);
+ }
+
+ /* send a non-padding cell to move to the next machine state */
+ circpad_cell_event_nonpadding_received(client_side);
+ }
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ UNMOCK(circpad_machine_schedule_padding);
+ testing_disable_reproducible_rng();
+}
+
+static circpad_decision_t
+circpad_machine_spec_transition_mock(circpad_machine_runtime_t *mi,
+ circpad_event_t event)
+{
+ (void) mi;
+ (void) event;
+
+ return CIRCPAD_STATE_UNCHANGED;
+}
+
+/* Test per-machine padding rate limits */
+static void
+test_circuitpadding_machine_rate_limiting(void *arg)
+{
+ (void) arg;
+ bool retval;
+ circpad_machine_runtime_t *mi;
+ int i;
+
+ /* Ignore machine transitions for the purposes of this function, we only
+ * really care about padding counts */
+ MOCK(circpad_machine_spec_transition, circpad_machine_spec_transition_mock);
+ MOCK(circpad_send_command_to_hop, circpad_send_command_to_hop_mock);
+ testing_enable_reproducible_rng();
+
+ /* Setup machine and circuits */
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ helper_create_basic_machine();
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+ mi = client_side->padding_info[0];
+ /* Set up the machine info so that we can get through the basic functions */
+ mi->state_length = CIRCPAD_STATE_LENGTH_INFINITE;
+
+ /* First we are going to test the per-machine rate limits */
+ circ_client_machine.max_padding_percent = 50;
+ circ_client_machine.allowed_padding_count = 100;
+
+ /* Check padding limit, should be fine since we haven't sent anything yet. */
+ retval = circpad_machine_reached_padding_limit(mi);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Send 99 padding cells which is below circpad_global_allowed_cells=100, so
+ * the rate limit will not trigger */
+ for (i=0;i<99;i++) {
+ circpad_send_padding_cell_for_callback(mi);
+ }
+ retval = circpad_machine_reached_padding_limit(mi);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Now send another padding cell to pass circpad_global_allowed_cells=100,
+ and see that the limit will trigger */
+ circpad_send_padding_cell_for_callback(mi);
+ retval = circpad_machine_reached_padding_limit(mi);
+ tt_int_op(retval, OP_EQ, 1);
+
+ retval = circpad_machine_schedule_padding(mi);
+ tt_int_op(retval, OP_EQ, CIRCPAD_STATE_UNCHANGED);
+
+ /* Cover wrap */
+ for (;i<UINT16_MAX;i++) {
+ circpad_send_padding_cell_for_callback(mi);
+ }
+ tt_int_op(mi->padding_sent, OP_EQ, UINT16_MAX/2+1);
+
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, mi);
+ for (i=0;i<UINT16_MAX;i++) {
+ circpad_cell_event_nonpadding_sent(client_side);
+ }
+
+ tt_int_op(mi->nonpadding_sent, OP_EQ, UINT16_MAX/2);
+ tt_int_op(mi->padding_sent, OP_EQ, UINT16_MAX/4+1);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ testing_disable_reproducible_rng();
+}
+
+/* Test global padding rate limits */
+static void
+test_circuitpadding_global_rate_limiting(void *arg)
+{
+ (void) arg;
+ bool retval;
+ circpad_machine_runtime_t *mi;
+ int i;
+ int64_t actual_mocked_monotime_start;
+
+ /* Ignore machine transitions for the purposes of this function, we only
+ * really care about padding counts */
+ MOCK(circpad_machine_spec_transition, circpad_machine_spec_transition_mock);
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+ MOCK(monotime_absolute_usec, mock_monotime_absolute_usec);
+ testing_enable_reproducible_rng();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+ timers_initialize();
+
+ client_side = TO_CIRCUIT(origin_circuit_new());
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ dummy_channel.cmux = circuitmux_alloc();
+
+ /* Setup machine and circuits */
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ helper_create_basic_machine();
+ relay_side->padding_machine[0] = &circ_client_machine;
+ relay_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(relay_side, 0);
+ mi = relay_side->padding_info[0];
+ /* Set up the machine info so that we can get through the basic functions */
+ mi->state_length = CIRCPAD_STATE_LENGTH_INFINITE;
+
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Now test the global limits by setting up the consensus */
+ networkstatus_t vote1;
+ vote1.net_params = smartlist_new();
+ smartlist_split_string(vote1.net_params,
+ "circpad_global_allowed_cells=100 circpad_global_max_padding_pct=50",
+ NULL, 0, 0);
+ /* Register global limits with the padding subsystem */
+ circpad_new_consensus_params(&vote1);
+
+ /* Check padding limit, should be fine since we haven't sent anything yet. */
+ retval = circpad_machine_reached_padding_limit(mi);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Send 99 padding cells which is below circpad_global_allowed_cells=100, so
+ * the rate limit will not trigger */
+ for (i=0;i<99;i++) {
+ circpad_send_padding_cell_for_callback(mi);
+ }
+ retval = circpad_machine_reached_padding_limit(mi);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Now send another padding cell to pass circpad_global_allowed_cells=100,
+ and see that the limit will trigger */
+ circpad_send_padding_cell_for_callback(mi);
+ retval = circpad_machine_reached_padding_limit(mi);
+ tt_int_op(retval, OP_EQ, 1);
+
+ retval = circpad_machine_schedule_padding(mi);
+ tt_int_op(retval, OP_EQ, CIRCPAD_STATE_UNCHANGED);
+
+ /* Now send 92 non-padding cells to get near the
+ * circpad_global_max_padding_pct=50 limit; in particular with 96 non-padding
+ * cells, the padding traffic is still 51% of total traffic so limit should
+ * trigger */
+ for (i=0;i<92;i++) {
+ circpad_cell_event_nonpadding_sent(relay_side);
+ }
+ retval = circpad_machine_reached_padding_limit(mi);
+ tt_int_op(retval, OP_EQ, 1);
+
+ /* Send another non-padding cell to bring the padding traffic to 50% of total
+ * traffic and get past the limit */
+ circpad_cell_event_nonpadding_sent(relay_side);
+ retval = circpad_machine_reached_padding_limit(mi);
+ tt_int_op(retval, OP_EQ, 0);
+
+ done:
+ free_fake_orcirc(relay_side);
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
+ smartlist_free(vote1.net_params);
+ testing_disable_reproducible_rng();
+}
+
+/* Test reduced and disabled padding */
+static void
+test_circuitpadding_reduce_disable(void *arg)
+{
+ (void) arg;
+ int64_t actual_mocked_monotime_start;
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ testing_enable_reproducible_rng();
+
+ nodes_init();
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = (circuit_t *)new_fake_orcirc(&dummy_channel,
+ &dummy_channel);
+ client_side = (circuit_t *)origin_circuit_new();
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ circpad_machines_init();
+ helper_create_conditional_machines();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ actual_mocked_monotime_start = MONOTIME_MOCK_START;
+ monotime_set_mock_time_nsec(actual_mocked_monotime_start);
+ monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start);
+ curr_mocked_time = actual_mocked_monotime_start;
+ timers_initialize();
+
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
+ MOCK(circuit_package_relay_cell,
+ circuit_package_relay_cell_mock);
+ MOCK(node_get_by_id,
+ node_get_by_id_mock);
+
+ /* Simulate extend. This should result in the original machine getting
+ * added, since the circuit is not built */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Verify that machine #2 is added */
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ /* Deliver a padding cell to the client, to trigger burst state */
+ circpad_cell_event_padding_sent(client_side);
+
+ /* This should have trigger length shutdown condition on client.. */
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+
+ /* Verify machine is gone from both sides */
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ /* Now test the reduced padding machine by setting up the consensus */
+ networkstatus_t vote1;
+ vote1.net_params = smartlist_new();
+ smartlist_split_string(vote1.net_params,
+ "circpad_padding_reduced=1", NULL, 0, 0);
+
+ /* Register reduced padding machine with the padding subsystem */
+ circpad_new_consensus_params(&vote1);
+
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Verify that machine #0 is added */
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ tt_int_op(
+ circpad_machine_reached_padding_limit(client_side->padding_info[0]),
+ OP_EQ, 0);
+ tt_int_op(
+ circpad_machine_reached_padding_limit(relay_side->padding_info[0]),
+ OP_EQ, 0);
+
+ /* Test that machines get torn down when padding is disabled */
+ SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
+ smartlist_free(vote1.net_params);
+ vote1.net_params = smartlist_new();
+ smartlist_split_string(vote1.net_params,
+ "circpad_padding_disabled=1", NULL, 0, 0);
+
+ /* Register reduced padding machine with the padding subsystem */
+ circpad_new_consensus_params(&vote1);
+
+ tt_int_op(
+ circpad_machine_schedule_padding(client_side->padding_info[0]),
+ OP_EQ, CIRCPAD_STATE_UNCHANGED);
+ tt_int_op(
+ circpad_machine_schedule_padding(relay_side->padding_info[0]),
+ OP_EQ, CIRCPAD_STATE_UNCHANGED);
+
+ /* Signal that circuit is built: this event causes us to re-evaluate
+ * machine conditions (which don't apply because padding is disabled). */
+ circpad_machine_event_circ_built(TO_ORIGIN_CIRCUIT(client_side));
+
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
+ smartlist_free(vote1.net_params);
+ vote1.net_params = NULL;
+ circpad_new_consensus_params(&vote1);
+
+ get_options_mutable()->ReducedCircuitPadding = 1;
+
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* Verify that machine #0 is added */
+ tt_int_op(client_side->padding_machine[0]->machine_num, OP_EQ, 2);
+ tt_int_op(relay_side->padding_machine[0]->machine_num, OP_EQ, 2);
+
+ tt_int_op(
+ circpad_machine_reached_padding_limit(client_side->padding_info[0]),
+ OP_EQ, 0);
+ tt_int_op(
+ circpad_machine_reached_padding_limit(relay_side->padding_info[0]),
+ OP_EQ, 0);
+
+ get_options_mutable()->CircuitPadding = 0;
+
+ tt_int_op(
+ circpad_machine_schedule_padding(client_side->padding_info[0]),
+ OP_EQ, CIRCPAD_STATE_UNCHANGED);
+ tt_int_op(
+ circpad_machine_schedule_padding(relay_side->padding_info[0]),
+ OP_EQ, CIRCPAD_STATE_UNCHANGED);
+
+ /* Signal that circuit is built: this event causes us to re-evaluate
+ * machine conditions (which don't apply because padding is disabled). */
+
+ circpad_machine_event_circ_built(TO_ORIGIN_CIRCUIT(client_side));
+
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL);
+
+ done:
+ free_fake_orcirc(relay_side);
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ testing_disable_reproducible_rng();
+}
+
+/** Just a basic machine whose whole purpose is to reach the END state */
+static void
+helper_create_ender_machine(void)
+{
+ /* Start, burst */
+ circpad_machine_states_init(&circ_client_machine, 2);
+
+ circ_client_machine.states[CIRCPAD_STATE_START].
+ next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_END;
+
+ circ_client_machine.conditions.state_mask = CIRCPAD_STATE_ALL;
+ circ_client_machine.conditions.purpose_mask = CIRCPAD_PURPOSE_ALL;
+}
+
+static time_t mocked_timeofday;
+/** Set timeval to a mock date and time. This is necessary
+ * to make tor_gettimeofday() mockable. */
+static void
+mock_tor_gettimeofday(struct timeval *timeval)
+{
+ timeval->tv_sec = mocked_timeofday;
+ timeval->tv_usec = 0;
+}
+
+/** Test manual managing of circuit lifetimes by the circuitpadding
+ * subsystem. In particular this test goes through all the cases of the
+ * circpad_marked_circuit_for_padding() function, via
+ * circuit_mark_for_close() as well as
+ * circuit_expire_old_circuits_clientside(). */
+static void
+test_circuitpadding_manage_circuit_lifetime(void *arg)
+{
+ circpad_machine_runtime_t *mi;
+
+ (void) arg;
+
+ client_side = (circuit_t *)origin_circuit_new();
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ monotime_enable_test_mocking();
+ MOCK(tor_gettimeofday, mock_tor_gettimeofday);
+ mocked_timeofday = 23;
+
+ helper_create_ender_machine();
+
+ /* Enable manual circuit lifetime manage for this test */
+ circ_client_machine.manage_circ_lifetime = 1;
+
+ /* Test setup */
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+ mi = client_side->padding_info[0];
+
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_START);
+
+ /* Check that the circuit is not marked for close */
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL);
+
+ /* Mark this circuit for close due to a remote reason */
+ circuit_mark_for_close(client_side,
+ END_CIRC_REASON_FLAG_REMOTE|END_CIRC_REASON_NONE);
+ tt_ptr_op(client_side->padding_info[0], OP_NE, NULL);
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL);
+ client_side->marked_for_close = 0;
+
+ /* Mark this circuit for close due to a protocol issue */
+ circuit_mark_for_close(client_side, END_CIRC_REASON_TORPROTOCOL);
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL);
+ client_side->marked_for_close = 0;
+
+ /* Mark a measurement circuit for close */
+ client_side->purpose = CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT;
+ circuit_mark_for_close(client_side, END_CIRC_REASON_NONE);
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT);
+ client_side->marked_for_close = 0;
+
+ /* Mark a general circuit for close */
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ circuit_mark_for_close(client_side, END_CIRC_REASON_NONE);
+
+ /* Check that this circuit is still not marked for close since we are
+ * managing the lifetime manually, but the circuit was tagged as such by the
+ * circpadding subsystem */
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_CIRCUIT_PADDING);
+
+ /* We just tested case (1) from the comments of
+ * circpad_circuit_should_be_marked_for_close() */
+
+ /* Transition the machine to the END state but did not delete its machine */
+ tt_ptr_op(client_side->padding_info[0], OP_NE, NULL);
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ /* We just tested case (3) from the comments of
+ * circpad_circuit_should_be_marked_for_close().
+ * Now let's go for case (2). */
+
+ /* Reset the close mark */
+ client_side->marked_for_close = 0;
+
+ /* Mark this circuit for close */
+ circuit_mark_for_close(client_side, 0);
+
+ /* See that the circ got closed since we are already in END state */
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+
+ /* We just tested case (2). Now let's see that case (4) is unreachable as
+ that comment claims */
+
+ /* First, reset all close marks and tags */
+ client_side->marked_for_close = 0;
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ /* Now re-create the ender machine so that we can transition to END again */
+ /* Free up some stuff first */
+ circpad_circuit_free_all_machineinfos(client_side);
+ tor_free(circ_client_machine.states);
+ helper_create_ender_machine();
+
+ client_side->padding_machine[0] = &circ_client_machine;
+ client_side->padding_info[0] =
+ circpad_circuit_machineinfo_new(client_side, 0);
+ mi = client_side->padding_info[0];
+
+ /* Check we are in START. */
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_START);
+
+ /* Test that we don't expire this circuit yet */
+ client_side->timestamp_dirty = 0;
+ client_side->state = CIRCUIT_STATE_OPEN;
+ tor_gettimeofday(&client_side->timestamp_began);
+ TO_ORIGIN_CIRCUIT(client_side)->circuit_idle_timeout = 23;
+ mocked_timeofday += 24;
+ circuit_expire_old_circuits_clientside();
+ circuit_expire_old_circuits_clientside();
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->timestamp_dirty, OP_NE, 0);
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+ tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_C_CIRCUIT_PADDING);
+
+ /* Runaway circpad test: if the machine does not transition to end,
+ * test that after CIRCPAD_DELAY_MAX_SECS, we get marked anyway */
+ mocked_timeofday = client_side->timestamp_dirty
+ + get_options()->MaxCircuitDirtiness + 2;
+ client_side->padding_info[0]->last_cell_time_sec =
+ approx_time()-(CIRCPAD_DELAY_MAX_SECS+10);
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+
+ /* Test back to normal: if we had activity, we won't close */
+ client_side->padding_info[0]->last_cell_time_sec = approx_time();
+ client_side->marked_for_close = 0;
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+
+ /* Transition to END, but before we're past the dirty timer */
+ mocked_timeofday = client_side->timestamp_dirty;
+ circpad_cell_event_nonpadding_received(client_side);
+ tt_int_op(mi->current_state, OP_EQ, CIRCPAD_STATE_END);
+
+ /* Verify that the circuit was not closed. */
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+
+ /* Now that we are in END state, we can be closed by expiry, but via
+ * the timestamp_dirty path, not the idle path. So first test not dirty
+ * enough. */
+ mocked_timeofday = client_side->timestamp_dirty;
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->marked_for_close, OP_EQ, 0);
+ mocked_timeofday = client_side->timestamp_dirty
+ + get_options()->MaxCircuitDirtiness + 2;
+ circuit_expire_old_circuits_clientside();
+ tt_int_op(client_side->marked_for_close, OP_NE, 0);
+
+ done:
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+ tor_free(circ_client_machine.states);
+ monotime_disable_test_mocking();
+ UNMOCK(tor_gettimeofday);
+}
+
+/** Helper for the test_circuitpadding_hs_machines test:
+ *
+ * - Create a client and relay circuit.
+ * - Setup right circuit purpose and attach a machine to the client circuit.
+ * - Verify that state transitions work as intended and state length gets
+ * enforced.
+ *
+ * This function is able to do this test both for intro and rend circuits
+ * depending on the value of <b>test_intro_circs</b>.
+ */
+static void
+helper_test_hs_machines(bool test_intro_circs)
+{
+ /* Setup the circuits */
+ origin_circuit_t *origin_client_side = origin_circuit_new();
+ client_side = TO_CIRCUIT(origin_client_side);
+ client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ dummy_channel.cmux = circuitmux_alloc();
+ relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
+ relay_side->purpose = CIRCUIT_PURPOSE_OR;
+
+ /* extend the client circ to two hops */
+ simulate_single_hop_extend(client_side, relay_side, 1);
+ simulate_single_hop_extend(client_side, relay_side, 1);
+
+ /* machines only apply on opened circuits */
+ origin_client_side->has_opened = 1;
+
+ /************************************/
+
+ /* Attaching the client machine now won't work here because of a wrong
+ * purpose */
+ tt_assert(!client_side->padding_machine[0]);
+ circpad_add_matching_machines(origin_client_side, origin_padding_machines);
+ tt_assert(!client_side->padding_machine[0]);
+
+ /* Change the purpose, see the machine getting attached */
+ client_side->purpose = test_intro_circs ?
+ CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT : CIRCUIT_PURPOSE_C_REND_JOINED;
+ circpad_add_matching_machines(origin_client_side, origin_padding_machines);
+ tt_ptr_op(client_side->padding_info[0], OP_NE, NULL);
+ tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL);
+
+ tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL);
+ tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL);
+
+ /* Verify that the right machine is attached */
+ tt_str_op(client_side->padding_machine[0]->name, OP_EQ,
+ test_intro_circs ? "client_ip_circ" : "client_rp_circ");
+ tt_str_op(relay_side->padding_machine[0]->name, OP_EQ,
+ test_intro_circs ? "relay_ip_circ": "relay_rp_circ");
+
+ /***********************************/
+
+ /* Intro machines are at START state, but rend machines have already skipped
+ * to OBFUSCATE_CIRC_SETUP because of the sent PADDING_NEGOTIATE. */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
+
+ /*Send non-padding to move the machines from START to OBFUSCATE_CIRC_SETUP */
+ circpad_cell_event_nonpadding_received(client_side);
+ circpad_cell_event_nonpadding_received(relay_side);
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
+ tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
+
+ /* Check that the state lengths have been sampled and are within range */
+ circpad_machine_runtime_t *client_machine_runtime =
+ client_side->padding_info[0];
+ circpad_machine_runtime_t *relay_machine_runtime =
+ relay_side->padding_info[0];
+
+ if (test_intro_circs) {
+ /* on the client side, we don't send any padding so
+ * state length is not set */
+ tt_i64_op(client_machine_runtime->state_length, OP_EQ, -1);
+ /* relay side has state limits. check them */
+ tt_i64_op(relay_machine_runtime->state_length, OP_GE,
+ INTRO_MACHINE_MINIMUM_PADDING);
+ tt_i64_op(relay_machine_runtime->state_length, OP_LT,
+ INTRO_MACHINE_MAXIMUM_PADDING);
+ } else {
+ tt_i64_op(client_machine_runtime->state_length, OP_EQ, 1);
+ tt_i64_op(relay_machine_runtime->state_length, OP_EQ, 1);
+ }
+
+ if (test_intro_circs) {
+ int i;
+ /* Send state_length worth of padding from the relay and see that the
+ * client state goes to END */
+ for (i = (int) relay_machine_runtime->state_length ; i > 0 ; i--) {
+ circpad_send_padding_cell_for_callback(relay_machine_runtime);
+ }
+ /* See that the machine has been teared down after all the length has been
+ * exhausted (the padding info should now be null on both sides) */
+ tt_ptr_op(relay_side->padding_info[0], OP_EQ, NULL);
+ tt_ptr_op(client_side->padding_info[0], OP_EQ, NULL);
+ } else {
+ int i;
+ /* Send state_length worth of padding and see that the state goes to END */
+ for (i = (int) client_machine_runtime->state_length ; i > 0 ; i--) {
+ circpad_send_padding_cell_for_callback(client_machine_runtime);
+ }
+ /* See that the machine has been teared down after all the length has been
+ * exhausted. */
+ tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
+ CIRCPAD_STATE_END);
+ }
+
+ done:
+ free_fake_orcirc(relay_side);
+ circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
+ circuitmux_free(dummy_channel.cmux);
+ free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
+}
+
+/** Test that the HS circuit padding machines work as intended. */
+static void
+test_circuitpadding_hs_machines(void *arg)
+{
+ (void)arg;
+
+ /* Test logic:
+ *
+ * 1) Register the HS machines, which aim to hide the presense of
+ * onion service traffic on the client-side
+ *
+ * 2) Call helper_test_hs_machines() to perform tests for the intro circuit
+ * machines and for the rend circuit machines.
+ */
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
+ MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock);
+ MOCK(circuit_get_nth_node, circuit_get_nth_node_mock);
+ MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
+
+ origin_padding_machines = smartlist_new();
+ relay_padding_machines = smartlist_new();
+
+ nodes_init();
+
+ monotime_init();
+ monotime_enable_test_mocking();
+ monotime_set_mock_time_nsec(1*TOR_NSEC_PER_USEC);
+ monotime_coarse_set_mock_time_nsec(1*TOR_NSEC_PER_USEC);
+ curr_mocked_time = 1*TOR_NSEC_PER_USEC;
+
+ timers_initialize();
+
+ /* This is needed so that we are not considered to be dormant */
+ note_user_activity(20);
+
+ /************************************/
+
+ /* Register the HS machines */
+ circpad_machine_client_hide_intro_circuits(origin_padding_machines);
+ circpad_machine_client_hide_rend_circuits(origin_padding_machines);
+ circpad_machine_relay_hide_intro_circuits(relay_padding_machines);
+ circpad_machine_relay_hide_rend_circuits(relay_padding_machines);
+
+ /***********************************/
+
+ /* Do the tests for the intro circuit machines */
+ helper_test_hs_machines(true);
+ /* Do the tests for the rend circuit machines */
+ helper_test_hs_machines(false);
+
+ timers_shutdown();
+ monotime_disable_test_mocking();
+
+ SMARTLIST_FOREACH_BEGIN(origin_padding_machines,
+ circpad_machine_spec_t *, m) {
+ machine_spec_free(m);
+ } SMARTLIST_FOREACH_END(m);
+
+ SMARTLIST_FOREACH_BEGIN(relay_padding_machines,
+ circpad_machine_spec_t *, m) {
+ machine_spec_free(m);
+ } SMARTLIST_FOREACH_END(m);
+
+ smartlist_free(origin_padding_machines);
+ smartlist_free(relay_padding_machines);
+
+ UNMOCK(circuitmux_attach_circuit);
+ UNMOCK(circuit_package_relay_cell);
+ UNMOCK(circuit_get_nth_node);
+ UNMOCK(circpad_machine_schedule_padding);
+}
+
+/** Test that we effectively ignore non-padding cells in padding circuits. */
+static void
+test_circuitpadding_ignore_non_padding_cells(void *arg)
+{
+ int retval;
+ relay_header_t rh;
+
+ (void) arg;
+
+ client_side = (circuit_t *)origin_circuit_new();
+ client_side->purpose = CIRCUIT_PURPOSE_C_CIRCUIT_PADDING;
+
+ rh.command = RELAY_COMMAND_BEGIN;
+
+ setup_full_capture_of_logs(LOG_INFO);
+ retval = handle_relay_cell_command(NULL, client_side, NULL, NULL, &rh, 0);
+ tt_int_op(retval, OP_EQ, 0);
+ expect_log_msg_containing("Ignored cell");
+
+ done:
+ ;
+}
+
+#define TEST_CIRCUITPADDING(name, flags) \
+ { #name, test_##name, (flags), NULL, NULL }
+
+struct testcase_t circuitpadding_tests[] = {
+ TEST_CIRCUITPADDING(circuitpadding_tokens, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_state_length, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_negotiation, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_wronghop, TT_FORK),
+ /** Disabled unstable test until #29298 is implemented (see #29122) */
+ // TEST_CIRCUITPADDING(circuitpadding_circuitsetup_machine, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_conditions, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_rtt, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_sample_distribution, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_machine_rate_limiting, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_global_rate_limiting, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_reduce_disable, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_token_removal_lower, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_token_removal_higher, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_closest_token_removal, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_closest_token_removal_usec, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_token_removal_exact, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_manage_circuit_lifetime, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_hs_machines, TT_FORK),
+ TEST_CIRCUITPADDING(circuitpadding_ignore_non_padding_cells, TT_FORK),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c
index 1cbcb14f2b..9bfaabeb2f 100644
--- a/src/test/test_circuitstats.c
+++ b/src/test/test_circuitstats.c
@@ -28,7 +28,7 @@ origin_circuit_t *subtest_fourhop_circuit(struct timeval, int);
origin_circuit_t *add_opened_threehop(void);
origin_circuit_t *build_unopened_fourhop(struct timeval);
-int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
+int cpath_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
static int marked_for_close;
/* Mock function because we are not trying to test the close circuit that does
@@ -57,9 +57,9 @@ add_opened_threehop(void)
or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
or_circ->build_state->desired_path_len = DEFAULT_ROUTE_LEN;
- onion_append_hop(&or_circ->cpath, &fakehop);
- onion_append_hop(&or_circ->cpath, &fakehop);
- onion_append_hop(&or_circ->cpath, &fakehop);
+ cpath_append_hop(&or_circ->cpath, &fakehop);
+ cpath_append_hop(&or_circ->cpath, &fakehop);
+ cpath_append_hop(&or_circ->cpath, &fakehop);
or_circ->has_opened = 1;
TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
@@ -82,10 +82,10 @@ build_unopened_fourhop(struct timeval circ_start_time)
or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
or_circ->build_state->desired_path_len = 4;
- onion_append_hop(&or_circ->cpath, fakehop);
- onion_append_hop(&or_circ->cpath, fakehop);
- onion_append_hop(&or_circ->cpath, fakehop);
- onion_append_hop(&or_circ->cpath, fakehop);
+ cpath_append_hop(&or_circ->cpath, fakehop);
+ cpath_append_hop(&or_circ->cpath, fakehop);
+ cpath_append_hop(&or_circ->cpath, fakehop);
+ cpath_append_hop(&or_circ->cpath, fakehop);
tor_free(fakehop);
@@ -197,7 +197,7 @@ test_circuitstats_hoplen(void *arg)
}
#define TEST_CIRCUITSTATS(name, flags) \
- { #name, test_##name, (flags), NULL, NULL }
+ { #name, test_##name, (flags), &helper_pubsub_setup, NULL }
struct testcase_t circuitstats_tests[] = {
TEST_CIRCUITSTATS(circuitstats_hoplen, TT_FORK),
diff --git a/src/test/test_cmdline.sh b/src/test/test_cmdline.sh
new file mode 100755
index 0000000000..cf758c3851
--- /dev/null
+++ b/src/test/test_cmdline.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+umask 077
+set -e
+
+if [ $# -ge 1 ]; then
+ TOR_BINARY="${1}"
+ shift
+else
+ TOR_BINARY="${TESTING_TOR_BINARY:-./src/app/tor}"
+fi
+
+echo "TOR BINARY IS ${TOR_BINARY}"
+
+die() { echo "$1" >&2 ; exit 5; }
+
+echo "A"
+
+DATA_DIR=$(mktemp -d -t tor_cmdline_tests.XXXXXX)
+trap 'rm -rf "$DATA_DIR"' 0
+
+# 1. Test list-torrc-options.
+OUT="${DATA_DIR}/output"
+
+echo "B"
+"${TOR_BINARY}" --list-torrc-options > "$OUT"
+
+echo "C"
+
+# regular options are given.
+grep -i "SocksPort" "$OUT" >/dev/null || die "Did not find SocksPort"
+
+
+echo "D"
+
+# unlisted options are given, since they do not have the NOSET flag.
+grep -i "__SocksPort" "$OUT" > /dev/null || die "Did not find __SocksPort"
+
+echo "E"
+
+# unsettable options are not given.
+if grep -i "DisableIOCP" "$OUT" /dev/null; then
+ die "Found DisableIOCP"
+fi
+if grep -i "HiddenServiceOptions" "$OUT" /dev/null ; then
+ die "Found HiddenServiceOptions"
+fi
+echo "OK"
diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c
index 2f8646e897..ecd97e3474 100644
--- a/src/test/test_compat_libevent.c
+++ b/src/test/test_compat_libevent.c
@@ -151,8 +151,6 @@ test_compat_libevent_postloop_events(void *arg)
mainloop_event_t *a = NULL, *b = NULL;
periodic_timer_t *timed = NULL;
- tor_libevent_postfork();
-
/* If postloop events don't work, then these events will activate one
* another ad infinitum and, and the periodic event will never occur. */
b = mainloop_event_postloop_new(activate_event_cb, &a);
@@ -187,4 +185,3 @@ struct testcase_t compat_libevent_tests[] = {
TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
-
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 855725411a..ebc0624fb2 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -16,7 +16,7 @@
#include "core/or/circuitmux_ewma.h"
#include "core/or/circuitbuild.h"
#include "app/config/config.h"
-#include "app/config/confparse.h"
+#include "lib/confmgt/confparse.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
#include "test/test.h"
@@ -45,6 +45,7 @@
#include "app/config/statefile.h"
#include "test/test_helpers.h"
+#include "test/resolve_test_helpers.h"
#include "feature/dirclient/dir_server_st.h"
#include "core/or/port_cfg_st.h"
@@ -54,6 +55,7 @@
#include "lib/meminfo/meminfo.h"
#include "lib/net/gethostname.h"
#include "lib/encoding/confline.h"
+#include "lib/encoding/kvline.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -1754,6 +1756,18 @@ add_default_fallback_dir_servers_known_default(void)
n_add_default_fallback_dir_servers_known_default++;
}
+/* Helper for test_config_adding_dir_servers(), which should be
+ * refactored: clear the fields in the options which the options object
+ * does not really own. */
+static void
+ads_clear_helper(or_options_t *options)
+{
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = NULL;
+}
+
/* Test all the different combinations of adding dir servers */
static void
test_config_adding_dir_servers(void *arg)
@@ -1761,7 +1775,7 @@ test_config_adding_dir_servers(void *arg)
(void)arg;
/* allocate options */
- or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
+ or_options_t *options = options_new();
/* Allocate and populate configuration lines:
*
@@ -1884,7 +1898,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -1966,7 +1982,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2107,7 +2125,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2248,7 +2268,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2390,7 +2412,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2542,7 +2566,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2696,7 +2722,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2859,7 +2887,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -3016,7 +3046,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -3182,7 +3214,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -3345,7 +3379,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -3514,10 +3550,7 @@ test_config_adding_dir_servers(void *arg)
tor_free(test_fallback_directory->value);
tor_free(test_fallback_directory);
- options->DirAuthorities = NULL;
- options->AlternateBridgeAuthority = NULL;
- options->AlternateDirAuthority = NULL;
- options->FallbackDir = NULL;
+ ads_clear_helper(options);
or_options_free(options);
UNMOCK(add_default_fallback_dir_servers);
@@ -3532,7 +3565,7 @@ test_config_default_dir_servers(void *arg)
int fallback_count = 0;
/* new set of options should stop fallback parsing */
- opts = tor_malloc_zero(sizeof(or_options_t));
+ opts = options_new();
opts->UseDefaultFallbackDirs = 0;
/* set old_options to NULL to force dir update */
consider_adding_dir_servers(opts, NULL);
@@ -3546,7 +3579,7 @@ test_config_default_dir_servers(void *arg)
/* if we disable the default fallbacks, there must not be any extra */
tt_assert(fallback_count == trusted_count);
- opts = tor_malloc_zero(sizeof(or_options_t));
+ opts = options_new();
opts->UseDefaultFallbackDirs = 1;
consider_adding_dir_servers(opts, opts);
trusted_count = smartlist_len(router_get_trusted_dir_servers());
@@ -3606,7 +3639,7 @@ test_config_directory_fetch(void *arg)
(void)arg;
/* Test Setup */
- or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
+ or_options_t *options = options_new();
routerinfo_t routerinfo;
memset(&routerinfo, 0, sizeof(routerinfo));
mock_router_pick_published_address_result = -1;
@@ -3618,9 +3651,10 @@ test_config_directory_fetch(void *arg)
mock_router_my_exit_policy_is_reject_star);
MOCK(advertised_server_mode, mock_advertised_server_mode);
MOCK(router_get_my_routerinfo, mock_router_get_my_routerinfo);
+ or_options_free(options);
+ options = options_new();
/* Clients can use multiple directory mirrors for bootstrap */
- memset(options, 0, sizeof(or_options_t));
options->ClientOnly = 1;
tt_assert(server_mode(options) == 0);
tt_assert(public_server_mode(options) == 0);
@@ -3629,7 +3663,8 @@ test_config_directory_fetch(void *arg)
OP_EQ, 1);
/* Bridge Clients can use multiple directory mirrors for bootstrap */
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->UseBridges = 1;
tt_assert(server_mode(options) == 0);
tt_assert(public_server_mode(options) == 0);
@@ -3639,7 +3674,8 @@ test_config_directory_fetch(void *arg)
/* Bridge Relays (Bridges) must act like clients, and use multiple
* directory mirrors for bootstrap */
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->BridgeRelay = 1;
options->ORPort_set = 1;
tt_assert(server_mode(options) == 1);
@@ -3650,7 +3686,8 @@ test_config_directory_fetch(void *arg)
/* Clients set to FetchDirInfoEarly must fetch it from the authorities,
* but can use multiple authorities for bootstrap */
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->FetchDirInfoEarly = 1;
tt_assert(server_mode(options) == 0);
tt_assert(public_server_mode(options) == 0);
@@ -3661,7 +3698,8 @@ test_config_directory_fetch(void *arg)
/* OR servers only fetch the consensus from the authorities when they don't
* know their own address, but never use multiple directories for bootstrap
*/
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->ORPort_set = 1;
mock_router_pick_published_address_result = -1;
@@ -3681,7 +3719,8 @@ test_config_directory_fetch(void *arg)
/* Exit OR servers only fetch the consensus from the authorities when they
* refuse unknown exits, but never use multiple directories for bootstrap
*/
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->ORPort_set = 1;
options->ExitRelay = 1;
mock_router_pick_published_address_result = 0;
@@ -3711,7 +3750,8 @@ test_config_directory_fetch(void *arg)
* advertising their dirport, and never use multiple directories for
* bootstrap. This only applies if they are also OR servers.
* (We don't care much about the behaviour of non-OR directory servers.) */
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->DirPort_set = 1;
options->ORPort_set = 1;
options->DirCache = 1;
@@ -3765,7 +3805,7 @@ test_config_directory_fetch(void *arg)
OP_EQ, 0);
done:
- tor_free(options);
+ or_options_free(options);
UNMOCK(router_pick_published_address);
UNMOCK(router_get_my_routerinfo);
UNMOCK(advertised_server_mode);
@@ -4029,6 +4069,8 @@ test_config_parse_port_config__ports__ports_given(void *data)
slout = smartlist_new();
+ mock_hostname_resolver();
+
// Test error when encounters an invalid Port specification
config_port_invalid = mock_config_line("DNSPort", "");
ret = parse_port_config(NULL, config_port_invalid, "DNS", 0, NULL,
@@ -4725,6 +4767,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
#endif /* defined(_WIN32) */
done:
+ unmock_hostname_resolver();
if (slout)
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
smartlist_free(slout);
@@ -5063,7 +5106,7 @@ test_config_include_no_permission(void *data)
chmod(dir, 0700);
tor_free(dir);
}
-#endif
+#endif /* !defined(_WIN32) */
static void
test_config_include_recursion_before_after(void *data)
@@ -5778,7 +5821,7 @@ test_config_compute_max_mem_in_queues(void *data)
#else
/* We are on a 32-bit system. */
tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ, GIGABYTE(1));
-#endif
+#endif /* SIZEOF_VOID_P >= 8 */
/* We are able to detect the amount of RAM on the system. */
total_system_memory_return = 0;
@@ -5819,7 +5862,7 @@ test_config_compute_max_mem_in_queues(void *data)
/* We will at maximum get MAX_DEFAULT_MEMORY_QUEUE_SIZE here. */
tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ,
MAX_DEFAULT_MEMORY_QUEUE_SIZE);
-#endif
+#endif /* SIZEOF_SIZE_T > 4 */
done:
UNMOCK(get_total_system_memory);
@@ -5863,6 +5906,7 @@ test_config_extended_fmt(void *arg)
tt_str_op(lp->value, OP_EQ, "is back here");
tt_int_op(lp->command, OP_EQ, CONFIG_LINE_NORMAL);
lp = lp->next;
+ tt_assert(!lp);
config_free_lines(lines);
/* Try with the "extended" flag enabled. */
@@ -5889,9 +5933,166 @@ test_config_extended_fmt(void *arg)
tt_str_op(lp->value, OP_EQ, "");
tt_int_op(lp->command, OP_EQ, CONFIG_LINE_CLEAR);
lp = lp->next;
+ tt_assert(!lp);
+
+ done:
+ config_free_lines(lines);
+}
+
+static void
+test_config_kvline_parse(void *arg)
+{
+ (void)arg;
+
+ config_line_t *lines = NULL;
+ char *enc = NULL;
+
+ lines = kvline_parse("A=B CD=EF", 0);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "A");
+ tt_str_op(lines->value, OP_EQ, "B");
+ tt_str_op(lines->next->key, OP_EQ, "CD");
+ tt_str_op(lines->next->value, OP_EQ, "EF");
+ enc = kvline_encode(lines, 0);
+ tt_str_op(enc, OP_EQ, "A=B CD=EF");
+ tor_free(enc);
+ enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS);
+ tt_str_op(enc, OP_EQ, "A=B CD=EF");
+ tor_free(enc);
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB CDE=F", 0);
+ tt_assert(! lines);
+
+ lines = kvline_parse("AB CDE=F", KV_OMIT_KEYS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "");
+ tt_str_op(lines->value, OP_EQ, "AB");
+ tt_str_op(lines->next->key, OP_EQ, "CDE");
+ tt_str_op(lines->next->value, OP_EQ, "F");
+ tt_assert(lines);
+ enc = kvline_encode(lines, 0);
+ tt_assert(!enc);
+ enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS);
+ tt_str_op(enc, OP_EQ, "AB CDE=F");
+ tor_free(enc);
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=C CDE=\"F G\"", 0);
+ tt_assert(!lines);
+
+ lines = kvline_parse("AB=C CDE=\"F G\" \"GHI\" ", KV_QUOTED|KV_OMIT_KEYS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "C");
+ tt_str_op(lines->next->key, OP_EQ, "CDE");
+ tt_str_op(lines->next->value, OP_EQ, "F G");
+ tt_str_op(lines->next->next->key, OP_EQ, "");
+ tt_str_op(lines->next->next->value, OP_EQ, "GHI");
+ enc = kvline_encode(lines, 0);
+ tt_assert(!enc);
+ enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS);
+ tt_str_op(enc, OP_EQ, "AB=C CDE=\"F G\" GHI");
+ tor_free(enc);
+ config_free_lines(lines);
+
+ lines = kvline_parse("A\"B=C CDE=\"F\" \"GHI\" ", KV_QUOTED|KV_OMIT_KEYS);
+ tt_assert(! lines);
+
+ lines = kvline_parse("AB=", KV_QUOTED);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "");
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=", 0);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "");
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "");
+ config_free_lines(lines);
+
+ lines = kvline_parse(" AB ", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "");
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "");
+ enc = kvline_encode(lines, KV_OMIT_VALS);
+ tt_str_op(enc, OP_EQ, "AB");
+ tor_free(enc);
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=CD", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "CD");
+ enc = kvline_encode(lines, KV_OMIT_VALS);
+ tt_str_op(enc, OP_EQ, "AB=CD");
+ tor_free(enc);
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=CD DE FGH=I", KV_OMIT_VALS);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "CD");
+ tt_str_op(lines->next->key, OP_EQ, "DE");
+ tt_str_op(lines->next->value, OP_EQ, "");
+ tt_str_op(lines->next->next->key, OP_EQ, "FGH");
+ tt_str_op(lines->next->next->value, OP_EQ, "I");
+ enc = kvline_encode(lines, KV_OMIT_VALS);
+ tt_str_op(enc, OP_EQ, "AB=CD DE FGH=I");
+ tor_free(enc);
+ config_free_lines(lines);
+
+ lines = kvline_parse("AB=\"CD E\" DE FGH=\"I\"", KV_OMIT_VALS|KV_QUOTED);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "AB");
+ tt_str_op(lines->value, OP_EQ, "CD E");
+ tt_str_op(lines->next->key, OP_EQ, "DE");
+ tt_str_op(lines->next->value, OP_EQ, "");
+ tt_str_op(lines->next->next->key, OP_EQ, "FGH");
+ tt_str_op(lines->next->next->value, OP_EQ, "I");
+ enc = kvline_encode(lines, KV_OMIT_VALS|KV_QUOTED);
+ tt_str_op(enc, OP_EQ, "AB=\"CD E\" DE FGH=I");
done:
config_free_lines(lines);
+ tor_free(enc);
+}
+
+static void
+test_config_getinfo_config_names(void *arg)
+{
+ (void)arg;
+ char *answer = NULL;
+ const char *error = NULL;
+ int rv;
+
+ rv = getinfo_helper_config(NULL, "config/names", &answer, &error);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(error, OP_EQ, NULL);
+
+ // ContactInfo should be listed.
+ tt_assert(strstr(answer, "\nContactInfo String\n"));
+
+ // V1AuthoritativeDirectory should not be listed, since it is obsolete.
+ tt_assert(! strstr(answer, "V1AuthoritativeDirectory"));
+
+ // ___UsingTestNetworkDefaults should not be listed, since it is invisible.
+ tt_assert(! strstr(answer, "UsingTestNetworkDefaults"));
+
+ done:
+ tor_free(answer);
}
#define CONFIG_TEST(name, flags) \
@@ -5946,5 +6147,7 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(include_opened_file_list, 0),
CONFIG_TEST(compute_max_mem_in_queues, 0),
CONFIG_TEST(extended_fmt, 0),
+ CONFIG_TEST(kvline_parse, 0),
+ CONFIG_TEST(getinfo_config_names, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_confmgr.c b/src/test/test_confmgr.c
new file mode 100644
index 0000000000..d5c73b48e4
--- /dev/null
+++ b/src/test/test_confmgr.c
@@ -0,0 +1,325 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/*
+ * Tests for confparse.c's features that support multiple configuration
+ * formats and configuration objects.
+ */
+
+#define CONFPARSE_PRIVATE
+#include "orconfig.h"
+
+#include "core/or/or.h"
+#include "lib/encoding/confline.h"
+#include "lib/confmgt/confparse.h"
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+
+/*
+ * Set up a few objects: a pasture_cfg is toplevel; it has a llama_cfg and an
+ * alpaca_cfg.
+ */
+
+typedef struct {
+ uint32_t magic;
+ char *address;
+ int opentopublic;
+ config_suite_t *subobjs;
+} pasture_cfg_t;
+
+typedef struct {
+ char *llamaname;
+ int cuteness;
+ uint32_t magic;
+ int eats_meat; /* deprecated; llamas are never carnivorous. */
+
+ char *description; // derived from other fields.
+} llama_cfg_t;
+
+typedef struct {
+ uint32_t magic;
+ int fuzziness;
+ char *alpacaname;
+ int n_wings; /* deprecated; alpacas don't have wings. */
+} alpaca_cfg_t;
+
+/*
+ * Make the above into configuration objects.
+ */
+
+static pasture_cfg_t pasture_cfg_t_dummy;
+static llama_cfg_t llama_cfg_t_dummy;
+static alpaca_cfg_t alpaca_cfg_t_dummy;
+
+#define PV(name, type, dflt) \
+ CONFIG_VAR_ETYPE(pasture_cfg_t, #name, type, name, 0, dflt)
+#define LV(name, type, dflt) \
+ CONFIG_VAR_ETYPE(llama_cfg_t, #name, type, name, 0, dflt)
+#define AV(name, type, dflt) \
+ CONFIG_VAR_ETYPE(alpaca_cfg_t, #name, type, name, 0, dflt)
+static const config_var_t pasture_vars[] = {
+ PV(address, STRING, NULL),
+ PV(opentopublic, BOOL, "1"),
+ END_OF_CONFIG_VARS
+};
+static const config_var_t llama_vars[] =
+{
+ LV(llamaname, STRING, NULL),
+ LV(eats_meat, BOOL, NULL),
+ LV(cuteness, POSINT, "100"),
+ END_OF_CONFIG_VARS
+};
+static const config_var_t alpaca_vars[] =
+{
+ AV(alpacaname, STRING, NULL),
+ AV(fuzziness, POSINT, "50"),
+ AV(n_wings, POSINT, "0"),
+ END_OF_CONFIG_VARS
+};
+
+static config_deprecation_t llama_deprecations[] = {
+ { "eats_meat", "Llamas are herbivores." },
+ {NULL,NULL}
+};
+
+static config_deprecation_t alpaca_deprecations[] = {
+ { "n_wings", "Alpacas are quadrupeds." },
+ {NULL,NULL}
+};
+
+static int clear_llama_cfg_called = 0;
+static void
+clear_llama_cfg(const config_mgr_t *mgr, void *llamacfg)
+{
+ (void)mgr;
+ llama_cfg_t *lc = llamacfg;
+ tor_free(lc->description);
+ ++clear_llama_cfg_called;
+}
+
+static config_abbrev_t llama_abbrevs[] = {
+ { "gracia", "cuteness", 0, 0 },
+ { "gentillesse", "cuteness", 0, 0 },
+ { NULL, NULL, 0, 0 },
+};
+
+static const config_format_t pasture_fmt = {
+ sizeof(pasture_cfg_t),
+ {
+ "pasture_cfg_t",
+ 8989,
+ offsetof(pasture_cfg_t, magic)
+ },
+ .vars = pasture_vars,
+ .config_suite_offset = offsetof(pasture_cfg_t, subobjs),
+};
+
+static const config_format_t llama_fmt = {
+ sizeof(llama_cfg_t),
+ {
+ "llama_cfg_t",
+ 0x11aa11,
+ offsetof(llama_cfg_t, magic)
+ },
+ .vars = llama_vars,
+ .config_suite_offset = -1,
+ .deprecations = llama_deprecations,
+ .abbrevs = llama_abbrevs,
+ .clear_fn = clear_llama_cfg,
+};
+
+static const config_format_t alpaca_fmt = {
+ sizeof(alpaca_cfg_t),
+ {
+ "alpaca_cfg_t",
+ 0xa15aca,
+ offsetof(alpaca_cfg_t, magic)
+ },
+ .vars = alpaca_vars,
+ .config_suite_offset = -1,
+ .deprecations = alpaca_deprecations,
+};
+
+#define LLAMA_IDX 0
+#define ALPACA_IDX 1
+
+static config_mgr_t *
+get_mgr(bool freeze)
+{
+ config_mgr_t *mgr = config_mgr_new(&pasture_fmt);
+ tt_int_op(LLAMA_IDX, OP_EQ, config_mgr_add_format(mgr, &llama_fmt));
+ tt_int_op(ALPACA_IDX, OP_EQ, config_mgr_add_format(mgr, &alpaca_fmt));
+ if (freeze)
+ config_mgr_freeze(mgr);
+ return mgr;
+
+ done:
+ config_mgr_free(mgr);
+ return NULL;
+}
+
+static void
+test_confmgr_init(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = get_mgr(true);
+ smartlist_t *vars = NULL;
+ tt_ptr_op(mgr, OP_NE, NULL);
+
+ vars = config_mgr_list_vars(mgr);
+ tt_int_op(smartlist_len(vars), OP_EQ, 8); // 8 vars total.
+
+ tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "CUTENESS"));
+ tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "GRACIA"));
+ smartlist_free(vars);
+
+ vars = config_mgr_list_deprecated_vars(mgr); // 2 deprecated vars.
+ tt_int_op(smartlist_len(vars), OP_EQ, 2);
+ tt_assert(smartlist_contains_string(vars, "eats_meat"));
+ tt_assert(smartlist_contains_string(vars, "n_wings"));
+
+ tt_str_op("Llamas are herbivores.", OP_EQ,
+ config_find_deprecation(mgr, "EATS_MEAT"));
+ tt_str_op("Alpacas are quadrupeds.", OP_EQ,
+ config_find_deprecation(mgr, "N_WINGS"));
+
+ done:
+ smartlist_free(vars);
+ config_mgr_free(mgr);
+}
+
+static void
+test_confmgr_magic(void *args)
+{
+ (void)args;
+ // Every time we build a manager, it is supposed to get a different magic
+ // number. Let's test that.
+ config_mgr_t *mgr1 = get_mgr(true);
+ config_mgr_t *mgr2 = get_mgr(true);
+ config_mgr_t *mgr3 = get_mgr(true);
+
+ pasture_cfg_t *p1 = NULL, *p2 = NULL, *p3 = NULL;
+
+ tt_assert(mgr1);
+ tt_assert(mgr2);
+ tt_assert(mgr3);
+
+ p1 = config_new(mgr1);
+ p2 = config_new(mgr2);
+ p3 = config_new(mgr3);
+
+ tt_assert(p1);
+ tt_assert(p2);
+ tt_assert(p3);
+
+ // By chance, two managers get the same magic with P=2^-32. Let's
+ // make sure that at least two of them are different, so that our
+ // odds of a false positive are 1/2^-64.
+ tt_assert((p1->magic != p2->magic) || (p2->magic != p3->magic));
+
+ done:
+ config_free(mgr1, p1);
+ config_free(mgr2, p2);
+ config_free(mgr3, p3);
+
+ config_mgr_free(mgr1);
+ config_mgr_free(mgr2);
+ config_mgr_free(mgr3);
+}
+
+static const char *simple_pasture =
+ "LLamaname hugo\n"
+ "Alpacaname daphne\n"
+ "gentillesse 42\n"
+ "address 123 Camelid ave\n";
+
+static void
+test_confmgr_parse(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = get_mgr(true);
+ pasture_cfg_t *p = config_new(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ config_init(mgr, p); // set defaults.
+
+ int r = config_get_lines(simple_pasture, &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ r = config_assign(mgr, p, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ tt_int_op(p->opentopublic, OP_EQ, 1);
+ tt_str_op(p->address, OP_EQ, "123 Camelid ave");
+
+ // We are using this API directly; modules outside confparse will, in the
+ // future, not.
+ const alpaca_cfg_t *ac = config_mgr_get_obj(mgr, p, ALPACA_IDX);
+ const llama_cfg_t *lc = config_mgr_get_obj(mgr, p, LLAMA_IDX);
+ tt_str_op(lc->llamaname, OP_EQ, "hugo");
+ tt_str_op(ac->alpacaname, OP_EQ, "daphne");
+ tt_int_op(lc->cuteness, OP_EQ, 42);
+ tt_int_op(ac->fuzziness, OP_EQ, 50);
+
+ // We set the description for the llama here, so that the clear function
+ // can clear it. (Later we can do this in a verification function.)
+ clear_llama_cfg_called = 0;
+ llama_cfg_t *mut_lc = config_mgr_get_obj_mutable(mgr, p, LLAMA_IDX);
+ mut_lc->description = tor_strdup("A llama named Hugo.");
+ config_free(mgr, p);
+ tt_int_op(clear_llama_cfg_called, OP_EQ, 1);
+
+ done:
+ config_free_lines(lines);
+ config_free(mgr, p);
+ config_mgr_free(mgr);
+ tor_free(msg);
+}
+
+static void
+test_confmgr_dump(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = get_mgr(true);
+ pasture_cfg_t *p = config_new(mgr);
+ pasture_cfg_t *defaults = config_new(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+ char *s = NULL;
+
+ config_init(mgr, p); // set defaults.
+ config_init(mgr, defaults); // set defaults.
+
+ int r = config_get_lines(simple_pasture, &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ r = config_assign(mgr, p, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ s = config_dump(mgr, defaults, p, 1, 0);
+ tt_str_op("address 123 Camelid ave\n"
+ "alpacaname daphne\n"
+ "cuteness 42\n"
+ "llamaname hugo\n", OP_EQ, s);
+
+ done:
+ config_free_lines(lines);
+ config_free(mgr, p);
+ config_free(mgr, defaults);
+ config_mgr_free(mgr);
+
+ tor_free(msg);
+ tor_free(s);
+}
+
+#define CONFMGR_TEST(name, flags) \
+ { #name, test_confmgr_ ## name, flags, NULL, NULL }
+
+struct testcase_t confmgr_tests[] = {
+ CONFMGR_TEST(init, 0),
+ CONFMGR_TEST(magic, 0),
+ CONFMGR_TEST(parse, 0),
+ CONFMGR_TEST(dump, 0),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_confparse.c b/src/test/test_confparse.c
new file mode 100644
index 0000000000..5f29a22c10
--- /dev/null
+++ b/src/test/test_confparse.c
@@ -0,0 +1,1086 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/*
+ * Tests for confparse.c module that we use to parse various
+ * configuration/state file types.
+ */
+
+#define CONFPARSE_PRIVATE
+#include "orconfig.h"
+
+#include "core/or/or.h"
+#include "lib/encoding/confline.h"
+#include "feature/nodelist/routerset.h"
+#include "lib/confmgt/confparse.h"
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+
+#include "lib/confmgt/unitparse.h"
+
+typedef struct test_struct_t {
+ uint32_t magic;
+ char *s;
+ char *fn;
+ int pos;
+ int i;
+ int deprecated_int;
+ uint64_t u64;
+ int interval;
+ int msec_interval;
+ uint64_t mem;
+ double dbl;
+ int boolean;
+ int autobool;
+ time_t time;
+ smartlist_t *csv;
+ int csv_interval;
+ config_line_t *lines;
+ config_line_t *mixed_lines;
+ routerset_t *routerset;
+ int hidden_int;
+ config_line_t *mixed_hidden_lines;
+
+ config_line_t *extra_lines;
+} test_struct_t;
+
+static test_struct_t test_struct_t_dummy;
+
+#define VAR(varname,conftype,member,initvalue) \
+ CONFIG_VAR_ETYPE(test_struct_t, varname, conftype, member, 0, initvalue)
+#define V(member,conftype,initvalue) \
+ VAR(#member, conftype, member, initvalue)
+#define OBSOLETE(varname) \
+ CONFIG_VAR_OBSOLETE(varname)
+
+static const config_var_t test_vars[] = {
+ V(s, STRING, "hello"),
+ V(fn, FILENAME, NULL),
+ V(pos, POSINT, NULL),
+ V(i, INT, "-10"),
+ V(deprecated_int, INT, "3"),
+ V(u64, UINT64, NULL),
+ V(interval, INTERVAL, "10 seconds"),
+ V(msec_interval, MSEC_INTERVAL, "150 msec"),
+ V(mem, MEMUNIT, "10 MB"),
+ V(dbl, DOUBLE, NULL),
+ V(boolean, BOOL, "0"),
+ V(autobool, AUTOBOOL, "auto"),
+ V(time, ISOTIME, NULL),
+ V(csv, CSV, NULL),
+ V(csv_interval, CSV_INTERVAL, "5 seconds"),
+ V(lines, LINELIST, NULL),
+ VAR("MixedLines", LINELIST_V, mixed_lines, NULL),
+ VAR("LineTypeA", LINELIST_S, mixed_lines, NULL),
+ VAR("LineTypeB", LINELIST_S, mixed_lines, NULL),
+ OBSOLETE("obsolete"),
+ {
+ .member = { .name = "routerset",
+ .type = CONFIG_TYPE_EXTENDED,
+ .type_def = &ROUTERSET_type_defn,
+ .offset = offsetof(test_struct_t, routerset),
+ },
+ },
+ VAR("__HiddenInt", POSINT, hidden_int, "0"),
+ VAR("MixedHiddenLines", LINELIST_V, mixed_hidden_lines, NULL),
+ VAR("__HiddenLineA", LINELIST_S, mixed_hidden_lines, NULL),
+ VAR("VisibleLineB", LINELIST_S, mixed_hidden_lines, NULL),
+
+ END_OF_CONFIG_VARS,
+};
+
+static config_abbrev_t test_abbrevs[] = {
+ { "uint", "pos", 0, 0 },
+ { "float", "dbl", 0, 1 },
+ { NULL, NULL, 0, 0 }
+};
+
+static config_deprecation_t test_deprecation_notes[] = {
+ { "deprecated_int", "This integer is deprecated." },
+ { NULL, NULL }
+};
+
+static int
+test_validate_cb(void *old_options, void *options, void *default_options,
+ int from_setconf, char **msg)
+{
+ (void)old_options;
+ (void)default_options;
+ (void)from_setconf;
+ (void)msg;
+ test_struct_t *ts = options;
+
+ if (ts->i == 0xbad) {
+ *msg = tor_strdup("bad value for i");
+ return -1;
+ }
+ return 0;
+}
+
+#define TEST_MAGIC 0x1337
+
+static const config_format_t test_fmt = {
+ sizeof(test_struct_t),
+ {
+ "test_struct_t",
+ TEST_MAGIC,
+ offsetof(test_struct_t, magic),
+ },
+ test_abbrevs,
+ test_deprecation_notes,
+ test_vars,
+ test_validate_cb,
+ NULL,
+ NULL,
+ -1,
+};
+
+/* Make sure that config_init sets everything to the right defaults. */
+static void
+test_confparse_init(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = config_new(mgr);
+ config_init(mgr, tst);
+
+ // Make sure that options are initialized right. */
+ tt_str_op(tst->s, OP_EQ, "hello");
+ tt_ptr_op(tst->fn, OP_EQ, NULL);
+ tt_int_op(tst->pos, OP_EQ, 0);
+ tt_int_op(tst->i, OP_EQ, -10);
+ tt_int_op(tst->deprecated_int, OP_EQ, 3);
+ tt_u64_op(tst->u64, OP_EQ, 0);
+ tt_int_op(tst->interval, OP_EQ, 10);
+ tt_int_op(tst->msec_interval, OP_EQ, 150);
+ tt_u64_op(tst->mem, OP_EQ, 10 * 1024 * 1024);
+ tt_double_op(tst->dbl, OP_LT, .0000000001);
+ tt_double_op(tst->dbl, OP_GT, -0.0000000001);
+ tt_int_op(tst->boolean, OP_EQ, 0);
+ tt_int_op(tst->autobool, OP_EQ, -1);
+ tt_i64_op(tst->time, OP_EQ, 0);
+ tt_ptr_op(tst->csv, OP_EQ, NULL);
+ tt_int_op(tst->csv_interval, OP_EQ, 5);
+ tt_ptr_op(tst->lines, OP_EQ, NULL);
+ tt_ptr_op(tst->mixed_lines, OP_EQ, NULL);
+ tt_int_op(tst->hidden_int, OP_EQ, 0);
+
+ done:
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
+}
+
+static const char simple_settings[] =
+ "s this is a \n"
+ "fn /simple/test of the\n"
+ "uint 77\n" // this is an abbrev
+ "i 3\n"
+ "u64 1000000000000 \n"
+ "interval 5 minutes \n"
+ "msec_interval 5 minutes \n"
+ "mem 10\n"
+ "dbl 6.060842\n"
+ "BOOLEAN 1\n"
+ "aUtObOOl 0\n"
+ "time 2019-06-14 13:58:51\n"
+ "csv configuration, parsing , system \n"
+ "csv_interval 10 seconds, 5 seconds, 10 hours\n"
+ "lines hello\n"
+ "LINES world\n"
+ "linetypea i d\n"
+ "linetypeb i c\n"
+ "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n"
+ "__hiddenint 11\n"
+ "__hiddenlineA XYZ\n"
+ "visiblelineB ABC\n";
+
+/* Return a configuration object set up from simple_settings above. */
+static test_struct_t *
+get_simple_config(const config_mgr_t *mgr)
+{
+ test_struct_t *result = NULL;
+ test_struct_t *tst = config_new(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ config_init(mgr, tst);
+
+ int r = config_get_lines(simple_settings, &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ r = config_assign(mgr, tst, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ result = tst;
+ tst = NULL; // prevent free
+ done:
+ tor_free(msg);
+ config_free_lines(lines);
+ config_free(mgr, tst);
+ return result;
+}
+
+/* Make sure that config_assign can parse things. */
+static void
+test_confparse_assign_simple(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+
+ tt_str_op(tst->s, OP_EQ, "this is a");
+ tt_str_op(tst->fn, OP_EQ, "/simple/test of the");
+ tt_int_op(tst->pos, OP_EQ, 77);
+ tt_int_op(tst->i, OP_EQ, 3);
+ tt_int_op(tst->deprecated_int, OP_EQ, 3);
+ tt_u64_op(tst->u64, OP_EQ, UINT64_C(1000000000000));
+ tt_int_op(tst->interval, OP_EQ, 5 * 60);
+ tt_int_op(tst->msec_interval, OP_EQ, 5 * 60 * 1000);
+ tt_u64_op(tst->mem, OP_EQ, 10);
+ tt_double_op(tst->dbl, OP_LT, 6.060843);
+ tt_double_op(tst->dbl, OP_GT, 6.060841);
+ tt_int_op(tst->boolean, OP_EQ, 1);
+ tt_int_op(tst->autobool, OP_EQ, 0);
+ tt_i64_op(tst->time, OP_EQ, 1560520731);
+ tt_ptr_op(tst->csv, OP_NE, NULL);
+ tt_int_op(smartlist_len(tst->csv), OP_EQ, 3);
+ tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "configuration");
+ tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "parsing");
+ tt_str_op(smartlist_get(tst->csv, 2), OP_EQ, "system");
+ tt_int_op(tst->csv_interval, OP_EQ, 10);
+ tt_int_op(tst->hidden_int, OP_EQ, 11);
+
+ tt_assert(tst->lines);
+ tt_str_op(tst->lines->key, OP_EQ, "lines");
+ tt_str_op(tst->lines->value, OP_EQ, "hello");
+ tt_assert(tst->lines->next);
+ tt_str_op(tst->lines->next->key, OP_EQ, "lines");
+ tt_str_op(tst->lines->next->value, OP_EQ, "world");
+ tt_assert(!tst->lines->next->next);
+
+ tt_assert(tst->mixed_lines);
+ tt_str_op(tst->mixed_lines->key, OP_EQ, "LineTypeA");
+ tt_str_op(tst->mixed_lines->value, OP_EQ, "i d");
+ tt_assert(tst->mixed_lines->next);
+ tt_str_op(tst->mixed_lines->next->key, OP_EQ, "LineTypeB");
+ tt_str_op(tst->mixed_lines->next->value, OP_EQ, "i c");
+ tt_assert(!tst->mixed_lines->next->next);
+
+ tt_assert(tst->mixed_hidden_lines);
+ tt_str_op(tst->mixed_hidden_lines->key, OP_EQ, "__HiddenLineA");
+ tt_str_op(tst->mixed_hidden_lines->value, OP_EQ, "XYZ");
+ tt_assert(tst->mixed_hidden_lines->next);
+ tt_str_op(tst->mixed_hidden_lines->next->key, OP_EQ, "VisibleLineB");
+ tt_str_op(tst->mixed_hidden_lines->next->value, OP_EQ, "ABC");
+ tt_assert(!tst->mixed_hidden_lines->next->next);
+
+ tt_assert(config_check_ok(mgr, tst, LOG_ERR));
+
+ done:
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
+}
+
+/* Try to assign to an obsolete option, and make sure we get a warning. */
+static void
+test_confparse_assign_obsolete(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ config_init(mgr, tst);
+
+ int r = config_get_lines("obsolete option here",
+ &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ setup_capture_of_logs(LOG_WARN);
+ r = config_assign(mgr, tst, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ expect_single_log_msg_containing("Skipping obsolete configuration option");
+
+ done:
+ teardown_capture_of_logs();
+ config_free(mgr, tst);
+ config_free_lines(lines);
+ tor_free(msg);
+ config_mgr_free(mgr);
+}
+
+/* Try to assign to an deprecated option, and make sure we get a warning
+ * but the assignment works anyway. */
+static void
+test_confparse_assign_deprecated(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ config_init(mgr, tst);
+
+ int r = config_get_lines("deprecated_int 7",
+ &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ setup_capture_of_logs(LOG_WARN);
+ r = config_assign(mgr, tst, lines, CAL_WARN_DEPRECATIONS, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ expect_single_log_msg_containing("This integer is deprecated.");
+
+ tt_int_op(tst->deprecated_int, OP_EQ, 7);
+
+ tt_assert(config_check_ok(mgr, tst, LOG_ERR));
+
+ done:
+ teardown_capture_of_logs();
+ config_free(mgr, tst);
+ config_free_lines(lines);
+ tor_free(msg);
+ config_mgr_free(mgr);
+}
+
+/* Try to re-assign an option name that has been depreacted in favor of
+ * another. */
+static void
+test_confparse_assign_replaced(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ config_init(mgr, tst);
+
+ int r = config_get_lines("float 1000\n", &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ setup_capture_of_logs(LOG_WARN);
+ r = config_assign(mgr, tst, lines, CAL_WARN_DEPRECATIONS, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ expect_single_log_msg_containing("use 'dbl' instead.");
+
+ tt_double_op(tst->dbl, OP_GT, 999.999);
+ tt_double_op(tst->dbl, OP_LT, 1000.001);
+
+ done:
+ teardown_capture_of_logs();
+ config_free(mgr, tst);
+ config_free_lines(lines);
+ tor_free(msg);
+ config_mgr_free(mgr);
+}
+
+/* Try to set a linelist value with no option. */
+static void
+test_confparse_assign_emptystring(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ config_init(mgr, tst);
+
+ int r = config_get_lines("lines\n", &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ setup_capture_of_logs(LOG_WARN);
+ r = config_assign(mgr, tst, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ expect_single_log_msg_containing("has no value");
+
+ done:
+ teardown_capture_of_logs();
+ config_free(mgr, tst);
+ config_free_lines(lines);
+ tor_free(msg);
+ config_mgr_free(mgr);
+}
+
+/* Try to set a the same option twice; make sure we get a warning. */
+static void
+test_confparse_assign_twice(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ config_init(mgr, tst);
+
+ int r = config_get_lines("pos 10\n"
+ "pos 99\n", &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ setup_capture_of_logs(LOG_WARN);
+ r = config_assign(mgr, tst, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ expect_single_log_msg_containing("used more than once");
+
+ done:
+ teardown_capture_of_logs();
+ config_free(mgr, tst);
+ config_free_lines(lines);
+ tor_free(msg);
+ config_mgr_free(mgr);
+}
+
+typedef struct badval_test_t {
+ const char *cfg;
+ const char *expect_msg;
+} badval_test_t;
+
+/* Try to set an option and make sure that we get a failure and an expected
+ * warning. */
+static void
+test_confparse_assign_badval(void *arg)
+{
+ const badval_test_t *bt = arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ config_init(mgr, tst);
+
+ int r = config_get_lines(bt->cfg, &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ setup_capture_of_logs(LOG_WARN);
+ r = config_assign(mgr, tst, lines, 0, &msg);
+ tt_int_op(r, OP_LT, 0);
+ tt_ptr_op(msg, OP_NE, NULL);
+ if (! strstr(msg, bt->expect_msg)) {
+ TT_DIE(("'%s' did not contain '%s'" , msg, bt->expect_msg));
+ }
+
+ done:
+ teardown_capture_of_logs();
+ config_free(mgr, tst);
+ config_free_lines(lines);
+ tor_free(msg);
+ config_mgr_free(mgr);
+}
+
+/* Various arguments for badval test.
+ *
+ * Note that the expected warnings here are _very_ truncated, since we
+ * are writing these tests before a refactoring that we expect will
+ * change them.
+ */
+static const badval_test_t bv_notint = { "pos X\n", "malformed" };
+static const badval_test_t bv_negint = { "pos -10\n", "out of bounds" };
+static const badval_test_t bv_badu64 = { "u64 u64\n", "malformed" };
+static const badval_test_t bv_dbl1 = { "dbl xxx\n", "Could not convert" };
+static const badval_test_t bv_dbl2 = { "dbl 1.0 xx\n", "Could not convert" };
+static const badval_test_t bv_dbl3 = {
+ "dbl 1e-10000\n", "too small to express" };
+static const badval_test_t bv_dbl4 = {
+ "dbl 1e1000\n", "too large to express" };
+static const badval_test_t bv_dbl5 = {
+ "dbl -1e-10000\n", "too small to express" };
+static const badval_test_t bv_dbl6 = {
+ "dbl -1e1000\n", "too large to express" };
+static const badval_test_t bv_badcsvi1 =
+ { "csv_interval 10 wl\n", "malformed" };
+static const badval_test_t bv_badcsvi2 =
+ { "csv_interval cl,10\n", "malformed" };
+static const badval_test_t bv_nonoption = { "fnord 10\n", "Unknown option" };
+static const badval_test_t bv_badmem = { "mem 3 trits\n", "malformed" };
+static const badval_test_t bv_badbool = { "boolean 7\n", "Unrecognized value"};
+static const badval_test_t bv_badabool =
+ { "autobool 7\n", "Unrecognized value" };
+static const badval_test_t bv_badtime = { "time lunchtime\n", "Invalid time" };
+static const badval_test_t bv_virt = { "MixedLines 7\n", "virtual option" };
+static const badval_test_t bv_rs = { "Routerset 2.2.2.2.2\n", "Invalid" };
+static const badval_test_t bv_big_interval =
+ { "interval 1000 months", "too large" };
+
+/* Try config_dump(), and make sure it behaves correctly */
+static void
+test_confparse_dump(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ char *dumped = NULL;
+
+ /* Minimal version. */
+ dumped = config_dump(mgr, NULL, tst, 1, 0);
+ tt_str_op(dumped, OP_EQ,
+ "autobool 0\n"
+ "boolean 1\n"
+ "csv configuration,parsing,system\n"
+ "csv_interval 10\n"
+ "dbl 6.060842\n"
+ "fn /simple/test of the\n"
+ "i 3\n"
+ "interval 300\n"
+ "lines hello\n"
+ "lines world\n"
+ "mem 10\n"
+ "VisibleLineB ABC\n"
+ "LineTypeA i d\n"
+ "LineTypeB i c\n"
+ "msec_interval 300000\n"
+ "pos 77\n"
+ "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n"
+ "s this is a\n"
+ "time 2019-06-14 13:58:51\n"
+ "u64 1000000000000\n");
+
+ tor_free(dumped);
+ dumped = config_dump(mgr, NULL, tst, 0, 0);
+ tt_str_op(dumped, OP_EQ,
+ "autobool 0\n"
+ "boolean 1\n"
+ "csv configuration,parsing,system\n"
+ "csv_interval 10\n"
+ "dbl 6.060842\n"
+ "deprecated_int 3\n"
+ "fn /simple/test of the\n"
+ "i 3\n"
+ "interval 300\n"
+ "lines hello\n"
+ "lines world\n"
+ "mem 10\n"
+ "VisibleLineB ABC\n"
+ "LineTypeA i d\n"
+ "LineTypeB i c\n"
+ "msec_interval 300000\n"
+ "pos 77\n"
+ "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n"
+ "s this is a\n"
+ "time 2019-06-14 13:58:51\n"
+ "u64 1000000000000\n");
+
+ /* commented */
+ tor_free(dumped);
+ dumped = config_dump(mgr, NULL, tst, 0, 1);
+ tt_str_op(dumped, OP_EQ,
+ "autobool 0\n"
+ "boolean 1\n"
+ "csv configuration,parsing,system\n"
+ "csv_interval 10\n"
+ "dbl 6.060842\n"
+ "# deprecated_int 3\n"
+ "fn /simple/test of the\n"
+ "i 3\n"
+ "interval 300\n"
+ "lines hello\n"
+ "lines world\n"
+ "mem 10\n"
+ "VisibleLineB ABC\n"
+ "LineTypeA i d\n"
+ "LineTypeB i c\n"
+ "msec_interval 300000\n"
+ "pos 77\n"
+ "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n"
+ "s this is a\n"
+ "time 2019-06-14 13:58:51\n"
+ "u64 1000000000000\n");
+
+ done:
+ config_free(mgr, tst);
+ tor_free(dumped);
+ config_mgr_free(mgr);
+}
+
+/* Try confparse_reset_line(), and make sure it behaves correctly */
+static void
+test_confparse_reset(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+
+ config_reset_line(mgr, tst, "interval", 0);
+ tt_int_op(tst->interval, OP_EQ, 0);
+
+ config_reset_line(mgr, tst, "interval", 1);
+ tt_int_op(tst->interval, OP_EQ, 10);
+
+ tt_ptr_op(tst->routerset, OP_NE, NULL);
+ config_reset_line(mgr, tst, "routerset", 0);
+ tt_ptr_op(tst->routerset, OP_EQ, NULL);
+
+ done:
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
+}
+
+/* Try setting options a second time on a config object, and make sure
+ * it behaves correctly. */
+static void
+test_confparse_reassign(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL, *rs = NULL;
+
+ int r = config_get_lines(
+ "s eleven\n"
+ "i 12\n"
+ "lines 13\n"
+ "csv 14,15\n"
+ "routerset 127.0.0.1\n",
+ &lines, 0);
+ r = config_assign(mgr, tst,lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ tt_str_op(tst->s, OP_EQ, "eleven");
+ tt_str_op(tst->fn, OP_EQ, "/simple/test of the"); // unchanged
+ tt_int_op(tst->pos, OP_EQ, 77); // unchanged
+ tt_int_op(tst->i, OP_EQ, 12);
+ tt_ptr_op(tst->lines, OP_NE, NULL);
+ tt_str_op(tst->lines->key, OP_EQ, "lines");
+ tt_str_op(tst->lines->value, OP_EQ, "13");
+ tt_ptr_op(tst->lines->next, OP_EQ, NULL);
+ tt_int_op(smartlist_len(tst->csv), OP_EQ, 2);
+ tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "14");
+ tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "15");
+
+ rs = routerset_to_string(tst->routerset);
+ tt_str_op(rs, OP_EQ, "127.0.0.1");
+
+ // Try again with the CLEAR_FIRST and USE_DEFAULTS flags
+ r = config_assign(mgr, tst, lines,
+ CAL_CLEAR_FIRST|CAL_USE_DEFAULTS, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ tt_ptr_op(msg, OP_EQ, NULL);
+ tt_str_op(tst->s, OP_EQ, "eleven");
+ // tt_ptr_op(tst->fn, OP_EQ, NULL); //XXXX why is this not cleared?
+ // tt_int_op(tst->pos, OP_EQ, 0); //XXXX why is this not cleared?
+ tt_int_op(tst->i, OP_EQ, 12);
+
+ done:
+ config_free(mgr, tst);
+ config_free_lines(lines);
+ tor_free(msg);
+ tor_free(rs);
+ config_mgr_free(mgr);
+}
+
+/* Try setting options a second time on a config object, using the +foo
+ * linelist-extending syntax. */
+static void
+test_confparse_reassign_extend(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ int r = config_get_lines(
+ "+lines 13\n",
+ &lines, 1); // allow extended format.
+ tt_int_op(r, OP_EQ, 0);
+ r = config_assign(mgr, tst,lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ tt_assert(tst->lines);
+ tt_str_op(tst->lines->key, OP_EQ, "lines");
+ tt_str_op(tst->lines->value, OP_EQ, "hello");
+ tt_assert(tst->lines->next);
+ tt_str_op(tst->lines->next->key, OP_EQ, "lines");
+ tt_str_op(tst->lines->next->value, OP_EQ, "world");
+ tt_assert(tst->lines->next->next);
+ tt_str_op(tst->lines->next->next->key, OP_EQ, "lines");
+ tt_str_op(tst->lines->next->next->value, OP_EQ, "13");
+ tt_assert(tst->lines->next->next->next == NULL);
+ config_free_lines(lines);
+
+ r = config_get_lines(
+ "/lines\n",
+ &lines, 1); // allow extended format.
+ tt_int_op(r, OP_EQ, 0);
+ r = config_assign(mgr, tst, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ tt_assert(tst->lines == NULL);
+ config_free_lines(lines);
+
+ config_free(mgr, tst);
+ tst = get_simple_config(mgr);
+ r = config_get_lines(
+ "/lines away!\n",
+ &lines, 1); // allow extended format.
+ tt_int_op(r, OP_EQ, 0);
+ r = config_assign(mgr, tst, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ tt_assert(tst->lines == NULL);
+
+ done:
+ config_free(mgr, tst);
+ config_free_lines(lines);
+ tor_free(msg);
+ config_mgr_free(mgr);
+}
+
+/* Test out confparse_get_assigned(). */
+static void
+test_confparse_get_assigned(void *arg)
+{
+ (void)arg;
+
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
+ config_line_t *lines = NULL;
+
+ lines = config_get_assigned_option(mgr, tst, "I", 1);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "i");
+ tt_str_op(lines->value, OP_EQ, "3");
+ tt_assert(lines->next == NULL);
+ config_free_lines(lines);
+
+ lines = config_get_assigned_option(mgr, tst, "s", 1);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "s");
+ tt_str_op(lines->value, OP_EQ, "this is a");
+ tt_assert(lines->next == NULL);
+ config_free_lines(lines);
+
+ lines = config_get_assigned_option(mgr, tst, "obsolete", 1);
+ tt_assert(!lines);
+
+ lines = config_get_assigned_option(mgr, tst, "nonesuch", 1);
+ tt_assert(!lines);
+
+ lines = config_get_assigned_option(mgr, tst, "mixedlines", 1);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "LineTypeA");
+ tt_str_op(lines->value, OP_EQ, "i d");
+ tt_assert(lines->next);
+ tt_str_op(lines->next->key, OP_EQ, "LineTypeB");
+ tt_str_op(lines->next->value, OP_EQ, "i c");
+ tt_assert(lines->next->next == NULL);
+ config_free_lines(lines);
+
+ lines = config_get_assigned_option(mgr, tst, "linetypeb", 1);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "LineTypeB");
+ tt_str_op(lines->value, OP_EQ, "i c");
+ tt_assert(lines->next == NULL);
+ config_free_lines(lines);
+
+ tor_free(tst->s);
+ tst->s = tor_strdup("Hello\nWorld");
+ lines = config_get_assigned_option(mgr, tst, "s", 1);
+ tt_assert(lines);
+ tt_str_op(lines->key, OP_EQ, "s");
+ tt_str_op(lines->value, OP_EQ, "\"Hello\\nWorld\"");
+ tt_assert(lines->next == NULL);
+ config_free_lines(lines);
+
+ done:
+ config_free(mgr, tst);
+ config_free_lines(lines);
+ config_mgr_free(mgr);
+}
+
+/* Another variant, which accepts and stores unrecognized lines.*/
+#define ETEST_MAGIC 13371337
+
+static struct_member_t extra = {
+ .name = "__extra",
+ .type = CONFIG_TYPE_LINELIST,
+ .offset = offsetof(test_struct_t, extra_lines),
+};
+
+static config_format_t etest_fmt = {
+ sizeof(test_struct_t),
+ {
+ "test_struct_t (with extra lines)",
+ ETEST_MAGIC,
+ offsetof(test_struct_t, magic),
+ },
+ test_abbrevs,
+ test_deprecation_notes,
+ test_vars,
+ test_validate_cb,
+ NULL,
+ &extra,
+ -1,
+};
+
+/* Try out the feature where we can store unrecognized lines and dump them
+ * again. (State files use this.) */
+static void
+test_confparse_extra_lines(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&etest_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = config_new(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL, *dump = NULL;
+
+ config_init(mgr, tst);
+
+ int r = config_get_lines(
+ "unknotty addita\n"
+ "pos 99\n"
+ "wombat knish\n", &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ r = config_assign(mgr, tst, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ tt_assert(tst->extra_lines);
+
+ dump = config_dump(mgr, NULL, tst, 1, 0);
+ tt_str_op(dump, OP_EQ,
+ "pos 99\n"
+ "unknotty addita\n"
+ "wombat knish\n");
+
+ done:
+ tor_free(msg);
+ tor_free(dump);
+ config_free_lines(lines);
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
+}
+
+static void
+test_confparse_unitparse(void *args)
+{
+ (void)args;
+ /* spot-check a few memunit values. */
+ int ok = 3;
+ tt_u64_op(config_parse_memunit("100 MB", &ok), OP_EQ, 100<<20);
+ tt_assert(ok);
+ tt_u64_op(config_parse_memunit("100 TB", &ok), OP_EQ, UINT64_C(100)<<40);
+ tt_assert(ok);
+ // This is a floating-point value, but note that 1.5 can be represented
+ // precisely.
+ tt_u64_op(config_parse_memunit("1.5 MB", &ok), OP_EQ, 3<<19);
+ tt_assert(ok);
+
+ /* Try some good intervals and msec intervals */
+ tt_int_op(config_parse_interval("2 days", &ok), OP_EQ, 48*3600);
+ tt_assert(ok);
+ tt_int_op(config_parse_interval("1.5 hour", &ok), OP_EQ, 5400);
+ tt_assert(ok);
+ tt_u64_op(config_parse_interval("1 minute", &ok), OP_EQ, 60);
+ tt_assert(ok);
+ tt_int_op(config_parse_msec_interval("2 days", &ok), OP_EQ, 48*3600*1000);
+ tt_assert(ok);
+ tt_int_op(config_parse_msec_interval("10 msec", &ok), OP_EQ, 10);
+ tt_assert(ok);
+
+ /* Try a couple of unitless values. */
+ tt_int_op(config_parse_interval("10", &ok), OP_EQ, 10);
+ tt_assert(ok);
+ tt_u64_op(config_parse_interval("15.0", &ok), OP_EQ, 15);
+ tt_assert(ok);
+
+ /* u64 overflow */
+ /* XXXX our implementation does not currently detect this. See bug 30920. */
+ /*
+ tt_u64_op(config_parse_memunit("20000000 TB", &ok), OP_EQ, 0);
+ tt_assert(!ok);
+ */
+
+ /* i32 overflow */
+ tt_int_op(config_parse_interval("1000 months", &ok), OP_EQ, -1);
+ tt_assert(!ok);
+ tt_int_op(config_parse_msec_interval("4 weeks", &ok), OP_EQ, -1);
+ tt_assert(!ok);
+
+ /* bad units */
+ tt_u64_op(config_parse_memunit("7 nybbles", &ok), OP_EQ, 0);
+ tt_assert(!ok);
+ // XXXX these next two should return -1 according to the documentation.
+ tt_int_op(config_parse_interval("7 cowznofski", &ok), OP_EQ, 0);
+ tt_assert(!ok);
+ tt_int_op(config_parse_msec_interval("1 kalpa", &ok), OP_EQ, 0);
+ tt_assert(!ok);
+
+ done:
+ ;
+}
+
+static void
+test_confparse_check_ok_fail(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = config_new(mgr);
+ tst->pos = -10;
+ tt_assert(! config_check_ok(mgr, tst, LOG_INFO));
+
+ done:
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
+}
+
+static void
+test_confparse_list_vars(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ smartlist_t *vars = config_mgr_list_vars(mgr);
+ smartlist_t *varnames = smartlist_new();
+ char *joined = NULL;
+
+ tt_assert(vars);
+ SMARTLIST_FOREACH(vars, config_var_t *, cv,
+ smartlist_add(varnames, (void*)cv->member.name));
+ smartlist_sort_strings(varnames);
+ joined = smartlist_join_strings(varnames, "::", 0, NULL);
+ tt_str_op(joined, OP_EQ,
+ "LineTypeA::"
+ "LineTypeB::"
+ "MixedHiddenLines::"
+ "MixedLines::"
+ "VisibleLineB::"
+ "__HiddenInt::"
+ "__HiddenLineA::"
+ "autobool::"
+ "boolean::"
+ "csv::"
+ "csv_interval::"
+ "dbl::"
+ "deprecated_int::"
+ "fn::"
+ "i::"
+ "interval::"
+ "lines::"
+ "mem::"
+ "msec_interval::"
+ "obsolete::"
+ "pos::"
+ "routerset::"
+ "s::"
+ "time::"
+ "u64");
+
+ done:
+ tor_free(joined);
+ smartlist_free(varnames);
+ smartlist_free(vars);
+ config_mgr_free(mgr);
+}
+
+static void
+test_confparse_list_deprecated(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ smartlist_t *vars = config_mgr_list_deprecated_vars(mgr);
+ char *joined = NULL;
+
+ tt_assert(vars);
+ smartlist_sort_strings(vars);
+ joined = smartlist_join_strings(vars, "::", 0, NULL);
+
+ tt_str_op(joined, OP_EQ, "deprecated_int");
+
+ done:
+ tor_free(joined);
+ smartlist_free(vars);
+ config_mgr_free(mgr);
+}
+
+static void
+test_confparse_find_option_name(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+
+ // exact match
+ tt_str_op(config_find_option_name(mgr, "u64"), OP_EQ, "u64");
+ // case-insensitive match
+ tt_str_op(config_find_option_name(mgr, "S"), OP_EQ, "s");
+ tt_str_op(config_find_option_name(mgr, "linetypea"), OP_EQ, "LineTypeA");
+ // prefix match
+ tt_str_op(config_find_option_name(mgr, "deprec"), OP_EQ, "deprecated_int");
+ // explicit abbreviation
+ tt_str_op(config_find_option_name(mgr, "uint"), OP_EQ, "pos");
+ tt_str_op(config_find_option_name(mgr, "UINT"), OP_EQ, "pos");
+ // no match
+ tt_ptr_op(config_find_option_name(mgr, "absent"), OP_EQ, NULL);
+
+ done:
+ config_mgr_free(mgr);
+}
+
+#define CONFPARSE_TEST(name, flags) \
+ { #name, test_confparse_ ## name, flags, NULL, NULL }
+
+#define BADVAL_TEST(name) \
+ { "badval_" #name, test_confparse_assign_badval, 0, \
+ &passthrough_setup, (void*)&bv_ ## name }
+
+struct testcase_t confparse_tests[] = {
+ CONFPARSE_TEST(init, 0),
+ CONFPARSE_TEST(assign_simple, 0),
+ CONFPARSE_TEST(assign_obsolete, 0),
+ CONFPARSE_TEST(assign_deprecated, 0),
+ CONFPARSE_TEST(assign_replaced, 0),
+ CONFPARSE_TEST(assign_emptystring, 0),
+ CONFPARSE_TEST(assign_twice, 0),
+ BADVAL_TEST(notint),
+ BADVAL_TEST(negint),
+ BADVAL_TEST(badu64),
+ BADVAL_TEST(dbl1),
+ BADVAL_TEST(dbl2),
+ BADVAL_TEST(dbl3),
+ BADVAL_TEST(dbl4),
+ BADVAL_TEST(dbl5),
+ BADVAL_TEST(dbl6),
+ BADVAL_TEST(badcsvi1),
+ BADVAL_TEST(badcsvi2),
+ BADVAL_TEST(nonoption),
+ BADVAL_TEST(badmem),
+ BADVAL_TEST(badbool),
+ BADVAL_TEST(badabool),
+ BADVAL_TEST(badtime),
+ BADVAL_TEST(virt),
+ BADVAL_TEST(rs),
+ BADVAL_TEST(big_interval),
+ CONFPARSE_TEST(dump, 0),
+ CONFPARSE_TEST(reset, 0),
+ CONFPARSE_TEST(reassign, 0),
+ CONFPARSE_TEST(reassign_extend, 0),
+ CONFPARSE_TEST(get_assigned, 0),
+ CONFPARSE_TEST(extra_lines, 0),
+ CONFPARSE_TEST(unitparse, 0),
+ CONFPARSE_TEST(check_ok_fail, 0),
+ CONFPARSE_TEST(list_vars, 0),
+ CONFPARSE_TEST(list_deprecated, 0),
+ CONFPARSE_TEST(find_option_name, 0),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_connection.h b/src/test/test_connection.h
index 47a5599e5f..40121e6d38 100644
--- a/src/test/test_connection.h
+++ b/src/test/test_connection.h
@@ -1,6 +1,9 @@
/* Copyright (c) 2014-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#ifndef TOR_TEST_CONNECTION_H
+#define TOR_TEST_CONNECTION_H
+
/** Some constants used by test_connection and helpers */
#define TEST_CONN_FAMILY (AF_INET)
#define TEST_CONN_ADDRESS "127.0.0.1"
@@ -11,3 +14,4 @@
void test_conn_lookup_addr_helper(const char *address,
int family, tor_addr_t *addr);
+#endif /* !defined(TOR_TEST_CONNECTION_H) */
diff --git a/src/test/test_consdiff.c b/src/test/test_consdiff.c
index 682ba5b970..7c4c92ea42 100644
--- a/src/test/test_consdiff.c
+++ b/src/test/test_consdiff.c
@@ -14,6 +14,39 @@
#define tt_str_eq_line(a,b) \
tt_assert(line_str_eq((b),(a)))
+static int
+consensus_split_lines_(smartlist_t *out, const char *s, memarea_t *area)
+{
+ size_t len = strlen(s);
+ return consensus_split_lines(out, s, len, area);
+}
+
+static int
+consensus_compute_digest_(const char *cons,
+ consensus_digest_t *digest_out)
+{
+ size_t len = strlen(cons);
+ char *tmp = tor_memdup(cons, len);
+ // We use memdup here to ensure that the input is NOT nul-terminated.
+ // This makes it likelier for us to spot bugs.
+ int r = consensus_compute_digest(tmp, len, digest_out);
+ tor_free(tmp);
+ return r;
+}
+
+static int
+consensus_compute_digest_as_signed_(const char *cons,
+ consensus_digest_t *digest_out)
+{
+ size_t len = strlen(cons);
+ char *tmp = tor_memdup(cons, len);
+ // We use memdup here to ensure that the input is NOT nul-terminated.
+ // This makes it likelier for us to spot bugs.
+ int r = consensus_compute_digest_as_signed(tmp, len, digest_out);
+ tor_free(tmp);
+ return r;
+}
+
static void
test_consdiff_smartlist_slice(void *arg)
{
@@ -58,7 +91,7 @@ test_consdiff_smartlist_slice_string_pos(void *arg)
/* Create a regular smartlist. */
(void)arg;
- consensus_split_lines(sl, "a\nd\nc\na\nb\n", area);
+ consensus_split_lines_(sl, "a\nd\nc\na\nb\n", area);
/* See that smartlist_slice_string_pos respects the bounds of the slice. */
sls = smartlist_slice(sl, 2, 5);
@@ -87,8 +120,8 @@ test_consdiff_lcs_lengths(void *arg)
int e_lengths2[] = { 0, 1, 1, 2, 3, 4 };
(void)arg;
- consensus_split_lines(sl1, "a\nb\nc\nd\ne\n", area);
- consensus_split_lines(sl2, "a\nc\nd\ni\ne\n", area);
+ consensus_split_lines_(sl1, "a\nb\nc\nd\ne\n", area);
+ consensus_split_lines_(sl2, "a\nc\nd\ni\ne\n", area);
sls1 = smartlist_slice(sl1, 0, -1);
sls2 = smartlist_slice(sl2, 0, -1);
@@ -119,10 +152,10 @@ test_consdiff_trim_slices(void *arg)
memarea_t *area = memarea_new();
(void)arg;
- consensus_split_lines(sl1, "a\nb\nb\nb\nd\n", area);
- consensus_split_lines(sl2, "a\nc\nc\nc\nd\n", area);
- consensus_split_lines(sl3, "a\nb\nb\nb\na\n", area);
- consensus_split_lines(sl4, "c\nb\nb\nb\nc\n", area);
+ consensus_split_lines_(sl1, "a\nb\nb\nb\nd\n", area);
+ consensus_split_lines_(sl2, "a\nc\nc\nc\nd\n", area);
+ consensus_split_lines_(sl3, "a\nb\nb\nb\na\n", area);
+ consensus_split_lines_(sl4, "c\nb\nb\nb\nc\n", area);
sls1 = smartlist_slice(sl1, 0, -1);
sls2 = smartlist_slice(sl2, 0, -1);
sls3 = smartlist_slice(sl3, 0, -1);
@@ -165,8 +198,8 @@ test_consdiff_set_changed(void *arg)
memarea_t *area = memarea_new();
(void)arg;
- consensus_split_lines(sl1, "a\nb\na\na\n", area);
- consensus_split_lines(sl2, "a\na\na\na\n", area);
+ consensus_split_lines_(sl1, "a\nb\na\na\n", area);
+ consensus_split_lines_(sl2, "a\na\na\na\n", area);
/* Length of sls1 is 0. */
sls1 = smartlist_slice(sl1, 0, 0);
@@ -240,8 +273,8 @@ test_consdiff_calc_changes(void *arg)
memarea_t *area = memarea_new();
(void)arg;
- consensus_split_lines(sl1, "a\na\na\na\n", area);
- consensus_split_lines(sl2, "a\na\na\na\n", area);
+ consensus_split_lines_(sl1, "a\na\na\na\n", area);
+ consensus_split_lines_(sl2, "a\na\na\na\n", area);
sls1 = smartlist_slice(sl1, 0, -1);
sls2 = smartlist_slice(sl2, 0, -1);
@@ -259,7 +292,7 @@ test_consdiff_calc_changes(void *arg)
tt_assert(!bitarray_is_set(changed2, 3));
smartlist_clear(sl2);
- consensus_split_lines(sl2, "a\nb\na\nb\n", area);
+ consensus_split_lines_(sl2, "a\nb\na\nb\n", area);
tor_free(sls1);
tor_free(sls2);
sls1 = smartlist_slice(sl1, 0, -1);
@@ -282,7 +315,7 @@ test_consdiff_calc_changes(void *arg)
bitarray_clear(changed1, 3);
smartlist_clear(sl2);
- consensus_split_lines(sl2, "b\nb\nb\nb\n", area);
+ consensus_split_lines_(sl2, "b\nb\nb\nb\n", area);
tor_free(sls1);
tor_free(sls2);
sls1 = smartlist_slice(sl1, 0, -1);
@@ -610,8 +643,8 @@ test_consdiff_gen_ed_diff(void *arg)
/* Test 'a', 'c' and 'd' together. See that it is done in reverse order. */
smartlist_clear(cons1);
smartlist_clear(cons2);
- consensus_split_lines(cons1, "A\nB\nC\nD\nE\n", area);
- consensus_split_lines(cons2, "A\nC\nO\nE\nU\n", area);
+ consensus_split_lines_(cons1, "A\nB\nC\nD\nE\n", area);
+ consensus_split_lines_(cons2, "A\nC\nO\nE\nU\n", area);
diff = gen_ed_diff(cons1, cons2, area);
tt_ptr_op(NULL, OP_NE, diff);
tt_int_op(7, OP_EQ, smartlist_len(diff));
@@ -627,8 +660,8 @@ test_consdiff_gen_ed_diff(void *arg)
smartlist_clear(cons1);
smartlist_clear(cons2);
- consensus_split_lines(cons1, "B\n", area);
- consensus_split_lines(cons2, "A\nB\n", area);
+ consensus_split_lines_(cons1, "B\n", area);
+ consensus_split_lines_(cons2, "A\nB\n", area);
diff = gen_ed_diff(cons1, cons2, area);
tt_ptr_op(NULL, OP_NE, diff);
tt_int_op(3, OP_EQ, smartlist_len(diff));
@@ -656,7 +689,7 @@ test_consdiff_apply_ed_diff(void *arg)
diff = smartlist_new();
setup_capture_of_logs(LOG_WARN);
- consensus_split_lines(cons1, "A\nB\nC\nD\nE\n", area);
+ consensus_split_lines_(cons1, "A\nB\nC\nD\nE\n", area);
/* Command without range. */
smartlist_add_linecpy(diff, area, "a");
@@ -829,7 +862,7 @@ test_consdiff_apply_ed_diff(void *arg)
smartlist_clear(diff);
/* Test appending text, 'a'. */
- consensus_split_lines(diff, "3a\nU\nO\n.\n0a\nV\n.\n", area);
+ consensus_split_lines_(diff, "3a\nU\nO\n.\n0a\nV\n.\n", area);
cons2 = apply_ed_diff(cons1, diff, 0);
tt_ptr_op(NULL, OP_NE, cons2);
tt_int_op(8, OP_EQ, smartlist_len(cons2));
@@ -846,7 +879,7 @@ test_consdiff_apply_ed_diff(void *arg)
smartlist_free(cons2);
/* Test deleting text, 'd'. */
- consensus_split_lines(diff, "4d\n1,2d\n", area);
+ consensus_split_lines_(diff, "4d\n1,2d\n", area);
cons2 = apply_ed_diff(cons1, diff, 0);
tt_ptr_op(NULL, OP_NE, cons2);
tt_int_op(2, OP_EQ, smartlist_len(cons2));
@@ -857,7 +890,7 @@ test_consdiff_apply_ed_diff(void *arg)
smartlist_free(cons2);
/* Test changing text, 'c'. */
- consensus_split_lines(diff, "4c\nT\nX\n.\n1,2c\nM\n.\n", area);
+ consensus_split_lines_(diff, "4c\nT\nX\n.\n1,2c\nM\n.\n", area);
cons2 = apply_ed_diff(cons1, diff, 0);
tt_ptr_op(NULL, OP_NE, cons2);
tt_int_op(5, OP_EQ, smartlist_len(cons2));
@@ -871,7 +904,7 @@ test_consdiff_apply_ed_diff(void *arg)
smartlist_free(cons2);
/* Test 'a', 'd' and 'c' together. */
- consensus_split_lines(diff, "4c\nT\nX\n.\n2d\n0a\nM\n.\n", area);
+ consensus_split_lines_(diff, "4c\nT\nX\n.\n2d\n0a\nM\n.\n", area);
cons2 = apply_ed_diff(cons1, diff, 0);
tt_ptr_op(NULL, OP_NE, cons2);
tt_int_op(6, OP_EQ, smartlist_len(cons2));
@@ -918,12 +951,12 @@ test_consdiff_gen_diff(void *arg)
);
tt_int_op(0, OP_EQ,
- consensus_compute_digest_as_signed(cons1_str, &digests1));
+ consensus_compute_digest_as_signed_(cons1_str, &digests1));
tt_int_op(0, OP_EQ,
- consensus_compute_digest(cons2_str, &digests2));
+ consensus_compute_digest_(cons2_str, &digests2));
- consensus_split_lines(cons1, cons1_str, area);
- consensus_split_lines(cons2, cons2_str, area);
+ consensus_split_lines_(cons1, cons1_str, area);
+ consensus_split_lines_(cons2, cons2_str, area);
diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area);
tt_ptr_op(NULL, OP_EQ, diff);
@@ -937,9 +970,9 @@ test_consdiff_gen_diff(void *arg)
"directory-signature foo bar\nbar\n"
);
tt_int_op(0, OP_EQ,
- consensus_compute_digest_as_signed(cons1_str, &digests1));
+ consensus_compute_digest_as_signed_(cons1_str, &digests1));
smartlist_clear(cons1);
- consensus_split_lines(cons1, cons1_str, area);
+ consensus_split_lines_(cons1, cons1_str, area);
diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area);
tt_ptr_op(NULL, OP_NE, diff);
tt_int_op(11, OP_EQ, smartlist_len(diff));
@@ -991,8 +1024,8 @@ test_consdiff_apply_diff(void *arg)
"directory-signature foo bar\nbar\n"
);
tt_int_op(0, OP_EQ,
- consensus_compute_digest(cons1_str, &digests1));
- consensus_split_lines(cons1, cons1_str, area);
+ consensus_compute_digest_(cons1_str, &digests1));
+ consensus_split_lines_(cons1, cons1_str, area);
/* diff doesn't have enough lines. */
cons2 = consdiff_apply_diff(cons1, diff, &digests1);
@@ -1182,4 +1215,3 @@ struct testcase_t consdiff_tests[] = {
CONSDIFF_LEGACY(apply_diff),
END_OF_TESTCASES
};
-
diff --git a/src/test/test_consdiffmgr.c b/src/test/test_consdiffmgr.c
index 254a5ba5d0..74226b8c52 100644
--- a/src/test/test_consdiffmgr.c
+++ b/src/test/test_consdiffmgr.c
@@ -21,6 +21,23 @@
#include "test/test.h"
#include "test/log_test_helpers.h"
+#define consdiffmgr_add_consensus consdiffmgr_add_consensus_nulterm
+
+static char *
+consensus_diff_apply_(const char *c, const char *d)
+{
+ size_t c_len = strlen(c);
+ size_t d_len = strlen(d);
+ // We use memdup here to ensure that the input is NOT nul-terminated.
+ // This makes it likelier for us to spot bugs.
+ char *c_tmp = tor_memdup(c, c_len);
+ char *d_tmp = tor_memdup(d, d_len);
+ char *result = consensus_diff_apply(c_tmp, c_len, d_tmp, d_len);
+ tor_free(c_tmp);
+ tor_free(d_tmp);
+ return result;
+}
+
// ============================== Setup/teardown the consdiffmgr
// These functions get run before/after each test in this module
@@ -153,7 +170,8 @@ lookup_diff_from(consensus_cache_entry_t **out,
const char *str1)
{
uint8_t digest[DIGEST256_LEN];
- if (router_get_networkstatus_v3_sha3_as_signed(digest, str1)<0) {
+ if (router_get_networkstatus_v3_sha3_as_signed(digest,
+ str1, strlen(str1))<0) {
TT_FAIL(("Unable to compute sha3-as-signed"));
return CONSDIFF_NOT_FOUND;
}
@@ -175,14 +193,15 @@ lookup_apply_and_verify_diff(consensus_flavor_t flav,
consensus_cache_entry_incref(ent);
size_t size;
- char *diff_string = NULL;
- int r = uncompress_or_copy(&diff_string, &size, ent);
+ const char *diff_string = NULL;
+ char *diff_owned = NULL;
+ int r = uncompress_or_set_ptr(&diff_string, &size, &diff_owned, ent);
consensus_cache_entry_decref(ent);
if (diff_string == NULL || r < 0)
return -1;
- char *applied = consensus_diff_apply(str1, diff_string);
- tor_free(diff_string);
+ char *applied = consensus_diff_apply(str1, strlen(str1), diff_string, size);
+ tor_free(diff_owned);
if (applied == NULL)
return -1;
@@ -282,7 +301,8 @@ test_consdiffmgr_add(void *arg)
(void) arg;
time_t now = approx_time();
- char *body = NULL;
+ const char *body = NULL;
+ char *body_owned = NULL;
consensus_cache_entry_t *ent = NULL;
networkstatus_t *ns_tmp = fake_ns_new(FLAV_NS, now);
@@ -324,7 +344,7 @@ test_consdiffmgr_add(void *arg)
tt_assert(ent);
consensus_cache_entry_incref(ent);
size_t s;
- r = uncompress_or_copy(&body, &s, ent);
+ r = uncompress_or_set_ptr(&body, &s, &body_owned, ent);
tt_int_op(r, OP_EQ, 0);
tt_int_op(s, OP_EQ, 4);
tt_mem_op(body, OP_EQ, "quux", 4);
@@ -337,7 +357,7 @@ test_consdiffmgr_add(void *arg)
networkstatus_vote_free(ns_tmp);
teardown_capture_of_logs();
consensus_cache_entry_decref(ent);
- tor_free(body);
+ tor_free(body_owned);
}
static void
@@ -370,7 +390,8 @@ test_consdiffmgr_make_diffs(void *arg)
ns = fake_ns_new(FLAV_MICRODESC, now-3600);
md_ns_body = fake_ns_body_new(FLAV_MICRODESC, now-3600);
r = consdiffmgr_add_consensus(md_ns_body, ns);
- router_get_networkstatus_v3_sha3_as_signed(md_ns_sha3, md_ns_body);
+ router_get_networkstatus_v3_sha3_as_signed(md_ns_sha3, md_ns_body,
+ strlen(md_ns_body));
networkstatus_vote_free(ns);
tt_int_op(r, OP_EQ, 0);
@@ -414,7 +435,7 @@ test_consdiffmgr_make_diffs(void *arg)
r = consensus_cache_entry_get_body(diff, &diff_body, &diff_size);
tt_int_op(r, OP_EQ, 0);
diff_text = tor_memdup_nulterm(diff_body, diff_size);
- applied = consensus_diff_apply(md_ns_body, diff_text);
+ applied = consensus_diff_apply_(md_ns_body, diff_text);
tt_assert(applied);
tt_str_op(applied, OP_EQ, md_ns_body_2);
diff --git a/src/test/test_containers.c b/src/test/test_containers.c
index aedd2f7a89..67ba457975 100644
--- a/src/test/test_containers.c
+++ b/src/test/test_containers.c
@@ -96,6 +96,30 @@ test_container_smartlist_basic(void *arg)
tor_free(v555);
}
+/** Test SMARTLIST_FOREACH_REVERSE_BEGIN loop macro */
+static void
+test_container_smartlist_foreach_reverse(void *arg)
+{
+ smartlist_t *sl = smartlist_new();
+ int i;
+
+ (void) arg;
+
+ /* Add integers to smartlist in increasing order */
+ for (i=0;i<100;i++) {
+ smartlist_add(sl, (void*)(uintptr_t)i);
+ }
+
+ /* Pop them out in reverse and test their value */
+ SMARTLIST_FOREACH_REVERSE_BEGIN(sl, void*, k) {
+ i--;
+ tt_ptr_op(k, OP_EQ, (void*)(uintptr_t)i);
+ } SMARTLIST_FOREACH_END(k);
+
+ done:
+ smartlist_free(sl);
+}
+
/** Run unit tests for smartlist-of-strings functionality. */
static void
test_container_smartlist_strings(void *arg)
@@ -582,6 +606,66 @@ test_container_smartlist_ints_eq(void *arg)
smartlist_free(sl2);
}
+static void
+test_container_smartlist_grow(void *arg)
+{
+ (void)arg;
+ smartlist_t *sl = smartlist_new();
+ int i;
+ const char *s[] = { "first", "2nd", "3rd" };
+
+ /* case 1: starting from empty. */
+ smartlist_grow(sl, 10);
+ tt_int_op(10, OP_EQ, smartlist_len(sl));
+ for (i = 0; i < 10; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, NULL);
+ }
+
+ /* case 2: starting with a few elements, probably not reallocating. */
+ smartlist_free(sl);
+ sl = smartlist_new();
+ smartlist_add(sl, (char*)s[0]);
+ smartlist_add(sl, (char*)s[1]);
+ smartlist_add(sl, (char*)s[2]);
+ smartlist_grow(sl, 5);
+ tt_int_op(5, OP_EQ, smartlist_len(sl));
+ for (i = 0; i < 3; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, s[i]);
+ }
+ tt_ptr_op(smartlist_get(sl, 3), OP_EQ, NULL);
+ tt_ptr_op(smartlist_get(sl, 4), OP_EQ, NULL);
+
+ /* case 3: starting with a few elements, but reallocating. */
+ smartlist_free(sl);
+ sl = smartlist_new();
+ smartlist_add(sl, (char*)s[0]);
+ smartlist_add(sl, (char*)s[1]);
+ smartlist_add(sl, (char*)s[2]);
+ smartlist_grow(sl, 100);
+ tt_int_op(100, OP_EQ, smartlist_len(sl));
+ for (i = 0; i < 3; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, s[i]);
+ }
+ for (i = 3; i < 100; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, NULL);
+ }
+
+ /* case 4: shrinking doesn't happen. */
+ smartlist_free(sl);
+ sl = smartlist_new();
+ smartlist_add(sl, (char*)s[0]);
+ smartlist_add(sl, (char*)s[1]);
+ smartlist_add(sl, (char*)s[2]);
+ smartlist_grow(sl, 1);
+ tt_int_op(3, OP_EQ, smartlist_len(sl));
+ for (i = 0; i < 3; ++i) {
+ tt_ptr_op(smartlist_get(sl, i), OP_EQ, s[i]);
+ }
+
+ done:
+ smartlist_free(sl);
+}
+
/** Run unit tests for bitarray code */
static void
test_container_bitarray(void *arg)
@@ -922,6 +1006,10 @@ test_container_smartlist_remove(void *arg)
tt_ptr_op(smartlist_get(sl, 1), OP_EQ, &array[2]);
tt_ptr_op(smartlist_get(sl, 2), OP_EQ, &array[1]);
tt_ptr_op(smartlist_get(sl, 3), OP_EQ, &array[2]);
+ /* Ordinary code should never look at this pointer; we're doing it here
+ * to make sure that we really cleared the pointer we removed.
+ */
+ tt_ptr_op(sl->list[4], OP_EQ, NULL);
done:
smartlist_free(sl);
@@ -1281,12 +1369,14 @@ test_container_smartlist_strings_eq(void *arg)
struct testcase_t container_tests[] = {
CONTAINER_LEGACY(smartlist_basic),
CONTAINER_LEGACY(smartlist_strings),
+ CONTAINER_LEGACY(smartlist_foreach_reverse),
CONTAINER_LEGACY(smartlist_overlap),
CONTAINER_LEGACY(smartlist_digests),
CONTAINER_LEGACY(smartlist_join),
CONTAINER_LEGACY(smartlist_pos),
CONTAINER(smartlist_remove, 0),
CONTAINER(smartlist_ints_eq, 0),
+ CONTAINER(smartlist_grow, 0),
CONTAINER_LEGACY(bitarray),
CONTAINER_LEGACY(digestset),
CONTAINER_LEGACY(strmap),
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 5b406e159b..55eb79e448 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -1,11 +1,15 @@
/* Copyright (c) 2015-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#define CONTROL_PRIVATE
+#define CONTROL_CMD_PRIVATE
+#define CONTROL_GETINFO_PRIVATE
#include "core/or/or.h"
#include "lib/crypt_ops/crypto_ed25519.h"
#include "feature/client/bridges.h"
#include "feature/control/control.h"
+#include "feature/control/control_cmd.h"
+#include "feature/control/control_getinfo.h"
+#include "feature/control/control_proto.h"
#include "feature/client/entrynodes.h"
#include "feature/hs/hs_common.h"
#include "feature/nodelist/networkstatus.h"
@@ -15,48 +19,255 @@
#include "test/test.h"
#include "test/test_helpers.h"
#include "lib/net/resolve.h"
+#include "lib/encoding/confline.h"
+#include "lib/encoding/kvline.h"
#include "feature/control/control_connection_st.h"
+#include "feature/control/control_cmd_args_st.h"
#include "feature/dirclient/download_status_st.h"
#include "feature/nodelist/microdesc_st.h"
#include "feature/nodelist/node_st.h"
+typedef struct {
+ const char *input;
+ const char *expected_parse;
+ const char *expected_error;
+} parser_testcase_t;
+
+typedef struct {
+ const control_cmd_syntax_t *syntax;
+ size_t n_testcases;
+ const parser_testcase_t *testcases;
+} parse_test_params_t;
+
+static char *
+control_cmd_dump_args(const control_cmd_args_t *result)
+{
+ buf_t *buf = buf_new();
+ buf_add_string(buf, "{ args=[");
+ if (result->args) {
+ if (smartlist_len(result->args)) {
+ buf_add_string(buf, " ");
+ }
+ SMARTLIST_FOREACH_BEGIN(result->args, const char *, s) {
+ const bool last = (s_sl_idx == smartlist_len(result->args)-1);
+ buf_add_printf(buf, "%s%s ",
+ escaped(s),
+ last ? "" : ",");
+ } SMARTLIST_FOREACH_END(s);
+ }
+ buf_add_string(buf, "]");
+ if (result->cmddata) {
+ buf_add_string(buf, ", obj=");
+ buf_add_string(buf, escaped(result->cmddata));
+ }
+ if (result->kwargs) {
+ buf_add_string(buf, ", { ");
+ const config_line_t *line;
+ for (line = result->kwargs; line; line = line->next) {
+ const bool last = (line->next == NULL);
+ buf_add_printf(buf, "%s=%s%s ", line->key, escaped(line->value),
+ last ? "" : ",");
+ }
+ buf_add_string(buf, "}");
+ }
+ buf_add_string(buf, " }");
+
+ char *encoded = buf_extract(buf, NULL);
+ buf_free(buf);
+ return encoded;
+}
+
+static void
+test_controller_parse_cmd(void *arg)
+{
+ const parse_test_params_t *params = arg;
+ control_cmd_args_t *result = NULL;
+ char *error = NULL;
+ char *encoded = NULL;
+
+ for (size_t i = 0; i < params->n_testcases; ++i) {
+ const parser_testcase_t *t = &params->testcases[i];
+ result = control_cmd_parse_args("EXAMPLE",
+ params->syntax,
+ strlen(t->input),
+ t->input,
+ &error);
+ // A valid test should expect exactly one parse or error.
+ tt_int_op((t->expected_parse == NULL), OP_NE,
+ (t->expected_error == NULL));
+ // We get a result or an error, not both.
+ tt_int_op((result == NULL), OP_EQ, (error != NULL));
+ // We got the one we expected.
+ tt_int_op((result == NULL), OP_EQ, (t->expected_parse == NULL));
+
+ if (result) {
+ encoded = control_cmd_dump_args(result);
+ tt_str_op(encoded, OP_EQ, t->expected_parse);
+ } else {
+ tt_str_op(error, OP_EQ, t->expected_error);
+ }
+
+ tor_free(error);
+ tor_free(encoded);
+ control_cmd_args_free(result);
+ }
+
+ done:
+ tor_free(error);
+ tor_free(encoded);
+ control_cmd_args_free(result);
+}
+
+#define OK(inp, out) \
+ { inp "\r\n", out, NULL }
+#define ERR(inp, err) \
+ { inp "\r\n", NULL, err }
+
+#define TESTPARAMS(syntax, array) \
+ { &syntax, \
+ ARRAY_LENGTH(array), \
+ array }
+
+static const parser_testcase_t one_to_three_tests[] = {
+ ERR("", "Need at least 1 argument(s)"),
+ ERR(" \t", "Need at least 1 argument(s)"),
+ OK("hello", "{ args=[ \"hello\" ] }"),
+ OK("hello world", "{ args=[ \"hello\", \"world\" ] }"),
+ OK("hello world", "{ args=[ \"hello\", \"world\" ] }"),
+ OK(" hello world", "{ args=[ \"hello\", \"world\" ] }"),
+ OK(" hello world ", "{ args=[ \"hello\", \"world\" ] }"),
+ OK("hello there world", "{ args=[ \"hello\", \"there\", \"world\" ] }"),
+ ERR("why hello there world", "Cannot accept more than 3 argument(s)"),
+ ERR("hello\r\nworld.\r\n.", "Unexpected body"),
+};
+
+static const control_cmd_syntax_t one_to_three_syntax = {
+ .min_args=1, .max_args=3
+};
+
+static const parse_test_params_t parse_one_to_three_params =
+ TESTPARAMS( one_to_three_syntax, one_to_three_tests );
+
+// =
+static const parser_testcase_t no_args_one_obj_tests[] = {
+ ERR("Hi there!\r\n.", "Cannot accept more than 0 argument(s)"),
+ ERR("", "Empty body"),
+ OK("\r\n", "{ args=[], obj=\"\\n\" }"),
+ OK("\r\nHello world\r\n", "{ args=[], obj=\"Hello world\\n\\n\" }"),
+ OK("\r\nHello\r\nworld\r\n", "{ args=[], obj=\"Hello\\nworld\\n\\n\" }"),
+ OK("\r\nHello\r\n..\r\nworld\r\n",
+ "{ args=[], obj=\"Hello\\n.\\nworld\\n\\n\" }"),
+};
+static const control_cmd_syntax_t no_args_one_obj_syntax = {
+ .min_args=0, .max_args=0,
+ .want_cmddata=true,
+};
+static const parse_test_params_t parse_no_args_one_obj_params =
+ TESTPARAMS( no_args_one_obj_syntax, no_args_one_obj_tests );
+
+static const parser_testcase_t no_args_kwargs_tests[] = {
+ OK("", "{ args=[] }"),
+ OK(" ", "{ args=[] }"),
+ OK("hello there=world", "{ args=[], { hello=\"\", there=\"world\" } }"),
+ OK("hello there=world today",
+ "{ args=[], { hello=\"\", there=\"world\", today=\"\" } }"),
+ ERR("=Foo", "Cannot parse keyword argument(s)"),
+};
+static const control_cmd_syntax_t no_args_kwargs_syntax = {
+ .min_args=0, .max_args=0,
+ .accept_keywords=true,
+ .kvline_flags=KV_OMIT_VALS
+};
+static const parse_test_params_t parse_no_args_kwargs_params =
+ TESTPARAMS( no_args_kwargs_syntax, no_args_kwargs_tests );
+
+static const char *one_arg_kwargs_allow_keywords[] = {
+ "Hello", "world", NULL
+};
+static const parser_testcase_t one_arg_kwargs_tests[] = {
+ ERR("", "Need at least 1 argument(s)"),
+ OK("Hi", "{ args=[ \"Hi\" ] }"),
+ ERR("hello there=world", "Unrecognized keyword argument \"there\""),
+ OK("Hi HELLO=foo", "{ args=[ \"Hi\" ], { HELLO=\"foo\" } }"),
+ OK("Hi world=\"bar baz\" hello ",
+ "{ args=[ \"Hi\" ], { world=\"bar baz\", hello=\"\" } }"),
+};
+static const control_cmd_syntax_t one_arg_kwargs_syntax = {
+ .min_args=1, .max_args=1,
+ .accept_keywords=true,
+ .allowed_keywords=one_arg_kwargs_allow_keywords,
+ .kvline_flags=KV_OMIT_VALS|KV_QUOTED,
+};
+static const parse_test_params_t parse_one_arg_kwargs_params =
+ TESTPARAMS( one_arg_kwargs_syntax, one_arg_kwargs_tests );
+
+static char *reply_str = NULL;
+/* Mock for control_write_reply that copies the string for inspection
+ * by tests */
+static void
+mock_control_write_reply(control_connection_t *conn, int code, int c,
+ const char *s)
+{
+ (void)conn;
+ (void)code;
+ (void)c;
+ tor_free(reply_str);
+ reply_str = tor_strdup(s);
+}
+
static void
test_add_onion_helper_keyarg_v3(void *arg)
{
int ret, hs_version;
add_onion_secret_key_t pk;
char *key_new_blob = NULL;
- char *err_msg = NULL;
const char *key_new_alg = NULL;
(void) arg;
+ MOCK(control_write_reply, mock_control_write_reply);
memset(&pk, 0, sizeof(pk));
/* Test explicit ED25519-V3 key generation. */
+ tor_free(reply_str);
ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg,
&key_new_blob, &pk, &hs_version,
- &err_msg);
+ NULL);
tt_int_op(ret, OP_EQ, 0);
tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
tt_assert(pk.v3);
tt_str_op(key_new_alg, OP_EQ, "ED25519-V3");
tt_assert(key_new_blob);
- tt_ptr_op(err_msg, OP_EQ, NULL);
+ tt_ptr_op(reply_str, OP_EQ, NULL);
tor_free(pk.v3); pk.v3 = NULL;
tor_free(key_new_blob);
+ /* Test "BEST" key generation (Assumes BEST = ED25519-V3). */
+ tor_free(pk.v3); pk.v3 = NULL;
+ tor_free(key_new_blob);
+ ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, NULL);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
+ tt_assert(pk.v3);
+ tt_str_op(key_new_alg, OP_EQ, "ED25519-V3");
+ tt_assert(key_new_blob);
+ tt_ptr_op(reply_str, OP_EQ, NULL);
+
/* Test discarding the private key. */
+ tor_free(reply_str);
+ tor_free(pk.v3); pk.v3 = NULL;
+ tor_free(key_new_blob);
ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg,
&key_new_blob, &pk, &hs_version,
- &err_msg);
+ NULL);
tt_int_op(ret, OP_EQ, 0);
tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
tt_assert(pk.v3);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
- tt_ptr_op(err_msg, OP_EQ, NULL);
+ tt_ptr_op(reply_str, OP_EQ, NULL);
tor_free(pk.v3); pk.v3 = NULL;
tor_free(key_new_blob);
@@ -76,9 +287,10 @@ test_add_onion_helper_keyarg_v3(void *arg)
tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk);
tt_assert(key_blob);
+ tor_free(reply_str);
ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg,
&key_new_blob, &pk, &hs_version,
- &err_msg);
+ NULL);
tor_free(key_blob);
tt_int_op(ret, OP_EQ, 0);
tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
@@ -86,7 +298,7 @@ test_add_onion_helper_keyarg_v3(void *arg)
tt_mem_op(pk.v3, OP_EQ, hex_sk, 64);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
- tt_ptr_op(err_msg, OP_EQ, NULL);
+ tt_ptr_op(reply_str, OP_EQ, NULL);
tor_free(pk.v3); pk.v3 = NULL;
tor_free(key_new_blob);
}
@@ -94,7 +306,8 @@ test_add_onion_helper_keyarg_v3(void *arg)
done:
tor_free(pk.v3);
tor_free(key_new_blob);
- tor_free(err_msg);
+ tor_free(reply_str);
+ UNMOCK(control_write_reply);
}
static void
@@ -105,72 +318,61 @@ test_add_onion_helper_keyarg_v2(void *arg)
crypto_pk_t *pk1 = NULL;
const char *key_new_alg = NULL;
char *key_new_blob = NULL;
- char *err_msg = NULL;
char *encoded = NULL;
char *arg_str = NULL;
(void) arg;
+ MOCK(control_write_reply, mock_control_write_reply);
memset(&pk, 0, sizeof(pk));
/* Test explicit RSA1024 key generation. */
+ tor_free(reply_str);
ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob,
- &pk, &hs_version, &err_msg);
+ &pk, &hs_version, NULL);
tt_int_op(ret, OP_EQ, 0);
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
tt_assert(pk.v2);
tt_str_op(key_new_alg, OP_EQ, "RSA1024");
tt_assert(key_new_blob);
- tt_ptr_op(err_msg, OP_EQ, NULL);
-
- /* Test "BEST" key generation (Assumes BEST = RSA1024). */
- crypto_pk_free(pk.v2); pk.v2 = NULL;
- tor_free(key_new_blob);
- ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob,
- &pk, &hs_version, &err_msg);
- tt_int_op(ret, OP_EQ, 0);
- tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
- tt_assert(pk.v2);
- tt_str_op(key_new_alg, OP_EQ, "RSA1024");
- tt_assert(key_new_blob);
- tt_ptr_op(err_msg, OP_EQ, NULL);
+ tt_ptr_op(reply_str, OP_EQ, NULL);
/* Test discarding the private key. */
crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_free(key_new_blob);
- ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob,
- &pk, &hs_version, &err_msg);
+ ret = add_onion_helper_keyarg("NEW:RSA1024", 1, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, NULL);
tt_int_op(ret, OP_EQ, 0);
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
tt_assert(pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
- tt_ptr_op(err_msg, OP_EQ, NULL);
+ tt_ptr_op(reply_str, OP_EQ, NULL);
/* Test generating a invalid key type. */
crypto_pk_free(pk.v2); pk.v2 = NULL;
ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
- &pk, &hs_version, &err_msg);
+ &pk, &hs_version, NULL);
tt_int_op(ret, OP_EQ, -1);
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
tt_assert(!pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
- tt_assert(err_msg);
+ tt_assert(reply_str);
/* Test loading a RSA1024 key. */
- tor_free(err_msg);
+ tor_free(reply_str);
pk1 = pk_generate(0);
tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded));
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
- &pk, &hs_version, &err_msg);
+ &pk, &hs_version, NULL);
tt_int_op(ret, OP_EQ, 0);
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
tt_assert(pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
- tt_ptr_op(err_msg, OP_EQ, NULL);
+ tt_ptr_op(reply_str, OP_EQ, NULL);
tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0);
/* Test loading a invalid key type. */
@@ -179,36 +381,37 @@ test_add_onion_helper_keyarg_v2(void *arg)
crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_asprintf(&arg_str, "RSA512:%s", encoded);
ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
- &pk, &hs_version, &err_msg);
+ &pk, &hs_version, NULL);
tt_int_op(ret, OP_EQ, -1);
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
tt_assert(!pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
- tt_assert(err_msg);
+ tt_assert(reply_str);
/* Test loading a invalid key. */
tor_free(arg_str);
crypto_pk_free(pk.v2); pk.v2 = NULL;
- tor_free(err_msg);
+ tor_free(reply_str);
encoded[strlen(encoded)/2] = '\0';
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
- &pk, &hs_version, &err_msg);
+ &pk, &hs_version, NULL);
tt_int_op(ret, OP_EQ, -1);
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
tt_assert(!pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
- tt_assert(err_msg);
+ tt_assert(reply_str);
done:
crypto_pk_free(pk1);
crypto_pk_free(pk.v2);
tor_free(key_new_blob);
- tor_free(err_msg);
+ tor_free(reply_str);
tor_free(encoded);
tor_free(arg_str);
+ UNMOCK(control_write_reply);
}
static void
@@ -362,49 +565,52 @@ static void
test_add_onion_helper_clientauth(void *arg)
{
rend_authorized_client_t *client = NULL;
- char *err_msg = NULL;
int created = 0;
(void)arg;
+ MOCK(control_write_reply, mock_control_write_reply);
/* Test "ClientName" only. */
- client = add_onion_helper_clientauth("alice", &created, &err_msg);
+ tor_free(reply_str);
+ client = add_onion_helper_clientauth("alice", &created, NULL);
tt_assert(client);
tt_assert(created);
- tt_ptr_op(err_msg, OP_EQ, NULL);
+ tt_ptr_op(reply_str, OP_EQ, NULL);
rend_authorized_client_free(client);
/* Test "ClientName:Blob" */
+ tor_free(reply_str);
client = add_onion_helper_clientauth("alice:475hGBHPlq7Mc0cRZitK/B",
- &created, &err_msg);
+ &created, NULL);
tt_assert(client);
tt_assert(!created);
- tt_ptr_op(err_msg, OP_EQ, NULL);
+ tt_ptr_op(reply_str, OP_EQ, NULL);
rend_authorized_client_free(client);
/* Test invalid client names */
+ tor_free(reply_str);
client = add_onion_helper_clientauth("no*asterisks*allowed", &created,
- &err_msg);
+ NULL);
tt_ptr_op(client, OP_EQ, NULL);
- tt_assert(err_msg);
- tor_free(err_msg);
+ tt_assert(reply_str);
/* Test invalid auth cookie */
- client = add_onion_helper_clientauth("alice:12345", &created, &err_msg);
+ tor_free(reply_str);
+ client = add_onion_helper_clientauth("alice:12345", &created, NULL);
tt_ptr_op(client, OP_EQ, NULL);
- tt_assert(err_msg);
- tor_free(err_msg);
+ tt_assert(reply_str);
/* Test invalid syntax */
+ tor_free(reply_str);
client = add_onion_helper_clientauth(":475hGBHPlq7Mc0cRZitK/B", &created,
- &err_msg);
+ NULL);
tt_ptr_op(client, OP_EQ, NULL);
- tt_assert(err_msg);
- tor_free(err_msg);
+ tt_assert(reply_str);
done:
rend_authorized_client_free(client);
- tor_free(err_msg);
+ tor_free(reply_str);
+ UNMOCK(control_write_reply);
}
/* Mocks and data/variables used for GETINFO download status tests */
@@ -1543,7 +1749,7 @@ test_current_time(void *arg)
static size_t n_nodelist_get_list = 0;
static smartlist_t *nodes = NULL;
-static smartlist_t *
+static const smartlist_t *
mock_nodelist_get_list(void)
{
n_nodelist_get_list++;
@@ -1614,7 +1820,15 @@ test_getinfo_md_all(void *arg)
return;
}
+#define PARSER_TEST(type) \
+ { "parse/" #type, test_controller_parse_cmd, 0, &passthrough_setup, \
+ (void*)&parse_ ## type ## _params }
+
struct testcase_t controller_tests[] = {
+ PARSER_TEST(one_to_three),
+ PARSER_TEST(no_args_one_obj),
+ PARSER_TEST(no_args_kwargs),
+ PARSER_TEST(one_arg_kwargs),
{ "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0,
NULL, NULL },
{ "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0,
diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c
index 70d36e53d4..0d276ef8a6 100644
--- a/src/test/test_controller_events.c
+++ b/src/test/test_controller_events.c
@@ -4,13 +4,21 @@
#define CONNECTION_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#define CONTROL_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
+#define OCIRC_EVENT_PRIVATE
+#define ORCONN_EVENT_PRIVATE
+#include "app/main/subsysmgr.h"
#include "core/or/or.h"
#include "core/or/channel.h"
#include "core/or/channeltls.h"
#include "core/or/circuitlist.h"
+#include "core/or/ocirc_event.h"
+#include "core/or/orconn_event.h"
#include "core/mainloop/connection.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "test/test.h"
+#include "test/test_helpers.h"
+#include "test/log_test_helpers.h"
#include "core/or/or_circuit_st.h"
#include "core/or/origin_circuit_st.h"
@@ -351,10 +359,10 @@ test_cntev_dirboot_defer_desc(void *arg)
/* This event should get deferred */
control_event_boot_dir(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
assert_bootmsg("0 TAG=starting");
- control_event_bootstrap(BOOTSTRAP_STATUS_CONN_DIR, 0);
- assert_bootmsg("5 TAG=conn_dir");
+ control_event_bootstrap(BOOTSTRAP_STATUS_CONN, 0);
+ assert_bootmsg("5 TAG=conn");
control_event_bootstrap(BOOTSTRAP_STATUS_HANDSHAKE, 0);
- assert_bootmsg("10 TAG=handshake_dir");
+ assert_bootmsg("14 TAG=handshake");
/* The deferred event should appear */
control_event_boot_first_orconn();
assert_bootmsg("45 TAG=requesting_descriptors");
@@ -374,29 +382,215 @@ test_cntev_dirboot_defer_orconn(void *arg)
control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
assert_bootmsg("0 TAG=starting");
/* This event should get deferred */
- control_event_boot_dir(BOOTSTRAP_STATUS_CONN_OR, 0);
+ control_event_boot_dir(BOOTSTRAP_STATUS_ENOUGH_DIRINFO, 0);
assert_bootmsg("0 TAG=starting");
- control_event_bootstrap(BOOTSTRAP_STATUS_CONN_DIR, 0);
- assert_bootmsg("5 TAG=conn_dir");
+ control_event_bootstrap(BOOTSTRAP_STATUS_CONN, 0);
+ assert_bootmsg("5 TAG=conn");
control_event_bootstrap(BOOTSTRAP_STATUS_HANDSHAKE, 0);
- assert_bootmsg("10 TAG=handshake_dir");
+ assert_bootmsg("14 TAG=handshake");
/* The deferred event should appear */
control_event_boot_first_orconn();
- assert_bootmsg("80 TAG=conn_or");
+ assert_bootmsg("75 TAG=enough_dirinfo");
done:
tor_free(saved_event_str);
UNMOCK(queue_control_event_string);
}
-#define TEST(name, flags) \
+static void
+test_cntev_signal(void *arg)
+{
+ (void)arg;
+ int rv;
+
+ MOCK(queue_control_event_string, mock_queue_control_event_string);
+
+ /* Nothing is listening for signals, so no event should be queued. */
+ rv = control_event_signal(SIGHUP);
+ tt_int_op(0, OP_EQ, rv);
+ tt_ptr_op(saved_event_str, OP_EQ, NULL);
+
+ /* Now try with signals included in the event mask. */
+ control_testing_set_global_event_mask(EVENT_MASK_(EVENT_GOT_SIGNAL));
+ rv = control_event_signal(SIGHUP);
+ tt_int_op(0, OP_EQ, rv);
+ tt_str_op(saved_event_str, OP_EQ, "650 SIGNAL RELOAD\r\n");
+
+ rv = control_event_signal(SIGACTIVE);
+ tt_int_op(0, OP_EQ, rv);
+ tt_str_op(saved_event_str, OP_EQ, "650 SIGNAL ACTIVE\r\n");
+
+ /* Try a signal that doesn't exist. */
+ setup_full_capture_of_logs(LOG_WARN);
+ tor_free(saved_event_str);
+ rv = control_event_signal(99999);
+ tt_int_op(-1, OP_EQ, rv);
+ tt_ptr_op(saved_event_str, OP_EQ, NULL);
+ expect_single_log_msg_containing("Unrecognized signal 99999");
+
+ done:
+ tor_free(saved_event_str);
+ teardown_capture_of_logs();
+ UNMOCK(queue_control_event_string);
+}
+
+static void
+setup_orconn_state(orconn_state_msg_t *msg, uint64_t gid, uint64_t chan,
+ int proxy_type)
+{
+ msg->gid = gid;
+ msg->chan = chan;
+ msg->proxy_type = proxy_type;
+}
+
+static void
+send_orconn_state(const orconn_state_msg_t *msg_in, uint8_t state)
+{
+ orconn_state_msg_t *msg = tor_malloc(sizeof(*msg));
+
+ *msg = *msg_in;
+ msg->state = state;
+ orconn_state_publish(msg);
+}
+
+static void
+send_ocirc_chan(uint32_t gid, uint64_t chan, bool onehop)
+{
+ ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg));
+
+ msg->gid = gid;
+ msg->chan = chan;
+ msg->onehop = onehop;
+ ocirc_chan_publish(msg);
+}
+
+static void
+test_cntev_orconn_state(void *arg)
+{
+ orconn_state_msg_t conn;
+ memset(&conn, 0, sizeof(conn));
+
+ (void)arg;
+ MOCK(queue_control_event_string, mock_queue_control_event_string);
+ control_testing_set_global_event_mask(EVENT_MASK_(EVENT_STATUS_CLIENT));
+ setup_orconn_state(&conn, 1, 1, PROXY_NONE);
+
+ send_orconn_state(&conn, OR_CONN_STATE_CONNECTING);
+ send_ocirc_chan(1, 1, true);
+ assert_bootmsg("5 TAG=conn");
+ send_orconn_state(&conn, OR_CONN_STATE_TLS_HANDSHAKING);
+ assert_bootmsg("10 TAG=conn_done");
+ send_orconn_state(&conn, OR_CONN_STATE_OR_HANDSHAKING_V3);
+ assert_bootmsg("14 TAG=handshake");
+ send_orconn_state(&conn, OR_CONN_STATE_OPEN);
+ assert_bootmsg("15 TAG=handshake_done");
+
+ conn.gid = 2;
+ conn.chan = 2;
+ send_orconn_state(&conn, OR_CONN_STATE_CONNECTING);
+ /* It doesn't know it's an origin circuit yet */
+ assert_bootmsg("15 TAG=handshake_done");
+ send_ocirc_chan(2, 2, false);
+ assert_bootmsg("80 TAG=ap_conn");
+ send_orconn_state(&conn, OR_CONN_STATE_TLS_HANDSHAKING);
+ assert_bootmsg("85 TAG=ap_conn_done");
+ send_orconn_state(&conn, OR_CONN_STATE_OR_HANDSHAKING_V3);
+ assert_bootmsg("89 TAG=ap_handshake");
+ send_orconn_state(&conn, OR_CONN_STATE_OPEN);
+ assert_bootmsg("90 TAG=ap_handshake_done");
+
+ done:
+ tor_free(saved_event_str);
+ UNMOCK(queue_control_event_string);
+}
+
+static void
+test_cntev_orconn_state_pt(void *arg)
+{
+ orconn_state_msg_t conn;
+ memset(&conn, 0, sizeof(conn));
+
+ (void)arg;
+ MOCK(queue_control_event_string, mock_queue_control_event_string);
+ control_testing_set_global_event_mask(EVENT_MASK_(EVENT_STATUS_CLIENT));
+ setup_orconn_state(&conn, 1, 1, PROXY_PLUGGABLE);
+ send_ocirc_chan(1, 1, true);
+
+ send_orconn_state(&conn, OR_CONN_STATE_CONNECTING);
+ assert_bootmsg("1 TAG=conn_pt");
+ send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING);
+ assert_bootmsg("2 TAG=conn_done_pt");
+ send_orconn_state(&conn, OR_CONN_STATE_TLS_HANDSHAKING);
+ assert_bootmsg("10 TAG=conn_done");
+ send_orconn_state(&conn, OR_CONN_STATE_OR_HANDSHAKING_V3);
+ assert_bootmsg("14 TAG=handshake");
+ send_orconn_state(&conn, OR_CONN_STATE_OPEN);
+ assert_bootmsg("15 TAG=handshake_done");
+
+ send_ocirc_chan(2, 2, false);
+ conn.gid = 2;
+ conn.chan = 2;
+ send_orconn_state(&conn, OR_CONN_STATE_CONNECTING);
+ assert_bootmsg("76 TAG=ap_conn_pt");
+ send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING);
+ assert_bootmsg("77 TAG=ap_conn_done_pt");
+
+ done:
+ tor_free(saved_event_str);
+ UNMOCK(queue_control_event_string);
+}
+
+static void
+test_cntev_orconn_state_proxy(void *arg)
+{
+ orconn_state_msg_t conn;
+ memset(&conn, 0, sizeof(conn));
+
+ (void)arg;
+ MOCK(queue_control_event_string, mock_queue_control_event_string);
+ control_testing_set_global_event_mask(EVENT_MASK_(EVENT_STATUS_CLIENT));
+ setup_orconn_state(&conn, 1, 1, PROXY_CONNECT);
+ send_ocirc_chan(1, 1, true);
+
+ send_orconn_state(&conn, OR_CONN_STATE_CONNECTING);
+ assert_bootmsg("3 TAG=conn_proxy");
+ send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING);
+ assert_bootmsg("4 TAG=conn_done_proxy");
+ send_orconn_state(&conn, OR_CONN_STATE_TLS_HANDSHAKING);
+ assert_bootmsg("10 TAG=conn_done");
+ send_orconn_state(&conn, OR_CONN_STATE_OR_HANDSHAKING_V3);
+ assert_bootmsg("14 TAG=handshake");
+ send_orconn_state(&conn, OR_CONN_STATE_OPEN);
+ assert_bootmsg("15 TAG=handshake_done");
+
+ send_ocirc_chan(2, 2, false);
+ conn.gid = 2;
+ conn.chan = 2;
+ send_orconn_state(&conn, OR_CONN_STATE_CONNECTING);
+ assert_bootmsg("78 TAG=ap_conn_proxy");
+ send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING);
+ assert_bootmsg("79 TAG=ap_conn_done_proxy");
+
+ done:
+ tor_free(saved_event_str);
+ UNMOCK(queue_control_event_string);
+}
+
+#define TEST(name, flags) \
{ #name, test_cntev_ ## name, flags, 0, NULL }
+#define T_PUBSUB(name, setup) \
+ { #name, test_cntev_ ## name, TT_FORK, &helper_pubsub_setup, NULL }
+
struct testcase_t controller_event_tests[] = {
TEST(sum_up_cell_stats, TT_FORK),
TEST(append_cell_stats, TT_FORK),
TEST(format_cell_stats, TT_FORK),
TEST(event_mask, TT_FORK),
- TEST(dirboot_defer_desc, TT_FORK),
- TEST(dirboot_defer_orconn, TT_FORK),
+ T_PUBSUB(dirboot_defer_desc, TT_FORK),
+ T_PUBSUB(dirboot_defer_orconn, TT_FORK),
+ T_PUBSUB(signal, TT_FORK),
+ T_PUBSUB(orconn_state, TT_FORK),
+ T_PUBSUB(orconn_state_pt, TT_FORK),
+ T_PUBSUB(orconn_state_proxy, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 5af0cce130..ed39f41560 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -32,7 +32,7 @@
DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/dh.h>
ENABLE_GCC_WARNING(redundant-decls)
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
/** Run unit tests for Diffie-Hellman functionality. */
static void
@@ -190,7 +190,7 @@ test_crypto_dh(void *arg)
DH_get0_key(dh4, &pk, &sk);
#else
pk = dh4->pub_key;
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
tt_assert(pk);
tt_int_op(BN_num_bytes(pk), OP_LE, DH1024_KEY_LEN);
tt_int_op(BN_num_bytes(pk), OP_GT, 0);
@@ -207,7 +207,7 @@ test_crypto_dh(void *arg)
tt_int_op(s1len, OP_GT, 0);
tt_mem_op(s1, OP_EQ, s2, s1len);
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
done:
crypto_dh_free(dh1);
@@ -219,7 +219,7 @@ test_crypto_dh(void *arg)
DH_free(dh4);
if (pubkey_tmp)
BN_free(pubkey_tmp);
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
}
static void
@@ -248,174 +248,12 @@ test_crypto_openssl_version(void *arg)
tt_int_op(a, OP_GE, 0);
tt_int_op(b, OP_GE, 0);
tt_int_op(c, OP_GE, 0);
-#endif
+#endif /* defined(ENABLE_NSS) */
done:
;
}
-/** Run unit tests for our random number generation function and its wrappers.
- */
-static void
-test_crypto_rng(void *arg)
-{
- int i, j, allok;
- char data1[100], data2[100];
- double d;
- char *h=NULL;
-
- /* Try out RNG. */
- (void)arg;
- tt_assert(! crypto_seed_rng());
- crypto_rand(data1, 100);
- crypto_rand(data2, 100);
- tt_mem_op(data1,OP_NE, data2,100);
- allok = 1;
- for (i = 0; i < 100; ++i) {
- uint64_t big;
- char *host;
- j = crypto_rand_int(100);
- if (j < 0 || j >= 100)
- allok = 0;
- big = crypto_rand_uint64(UINT64_C(1)<<40);
- if (big >= (UINT64_C(1)<<40))
- allok = 0;
- big = crypto_rand_uint64(UINT64_C(5));
- if (big >= 5)
- allok = 0;
- d = crypto_rand_double();
- tt_assert(d >= 0);
- tt_assert(d < 1.0);
- host = crypto_random_hostname(3,8,"www.",".onion");
- if (strcmpstart(host,"www.") ||
- strcmpend(host,".onion") ||
- strlen(host) < 13 ||
- strlen(host) > 18)
- 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
-test_crypto_rng_range(void *arg)
-{
- int got_smallest = 0, got_largest = 0;
- int i;
-
- (void)arg;
- for (i = 0; i < 1000; ++i) {
- int x = crypto_rand_int_range(5,9);
- tt_int_op(x, OP_GE, 5);
- tt_int_op(x, OP_LT, 9);
- if (x == 5)
- got_smallest = 1;
- 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
-}
-
/** Run unit tests for our AES128 functionality */
static void
test_crypto_aes128(void *arg)
@@ -551,7 +389,7 @@ test_crypto_aes128(void *arg)
"\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff");
crypto_cipher_crypt_inplace(env1, data2, 64);
- tt_assert(tor_mem_is_zero(data2, 64));
+ tt_assert(fast_mem_is_zero(data2, 64));
done:
tor_free(mem_op_hex_tmp);
@@ -1173,13 +1011,19 @@ test_crypto_sha3_xof(void *arg)
crypto_xof_free(xof);
memset(out, 0, sizeof(out));
+ /* Test one-function absorb/squeeze. */
+ crypto_xof(out, sizeof(out), msg, sizeof(msg));
+ test_memeq_hex(out, squeezed_hex);
+ memset(out, 0, sizeof(out));
+
/* Test incremental absorb/squeeze. */
xof = crypto_xof_new();
tt_assert(xof);
for (size_t i = 0; i < sizeof(msg); i++)
crypto_xof_add_bytes(xof, msg + i, 1);
- for (size_t i = 0; i < sizeof(out); i++)
+ for (size_t i = 0; i < sizeof(out); i++) {
crypto_xof_squeeze_bytes(xof, out + i, 1);
+ }
test_memeq_hex(out, squeezed_hex);
done:
@@ -1903,13 +1747,13 @@ test_crypto_base32_decode(void *arg)
/* Encode and decode a random string. */
base32_encode(encoded, 96 + 1, plain, 60);
res = base32_decode(decoded, 60, encoded, 96);
- tt_int_op(res,OP_EQ, 0);
+ tt_int_op(res, OP_EQ, 60);
tt_mem_op(plain,OP_EQ, decoded, 60);
/* Encode, uppercase, and decode a random string. */
base32_encode(encoded, 96 + 1, plain, 60);
tor_strupper(encoded);
res = base32_decode(decoded, 60, encoded, 96);
- tt_int_op(res,OP_EQ, 0);
+ tt_int_op(res, OP_EQ, 60);
tt_mem_op(plain,OP_EQ, decoded, 60);
/* Change encoded string and decode. */
if (encoded[0] == 'A' || encoded[0] == 'a')
@@ -1917,12 +1761,12 @@ test_crypto_base32_decode(void *arg)
else
encoded[0] = 'A';
res = base32_decode(decoded, 60, encoded, 96);
- tt_int_op(res,OP_EQ, 0);
+ tt_int_op(res, OP_EQ, 60);
tt_mem_op(plain,OP_NE, decoded, 60);
/* Bad encodings. */
encoded[0] = '!';
res = base32_decode(decoded, 60, encoded, 96);
- tt_int_op(0, OP_GT, res);
+ tt_int_op(res, OP_LT, 0);
done:
;
@@ -2117,7 +1961,7 @@ test_crypto_curve25519_impl(void *arg)
"e0544770bc7de853b38f9100489e3e79";
const char e1e2k_expected[] = "cd6e8269104eb5aaee886bd2071fba88"
"bd13861475516bc2cd2b6e005e805064";
-#else /* !(defined(SLOW_CURVE25519_TEST)) */
+#else /* !defined(SLOW_CURVE25519_TEST) */
const int loop_max=200;
const char e1_expected[] = "bc7112cde03f97ef7008cad1bdc56be3"
"c6a1037d74cceb3712e9206871dcf654";
@@ -2269,7 +2113,7 @@ test_crypto_curve25519_encode(void *arg)
curve25519_secret_key_generate(&seckey, 0);
curve25519_public_key_generate(&key1, &seckey);
- tt_int_op(0, OP_EQ, curve25519_public_to_base64(buf, &key1));
+ curve25519_public_to_base64(buf, &key1);
tt_int_op(CURVE25519_BASE64_PADDED_LEN, OP_EQ, strlen(buf));
tt_int_op(0, OP_EQ, curve25519_public_from_base64(&key2, buf));
@@ -2328,7 +2172,7 @@ test_crypto_curve25519_persist(void *arg)
tt_u64_op((uint64_t)st.st_size, OP_EQ,
32+CURVE25519_PUBKEY_LEN+CURVE25519_SECKEY_LEN);
tt_assert(fast_memeq(content, "== c25519v1: testing ==", taglen));
- tt_assert(tor_mem_is_zero(content+taglen, 32-taglen));
+ tt_assert(fast_mem_is_zero(content+taglen, 32-taglen));
cp = content + 32;
tt_mem_op(keypair.seckey.secret_key,OP_EQ,
cp,
@@ -2649,13 +2493,13 @@ test_crypto_ed25519_encode(void *arg)
/* Test roundtrip. */
tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, 0));
- tt_int_op(0, OP_EQ, ed25519_public_to_base64(buf, &kp.pubkey));
+ ed25519_public_to_base64(buf, &kp.pubkey);
tt_int_op(ED25519_BASE64_LEN, OP_EQ, strlen(buf));
tt_int_op(0, OP_EQ, ed25519_public_from_base64(&pk, buf));
tt_mem_op(kp.pubkey.pubkey, OP_EQ, pk.pubkey, ED25519_PUBKEY_LEN);
tt_int_op(0, OP_EQ, ed25519_sign(&sig1, (const uint8_t*)"ABC", 3, &kp));
- tt_int_op(0, OP_EQ, ed25519_signature_to_base64(buf, &sig1));
+ ed25519_signature_to_base64(buf, &sig1);
tt_int_op(0, OP_EQ, ed25519_signature_from_base64(&sig2, buf));
tt_mem_op(sig1.sig, OP_EQ, sig2.sig, ED25519_SIG_LEN);
@@ -3178,15 +3022,6 @@ test_crypto_failure_modes(void *arg)
struct testcase_t crypto_tests[] = {
CRYPTO_LEGACY(formats),
- CRYPTO_LEGACY(rng),
- { "rng_range", test_crypto_rng_range, 0, 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_aes128, TT_FORK, &passthrough_setup, (void*)"aes" },
{ "aes_EVP", test_crypto_aes128, TT_FORK, &passthrough_setup, (void*)"evp" },
diff --git a/src/test/test_crypto_rng.c b/src/test/test_crypto_rng.c
new file mode 100644
index 0000000000..6b7749a889
--- /dev/null
+++ b/src/test/test_crypto_rng.c
@@ -0,0 +1,332 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define CRYPTO_RAND_PRIVATE
+#include "core/or/or.h"
+#include "test/test.h"
+#include "lib/crypt_ops/aes.h"
+#include "lib/crypt_ops/crypto_format.h"
+#include "lib/crypt_ops/crypto_rand.h"
+
+/** Run unit tests for our random number generation function and its wrappers.
+ */
+static void
+test_crypto_rng(void *arg)
+{
+ int i, j, allok;
+ char data1[100], data2[100];
+ double d;
+ char *h=NULL;
+
+ /* Try out RNG. */
+ (void)arg;
+ tt_assert(! crypto_seed_rng());
+ crypto_rand(data1, 100);
+ crypto_rand(data2, 100);
+ tt_mem_op(data1,OP_NE, data2,100);
+ allok = 1;
+ for (i = 0; i < 100; ++i) {
+ uint64_t big;
+ char *host;
+ j = crypto_rand_int(100);
+ if (j < 0 || j >= 100)
+ allok = 0;
+ big = crypto_rand_uint64(UINT64_C(1)<<40);
+ if (big >= (UINT64_C(1)<<40))
+ allok = 0;
+ big = crypto_rand_uint64(UINT64_C(5));
+ if (big >= 5)
+ allok = 0;
+ d = crypto_rand_double();
+ tt_assert(d >= 0);
+ tt_assert(d < 1.0);
+ host = crypto_random_hostname(3,8,"www.",".onion");
+ if (strcmpstart(host,"www.") ||
+ strcmpend(host,".onion") ||
+ strlen(host) < 13 ||
+ strlen(host) > 18)
+ 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
+test_crypto_rng_range(void *arg)
+{
+ int got_smallest = 0, got_largest = 0;
+ int i;
+
+ (void)arg;
+ for (i = 0; i < 1000; ++i) {
+ int x = crypto_rand_int_range(5,9);
+ tt_int_op(x, OP_GE, 5);
+ tt_int_op(x, OP_LT, 9);
+ if (x == 5)
+ got_smallest = 1;
+ 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
+}
+
+static void
+test_crypto_rng_fast(void *arg)
+{
+ (void)arg;
+ crypto_fast_rng_t *rng = crypto_fast_rng_new();
+ tt_assert(rng);
+
+ /* Rudimentary black-block test to make sure that our prng outputs
+ * have all bits sometimes on and all bits sometimes off. */
+ uint64_t m1 = 0, m2 = ~(uint64_t)0;
+ const int N = 128;
+
+ for (int i=0; i < N; ++i) {
+ uint64_t v;
+ crypto_fast_rng_getbytes(rng, (void*)&v, sizeof(v));
+ m1 |= v;
+ m2 &= v;
+ }
+
+ tt_u64_op(m1, OP_EQ, ~(uint64_t)0);
+ tt_u64_op(m2, OP_EQ, 0);
+
+ /* Check range functions. */
+ int counts[5];
+ memset(counts, 0, sizeof(counts));
+ for (int i=0; i < N; ++i) {
+ unsigned u = crypto_fast_rng_get_uint(rng, 5);
+ tt_int_op(u, OP_GE, 0);
+ tt_int_op(u, OP_LT, 5);
+ counts[u]++;
+
+ uint64_t u64 = crypto_fast_rng_get_uint64(rng, UINT64_C(1)<<40);
+ tt_u64_op(u64, OP_GE, 0);
+ tt_u64_op(u64, OP_LT, UINT64_C(1)<<40);
+
+ double d = crypto_fast_rng_get_double(rng);
+ tt_assert(d >= 0.0);
+ tt_assert(d < 1.0);
+ }
+
+ /* All values should have come up once. */
+ for (int i=0; i<5; ++i) {
+ tt_int_op(counts[i], OP_GT, 0);
+ }
+
+ /* per-thread rand_fast shouldn't crash or leak. */
+ crypto_fast_rng_t *t_rng = get_thread_fast_rng();
+ for (int i = 0; i < N; ++i) {
+ uint64_t u64 = crypto_fast_rng_get_uint64(t_rng, UINT64_C(1)<<40);
+ tt_u64_op(u64, OP_GE, 0);
+ tt_u64_op(u64, OP_LT, UINT64_C(1)<<40);
+ }
+
+ done:
+ crypto_fast_rng_free(rng);
+}
+
+static void
+test_crypto_rng_fast_whitebox(void *arg)
+{
+ (void)arg;
+ const size_t buflen = crypto_fast_rng_get_bytes_used_per_stream();
+ char *buf = tor_malloc_zero(buflen);
+ char *buf2 = tor_malloc_zero(buflen);
+ char *buf3 = NULL, *buf4 = NULL;
+
+ crypto_cipher_t *cipher = NULL, *cipher2 = NULL;
+ uint8_t seed[CRYPTO_FAST_RNG_SEED_LEN];
+ memset(seed, 0, sizeof(seed));
+
+ /* Start with a prng with zero key and zero IV. */
+ crypto_fast_rng_t *rng = crypto_fast_rng_new_from_seed(seed);
+ tt_assert(rng);
+
+ /* We'll use a stream cipher to keep in sync */
+ cipher = crypto_cipher_new_with_iv_and_bits(seed, seed+32, 256);
+
+ /* The first 48 bytes are used for the next seed -- let's make sure we have
+ * them.
+ */
+ memset(seed, 0, sizeof(seed));
+ crypto_cipher_crypt_inplace(cipher, (char*)seed, sizeof(seed));
+
+ /* if we get 128 bytes, they should match the bytes from the aes256-counter
+ * stream, starting at position 48.
+ */
+ crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 128);
+ memset(buf2, 0, 128);
+ crypto_cipher_crypt_inplace(cipher, buf2, 128);
+ tt_mem_op(buf, OP_EQ, buf2, 128);
+
+ /* Try that again, with an odd number of bytes. */
+ crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 199);
+ memset(buf2, 0, 199);
+ crypto_cipher_crypt_inplace(cipher, buf2, 199);
+ tt_mem_op(buf, OP_EQ, buf2, 199);
+
+ /* Make sure that refilling works as expected: skip all but the last 5 bytes
+ * of this steam. */
+ size_t skip = buflen - (199+128) - 5;
+ crypto_fast_rng_getbytes(rng, (uint8_t*)buf, skip);
+ crypto_cipher_crypt_inplace(cipher, buf2, skip);
+
+ /* Now get the next 128 bytes. The first 5 will come from this stream, and
+ * the next 5 will come from the stream keyed by the new value of 'seed'. */
+ crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 128);
+ memset(buf2, 0, 128);
+ crypto_cipher_crypt_inplace(cipher, buf2, 5);
+ crypto_cipher_free(cipher);
+ cipher = crypto_cipher_new_with_iv_and_bits(seed, seed+32, 256);
+ memset(seed, 0, sizeof(seed));
+ crypto_cipher_crypt_inplace(cipher, (char*)seed, sizeof(seed));
+ crypto_cipher_crypt_inplace(cipher, buf2+5, 128-5);
+ tt_mem_op(buf, OP_EQ, buf2, 128);
+
+ /* And check the next 7 bytes to make sure we didn't discard anything. */
+ crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 7);
+ memset(buf2, 0, 7);
+ crypto_cipher_crypt_inplace(cipher, buf2, 7);
+ tt_mem_op(buf, OP_EQ, buf2, 7);
+
+ /* Now try the optimization for long outputs. */
+ buf3 = tor_malloc(65536);
+ crypto_fast_rng_getbytes(rng, (uint8_t*)buf3, 65536);
+
+ buf4 = tor_malloc_zero(65536);
+ uint8_t seed2[CRYPTO_FAST_RNG_SEED_LEN];
+ memset(seed2, 0, sizeof(seed2));
+ crypto_cipher_crypt_inplace(cipher, (char*)seed2, sizeof(seed2));
+ cipher2 = crypto_cipher_new_with_iv_and_bits(seed2, seed2+32, 256);
+ crypto_cipher_crypt_inplace(cipher2, buf4, 65536);
+ tt_mem_op(buf3, OP_EQ, buf4, 65536);
+
+ done:
+ crypto_fast_rng_free(rng);
+ crypto_cipher_free(cipher);
+ crypto_cipher_free(cipher2);
+ tor_free(buf);
+ tor_free(buf2);
+ tor_free(buf3);
+ tor_free(buf4);
+}
+
+struct testcase_t crypto_rng_tests[] = {
+ { "rng", test_crypto_rng, 0, NULL, NULL },
+ { "rng_range", test_crypto_rng_range, 0, 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" },
+ { "fast", test_crypto_rng_fast, 0, NULL, NULL },
+ { "fast_whitebox", test_crypto_rng_fast_whitebox, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c
index e24aee8930..3b20dfa587 100644
--- a/src/test/test_crypto_slow.c
+++ b/src/test/test_crypto_slow.c
@@ -109,7 +109,7 @@ run_s2k_tests(const unsigned flags, const unsigned type,
secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
pw1, strlen(pw1)));
tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3));
- tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
+ tt_assert(!fast_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
done:
;
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 0e44c47f3f..6329ff7750 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -8,7 +8,7 @@
#define BWAUTH_PRIVATE
#define CONFIG_PRIVATE
-#define CONTROL_PRIVATE
+#define CONTROL_GETINFO_PRIVATE
#define DIRCACHE_PRIVATE
#define DIRCLIENT_PRIVATE
#define DIRSERV_PRIVATE
@@ -26,13 +26,13 @@
#include "core/or/or.h"
#include "app/config/config.h"
-#include "app/config/confparse.h"
+#include "lib/confmgt/confparse.h"
#include "core/mainloop/connection.h"
#include "core/or/relay.h"
#include "core/or/versions.h"
#include "feature/client/bridges.h"
#include "feature/client/entrynodes.h"
-#include "feature/control/control.h"
+#include "feature/control/control_getinfo.h"
#include "feature/dirauth/bwauth.h"
#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/dsigs_parse.h"
@@ -91,9 +91,29 @@
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
#define NS_MODULE dir
+static networkstatus_t *
+networkstatus_parse_vote_from_string_(const char *s,
+ const char **eos_out,
+ enum networkstatus_type_t ns_type)
+{
+ size_t len = strlen(s);
+ // memdup so that it won't be nul-terminated.
+ char *tmp = tor_memdup(s, len);
+ networkstatus_t *result =
+ networkstatus_parse_vote_from_string(tmp, len, eos_out, ns_type);
+ if (eos_out && *eos_out) {
+ *eos_out = s + (*eos_out - tmp);
+ }
+ tor_free(tmp);
+ return result;
+}
+
static void
test_dir_nicknames(void *arg)
{
@@ -142,6 +162,269 @@ test_dir_nicknames(void *arg)
;
}
+/* Allocate and return a new routerinfo, with the fields set from the
+ * arguments to this function.
+ *
+ * Also sets:
+ * - random RSA identity and onion keys,
+ * - the platform field using get_platform_str(), and
+ * - supports_tunnelled_dir_requests to 1.
+ *
+ * If rsa_onion_keypair_out is not NULL, it is set to the onion keypair.
+ * The caller must free this keypair.
+ */
+static routerinfo_t *
+basic_routerinfo_new(const char *nickname, uint32_t ipv4_addr,
+ uint16_t or_port, uint16_t dir_port,
+ uint32_t bandwidthrate, uint32_t bandwidthburst,
+ uint32_t bandwidthcapacity,
+ time_t published_on,
+ crypto_pk_t **rsa_onion_keypair_out)
+{
+ char platform[256];
+
+ tor_assert(nickname);
+
+ crypto_pk_t *pk1 = NULL, *pk2 = NULL;
+ /* These keys are random: idx is ignored. */
+ pk1 = pk_generate(0);
+ pk2 = pk_generate(1);
+
+ tor_assert(pk1);
+ tor_assert(pk2);
+
+ get_platform_str(platform, sizeof(platform));
+
+ routerinfo_t *r1 = tor_malloc_zero(sizeof(routerinfo_t));
+
+ r1->nickname = tor_strdup(nickname);
+ r1->platform = tor_strdup(platform);
+
+ r1->addr = ipv4_addr;
+ r1->or_port = or_port;
+ r1->dir_port = dir_port;
+ r1->supports_tunnelled_dir_requests = 1;
+
+ router_set_rsa_onion_pkey(pk1, &r1->onion_pkey, &r1->onion_pkey_len);
+ r1->identity_pkey = pk2;
+
+ r1->bandwidthrate = bandwidthrate;
+ r1->bandwidthburst = bandwidthburst;
+ r1->bandwidthcapacity = bandwidthcapacity;
+
+ r1->cache_info.published_on = published_on;
+
+ if (rsa_onion_keypair_out) {
+ *rsa_onion_keypair_out = pk1;
+ } else {
+ crypto_pk_free(pk1);
+ }
+
+ return r1;
+}
+
+/* Allocate and return a new string containing a "router" line for r1. */
+static char *
+get_new_router_line(const routerinfo_t *r1)
+{
+ char *line = NULL;
+
+ tor_assert(r1);
+
+ tor_asprintf(&line,
+ "router %s %s %d 0 %d\n",
+ r1->nickname, fmt_addr32(r1->addr),
+ r1->or_port, r1->dir_port);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a "platform" line for the
+ * current Tor version and OS. */
+static char *
+get_new_platform_line(void)
+{
+ char *line = NULL;
+
+ tor_asprintf(&line,
+ "platform Tor %s on %s\n",
+ VERSION, get_uname());
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a "published" line for r1.
+ * r1->cache_info.published_on must be between 0 and 59 seconds. */
+static char *
+get_new_published_line(const routerinfo_t *r1)
+{
+ char *line = NULL;
+
+ tor_assert(r1);
+
+ tor_assert(r1->cache_info.published_on >= 0);
+ tor_assert(r1->cache_info.published_on <= 59);
+
+ tor_asprintf(&line,
+ "published 1970-01-01 00:00:%02u\n",
+ (unsigned)r1->cache_info.published_on);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a "fingerprint" line for r1. */
+static char *
+get_new_fingerprint_line(const routerinfo_t *r1)
+{
+ char *line = NULL;
+ char fingerprint[FINGERPRINT_LEN+1];
+
+ tor_assert(r1);
+
+ tor_assert(!crypto_pk_get_fingerprint(r1->identity_pkey, fingerprint, 1));
+ tor_assert(strlen(fingerprint) > 0);
+
+ tor_asprintf(&line,
+ "fingerprint %s\n",
+ fingerprint);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing an "uptime" line with uptime t.
+ *
+ * You should pass a hard-coded value to this function, because even if we made
+ * it reflect uptime, that still wouldn't make it right, because the two
+ * descriptors might be made on different seconds.
+ */
+static char *
+get_new_uptime_line(time_t t)
+{
+ char *line = NULL;
+
+ tor_asprintf(&line,
+ "uptime %u\n",
+ (unsigned)t);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing an "bandwidth" line for r1.
+ */
+static char *
+get_new_bandwidth_line(const routerinfo_t *r1)
+{
+ char *line = NULL;
+
+ tor_assert(r1);
+
+ tor_asprintf(&line,
+ "bandwidth %u %u %u\n",
+ r1->bandwidthrate,
+ r1->bandwidthburst,
+ r1->bandwidthcapacity);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a key_name block for the
+ * RSA key pk1.
+ */
+static char *
+get_new_rsa_key_block(const char *key_name, crypto_pk_t *pk1)
+{
+ char *block = NULL;
+ char *pk1_str = NULL;
+ size_t pk1_str_len = 0;
+
+ tor_assert(key_name);
+ tor_assert(pk1);
+
+ tor_assert(!crypto_pk_write_public_key_to_string(pk1, &pk1_str,
+ &pk1_str_len));
+ tor_assert(pk1_str);
+ tor_assert(pk1_str_len);
+
+ tor_asprintf(&block,
+ "%s\n%s",
+ key_name,
+ pk1_str);
+ tor_free(pk1_str);
+
+ tor_assert(block);
+ return block;
+}
+
+/* Allocate and return a new string containing an "onion-key" block for the
+ * router r1.
+ */
+static char *
+get_new_onion_key_block(const routerinfo_t *r1)
+{
+ char *block = NULL;
+ tor_assert(r1);
+ crypto_pk_t *pk_tmp = router_get_rsa_onion_pkey(r1->onion_pkey,
+ r1->onion_pkey_len);
+ block = get_new_rsa_key_block("onion-key", pk_tmp);
+ crypto_pk_free(pk_tmp);
+ return block;
+}
+
+/* Allocate and return a new string containing an "signing-key" block for the
+ * router r1.
+ */
+static char *
+get_new_signing_key_block(const routerinfo_t *r1)
+{
+ tor_assert(r1);
+ return get_new_rsa_key_block("signing-key", r1->identity_pkey);
+}
+
+/* Allocate and return a new string containing an "ntor-onion-key" line for
+ * the curve25519 public key ntor_onion_pubkey.
+ */
+static char *
+get_new_ntor_onion_key_line(const curve25519_public_key_t *ntor_onion_pubkey)
+{
+ char *line = NULL;
+ char cert_buf[256];
+ int rv = 0;
+
+ tor_assert(ntor_onion_pubkey);
+
+ rv = base64_encode(cert_buf, sizeof(cert_buf),
+ (const char*)ntor_onion_pubkey->public_key, 32,
+ BASE64_ENCODE_MULTILINE);
+ tor_assert(rv > 0);
+ tor_assert(strlen(cert_buf) > 0);
+
+ tor_asprintf(&line,
+ "ntor-onion-key %s",
+ cert_buf);
+ tor_assert(line);
+
+ return line;
+}
+
+/* Allocate and return a new string containing a "bridge-distribution-request"
+ * line for options.
+ */
+static char *
+get_new_bridge_distribution_request_line(const or_options_t *options)
+{
+ if (options->BridgeRelay) {
+ return tor_strdup("bridge-distribution-request any\n");
+ } else {
+ return tor_strdup("");
+ }
+}
+
static smartlist_t *mocked_configured_ports = NULL;
/** Returns mocked_configured_ports */
@@ -151,71 +434,510 @@ mock_get_configured_ports(void)
return mocked_configured_ports;
}
-/** Run unit tests for router descriptor generation logic. */
+static tor_cert_t *
+mock_tor_cert_dup_null(const tor_cert_t *cert)
+{
+ (void)cert;
+ return NULL;
+}
+
+static crypto_pk_t *mocked_server_identitykey = NULL;
+
+/* Returns mocked_server_identitykey with no checks. */
+static crypto_pk_t *
+mock_get_server_identity_key(void)
+{
+ return mocked_server_identitykey;
+}
+
+static crypto_pk_t *mocked_onionkey = NULL;
+
+/* Returns mocked_onionkey with no checks. */
+static crypto_pk_t *
+mock_get_onion_key(void)
+{
+ return mocked_onionkey;
+}
+
+static routerinfo_t *mocked_routerinfo = NULL;
+
+/* Returns 0 and sets ri_out to mocked_routerinfo.
+ * ri_out must not be NULL. There are no other checks. */
+static int
+mock_router_build_fresh_unsigned_routerinfo(routerinfo_t **ri_out)
+{
+ tor_assert(ri_out);
+ *ri_out = mocked_routerinfo;
+ return 0;
+}
+
+static ed25519_keypair_t *mocked_master_signing_key = NULL;
+
+/* Returns mocked_master_signing_key with no checks. */
+static const ed25519_keypair_t *
+mock_get_master_signing_keypair(void)
+{
+ return mocked_master_signing_key;
+}
+
+static struct tor_cert_st *mocked_signing_key_cert = NULL;
+
+/* Returns mocked_signing_key_cert with no checks. */
+static const struct tor_cert_st *
+mock_get_master_signing_key_cert(void)
+{
+ return mocked_signing_key_cert;
+}
+
+static curve25519_keypair_t *mocked_curve25519_onion_key = NULL;
+
+/* Returns mocked_curve25519_onion_key with no checks. */
+static const curve25519_keypair_t *
+mock_get_current_curve25519_keypair(void)
+{
+ return mocked_curve25519_onion_key;
+}
+
+/* Unmock get_configured_ports() and free mocked_configured_ports. */
+static void
+cleanup_mock_configured_ports(void)
+{
+ UNMOCK(get_configured_ports);
+
+ if (mocked_configured_ports) {
+ SMARTLIST_FOREACH(mocked_configured_ports, port_cfg_t *, p, tor_free(p));
+ smartlist_free(mocked_configured_ports);
+ }
+}
+
+/* Mock get_configured_ports() with a list containing or_port and dir_port.
+ * If a port is 0, don't set it.
+ * Only sets the minimal data required for the tests to pass. */
+static void
+setup_mock_configured_ports(uint16_t or_port, uint16_t dir_port)
+{
+ cleanup_mock_configured_ports();
+
+ /* Fake just enough of an ORPort and DirPort to get by */
+ MOCK(get_configured_ports, mock_get_configured_ports);
+ mocked_configured_ports = smartlist_new();
+
+ if (or_port) {
+ port_cfg_t *or_port_cfg = tor_malloc_zero(sizeof(*or_port_cfg));
+ or_port_cfg->type = CONN_TYPE_OR_LISTENER;
+ or_port_cfg->addr.family = AF_INET;
+ or_port_cfg->port = or_port;
+ smartlist_add(mocked_configured_ports, or_port_cfg);
+ }
+
+ if (dir_port) {
+ port_cfg_t *dir_port_cfg = tor_malloc_zero(sizeof(*dir_port_cfg));
+ dir_port_cfg->type = CONN_TYPE_DIR_LISTENER;
+ dir_port_cfg->addr.family = AF_INET;
+ dir_port_cfg->port = dir_port;
+ smartlist_add(mocked_configured_ports, dir_port_cfg);
+ }
+}
+
+/* Clean up the data structures and unmock the functions needed for generating
+ * a fresh descriptor. */
+static void
+cleanup_mocks_for_fresh_descriptor(void)
+{
+ tor_free(get_options_mutable()->Nickname);
+
+ mocked_server_identitykey = NULL;
+ UNMOCK(get_server_identity_key);
+
+ crypto_pk_free(mocked_onionkey);
+ UNMOCK(get_onion_key);
+}
+
+/* Mock the data structures and functions needed for generating a fresh
+ * descriptor.
+ *
+ * Sets options->Nickname from r1->nickname.
+ * Mocks get_server_identity_key() with r1->identity_pkey.
+ *
+ * If rsa_onion_keypair is not NULL, it is used to mock get_onion_key().
+ * Otherwise, the public key in r1->onion_pkey is used to mock get_onion_key().
+ */
+static void
+setup_mocks_for_fresh_descriptor(const routerinfo_t *r1,
+ crypto_pk_t *rsa_onion_keypair)
+{
+ cleanup_mocks_for_fresh_descriptor();
+
+ tor_assert(r1);
+
+ /* router_build_fresh_signed_extrainfo() requires options->Nickname */
+ get_options_mutable()->Nickname = tor_strdup(r1->nickname);
+
+ /* router_build_fresh_signed_extrainfo() requires get_server_identity_key().
+ * Use the same one as the call to router_dump_router_to_string() above.
+ */
+ mocked_server_identitykey = r1->identity_pkey;
+ MOCK(get_server_identity_key, mock_get_server_identity_key);
+
+ /* router_dump_and_sign_routerinfo_descriptor_body() requires
+ * get_onion_key(). Use the same one as r1.
+ */
+ if (rsa_onion_keypair) {
+ mocked_onionkey = crypto_pk_dup_key(rsa_onion_keypair);
+ } else {
+ mocked_onionkey = router_get_rsa_onion_pkey(r1->onion_pkey,
+ r1->onion_pkey_len);
+ }
+ MOCK(get_onion_key, mock_get_onion_key);
+}
+
+/* Set options based on arg.
+ *
+ * b: BridgeRelay 1
+ * e: ExtraInfoStatistics 1
+ * s: sets all the individual statistics options to 1
+ *
+ * Always sets AssumeReachable to 1.
+ *
+ * Does not set ServerTransportPlugin, because it's parsed before use.
+ *
+ * Does not set BridgeRecordUsageByCountry, because the tests don't have access
+ * to a GeoIPFile or GeoIPv6File. */
static void
-test_dir_formats(void *arg)
+setup_dir_formats_options(const char *arg, or_options_t *options)
+{
+ /* Skip reachability checks for DirPort, ORPort, and tunnelled-dir-server */
+ options->AssumeReachable = 1;
+
+ if (strchr(arg, 'b')) {
+ options->BridgeRelay = 1;
+ }
+
+ if (strchr(arg, 'e')) {
+ options->ExtraInfoStatistics = 1;
+ }
+
+ if (strchr(arg, 's')) {
+ options->DirReqStatistics = 1;
+ options->HiddenServiceStatistics = 1;
+ options->EntryStatistics = 1;
+ options->CellStatistics = 1;
+ options->ExitPortStatistics = 1;
+ options->ConnDirectionStatistics = 1;
+ options->PaddingStatistics = 1;
+ }
+}
+
+/* Check that routerinfos r1 and rp1 are consistent.
+ * Only performs some basic checks.
+ */
+#define CHECK_ROUTERINFO_CONSISTENCY(r1, rp1) \
+STMT_BEGIN \
+ tt_assert(r1); \
+ tt_assert(rp1); \
+\
+ tt_int_op(rp1->addr,OP_EQ, r1->addr); \
+ tt_int_op(rp1->or_port,OP_EQ, r1->or_port); \
+ tt_int_op(rp1->dir_port,OP_EQ, r1->dir_port); \
+ tt_int_op(rp1->bandwidthrate,OP_EQ, r1->bandwidthrate); \
+ tt_int_op(rp1->bandwidthburst,OP_EQ, r1->bandwidthburst); \
+ tt_int_op(rp1->bandwidthcapacity,OP_EQ, r1->bandwidthcapacity); \
+ crypto_pk_t *rp1_onion_pkey = router_get_rsa_onion_pkey(rp1->onion_pkey, \
+ rp1->onion_pkey_len); \
+ crypto_pk_t *r1_onion_pkey = router_get_rsa_onion_pkey(r1->onion_pkey, \
+ r1->onion_pkey_len); \
+ tt_int_op(crypto_pk_cmp_keys(rp1_onion_pkey, r1_onion_pkey), OP_EQ, 0); \
+ crypto_pk_free(rp1_onion_pkey); \
+ crypto_pk_free(r1_onion_pkey); \
+ tt_int_op(crypto_pk_cmp_keys(rp1->identity_pkey, r1->identity_pkey), \
+ OP_EQ, 0); \
+ tt_int_op(rp1->supports_tunnelled_dir_requests, OP_EQ, \
+ r1->supports_tunnelled_dir_requests); \
+STMT_END
+
+/* Check that routerinfo r1 and extrainfo e1 are consistent.
+ * Only performs some basic checks.
+ */
+#define CHECK_EXTRAINFO_CONSISTENCY(r1, e1) \
+STMT_BEGIN \
+ tt_assert(r1); \
+ tt_assert(e1); \
+\
+ tt_str_op(e1->nickname, OP_EQ, r1->nickname); \
+STMT_END
+
+/** Run unit tests for router descriptor generation logic for a RSA-only
+ * router. Tor versions without ed25519 (0.2.6 and earlier) are no longer
+ * officially supported, but the authorities still accept their descriptors.
+ */
+static void
+test_dir_formats_rsa(void *arg)
{
char *buf = NULL;
- char buf2[8192];
- char platform[256];
- char fingerprint[FINGERPRINT_LEN+1];
- char *pk1_str = NULL, *pk2_str = NULL, *cp;
- size_t pk1_str_len, pk2_str_len;
- routerinfo_t *r1=NULL, *r2=NULL;
- crypto_pk_t *pk1 = NULL, *pk2 = NULL;
- routerinfo_t *rp1 = NULL, *rp2 = NULL;
- addr_policy_t *ex1, *ex2;
- routerlist_t *dir1 = NULL, *dir2 = NULL;
+ char *buf2 = NULL;
+ char *cp = NULL;
+
uint8_t *rsa_cc = NULL;
- or_options_t *options = get_options_mutable();
- const addr_policy_t *p;
- time_t now = time(NULL);
- port_cfg_t orport, dirport;
- char cert_buf[256];
- (void)arg;
- pk1 = pk_generate(0);
- pk2 = pk_generate(1);
+ routerinfo_t *r1 = NULL;
+ extrainfo_t *e1 = NULL;
+ routerinfo_t *rp1 = NULL;
+ extrainfo_t *ep1 = NULL;
+
+ smartlist_t *chunks = NULL;
+ const char *msg = NULL;
+ int rv = -1;
- tt_assert(pk1 && pk2);
+ or_options_t *options = get_options_mutable();
+ setup_dir_formats_options((const char *)arg, options);
hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE);
- get_platform_str(platform, sizeof(platform));
- r1 = tor_malloc_zero(sizeof(routerinfo_t));
- r1->addr = 0xc0a80001u; /* 192.168.0.1 */
- r1->cache_info.published_on = 0;
- r1->or_port = 9000;
- r1->dir_port = 9003;
- r1->supports_tunnelled_dir_requests = 1;
- tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
- r1->ipv6_orport = 9999;
- router_set_rsa_onion_pkey(pk1, &r1->onion_pkey, &r1->onion_pkey_len);
- /* Fake just enough of an ntor key to get by */
+ /* r1 is a minimal, RSA-only descriptor, with DirPort and IPv6 */
+ r1 = basic_routerinfo_new("Magri", 0xc0a80001u /* 192.168.0.1 */,
+ 9000, 9003,
+ 1000, 5000, 10000,
+ 0,
+ NULL);
+
+ /* Fake just enough of an ntor key to get by */
curve25519_keypair_t r1_onion_keypair;
curve25519_keypair_generate(&r1_onion_keypair, 0);
r1->onion_curve25519_pkey = tor_memdup(&r1_onion_keypair.pubkey,
sizeof(curve25519_public_key_t));
- r1->identity_pkey = crypto_pk_dup_key(pk2);
- r1->bandwidthrate = 1000;
- r1->bandwidthburst = 5000;
- r1->bandwidthcapacity = 10000;
+
+ /* Now add IPv6 */
+ tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
+ r1->ipv6_orport = 9999;
+
r1->exit_policy = NULL;
- r1->nickname = tor_strdup("Magri");
- r1->platform = tor_strdup(platform);
- ex1 = tor_malloc_zero(sizeof(addr_policy_t));
- ex2 = tor_malloc_zero(sizeof(addr_policy_t));
- ex1->policy_type = ADDR_POLICY_ACCEPT;
- tor_addr_from_ipv4h(&ex1->addr, 0);
- ex1->maskbits = 0;
- ex1->prt_min = ex1->prt_max = 80;
- ex2->policy_type = ADDR_POLICY_REJECT;
- tor_addr_from_ipv4h(&ex2->addr, 18<<24);
- ex2->maskbits = 8;
- ex2->prt_min = ex2->prt_max = 24;
- r2 = tor_malloc_zero(sizeof(routerinfo_t));
- r2->addr = 0x0a030201u; /* 10.3.2.1 */
+ /* XXXX+++ router_dump_to_string should really take this from ri. */
+ options->ContactInfo = tor_strdup("Magri White "
+ "<magri@elsewhere.example.com>");
+
+ setup_mock_configured_ports(r1->or_port, r1->dir_port);
+
+ buf = router_dump_router_to_string(r1, r1->identity_pkey, NULL, NULL, NULL);
+ tt_assert(buf);
+
+ tor_free(options->ContactInfo);
+ cleanup_mock_configured_ports();
+
+ /* Synthesise a router descriptor, without the signature */
+ chunks = smartlist_new();
+
+ smartlist_add(chunks, get_new_router_line(r1));
+ smartlist_add_strdup(chunks, "or-address [1:2:3:4::]:9999\n");
+
+ smartlist_add(chunks, get_new_platform_line());
+ smartlist_add(chunks, get_new_published_line(r1));
+ smartlist_add(chunks, get_new_fingerprint_line(r1));
+
+ smartlist_add(chunks, get_new_uptime_line(0));
+ smartlist_add(chunks, get_new_bandwidth_line(r1));
+
+ smartlist_add(chunks, get_new_onion_key_block(r1));
+ smartlist_add(chunks, get_new_signing_key_block(r1));
+
+ smartlist_add_strdup(chunks, "hidden-service-dir\n");
+
+ smartlist_add_strdup(chunks, "contact Magri White "
+ "<magri@elsewhere.example.com>\n");
+
+ smartlist_add(chunks, get_new_bridge_distribution_request_line(options));
+ smartlist_add(chunks, get_new_ntor_onion_key_line(&r1_onion_keypair.pubkey));
+ smartlist_add_strdup(chunks, "reject *:*\n");
+ smartlist_add_strdup(chunks, "tunnelled-dir-server\n");
+
+ smartlist_add_strdup(chunks, "router-signature\n");
+
+ size_t len_out = 0;
+ buf2 = smartlist_join_strings(chunks, "", 0, &len_out);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+
+ tt_assert(len_out > 0);
+
+ buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
+ * twice */
+
+ tt_str_op(buf,OP_EQ, buf2);
+ tor_free(buf);
+
+ setup_mock_configured_ports(r1->or_port, r1->dir_port);
+
+ buf = router_dump_router_to_string(r1, r1->identity_pkey, NULL, NULL, NULL);
+ tt_assert(buf);
+
+ cleanup_mock_configured_ports();
+
+ /* Now, try to parse buf */
+ cp = buf;
+ rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
+
+ CHECK_ROUTERINFO_CONSISTENCY(r1, rp1);
+
+ tt_assert(rp1->policy_is_reject_star);
+
+ tor_free(buf);
+ routerinfo_free(rp1);
+
+ /* Test extrainfo creation.
+ * We avoid calling router_build_fresh_unsigned_routerinfo(), because it's
+ * too complex. Instead, we re-use the manually-created routerinfos.
+ */
+
+ /* Set up standard mocks and data */
+ setup_mocks_for_fresh_descriptor(r1, NULL);
+
+ /* router_build_fresh_signed_extrainfo() passes the result of
+ * get_master_signing_key_cert() directly to tor_cert_dup(), which fails on
+ * NULL. But we want a NULL ei->cache_info.signing_key_cert to test the
+ * non-ed key path.
+ */
+ MOCK(tor_cert_dup, mock_tor_cert_dup_null);
+
+ /* Fake just enough of an ORPort and DirPort to get by */
+ setup_mock_configured_ports(r1->or_port, r1->dir_port);
+
+ /* Test some of the low-level static functions. */
+ e1 = router_build_fresh_signed_extrainfo(r1);
+ tt_assert(e1);
+ router_update_routerinfo_from_extrainfo(r1, e1);
+ rv = router_dump_and_sign_routerinfo_descriptor_body(r1);
+ tt_assert(rv == 0);
+ msg = "";
+ rv = routerinfo_incompatible_with_extrainfo(r1->identity_pkey, e1,
+ &r1->cache_info, &msg);
+ /* If they are incompatible, fail and show the msg string */
+ tt_str_op(msg, OP_EQ, "");
+ tt_assert(rv == 0);
+
+ /* Now cleanup */
+ cleanup_mocks_for_fresh_descriptor();
+
+ UNMOCK(tor_cert_dup);
+
+ cleanup_mock_configured_ports();
+
+ CHECK_EXTRAINFO_CONSISTENCY(r1, e1);
+
+ /* Test that the signed ri is parseable */
+ tt_assert(r1->cache_info.signed_descriptor_body);
+ cp = r1->cache_info.signed_descriptor_body;
+ rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
+
+ CHECK_ROUTERINFO_CONSISTENCY(r1, rp1);
+
+ tt_assert(rp1->policy_is_reject_star);
+
+ routerinfo_free(rp1);
+
+ /* Test that the signed ei is parseable */
+ tt_assert(e1->cache_info.signed_descriptor_body);
+ cp = e1->cache_info.signed_descriptor_body;
+ ep1 = extrainfo_parse_entry_from_string((const char*)cp,NULL,1,NULL,NULL);
+
+ CHECK_EXTRAINFO_CONSISTENCY(r1, ep1);
+
+ /* In future tests, we could check the actual extrainfo statistics. */
+
+ extrainfo_free(ep1);
+
+ done:
+ dirserv_free_fingerprint_list();
+
+ tor_free(options->ContactInfo);
+ tor_free(options->Nickname);
+
+ cleanup_mock_configured_ports();
+ cleanup_mocks_for_fresh_descriptor();
+
+ if (chunks) {
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+ }
+
+ routerinfo_free(r1);
+ routerinfo_free(rp1);
+
+ extrainfo_free(e1);
+ extrainfo_free(ep1);
+
+ tor_free(rsa_cc);
+
+ tor_free(buf);
+ tor_free(buf2);
+}
+
+/* Check that the exit policy in rp2 is as expected. */
+#define CHECK_PARSED_EXIT_POLICY(rp2) \
+STMT_BEGIN \
+ tt_int_op(smartlist_len(rp2->exit_policy),OP_EQ, 2); \
+ \
+ p = smartlist_get(rp2->exit_policy, 0); \
+ tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_ACCEPT); \
+ tt_assert(tor_addr_is_null(&p->addr)); \
+ tt_int_op(p->maskbits,OP_EQ, 0); \
+ tt_int_op(p->prt_min,OP_EQ, 80); \
+ tt_int_op(p->prt_max,OP_EQ, 80); \
+ \
+ p = smartlist_get(rp2->exit_policy, 1); \
+ tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_REJECT); \
+ tt_assert(tor_addr_eq(&p->addr, &ex2->addr)); \
+ tt_int_op(p->maskbits,OP_EQ, 8); \
+ tt_int_op(p->prt_min,OP_EQ, 24); \
+ tt_int_op(p->prt_max,OP_EQ, 24); \
+STMT_END
+
+/** Run unit tests for router descriptor generation logic for a RSA + ed25519
+ * router.
+ */
+static void
+test_dir_formats_rsa_ed25519(void *arg)
+{
+ char *buf = NULL;
+ char *buf2 = NULL;
+ char *cp = NULL;
+
+ crypto_pk_t *r2_onion_pkey = NULL;
+ char cert_buf[256];
+ uint8_t *rsa_cc = NULL;
+ time_t now = time(NULL);
+
+ routerinfo_t *r2 = NULL;
+ extrainfo_t *e2 = NULL;
+ routerinfo_t *r2_out = NULL;
+ routerinfo_t *rp2 = NULL;
+ extrainfo_t *ep2 = NULL;
+ addr_policy_t *ex1, *ex2;
+ const addr_policy_t *p;
+
+ smartlist_t *chunks = NULL;
+ int rv = -1;
+
+ or_options_t *options = get_options_mutable();
+ setup_dir_formats_options((const char *)arg, options);
+
+ hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE);
+
+ /* r2 is a RSA + ed25519 descriptor, with an exit policy, but no DirPort or
+ * IPv6 */
+ r2 = basic_routerinfo_new("Fred", 0x0a030201u /* 10.3.2.1 */,
+ 9005, 0,
+ 3000, 3000, 3000,
+ 5,
+ &r2_onion_pkey);
+
+ /* Fake just enough of an ntor key to get by */
+ curve25519_keypair_t r2_onion_keypair;
+ curve25519_keypair_generate(&r2_onion_keypair, 0);
+ r2->onion_curve25519_pkey = tor_memdup(&r2_onion_keypair.pubkey,
+ sizeof(curve25519_public_key_t));
+
+ /* Now add relay ed25519 keys
+ * We can't use init_mock_ed_keys() here, because the keys are seeded */
ed25519_keypair_t kp1, kp2;
ed25519_secret_key_from_seed(&kp1.seckey,
(const uint8_t*)"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY");
@@ -228,157 +950,78 @@ test_dir_formats(void *arg)
&kp2.pubkey,
now, 86400,
CERT_FLAG_INCLUDE_SIGNING_KEY);
- r2->platform = tor_strdup(platform);
- r2->cache_info.published_on = 5;
- r2->or_port = 9005;
- r2->dir_port = 0;
- r2->supports_tunnelled_dir_requests = 1;
- router_set_rsa_onion_pkey(pk2, &r2->onion_pkey, &r2->onion_pkey_len);
- curve25519_keypair_t r2_onion_keypair;
- curve25519_keypair_generate(&r2_onion_keypair, 0);
- r2->onion_curve25519_pkey = tor_memdup(&r2_onion_keypair.pubkey,
- sizeof(curve25519_public_key_t));
- r2->identity_pkey = crypto_pk_dup_key(pk1);
- r2->bandwidthrate = r2->bandwidthburst = r2->bandwidthcapacity = 3000;
+
+ /* Now add an exit policy */
+ ex1 = tor_malloc_zero(sizeof(addr_policy_t));
+ ex2 = tor_malloc_zero(sizeof(addr_policy_t));
+ ex1->policy_type = ADDR_POLICY_ACCEPT;
+ tor_addr_from_ipv4h(&ex1->addr, 0);
+ ex1->maskbits = 0;
+ ex1->prt_min = ex1->prt_max = 80;
+ ex2->policy_type = ADDR_POLICY_REJECT;
+ tor_addr_from_ipv4h(&ex2->addr, 18<<24);
+ ex2->maskbits = 8;
+ ex2->prt_min = ex2->prt_max = 24;
+
r2->exit_policy = smartlist_new();
smartlist_add(r2->exit_policy, ex1);
smartlist_add(r2->exit_policy, ex2);
- r2->nickname = tor_strdup("Fred");
-
- tt_assert(!crypto_pk_write_public_key_to_string(pk1, &pk1_str,
- &pk1_str_len));
- tt_assert(!crypto_pk_write_public_key_to_string(pk2 , &pk2_str,
- &pk2_str_len));
-
- /* 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 */
- options->AssumeReachable = 1;
- /* Fake just enough of an ORPort and DirPort to get by */
- MOCK(get_configured_ports, mock_get_configured_ports);
- mocked_configured_ports = smartlist_new();
-
- memset(&orport, 0, sizeof(orport));
- orport.type = CONN_TYPE_OR_LISTENER;
- orport.addr.family = AF_INET;
- orport.port = 9000;
- smartlist_add(mocked_configured_ports, &orport);
-
- memset(&dirport, 0, sizeof(dirport));
- dirport.type = CONN_TYPE_DIR_LISTENER;
- dirport.addr.family = AF_INET;
- dirport.port = 9003;
- smartlist_add(mocked_configured_ports, &dirport);
-
- buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL);
-
- UNMOCK(get_configured_ports);
- smartlist_free(mocked_configured_ports);
- mocked_configured_ports = NULL;
+ /* Fake just enough of an ORPort to get by */
+ setup_mock_configured_ports(r2->or_port, 0);
- tor_free(options->ContactInfo);
+ buf = router_dump_router_to_string(r2,
+ r2->identity_pkey, r2_onion_pkey,
+ &r2_onion_keypair, &kp2);
tt_assert(buf);
- strlcpy(buf2, "router Magri 192.168.0.1 9000 0 9003\n"
- "or-address [1:2:3:4::]:9999\n"
- "platform Tor "VERSION" on ", sizeof(buf2));
- strlcat(buf2, get_uname(), sizeof(buf2));
- strlcat(buf2, "\n"
- "published 1970-01-01 00:00:00\n"
- "fingerprint ", sizeof(buf2));
- tt_assert(!crypto_pk_get_fingerprint(pk2, fingerprint, 1));
- strlcat(buf2, fingerprint, sizeof(buf2));
- strlcat(buf2, "\nuptime 0\n"
- /* XXX the "0" above is hard-coded, but even if we made it reflect
- * uptime, that still wouldn't make it right, because the two
- * descriptors might be made on different seconds... hm. */
- "bandwidth 1000 5000 10000\n"
- "onion-key\n", sizeof(buf2));
- strlcat(buf2, pk1_str, sizeof(buf2));
- strlcat(buf2, "signing-key\n", sizeof(buf2));
- strlcat(buf2, pk2_str, sizeof(buf2));
- strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
- strlcat(buf2, "contact Magri White <magri@elsewhere.example.com>\n",
- sizeof(buf2));
- strlcat(buf2, "ntor-onion-key ", sizeof(buf2));
- base64_encode(cert_buf, sizeof(cert_buf),
- (const char*)r1_onion_keypair.pubkey.public_key, 32,
- BASE64_ENCODE_MULTILINE);
- strlcat(buf2, cert_buf, sizeof(buf2));
- strlcat(buf2, "reject *:*\n", sizeof(buf2));
- strlcat(buf2, "tunnelled-dir-server\nrouter-signature\n", sizeof(buf2));
- buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
- * twice */
+ cleanup_mock_configured_ports();
- tt_str_op(buf,OP_EQ, buf2);
- tor_free(buf);
+ chunks = smartlist_new();
- buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL);
- tt_assert(buf);
- cp = buf;
- rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
- tt_assert(rp1);
- tt_int_op(rp1->addr,OP_EQ, r1->addr);
- tt_int_op(rp1->or_port,OP_EQ, r1->or_port);
- tt_int_op(rp1->dir_port,OP_EQ, r1->dir_port);
- tt_int_op(rp1->bandwidthrate,OP_EQ, r1->bandwidthrate);
- tt_int_op(rp1->bandwidthburst,OP_EQ, r1->bandwidthburst);
- tt_int_op(rp1->bandwidthcapacity,OP_EQ, r1->bandwidthcapacity);
- crypto_pk_t *onion_pkey = router_get_rsa_onion_pkey(rp1->onion_pkey,
- rp1->onion_pkey_len);
- tt_int_op(crypto_pk_cmp_keys(onion_pkey, pk1), OP_EQ, 0);
- crypto_pk_free(onion_pkey);
- tt_int_op(crypto_pk_cmp_keys(rp1->identity_pkey, pk2), OP_EQ, 0);
- tt_assert(rp1->supports_tunnelled_dir_requests);
- //tt_assert(rp1->exit_policy == NULL);
- tor_free(buf);
+ /* Synthesise a router descriptor, without the signatures */
+ smartlist_add(chunks, get_new_router_line(r2));
- strlcpy(buf2,
- "router Fred 10.3.2.1 9005 0 0\n"
- "identity-ed25519\n"
- "-----BEGIN ED25519 CERT-----\n", sizeof(buf2));
+ smartlist_add_strdup(chunks,
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n");
base64_encode(cert_buf, sizeof(cert_buf),
(const char*)r2->cache_info.signing_key_cert->encoded,
r2->cache_info.signing_key_cert->encoded_len,
BASE64_ENCODE_MULTILINE);
- strlcat(buf2, cert_buf, sizeof(buf2));
- strlcat(buf2, "-----END ED25519 CERT-----\n", sizeof(buf2));
- strlcat(buf2, "master-key-ed25519 ", sizeof(buf2));
+ smartlist_add_strdup(chunks, cert_buf);
+ smartlist_add_strdup(chunks, "-----END ED25519 CERT-----\n");
+
+ smartlist_add_strdup(chunks, "master-key-ed25519 ");
{
char k[ED25519_BASE64_LEN+1];
- tt_int_op(ed25519_public_to_base64(k,
- &r2->cache_info.signing_key_cert->signing_key),
- OP_GE, 0);
- strlcat(buf2, k, sizeof(buf2));
- strlcat(buf2, "\n", sizeof(buf2));
+ ed25519_public_to_base64(k, &r2->cache_info.signing_key_cert->signing_key);
+ smartlist_add_strdup(chunks, k);
+ smartlist_add_strdup(chunks, "\n");
}
- strlcat(buf2, "platform Tor "VERSION" on ", sizeof(buf2));
- strlcat(buf2, get_uname(), sizeof(buf2));
- strlcat(buf2, "\n"
- "published 1970-01-01 00:00:05\n"
- "fingerprint ", sizeof(buf2));
- tt_assert(!crypto_pk_get_fingerprint(pk1, fingerprint, 1));
- strlcat(buf2, fingerprint, sizeof(buf2));
- strlcat(buf2, "\nuptime 0\n"
- "bandwidth 3000 3000 3000\n", sizeof(buf2));
- strlcat(buf2, "onion-key\n", sizeof(buf2));
- strlcat(buf2, pk2_str, sizeof(buf2));
- strlcat(buf2, "signing-key\n", sizeof(buf2));
- strlcat(buf2, pk1_str, sizeof(buf2));
+
+ smartlist_add(chunks, get_new_platform_line());
+ smartlist_add(chunks, get_new_published_line(r2));
+ smartlist_add(chunks, get_new_fingerprint_line(r2));
+
+ smartlist_add(chunks, get_new_uptime_line(0));
+ smartlist_add(chunks, get_new_bandwidth_line(r2));
+
+ smartlist_add(chunks, get_new_onion_key_block(r2));
+ smartlist_add(chunks, get_new_signing_key_block(r2));
+
int rsa_cc_len;
- rsa_cc = make_tap_onion_key_crosscert(pk2,
+ rsa_cc = make_tap_onion_key_crosscert(r2_onion_pkey,
&kp1.pubkey,
- pk1,
+ r2->identity_pkey,
&rsa_cc_len);
tt_assert(rsa_cc);
base64_encode(cert_buf, sizeof(cert_buf), (char*)rsa_cc, rsa_cc_len,
BASE64_ENCODE_MULTILINE);
- strlcat(buf2, "onion-key-crosscert\n"
- "-----BEGIN CROSSCERT-----\n", sizeof(buf2));
- strlcat(buf2, cert_buf, sizeof(buf2));
- strlcat(buf2, "-----END CROSSCERT-----\n", sizeof(buf2));
+ smartlist_add_strdup(chunks, "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n");
+ smartlist_add_strdup(chunks, cert_buf);
+ smartlist_add_strdup(chunks, "-----END CROSSCERT-----\n");
int ntor_cc_sign;
{
tor_cert_t *ntor_cc = NULL;
@@ -393,112 +1036,165 @@ test_dir_formats(void *arg)
BASE64_ENCODE_MULTILINE);
tor_cert_free(ntor_cc);
}
- tor_snprintf(buf2+strlen(buf2), sizeof(buf2)-strlen(buf2),
+ smartlist_add_asprintf(chunks,
"ntor-onion-key-crosscert %d\n"
"-----BEGIN ED25519 CERT-----\n"
"%s"
"-----END ED25519 CERT-----\n", ntor_cc_sign, cert_buf);
- strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
- strlcat(buf2, "ntor-onion-key ", sizeof(buf2));
- base64_encode(cert_buf, sizeof(cert_buf),
- (const char*)r2_onion_keypair.pubkey.public_key, 32,
- BASE64_ENCODE_MULTILINE);
- strlcat(buf2, cert_buf, sizeof(buf2));
- strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2));
- strlcat(buf2, "tunnelled-dir-server\n", sizeof(buf2));
- strlcat(buf2, "router-sig-ed25519 ", sizeof(buf2));
+ smartlist_add_strdup(chunks, "hidden-service-dir\n");
- /* Fake just enough of an ORPort to get by */
- MOCK(get_configured_ports, mock_get_configured_ports);
- mocked_configured_ports = smartlist_new();
+ smartlist_add(chunks, get_new_bridge_distribution_request_line(options));
+ smartlist_add(chunks, get_new_ntor_onion_key_line(&r2_onion_keypair.pubkey));
+ smartlist_add_strdup(chunks, "accept *:80\nreject 18.0.0.0/8:24\n");
+ smartlist_add_strdup(chunks, "tunnelled-dir-server\n");
- memset(&orport, 0, sizeof(orport));
- orport.type = CONN_TYPE_OR_LISTENER;
- orport.addr.family = AF_INET;
- orport.port = 9005;
- smartlist_add(mocked_configured_ports, &orport);
+ smartlist_add_strdup(chunks, "router-sig-ed25519 ");
- buf = router_dump_router_to_string(r2, pk1, pk2, &r2_onion_keypair, &kp2);
- tt_assert(buf);
- buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
+ size_t len_out = 0;
+ buf2 = smartlist_join_strings(chunks, "", 0, &len_out);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+
+ tt_assert(len_out > 0);
+
+ buf[strlen(buf2)] = '\0'; /* Don't compare either sig; they're never the same
* twice */
tt_str_op(buf, OP_EQ, buf2);
tor_free(buf);
- buf = router_dump_router_to_string(r2, pk1, NULL, NULL, NULL);
+ setup_mock_configured_ports(r2->or_port, 0);
- UNMOCK(get_configured_ports);
- smartlist_free(mocked_configured_ports);
- mocked_configured_ports = NULL;
+ buf = router_dump_router_to_string(r2, r2->identity_pkey, NULL, NULL, NULL);
+ tt_assert(buf);
+
+ cleanup_mock_configured_ports();
- /* Reset for later */
+ /* Now, try to parse buf */
cp = buf;
rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
- tt_assert(rp2);
- tt_int_op(rp2->addr,OP_EQ, r2->addr);
- tt_int_op(rp2->or_port,OP_EQ, r2->or_port);
- tt_int_op(rp2->dir_port,OP_EQ, r2->dir_port);
- tt_int_op(rp2->bandwidthrate,OP_EQ, r2->bandwidthrate);
- tt_int_op(rp2->bandwidthburst,OP_EQ, r2->bandwidthburst);
- tt_int_op(rp2->bandwidthcapacity,OP_EQ, r2->bandwidthcapacity);
+
+ CHECK_ROUTERINFO_CONSISTENCY(r2, rp2);
+
tt_mem_op(rp2->onion_curve25519_pkey->public_key,OP_EQ,
r2->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN);
- onion_pkey = router_get_rsa_onion_pkey(rp2->onion_pkey,
- rp2->onion_pkey_len);
- tt_int_op(crypto_pk_cmp_keys(onion_pkey, pk2), OP_EQ, 0);
- crypto_pk_free(onion_pkey);
- tt_int_op(crypto_pk_cmp_keys(rp2->identity_pkey, pk1), OP_EQ, 0);
- tt_assert(rp2->supports_tunnelled_dir_requests);
-
- tt_int_op(smartlist_len(rp2->exit_policy),OP_EQ, 2);
-
- p = smartlist_get(rp2->exit_policy, 0);
- tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_ACCEPT);
- tt_assert(tor_addr_is_null(&p->addr));
- tt_int_op(p->maskbits,OP_EQ, 0);
- tt_int_op(p->prt_min,OP_EQ, 80);
- tt_int_op(p->prt_max,OP_EQ, 80);
-
- p = smartlist_get(rp2->exit_policy, 1);
- tt_int_op(p->policy_type,OP_EQ, ADDR_POLICY_REJECT);
- tt_assert(tor_addr_eq(&p->addr, &ex2->addr));
- tt_int_op(p->maskbits,OP_EQ, 8);
- tt_int_op(p->prt_min,OP_EQ, 24);
- tt_int_op(p->prt_max,OP_EQ, 24);
-
-#if 0
- /* Okay, now for the directories. */
- {
- fingerprint_list = smartlist_new();
- crypto_pk_get_fingerprint(pk2, buf, 1);
- add_fingerprint_to_dir(buf, fingerprint_list, 0);
- crypto_pk_get_fingerprint(pk1, buf, 1);
- add_fingerprint_to_dir(buf, fingerprint_list, 0);
+
+ CHECK_PARSED_EXIT_POLICY(rp2);
+
+ tor_free(buf);
+ routerinfo_free(rp2);
+
+ /* Test extrainfo creation. */
+
+ /* Set up standard mocks and data */
+ setup_mocks_for_fresh_descriptor(r2, r2_onion_pkey);
+
+ /* router_build_fresh_descriptor() requires
+ * router_build_fresh_unsigned_routerinfo(), but the implementation is
+ * too complex. Instead, we re-use r2.
+ */
+ mocked_routerinfo = r2;
+ MOCK(router_build_fresh_unsigned_routerinfo,
+ mock_router_build_fresh_unsigned_routerinfo);
+
+ /* r2 uses ed25519, so we need to mock the ed key functions */
+ mocked_master_signing_key = &kp2;
+ MOCK(get_master_signing_keypair, mock_get_master_signing_keypair);
+
+ mocked_signing_key_cert = r2->cache_info.signing_key_cert;
+ MOCK(get_master_signing_key_cert, mock_get_master_signing_key_cert);
+
+ mocked_curve25519_onion_key = &r2_onion_keypair;
+ MOCK(get_current_curve25519_keypair, mock_get_current_curve25519_keypair);
+
+ /* Fake just enough of an ORPort to get by */
+ setup_mock_configured_ports(r2->or_port, 0);
+
+ /* Test the high-level interface. */
+ rv = router_build_fresh_descriptor(&r2_out, &e2);
+ if (rv < 0) {
+ /* router_build_fresh_descriptor() frees r2 on failure. */
+ r2 = NULL;
+ /* Get rid of an alias to rp2 */
+ r2_out = NULL;
}
+ tt_assert(rv == 0);
+ tt_assert(r2_out);
+ tt_assert(e2);
+ /* Guaranteed by mock_router_build_fresh_unsigned_routerinfo() */
+ tt_ptr_op(r2_out, OP_EQ, r2);
+ /* Get rid of an alias to r2 */
+ r2_out = NULL;
+
+ /* Now cleanup */
+ cleanup_mocks_for_fresh_descriptor();
+
+ mocked_routerinfo = NULL;
+ UNMOCK(router_build_fresh_unsigned_routerinfo);
+ mocked_master_signing_key = NULL;
+ UNMOCK(get_master_signing_keypair);
+ mocked_signing_key_cert = NULL;
+ UNMOCK(get_master_signing_key_cert);
+ mocked_curve25519_onion_key = NULL;
+ UNMOCK(get_current_curve25519_keypair);
+
+ cleanup_mock_configured_ports();
+
+ CHECK_EXTRAINFO_CONSISTENCY(r2, e2);
+
+ /* Test that the signed ri is parseable */
+ tt_assert(r2->cache_info.signed_descriptor_body);
+ cp = r2->cache_info.signed_descriptor_body;
+ rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
-#endif /* 0 */
- dirserv_free_fingerprint_list();
+ CHECK_ROUTERINFO_CONSISTENCY(r2, rp2);
+
+ tt_mem_op(rp2->onion_curve25519_pkey->public_key,OP_EQ,
+ r2->onion_curve25519_pkey->public_key,
+ CURVE25519_PUBKEY_LEN);
+
+ CHECK_PARSED_EXIT_POLICY(rp2);
+
+ routerinfo_free(rp2);
+
+ /* Test that the signed ei is parseable */
+ tt_assert(e2->cache_info.signed_descriptor_body);
+ cp = e2->cache_info.signed_descriptor_body;
+ ep2 = extrainfo_parse_entry_from_string((const char*)cp,NULL,1,NULL,NULL);
+
+ CHECK_EXTRAINFO_CONSISTENCY(r2, ep2);
+
+ /* In future tests, we could check the actual extrainfo statistics. */
+
+ extrainfo_free(ep2);
done:
- if (r1)
- routerinfo_free(r1);
- if (r2)
- routerinfo_free(r2);
- if (rp2)
- routerinfo_free(rp2);
+ dirserv_free_fingerprint_list();
+
+ tor_free(options->Nickname);
+
+ cleanup_mock_configured_ports();
+ cleanup_mocks_for_fresh_descriptor();
+
+ if (chunks) {
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+ }
+
+ routerinfo_free(r2);
+ routerinfo_free(r2_out);
+ routerinfo_free(rp2);
+
+ extrainfo_free(e2);
+ extrainfo_free(ep2);
tor_free(rsa_cc);
+ crypto_pk_free(r2_onion_pkey);
+
tor_free(buf);
- tor_free(pk1_str);
- tor_free(pk2_str);
- if (pk1) crypto_pk_free(pk1);
- if (pk2) crypto_pk_free(pk2);
- if (rp1) routerinfo_free(rp1);
- tor_free(dir1); /* XXXX And more !*/
- tor_free(dir2); /* And more !*/
+ tor_free(buf2);
}
#include "failing_routerdescs.inc"
@@ -1767,7 +2463,8 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, "", 0);
setup_capture_of_logs(LOG_WARN);
tt_int_op(-1, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
expect_log_msg("Empty bandwidth file\n");
teardown_capture_of_logs();
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
@@ -1783,7 +2480,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(-1, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op("", OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1794,7 +2493,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, header_lines_v100, 0);
bw_file_headers = smartlist_new();
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1807,7 +2508,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1818,7 +2521,8 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
tor_asprintf(&content, "%s%s", header_lines_v100, relay_lines_v100);
write_str_to_file(fname, content, 0);
tor_free(content);
- tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, NULL));
+ tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, NULL,
+ NULL));
/* Test bandwidth file including v1.1.0 bandwidth headers and
* v1.0.0 relay lines. bw_file_headers will contain the v1.1.0 headers. */
@@ -1828,7 +2532,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1844,7 +2550,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1861,7 +2569,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1872,7 +2582,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
bw_file_headers = smartlist_new();
write_str_to_file(fname, header_lines_v110_no_terminator, 0);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1883,7 +2595,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
bw_file_headers = smartlist_new();
write_str_to_file(fname, header_lines_v110, 0);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1898,7 +2612,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1913,7 +2629,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1929,7 +2647,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1946,7 +2666,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
tt_str_op(bw_file_headers_str_bad, OP_EQ, bw_file_headers_str);
SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c));
@@ -1964,7 +2686,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
tt_int_op(MAX_BW_FILE_HEADER_COUNT_IN_VOTE, OP_EQ,
smartlist_len(bw_file_headers));
bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL);
@@ -1985,7 +2709,9 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
write_str_to_file(fname, content, 0);
tor_free(content);
tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL,
- bw_file_headers));
+ bw_file_headers,
+ NULL));
+
tt_int_op(MAX_BW_FILE_HEADER_COUNT_IN_VOTE, OP_EQ,
smartlist_len(bw_file_headers));
/* force bw_file_headers to be bigger than
@@ -2014,7 +2740,8 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
/* Read the bandwidth file */
setup_full_capture_of_logs(LOG_DEBUG);
- tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, NULL));
+ tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, NULL,
+ NULL));
expect_log_msg_containing("Ignoring bandwidth file line");
teardown_capture_of_logs();
@@ -2032,11 +2759,13 @@ test_dir_dirserv_read_measured_bandwidths(void *arg)
/* Read the bandwidth file */
setup_full_capture_of_logs(LOG_DEBUG);
- tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, NULL));
+ tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, NULL,
+ NULL));
expect_log_msg_not_containing("Ignoring bandwidth file line");
teardown_capture_of_logs();
done:
+ unlink(fname);
tor_free(fname);
tor_free(header_lines_v100);
tor_free(header_lines_v110_no_terminator);
@@ -2863,11 +3592,17 @@ test_a_networkstatus(
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);
+ cert1 = mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(cert1);
- cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
+ cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2,
+ strlen(AUTHORITY_CERT_2),
+ NULL);
tt_assert(cert2);
- cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+ cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3,
+ strlen(AUTHORITY_CERT_3),
+ NULL);
tt_assert(cert3);
sign_skey_1 = crypto_pk_new();
sign_skey_2 = crypto_pk_new();
@@ -2969,7 +3704,7 @@ test_a_networkstatus(
sign_skey_leg1,
FLAV_NS);
tt_assert(consensus_text);
- con = networkstatus_parse_vote_from_string(consensus_text, NULL,
+ con = networkstatus_parse_vote_from_string_(consensus_text, NULL,
NS_TYPE_CONSENSUS);
tt_assert(con);
//log_notice(LD_GENERAL, "<<%s>>\n<<%s>>\n<<%s>>\n",
@@ -2981,7 +3716,7 @@ test_a_networkstatus(
sign_skey_leg1,
FLAV_MICRODESC);
tt_assert(consensus_text_md);
- con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
+ con_md = networkstatus_parse_vote_from_string_(consensus_text_md, NULL,
NS_TYPE_CONSENSUS);
tt_assert(con_md);
tt_int_op(con_md->flavor,OP_EQ, FLAV_MICRODESC);
@@ -3080,13 +3815,13 @@ test_a_networkstatus(
tt_assert(consensus_text3);
tt_assert(consensus_text_md2);
tt_assert(consensus_text_md3);
- con2 = networkstatus_parse_vote_from_string(consensus_text2, NULL,
+ con2 = networkstatus_parse_vote_from_string_(consensus_text2, NULL,
NS_TYPE_CONSENSUS);
- con3 = networkstatus_parse_vote_from_string(consensus_text3, NULL,
+ con3 = networkstatus_parse_vote_from_string_(consensus_text3, NULL,
NS_TYPE_CONSENSUS);
- con_md2 = networkstatus_parse_vote_from_string(consensus_text_md2, NULL,
+ con_md2 = networkstatus_parse_vote_from_string_(consensus_text_md2, NULL,
NS_TYPE_CONSENSUS);
- con_md3 = networkstatus_parse_vote_from_string(consensus_text_md3, NULL,
+ con_md3 = networkstatus_parse_vote_from_string_(consensus_text_md3, NULL,
NS_TYPE_CONSENSUS);
tt_assert(con2);
tt_assert(con3);
@@ -3864,6 +4599,62 @@ mock_get_options(void)
return mock_options;
}
+/**
+ * Test dirauth_get_b64_digest_bw_file.
+ * This function should be near the other bwauth functions, but it needs
+ * mock_get_options, that is only defined here.
+ */
+
+static void
+test_dir_bwauth_bw_file_digest256(void *arg)
+{
+ (void)arg;
+ const char *content =
+ "1541171221\n"
+ "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 "
+ "master_key_ed25519=YaqV4vbvPYKucElk297eVdNArDz9HtIwUoIeo0+cVIpQ "
+ "bw=760 nick=Test time=2018-05-08T16:13:26\n";
+
+ char *fname = tor_strdup(get_fname("V3BandwidthsFile"));
+ /* Initialize to a wrong digest. */
+ uint8_t digest[DIGEST256_LEN] = "01234567890123456789abcdefghijkl";
+
+ /* Digest of an empty string. Initialize to a wrong digest. */
+ char digest_empty_str[DIGEST256_LEN] = "01234567890123456789abcdefghijkl";
+ crypto_digest256(digest_empty_str, "", 0, DIGEST_SHA256);
+
+ /* Digest of the content. Initialize to a wrong digest. */
+ char digest_expected[DIGEST256_LEN] = "01234567890123456789abcdefghijkl";
+ crypto_digest256(digest_expected, content, strlen(content), DIGEST_SHA256);
+
+ /* When the bandwidth file can not be found. */
+ tt_int_op(-1, OP_EQ,
+ dirserv_read_measured_bandwidths(fname,
+ NULL, NULL, digest));
+ tt_mem_op(digest, OP_EQ, digest_empty_str, DIGEST256_LEN);
+
+ /* When there is a timestamp but it is too old. */
+ write_str_to_file(fname, content, 0);
+ tt_int_op(-1, OP_EQ,
+ dirserv_read_measured_bandwidths(fname,
+ NULL, NULL, digest));
+ /* The digest will be correct. */
+ tt_mem_op(digest, OP_EQ, digest_expected, DIGEST256_LEN);
+
+ update_approx_time(1541171221);
+
+ /* When there is a bandwidth file and it can be read. */
+ tt_int_op(0, OP_EQ,
+ dirserv_read_measured_bandwidths(fname,
+ NULL, NULL, digest));
+ tt_mem_op(digest, OP_EQ, digest_expected, DIGEST256_LEN);
+
+ done:
+ unlink(fname);
+ tor_free(fname);
+ update_approx_time(time(NULL));
+}
+
static void
reset_routerstatus(routerstatus_t *rs,
const char *hex_identity_digest,
@@ -6087,6 +6878,80 @@ test_dir_find_dl_min_delay(void* data)
}
static void
+test_dir_matching_flags(void *arg)
+{
+ (void) arg;
+ routerstatus_t *rs_noflags = NULL;
+ routerstatus_t *rs = NULL;
+ char *s = NULL;
+
+ smartlist_t *tokens = smartlist_new();
+ memarea_t *area = memarea_new();
+
+ int expected_val_when_unused = 0;
+
+ const char *ex_noflags =
+ "r example hereiswhereyouridentitygoes 2015-08-30 12:00:00 "
+ "192.168.0.1 9001 0\n"
+ "m thisoneislongerbecauseitisa256bitmddigest33\n"
+ "s\n";
+ const char *cp = ex_noflags;
+ rs_noflags = routerstatus_parse_entry_from_string(
+ area, &cp,
+ cp + strlen(cp),
+ tokens, NULL, NULL,
+ MAX_SUPPORTED_CONSENSUS_METHOD, FLAV_MICRODESC);
+ tt_assert(rs_noflags);
+
+#define FLAG(string, field) STMT_BEGIN { \
+ tor_asprintf(&s,\
+ "r example hereiswhereyouridentitygoes 2015-08-30 12:00:00 " \
+ "192.168.0.1 9001 0\n" \
+ "m thisoneislongerbecauseitisa256bitmddigest33\n" \
+ "s %s\n", string); \
+ cp = s; \
+ rs = routerstatus_parse_entry_from_string( \
+ area, &cp, \
+ cp + strlen(cp), \
+ tokens, NULL, NULL, \
+ MAX_SUPPORTED_CONSENSUS_METHOD, FLAV_MICRODESC); \
+ /* the field should usually be 0 when no flags are listed */ \
+ tt_int_op(rs_noflags->field, OP_EQ, expected_val_when_unused); \
+ /* the field should be 1 when this flags islisted */ \
+ tt_int_op(rs->field, OP_EQ, 1); \
+ tor_free(s); \
+ routerstatus_free(rs); \
+} STMT_END
+
+ FLAG("Authority", is_authority);
+ FLAG("BadExit", is_bad_exit);
+ FLAG("Exit", is_exit);
+ FLAG("Fast", is_fast);
+ FLAG("Guard", is_possible_guard);
+ FLAG("HSDir", is_hs_dir);
+ FLAG("Stable", is_stable);
+ FLAG("StaleDesc", is_staledesc);
+ FLAG("V2Dir", is_v2_dir);
+
+ // These flags are assumed to be set whether they're declared or not.
+ expected_val_when_unused = 1;
+ FLAG("Running", is_flagged_running);
+ FLAG("Valid", is_valid);
+ expected_val_when_unused = 0;
+
+ // These flags are no longer used, but still parsed.
+ FLAG("Named", is_named);
+ FLAG("Unnamed", is_unnamed);
+
+ done:
+ tor_free(s);
+ routerstatus_free(rs);
+ routerstatus_free(rs_noflags);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+}
+
+static void
test_dir_assumed_flags(void *arg)
{
(void)arg;
@@ -6101,9 +6966,10 @@ test_dir_assumed_flags(void *arg)
"192.168.0.1 9001 0\n"
"m thisoneislongerbecauseitisa256bitmddigest33\n"
"s Fast Guard Stable\n";
+ const char *eos = str1 + strlen(str1);
const char *cp = str1;
- rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL,
+ rs = routerstatus_parse_entry_from_string(area, &cp, eos, tokens, NULL, NULL,
24, FLAV_MICRODESC);
tt_assert(rs);
tt_assert(rs->is_flagged_running);
@@ -6356,7 +7222,22 @@ test_dir_format_versions_list(void *arg)
struct testcase_t dir_tests[] = {
DIR_LEGACY(nicknames),
- DIR_LEGACY(formats),
+ /* extrainfo without any stats */
+ DIR_ARG(formats_rsa, TT_FORK, ""),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, ""),
+ /* on a bridge */
+ DIR_ARG(formats_rsa, TT_FORK, "b"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "b"),
+ /* extrainfo with basic stats */
+ DIR_ARG(formats_rsa, TT_FORK, "e"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "e"),
+ DIR_ARG(formats_rsa, TT_FORK, "be"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "be"),
+ /* extrainfo with all stats */
+ DIR_ARG(formats_rsa, TT_FORK, "es"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "es"),
+ DIR_ARG(formats_rsa, TT_FORK, "bes"),
+ DIR_ARG(formats_rsa_ed25519, TT_FORK, "bes"),
DIR(routerinfo_parsing, 0),
DIR(extrainfo_parsing, 0),
DIR(parse_router_list, TT_FORK),
@@ -6370,6 +7251,7 @@ struct testcase_t dir_tests[] = {
DIR_LEGACY(measured_bw_kb_line_is_after_headers),
DIR_LEGACY(measured_bw_kb_cache),
DIR_LEGACY(dirserv_read_measured_bandwidths),
+ DIR(bwauth_bw_file_digest256, 0),
DIR_LEGACY(param_voting),
DIR(param_voting_lookup, 0),
DIR_LEGACY(v3_networkstatus),
@@ -6410,6 +7292,7 @@ struct testcase_t dir_tests[] = {
DIR_ARG(find_dl_min_delay, TT_FORK, "cfr"),
DIR_ARG(find_dl_min_delay, TT_FORK, "car"),
DIR(assumed_flags, 0),
+ DIR(matching_flags, 0),
DIR(networkstatus_compute_bw_weights_v10, 0),
DIR(platform_str, 0),
DIR(networkstatus_consensus_has_ipv6, TT_FORK),
diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c
index 3723d6c31b..0b87e29873 100644
--- a/src/test/test_dir_common.c
+++ b/src/test/test_dir_common.c
@@ -42,14 +42,20 @@ dir_common_authority_pk_init(authority_cert_t **cert1,
{
/* Parse certificates and keys. */
authority_cert_t *cert;
- cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(cert);
tt_assert(cert->identity_key);
*cert1 = cert;
tt_assert(*cert1);
- *cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
+ *cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2,
+ strlen(AUTHORITY_CERT_2),
+ NULL);
tt_assert(*cert2);
- *cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+ *cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3,
+ strlen(AUTHORITY_CERT_3),
+ NULL);
tt_assert(*cert3);
*sign_skey_1 = crypto_pk_new();
*sign_skey_2 = crypto_pk_new();
@@ -266,7 +272,9 @@ dir_common_add_rs_and_parse(networkstatus_t *vote, networkstatus_t **vote_out,
/* dump the vote and try to parse it. */
v_text = format_networkstatus_vote(sign_skey, vote);
tt_assert(v_text);
- *vote_out = networkstatus_parse_vote_from_string(v_text, NULL, NS_TYPE_VOTE);
+ *vote_out = networkstatus_parse_vote_from_string(v_text,
+ strlen(v_text),
+ NULL, NS_TYPE_VOTE);
done:
if (v_text)
@@ -424,4 +432,3 @@ dir_common_construct_vote_3(networkstatus_t **vote, authority_cert_t *cert,
return 0;
}
-
diff --git a/src/test/test_dir_common.h b/src/test/test_dir_common.h
index d6c5241b14..619dc83eb9 100644
--- a/src/test/test_dir_common.h
+++ b/src/test/test_dir_common.h
@@ -3,6 +3,9 @@
* Copyright (c) 2007-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#ifndef TOR_TEST_DIR_COMMON_H
+#define TOR_TEST_DIR_COMMON_H
+
#include "core/or/or.h"
#include "feature/nodelist/networkstatus.h"
@@ -49,3 +52,4 @@ int dir_common_construct_vote_3(networkstatus_t **vote,
networkstatus_t **vote_out, int *n_vrs, time_t now,
int clear_rl);
+#endif /* !defined(TOR_TEST_DIR_COMMON_H) */
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index 90691fff94..edfd0c74e1 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -72,6 +72,8 @@ ENABLE_GCC_WARNING(overlength-strings)
#define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \
"Consensus not signed by sufficient number of requested authorities\r\n\r\n"
+#define consdiffmgr_add_consensus consdiffmgr_add_consensus_nulterm
+
static dir_connection_t *
new_dir_conn(void)
{
@@ -477,8 +479,7 @@ static or_options_t *mock_options = NULL;
static void
init_mock_options(void)
{
- mock_options = tor_malloc(sizeof(or_options_t));
- memset(mock_options, 0, sizeof(or_options_t));
+ mock_options = options_new();
mock_options->TestingTorNetwork = 1;
mock_options->DataDirectory = tor_strdup(get_fname_rnd("datadir_tmp"));
mock_options->CacheDirectory = tor_strdup(mock_options->DataDirectory);
@@ -1275,7 +1276,9 @@ test_dir_handle_get_server_keys_authority(void* data)
size_t body_used = 0;
(void) data;
- mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE,
+ strlen(TEST_CERTIFICATE),
+ NULL);
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
@@ -1425,7 +1428,9 @@ test_dir_handle_get_server_keys_sk(void* data)
size_t body_used = 0;
(void) data;
- mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE,
+ strlen(TEST_CERTIFICATE),
+ NULL);
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
@@ -2217,6 +2222,31 @@ test_dir_handle_get_status_vote_next_authority_not_found(void* data)
tor_free(header);
}
+static void
+test_dir_handle_get_status_vote_next_bandwidth_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = new_dir_conn();
+
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/bandwdith"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_minimal(TO_CONN(conn));
+ tor_free(header);
+}
+
NS_DECL(const char*,
dirvote_get_pending_consensus, (consensus_flavor_t flav));
@@ -2393,7 +2423,9 @@ test_dir_handle_get_status_vote_next_authority(void* data)
routerlist_free_all();
dirvote_free_all();
- mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE,
+ strlen(TEST_CERTIFICATE),
+ NULL);
/* create a trusted ds */
ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
@@ -2455,6 +2487,85 @@ test_dir_handle_get_status_vote_next_authority(void* data)
}
static void
+test_dir_handle_get_status_vote_next_bandwidth(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ const char *content =
+ "1541171221\n"
+ "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 "
+ "master_key_ed25519=YaqV4vbvPYKucElk297eVdNArDz9HtIwUoIeo0+cVIpQ "
+ "bw=760 nick=Test time=2018-05-08T16:13:26\n";
+
+ init_mock_options();
+ MOCK(get_options, mock_get_options);
+ mock_options->V3BandwidthsFile = tor_strdup(
+ get_fname_rnd("V3BandwidthsFile")
+ );
+
+ write_str_to_file(mock_options->V3BandwidthsFile, content, 0);
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = new_dir_conn();
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/bandwidth"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(content)+1, 0);
+
+ tt_assert(header);
+ 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, "Content-Length: 167\r\n"));
+
+ /* Check cache lifetime */
+ char expbuf[RFC1123_TIME_LEN+1];
+ time_t now = approx_time();
+ /* BANDWIDTH_CACHE_LIFETIME is defined in dircache.c. */
+ format_rfc1123_time(expbuf, (time_t)(now + 30*60));
+ char *expires = NULL;
+ /* Change to 'Cache-control: max-age=%d' if using http/1.1. */
+ tor_asprintf(&expires, "Expires: %s\r\n", expbuf);
+ tt_assert(strstr(header, expires));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(content, OP_EQ, body);
+
+ tor_free(header);
+ tor_free(body);
+
+ /* Request the file using compression, the result should be the same. */
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/bandwidth.z"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(content)+1, 0);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Encoding: deflate\r\n"));
+
+ /* Since using connection_write_to_buf_mock instead of mocking
+ * connection_buf_add_compress, the content is not actually compressed.
+ * If it would, the size and content would be different than the original.
+ */
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_minimal(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ tor_free(expires);
+ or_options_free(mock_options);
+}
+
+static void
test_dir_handle_get_status_vote_current_authority(void* data)
{
dir_connection_t *conn = NULL;
@@ -2471,7 +2582,9 @@ test_dir_handle_get_status_vote_current_authority(void* data)
routerlist_free_all();
dirvote_free_all();
- mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE,
+ strlen(TEST_CERTIFICATE),
+ NULL);
/* create a trusted ds */
ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
@@ -2627,6 +2740,8 @@ struct testcase_t dir_handle_get_tests[] = {
DIR_HANDLE_CMD(status_vote_current_authority, 0),
DIR_HANDLE_CMD(status_vote_next_authority_not_found, 0),
DIR_HANDLE_CMD(status_vote_next_authority, 0),
+ DIR_HANDLE_CMD(status_vote_next_bandwidth_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_bandwidth, 0),
DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, TT_FORK),
DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, TT_FORK),
DIR_HANDLE_CMD(status_vote_current_consensus_too_old, TT_FORK),
diff --git a/src/test/test_dispatch.c b/src/test/test_dispatch.c
new file mode 100644
index 0000000000..a62c18e0c9
--- /dev/null
+++ b/src/test/test_dispatch.c
@@ -0,0 +1,278 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define DISPATCH_NEW_PRIVATE
+#define DISPATCH_PRIVATE
+
+#include "test/test.h"
+
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_cfg.h"
+#include "lib/dispatch/dispatch_st.h"
+#include "lib/dispatch/msgtypes.h"
+
+#include "lib/log/escape.h"
+#include "lib/malloc/malloc.h"
+#include "lib/string/printf.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static dispatch_t *dispatcher_in_use=NULL;
+
+static void
+test_dispatch_max_in_u16_sl(void *arg)
+{
+ (void)arg;
+ smartlist_t *sl = smartlist_new();
+ uint16_t nums[] = { 10, 20, 30 };
+ tt_int_op(-1, OP_EQ, max_in_u16_sl(sl, -1));
+
+ smartlist_add(sl, NULL);
+ tt_int_op(-1, OP_EQ, max_in_u16_sl(sl, -1));
+
+ smartlist_add(sl, &nums[1]);
+ tt_int_op(20, OP_EQ, max_in_u16_sl(sl, -1));
+
+ smartlist_add(sl, &nums[0]);
+ tt_int_op(20, OP_EQ, max_in_u16_sl(sl, -1));
+
+ smartlist_add(sl, NULL);
+ tt_int_op(20, OP_EQ, max_in_u16_sl(sl, -1));
+
+ smartlist_add(sl, &nums[2]);
+ tt_int_op(30, OP_EQ, max_in_u16_sl(sl, -1));
+
+ done:
+ smartlist_free(sl);
+}
+
+/* Construct an empty dispatch_t. */
+static void
+test_dispatch_empty(void *arg)
+{
+ (void)arg;
+
+ dispatch_t *d=NULL;
+ dispatch_cfg_t *cfg=NULL;
+
+ cfg = dcfg_new();
+ d = dispatch_new(cfg);
+ tt_assert(d);
+
+ done:
+ dispatch_free(d);
+ dcfg_free(cfg);
+}
+
+static int total_recv1_simple = 0;
+static int total_recv2_simple = 0;
+
+static void
+simple_recv1(const msg_t *m)
+{
+ total_recv1_simple += m->aux_data__.u64;
+}
+
+static char *recv2_received = NULL;
+
+static void
+simple_recv2(const msg_t *m)
+{
+ tor_free(recv2_received);
+ recv2_received = dispatch_fmt_msg_data(dispatcher_in_use, m);
+
+ total_recv2_simple += m->aux_data__.u64*10;
+}
+
+/* Construct a dispatch_t with two messages, make sure that they both get
+ * delivered. */
+static void
+test_dispatch_simple(void *arg)
+{
+ (void)arg;
+
+ dispatch_t *d=NULL;
+ dispatch_cfg_t *cfg=NULL;
+ int r;
+
+ cfg = dcfg_new();
+ r = dcfg_msg_set_type(cfg,0,0);
+ r += dcfg_msg_set_chan(cfg,0,0);
+ r += dcfg_add_recv(cfg,0,1,simple_recv1);
+ r += dcfg_msg_set_type(cfg,1,0);
+ r += dcfg_msg_set_chan(cfg,1,0);
+ r += dcfg_add_recv(cfg,1,1,simple_recv2);
+ r += dcfg_add_recv(cfg,1,1,simple_recv2); /* second copy */
+ tt_int_op(r, OP_EQ, 0);
+
+ d = dispatch_new(cfg);
+ tt_assert(d);
+ dispatcher_in_use = d;
+
+ msg_aux_data_t data = {.u64 = 7};
+ r = dispatch_send(d, 99, 0, 0, 0, data);
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(total_recv1_simple, OP_EQ, 0);
+
+ r = dispatch_flush(d, 0, INT_MAX);
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(total_recv1_simple, OP_EQ, 7);
+ tt_int_op(total_recv2_simple, OP_EQ, 0);
+
+ total_recv1_simple = 0;
+ r = dispatch_send(d, 99, 0, 1, 0, data);
+ tt_int_op(r, OP_EQ, 0);
+ r = dispatch_flush(d, 0, INT_MAX);
+ tt_int_op(total_recv1_simple, OP_EQ, 0);
+ tt_int_op(total_recv2_simple, OP_EQ, 140);
+
+ tt_str_op(recv2_received, OP_EQ, "<>"); // no format function was set.
+
+ done:
+ dispatch_free(d);
+ dcfg_free(cfg);
+ tor_free(recv2_received);
+}
+
+/* Construct a dispatch_t with a message and no reciever; make sure that it
+ * gets dropped properly. */
+static void
+test_dispatch_no_recipient(void *arg)
+{
+ (void)arg;
+
+ dispatch_t *d=NULL;
+ dispatch_cfg_t *cfg=NULL;
+ int r;
+
+ cfg = dcfg_new();
+ r = dcfg_msg_set_type(cfg,0,0);
+ r += dcfg_msg_set_chan(cfg,0,0);
+ tt_int_op(r, OP_EQ, 0);
+
+ d = dispatch_new(cfg);
+ tt_assert(d);
+ dispatcher_in_use = d;
+
+ msg_aux_data_t data = { .u64 = 7};
+ r = dispatch_send(d, 99, 0, 0, 0, data);
+ tt_int_op(r, OP_EQ, 0);
+
+ r = dispatch_flush(d, 0, INT_MAX);
+ tt_int_op(r, OP_EQ, 0);
+
+ done:
+ dispatch_free(d);
+ dcfg_free(cfg);
+}
+
+struct coord { int x; int y; };
+static void
+free_coord(msg_aux_data_t d)
+{
+ tor_free(d.ptr);
+}
+static char *
+fmt_coord(msg_aux_data_t d)
+{
+ char *v;
+ struct coord *c = d.ptr;
+ tor_asprintf(&v, "[%d, %d]", c->x, c->y);
+ return v;
+}
+static dispatch_typefns_t coord_fns = {
+ .fmt_fn = fmt_coord,
+ .free_fn = free_coord,
+};
+static void
+alert_run_immediate(dispatch_t *d, channel_id_t ch, void *arg)
+{
+ (void)arg;
+ dispatch_flush(d, ch, INT_MAX);
+}
+
+static char *received_data=NULL;
+
+static void
+recv_typed_data(const msg_t *m)
+{
+ tor_free(received_data);
+ received_data = dispatch_fmt_msg_data(dispatcher_in_use, m);
+}
+
+static void
+test_dispatch_with_types(void *arg)
+{
+ (void)arg;
+
+ dispatch_t *d=NULL;
+ dispatch_cfg_t *cfg=NULL;
+ int r;
+
+ cfg = dcfg_new();
+ r = dcfg_msg_set_type(cfg,5,3);
+ r += dcfg_msg_set_chan(cfg,5,2);
+ r += dcfg_add_recv(cfg,5,0,recv_typed_data);
+ r += dcfg_type_set_fns(cfg,3,&coord_fns);
+ tt_int_op(r, OP_EQ, 0);
+
+ d = dispatch_new(cfg);
+ tt_assert(d);
+ dispatcher_in_use = d;
+
+ /* Make this message get run immediately. */
+ r = dispatch_set_alert_fn(d, 2, alert_run_immediate, NULL);
+ tt_int_op(r, OP_EQ, 0);
+
+ struct coord *xy = tor_malloc(sizeof(*xy));
+ xy->x = 13;
+ xy->y = 37;
+ msg_aux_data_t data = {.ptr = xy};
+ r = dispatch_send(d, 99/*sender*/, 2/*channel*/, 5/*msg*/, 3/*type*/, data);
+ tt_int_op(r, OP_EQ, 0);
+ tt_str_op(received_data, OP_EQ, "[13, 37]");
+
+ done:
+ dispatch_free(d);
+ dcfg_free(cfg);
+ tor_free(received_data);
+ dispatcher_in_use = NULL;
+}
+
+static void
+test_dispatch_bad_type_setup(void *arg)
+{
+ (void)arg;
+ static dispatch_typefns_t fns;
+ dispatch_cfg_t *cfg = dcfg_new();
+
+ tt_int_op(0, OP_EQ, dcfg_type_set_fns(cfg, 7, &coord_fns));
+
+ fns = coord_fns;
+ fns.fmt_fn = NULL;
+ tt_int_op(-1, OP_EQ, dcfg_type_set_fns(cfg, 7, &fns));
+
+ fns = coord_fns;
+ fns.free_fn = NULL;
+ tt_int_op(-1, OP_EQ, dcfg_type_set_fns(cfg, 7, &fns));
+
+ fns = coord_fns;
+ tt_int_op(0, OP_EQ, dcfg_type_set_fns(cfg, 7, &fns));
+
+ done:
+ dcfg_free(cfg);
+}
+
+#define T(name) \
+ { #name, test_dispatch_ ## name, TT_FORK, NULL, NULL }
+
+struct testcase_t dispatch_tests[] = {
+ T(max_in_u16_sl),
+ T(empty),
+ T(simple),
+ T(no_recipient),
+ T(with_types),
+ T(bad_type_setup),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index 41a56f65d8..51ff8729d0 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -1,6 +1,7 @@
/* Copyright (c) 2015-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#include "orconfig.h"
#include "core/or/or.h"
#include "test/test.h"
@@ -13,9 +14,71 @@
#include "core/or/edge_connection_st.h"
#include "core/or/or_circuit_st.h"
+#include "app/config/or_options_st.h"
+#include "app/config/config.h"
+
+#include <event2/event.h>
+#include <event2/dns.h>
#define NS_MODULE dns
+#ifdef HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR
+#define NS_SUBMODULE configure_nameservers_fallback
+
+static or_options_t options = {
+ .ORPort_set = 1,
+};
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &options;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ (void)arg;
+ tor_addr_t *nameserver_addr = NULL;
+
+ MOCK(get_options, mock_get_options);
+
+ options.ServerDNSResolvConfFile = (char *)"no_such_file!!!";
+
+ dns_init(); // calls configure_nameservers()
+
+ tt_int_op(number_of_configured_nameservers(), OP_EQ, 1);
+
+ nameserver_addr = configured_nameserver_address(0);
+
+ tt_assert(tor_addr_family(nameserver_addr) == AF_INET);
+ tt_assert(tor_addr_eq_ipv4h(nameserver_addr, 0x7f000001));
+
+#ifndef _WIN32
+ tor_free(nameserver_addr);
+
+ options.ServerDNSResolvConfFile = (char *)"/dev/null";
+
+ dns_init();
+
+ tt_int_op(number_of_configured_nameservers(), OP_EQ, 1);
+
+ nameserver_addr = configured_nameserver_address(0);
+
+ tt_assert(tor_addr_family(nameserver_addr) == AF_INET);
+ tt_assert(tor_addr_eq_ipv4h(nameserver_addr, 0x7f000001));
+#endif /* !defined(_WIN32) */
+
+ UNMOCK(get_options);
+
+ done:
+ tor_free(nameserver_addr);
+ return;
+}
+
+#undef NS_SUBMODULE
+#endif /* defined(HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR) */
+
#define NS_SUBMODULE clip_ttl
static void
@@ -736,6 +799,9 @@ NS(test_main)(void *arg)
#undef NS_SUBMODULE
struct testcase_t dns_tests[] = {
+#ifdef HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR
+ TEST_CASE(configure_nameservers_fallback),
+#endif
TEST_CASE(clip_ttl),
TEST_CASE(resolve),
TEST_CASE_ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve),
diff --git a/src/test/test_dos.c b/src/test/test_dos.c
index 01d7cd006e..c17cfedbf6 100644
--- a/src/test/test_dos.c
+++ b/src/test/test_dos.c
@@ -411,7 +411,7 @@ test_dos_bucket_refill(void *arg)
}
tt_uint_op(current_circ_count, OP_EQ, 0);
tt_uint_op(dos_stats->cc_stats.circuit_bucket, OP_EQ, current_circ_count);
-#endif
+#endif /* SIZEOF_TIME_T == 8 */
done:
tor_free(chan);
diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c
index fc7c5d5800..8f2d507743 100644
--- a/src/test/test_entryconn.c
+++ b/src/test/test_entryconn.c
@@ -11,7 +11,7 @@
#include "feature/client/addressmap.h"
#include "app/config/config.h"
-#include "app/config/confparse.h"
+#include "lib/confmgt/confparse.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
#include "feature/nodelist/nodelist.h"
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index a486b13ae1..d59b1c7153 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -5,6 +5,7 @@
#define CIRCUITLIST_PRIVATE
#define CIRCUITBUILD_PRIVATE
+#define CONFIG_PRIVATE
#define STATEFILE_PRIVATE
#define ENTRYNODES_PRIVATE
#define ROUTERLIST_PRIVATE
@@ -17,7 +18,7 @@
#include "core/or/circuitlist.h"
#include "core/or/circuitbuild.h"
#include "app/config/config.h"
-#include "app/config/confparse.h"
+#include "lib/confmgt/confparse.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/dircommon/directory.h"
#include "feature/dirclient/dirclient.h"
@@ -67,7 +68,7 @@ static networkstatus_t *dummy_consensus = NULL;
static smartlist_t *big_fake_net_nodes = NULL;
-static smartlist_t *
+static const smartlist_t *
bfn_mock_nodelist_get_list(void)
{
return big_fake_net_nodes;
@@ -127,6 +128,9 @@ big_fake_network_cleanup(const struct testcase_t *testcase, void *ptr)
return 1; /* NOP */
}
+#define REASONABLY_FUTURE " reasonably-future"
+#define REASONABLY_PAST " reasonably-past"
+
/* Unittest setup function: Setup a fake network. */
static void *
big_fake_network_setup(const struct testcase_t *testcase)
@@ -138,9 +142,10 @@ big_fake_network_setup(const struct testcase_t *testcase)
const int N_NODES = 271;
const char *argument = testcase->setup_data;
- int reasonably_live_consensus = 0;
+ int reasonably_future_consensus = 0, reasonably_past_consensus = 0;
if (argument) {
- reasonably_live_consensus = strstr(argument, "reasonably-live") != NULL;
+ reasonably_future_consensus = strstr(argument, REASONABLY_FUTURE) != NULL;
+ reasonably_past_consensus = strstr(argument, REASONABLY_PAST) != NULL;
}
big_fake_net_nodes = smartlist_new();
@@ -193,16 +198,21 @@ big_fake_network_setup(const struct testcase_t *testcase)
n->md->exit_policy = parse_short_policy("accept 443");
}
+ n->nodelist_idx = smartlist_len(big_fake_net_nodes);
smartlist_add(big_fake_net_nodes, n);
}
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
dummy_consensus = tor_malloc_zero(sizeof(networkstatus_t));
- if (reasonably_live_consensus) {
- /* Make the dummy consensus valid from 4 hours ago, but expired an hour
+ if (reasonably_future_consensus) {
+ /* Make the dummy consensus valid in 6 hours, and expiring in 7 hours. */
+ dummy_consensus->valid_after = approx_time() + 6*3600;
+ dummy_consensus->valid_until = approx_time() + 7*3600;
+ } else if (reasonably_past_consensus) {
+ /* Make the dummy consensus valid from 16 hours ago, but expired 12 hours
* ago. */
- dummy_consensus->valid_after = approx_time() - 4*3600;
- dummy_consensus->valid_until = approx_time() - 3600;
+ dummy_consensus->valid_after = approx_time() - 16*3600;
+ dummy_consensus->valid_until = approx_time() - 12*3600;
} else {
/* Make the dummy consensus valid for an hour either side of now. */
dummy_consensus->valid_after = approx_time() - 3600;
@@ -226,12 +236,12 @@ mock_randomize_time_no_randomization(time_t a, time_t b)
return a;
}
-static or_options_t mocked_options;
+static or_options_t *mocked_options;
static const or_options_t *
mock_get_options(void)
{
- return &mocked_options;
+ return mocked_options;
}
#define TEST_IPV4_ADDR "123.45.67.89"
@@ -250,7 +260,7 @@ test_node_preferred_orport(void *arg)
tor_addr_port_t ap;
/* Setup options */
- memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options = options_new();
/* We don't test ClientPreferIPv6ORPort here, because it's used in
* nodelist_set_consensus to setup node.ipv6_preferred, which we set
* directly. */
@@ -273,8 +283,8 @@ test_node_preferred_orport(void *arg)
/* Check the preferred address is IPv4 if we're only using IPv4, regardless
* of whether we prefer it or not */
- mocked_options.ClientUseIPv4 = 1;
- mocked_options.ClientUseIPv6 = 0;
+ mocked_options->ClientUseIPv4 = 1;
+ mocked_options->ClientUseIPv6 = 0;
node.ipv6_preferred = 0;
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
@@ -287,8 +297,8 @@ test_node_preferred_orport(void *arg)
/* Check the preferred address is IPv4 if we're using IPv4 and IPv6, but
* don't prefer the IPv6 address */
- mocked_options.ClientUseIPv4 = 1;
- mocked_options.ClientUseIPv6 = 1;
+ mocked_options->ClientUseIPv4 = 1;
+ mocked_options->ClientUseIPv6 = 1;
node.ipv6_preferred = 0;
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
@@ -296,28 +306,29 @@ test_node_preferred_orport(void *arg)
/* Check the preferred address is IPv6 if we prefer it and
* ClientUseIPv6 is 1, regardless of ClientUseIPv4 */
- mocked_options.ClientUseIPv4 = 1;
- mocked_options.ClientUseIPv6 = 1;
+ mocked_options->ClientUseIPv4 = 1;
+ mocked_options->ClientUseIPv6 = 1;
node.ipv6_preferred = 1;
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
tt_assert(ap.port == ipv6_port);
- mocked_options.ClientUseIPv4 = 0;
+ mocked_options->ClientUseIPv4 = 0;
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
tt_assert(ap.port == ipv6_port);
/* Check the preferred address is IPv6 if we don't prefer it, but
* ClientUseIPv4 is 0 */
- mocked_options.ClientUseIPv4 = 0;
- mocked_options.ClientUseIPv6 = 1;
- node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(&mocked_options);
+ mocked_options->ClientUseIPv4 = 0;
+ mocked_options->ClientUseIPv6 = 1;
+ node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(mocked_options);
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
tt_assert(ap.port == ipv6_port);
done:
+ or_options_free(mocked_options);
UNMOCK(get_options);
}
@@ -3039,13 +3050,17 @@ static const struct testcase_setup_t upgrade_circuits = {
#define BFN_TEST(name) \
EN_TEST_BASE(name, TT_FORK, &big_fake_network, NULL), \
- { #name "_reasonably_live", test_entry_guard_ ## name, TT_FORK, \
- &big_fake_network, (void*)("reasonably-live") }
+ { #name "_reasonably_future", test_entry_guard_ ## name, TT_FORK, \
+ &big_fake_network, (void*)(REASONABLY_FUTURE) }, \
+ { #name "_reasonably_past", test_entry_guard_ ## name, TT_FORK, \
+ &big_fake_network, (void*)(REASONABLY_PAST) }
#define UPGRADE_TEST(name, arg) \
EN_TEST_BASE(name, TT_FORK, &upgrade_circuits, arg), \
- { #name "_reasonably_live", test_entry_guard_ ## name, TT_FORK, \
- &upgrade_circuits, (void*)(arg " reasonably-live") }
+ { #name "_reasonably_future", test_entry_guard_ ## name, TT_FORK, \
+ &upgrade_circuits, (void*)(arg REASONABLY_FUTURE) }, \
+ { #name "_reasonably_past", test_entry_guard_ ## name, TT_FORK, \
+ &upgrade_circuits, (void*)(arg REASONABLY_PAST) }
struct testcase_t entrynodes_tests[] = {
NO_PREFIX_TEST(node_preferred_orport),
diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c
index 0c34d37a71..cb53a4e662 100644
--- a/src/test/test_extorport.c
+++ b/src/test/test_extorport.c
@@ -5,11 +5,11 @@
#define EXT_ORPORT_PRIVATE
#define MAINLOOP_PRIVATE
#include "core/or/or.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
#include "app/config/config.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/relay/ext_orport.h"
#include "core/mainloop/mainloop.h"
@@ -18,6 +18,7 @@
#include "test/test.h"
#include "test/test_helpers.h"
+#include "test/rng_test_helpers.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
@@ -176,7 +177,7 @@ test_ext_or_init_auth(void *arg)
/* Shouldn't be initialized already, or our tests will be a bit
* meaningless */
ext_or_auth_cookie = tor_malloc_zero(32);
- tt_assert(tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
+ tt_assert(fast_mem_is_zero((char*)ext_or_auth_cookie, 32));
/* Now make sure we use a temporary file */
fn = get_fname("ext_cookie_file");
@@ -201,7 +202,7 @@ test_ext_or_init_auth(void *arg)
tt_mem_op(cp,OP_EQ, "! Extended ORPort Auth Cookie !\x0a", 32);
tt_mem_op(cp+32,OP_EQ, ext_or_auth_cookie, 32);
memcpy(cookie0, ext_or_auth_cookie, 32);
- tt_assert(!tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
+ tt_assert(!fast_mem_is_zero((char*)ext_or_auth_cookie, 32));
/* Operation should be idempotent. */
tt_int_op(0, OP_EQ, init_ext_or_cookie_authentication(1));
@@ -303,16 +304,6 @@ test_ext_or_cookie_auth(void *arg)
}
static void
-crypto_rand_return_tse_str(char *to, size_t n)
-{
- if (n != 32) {
- TT_FAIL(("Asked for %d bytes, not 32", (int)n));
- return;
- }
- memcpy(to, "te road There is always another ", 32);
-}
-
-static void
test_ext_or_cookie_auth_testvec(void *arg)
{
char *reply=NULL, *client_hash=NULL;
@@ -326,7 +317,7 @@ test_ext_or_cookie_auth_testvec(void *arg)
memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32);
ext_or_auth_cookie_is_set = 1;
- MOCK(crypto_rand, crypto_rand_return_tse_str);
+ testing_enable_prefilled_rng("te road There is always another ", 32);
tt_int_op(0, OP_EQ,
handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply,
@@ -351,7 +342,7 @@ test_ext_or_cookie_auth_testvec(void *arg)
"33b3cd77ff79bd80c2074bbf438119a2");
done:
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
tor_free(reply);
tor_free(client_hash);
tor_free(mem_op_hex_tmp);
@@ -414,9 +405,9 @@ do_ext_or_handshake(or_connection_t *conn)
CONTAINS("\x01\x00", 2);
WRITE("\x01", 1);
WRITE("But when I look ahead up the whi", 32);
- MOCK(crypto_rand, crypto_rand_return_tse_str);
+ testing_enable_prefilled_rng("te road There is always another ", 32);
tt_int_op(0, OP_EQ, connection_ext_or_process_inbuf(conn));
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
tt_int_op(TO_CONN(conn)->state, OP_EQ,
EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH);
CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
@@ -481,9 +472,9 @@ test_ext_or_handshake(void *arg)
tt_int_op(0, OP_EQ, connection_ext_or_process_inbuf(conn));
/* send the rest of the nonce. */
WRITE("ahead up the whi", 16);
- MOCK(crypto_rand, crypto_rand_return_tse_str);
+ testing_enable_prefilled_rng("te road There is always another ", 32);
tt_int_op(0, OP_EQ, connection_ext_or_process_inbuf(conn));
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
/* We should get the right reply from the server. */
CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
"\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
@@ -582,7 +573,7 @@ test_ext_or_handshake(void *arg)
done:
UNMOCK(connection_write_to_buf_impl_);
- UNMOCK(crypto_rand);
+ testing_disable_prefilled_rng();
if (conn)
connection_free_minimal(TO_CONN(conn));
#undef CONTAINS
@@ -596,6 +587,6 @@ struct testcase_t extorport_tests[] = {
{ "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL },
{ "cookie_auth_testvec", test_ext_or_cookie_auth_testvec, TT_FORK,
NULL, NULL },
- { "handshake", test_ext_or_handshake, TT_FORK, NULL, NULL },
+ { "handshake", test_ext_or_handshake, TT_FORK, &helper_pubsub_setup, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c
index 802d0a9ebe..8eb3c2c928 100644
--- a/src/test/test_helpers.c
+++ b/src/test/test_helpers.c
@@ -14,15 +14,20 @@
#include "orconfig.h"
#include "core/or/or.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "app/config/config.h"
-#include "app/config/confparse.h"
+#include "lib/confmgt/confparse.h"
+#include "app/main/subsysmgr.h"
#include "core/mainloop/connection.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "core/mainloop/mainloop.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/relay.h"
#include "feature/nodelist/routerlist.h"
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/pubsub/pubsub_build.h"
+#include "lib/pubsub/pubsub_connect.h"
#include "lib/encoding/confline.h"
#include "lib/net/resolve.h"
@@ -78,7 +83,7 @@ helper_setup_fake_routerlist(void)
{
int retval;
routerlist_t *our_routerlist = NULL;
- smartlist_t *our_nodelist = NULL;
+ const smartlist_t *our_nodelist = NULL;
/* Read the file that contains our test descriptors. */
@@ -290,7 +295,7 @@ helper_parse_options(const char *conf)
if (ret != 0) {
goto done;
}
- ret = config_assign(&options_format, opt, line, 0, &msg);
+ ret = config_assign(get_options_mgr(), opt, line, 0, &msg);
if (ret != 0) {
goto done;
}
@@ -303,3 +308,54 @@ helper_parse_options(const char *conf)
}
return opt;
}
+
+/**
+ * Dispatch alertfn callback: flush all messages right now. Implements
+ * DELIV_IMMEDIATE.
+ **/
+static void
+alertfn_immediate(dispatch_t *d, channel_id_t chan, void *arg)
+{
+ (void) arg;
+ dispatch_flush(d, chan, INT_MAX);
+}
+
+/**
+ * Setup helper for tests that need pubsub active
+ *
+ * Does not hook up mainloop events. Does set immediate delivery for
+ * all channels.
+ */
+void *
+helper_setup_pubsub(const struct testcase_t *testcase)
+{
+ dispatch_t *dispatcher = NULL;
+ pubsub_builder_t *builder = pubsub_builder_new();
+ channel_id_t chan = get_channel_id("orconn");
+
+ (void)testcase;
+ (void)subsystems_add_pubsub(builder);
+ dispatcher = pubsub_builder_finalize(builder, NULL);
+ tor_assert(dispatcher);
+ dispatch_set_alert_fn(dispatcher, chan, alertfn_immediate, NULL);
+ chan = get_channel_id("ocirc");
+ dispatch_set_alert_fn(dispatcher, chan, alertfn_immediate, NULL);
+ return dispatcher;
+}
+
+/**
+ * Cleanup helper for tests that need pubsub active
+ */
+int
+helper_cleanup_pubsub(const struct testcase_t *testcase, void *dispatcher_)
+{
+ dispatch_t *dispatcher = dispatcher_;
+
+ (void)testcase;
+ dispatch_free(dispatcher);
+ return 1;
+}
+
+const struct testcase_setup_t helper_pubsub_setup = {
+ helper_setup_pubsub, helper_cleanup_pubsub
+};
diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h
index 9e376a563d..d82072bb34 100644
--- a/src/test/test_helpers.h
+++ b/src/test/test_helpers.h
@@ -7,6 +7,7 @@
#define BUFFERS_PRIVATE
#include "core/or/or.h"
+#include "tinytest.h"
const char *get_yesterday_date_str(void);
@@ -31,5 +32,10 @@ or_options_t *helper_parse_options(const char *conf);
extern const char TEST_DESCRIPTORS[];
+void *helper_setup_pubsub(const struct testcase_t *);
+int helper_cleanup_pubsub(const struct testcase_t *, void *);
+
+extern const struct testcase_setup_t helper_pubsub_setup;
+
#endif /* !defined(TOR_TEST_HELPERS_H) */
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index a611b46ca6..2b69aae547 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -6,7 +6,7 @@
* \brief Unit tests for hidden service.
**/
-#define CONTROL_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
#define CIRCUITBUILD_PRIVATE
#define RENDCOMMON_PRIVATE
#define RENDSERVICE_PRIVATE
@@ -15,6 +15,8 @@
#include "core/or/or.h"
#include "test/test.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
+#include "feature/control/control_fmt.h"
#include "app/config/config.h"
#include "feature/hs/hs_common.h"
#include "feature/rend/rendcommon.h"
@@ -321,6 +323,16 @@ test_hs_desc_event(void *arg)
tt_str_op(received_msg,OP_EQ, expected_msg);
tor_free(received_msg);
+ /* test HSDir rate limited */
+ rend_query.auth_type = REND_NO_AUTH;
+ control_event_hsv2_descriptor_failed(&rend_query.base_, NULL,
+ "QUERY_RATE_LIMITED");
+ expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \
+ "UNKNOWN REASON=QUERY_RATE_LIMITED\r\n";
+ tt_assert(received_msg);
+ tt_str_op(received_msg,OP_EQ, expected_msg);
+ tor_free(received_msg);
+
/* Test invalid content with no HSDir fingerprint. */
char *exp_msg;
control_event_hs_descriptor_content(rend_query.onion_address,
@@ -436,7 +448,7 @@ test_hs_rend_data(void *arg)
tt_int_op(client_v2->auth_type, OP_EQ, REND_BASIC_AUTH);
tt_int_op(strlen(client_v2->onion_address), OP_EQ, 0);
tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
- tt_int_op(tor_mem_is_zero(client_v2->descriptor_cookie,
+ tt_int_op(fast_mem_is_zero(client_v2->descriptor_cookie,
sizeof(client_v2->descriptor_cookie)), OP_EQ, 1);
tt_assert(client->hsdirs_fp);
tt_int_op(smartlist_len(client->hsdirs_fp), OP_EQ, 0);
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index 9182829116..86ac7e7fb1 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -10,6 +10,7 @@
#define DIRCACHE_PRIVATE
#define DIRCLIENT_PRIVATE
#define HS_CACHE_PRIVATE
+#define TOR_CHANNEL_INTERNAL_
#include "trunnel/ed25519_cert.h"
#include "feature/hs/hs_cache.h"
@@ -20,7 +21,12 @@
#include "core/mainloop/connection.h"
#include "core/proto/proto_http.h"
#include "lib/crypt_ops/crypto_format.h"
+#include "core/or/circuitlist.h"
+#include "core/or/channel.h"
+#include "core/or/edge_connection_st.h"
+#include "core/or/or_circuit_st.h"
+#include "core/or/or_connection_st.h"
#include "feature/dircommon/dir_connection_st.h"
#include "feature/nodelist/networkstatus_st.h"
@@ -232,22 +238,31 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key)
/* The dir conn we are going to simulate */
dir_connection_t *conn = NULL;
+ edge_connection_t *edge_conn = NULL;
+ or_circuit_t *or_circ = NULL;
/* First extract the blinded public key that we are going to use in our
query, and then build the actual query string. */
{
char hsdir_cache_key[ED25519_BASE64_LEN+1];
- retval = ed25519_public_to_base64(hsdir_cache_key,
- blinded_key);
- tt_int_op(retval, OP_EQ, 0);
+ ed25519_public_to_base64(hsdir_cache_key, blinded_key);
tor_asprintf(&hsdir_query_str, GET("/tor/hs/3/%s"), hsdir_cache_key);
}
/* Simulate an HTTP GET request to the HSDir */
conn = dir_connection_new(AF_INET);
+ tt_assert(conn);
+ TO_CONN(conn)->linked = 1; /* Signal that it is encrypted. */
tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001);
- TO_CONN(conn)->linked = 1;/* Pretend the conn is encrypted :) */
+
+ /* Pretend this conn is anonymous. */
+ edge_conn = edge_connection_new(CONN_TYPE_EXIT, AF_INET);
+ TO_CONN(conn)->linked_conn = TO_CONN(edge_conn);
+ or_circ = or_circuit_new(0, NULL);
+ or_circ->p_chan = tor_malloc_zero(sizeof(channel_t));
+ edge_conn->on_circuit = TO_CIRCUIT(or_circ);
+
retval = directory_handle_command_get(conn, hsdir_query_str,
NULL, 0);
tt_int_op(retval, OP_EQ, 0);
@@ -264,8 +279,11 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key)
done:
tor_free(hsdir_query_str);
- if (conn)
+ if (conn) {
+ tor_free(or_circ->p_chan);
+ connection_free_minimal(TO_CONN(conn)->linked_conn);
connection_free_minimal(TO_CONN(conn));
+ }
return received_desc;
}
@@ -487,7 +505,7 @@ test_client_cache(void *arg)
NULL, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
memcpy(wanted_subcredential, published_desc->subcredential, DIGEST256_LEN);
- tt_assert(!tor_mem_is_zero((char*)wanted_subcredential, DIGEST256_LEN));
+ tt_assert(!fast_mem_is_zero((char*)wanted_subcredential, DIGEST256_LEN));
}
/* Test handle_response_fetch_hsdesc_v3() */
diff --git a/src/test/test_hs_cell.c b/src/test/test_hs_cell.c
index f8af631c8b..403509fbc8 100644
--- a/src/test/test_hs_cell.c
+++ b/src/test/test_hs_cell.c
@@ -20,6 +20,7 @@
#include "feature/hs/hs_service.h"
/* Trunnel. */
+#include "trunnel/hs/cell_common.h"
#include "trunnel/hs/cell_establish_intro.h"
/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we
@@ -38,11 +39,13 @@ test_gen_establish_intro_cell(void *arg)
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
attempt to parse it. */
{
+ hs_service_config_t config;
+ memset(&config, 0, sizeof(config));
/* We only need the auth key pair here. */
- hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0, 0);
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL);
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
- ret = hs_cell_build_establish_intro(circ_nonce, ip, buf);
+ ret = hs_cell_build_establish_intro(circ_nonce, &config, ip, buf);
service_intro_point_free(ip);
tt_u64_op(ret, OP_GT, 0);
}
@@ -97,6 +100,9 @@ test_gen_establish_intro_cell_bad(void *arg)
trn_cell_establish_intro_t *cell = NULL;
char circ_nonce[DIGEST_LEN] = {0};
hs_service_intro_point_t *ip = NULL;
+ hs_service_config_t config;
+
+ memset(&config, 0, sizeof(config));
MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed);
@@ -107,8 +113,8 @@ test_gen_establish_intro_cell_bad(void *arg)
ed25519_sign_prefixed() function and make it fail. */
cell = trn_cell_establish_intro_new();
tt_assert(cell);
- ip = service_intro_point_new(NULL, 0, 0);
- cell_len = hs_cell_build_establish_intro(circ_nonce, ip, NULL);
+ ip = service_intro_point_new(NULL);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, NULL);
service_intro_point_free(ip);
expect_log_msg_containing("Unable to make signature for "
"ESTABLISH_INTRO cell.");
@@ -120,11 +126,97 @@ test_gen_establish_intro_cell_bad(void *arg)
UNMOCK(ed25519_sign_prefixed);
}
+static void
+test_gen_establish_intro_dos_ext(void *arg)
+{
+ ssize_t ret;
+ hs_service_config_t config;
+ hs_service_intro_point_t *ip = NULL;
+ trn_cell_extension_t *extensions = NULL;
+ trn_cell_extension_dos_t *dos = NULL;
+
+ (void) arg;
+
+ memset(&config, 0, sizeof(config));
+ ip = service_intro_point_new(NULL);
+ tt_assert(ip);
+ ip->support_intro2_dos_defense = 1;
+
+ /* Case 1: No DoS parameters so no extension to be built. */
+ extensions = build_establish_intro_extensions(&config, ip);
+ tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 0);
+ trn_cell_extension_free(extensions);
+ extensions = NULL;
+
+ /* Case 2: Enable the DoS extension. Parameter set to 0 should indicate to
+ * disable the defense on the intro point but there should be an extension
+ * nonetheless in the cell. */
+ config.has_dos_defense_enabled = 1;
+ extensions = build_establish_intro_extensions(&config, ip);
+ tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 1);
+ /* Validate the extension. */
+ const trn_cell_extension_field_t *field =
+ trn_cell_extension_getconst_fields(extensions, 0);
+ tt_int_op(trn_cell_extension_field_get_field_type(field), OP_EQ,
+ TRUNNEL_CELL_EXTENSION_TYPE_DOS);
+ ret = trn_cell_extension_dos_parse(&dos,
+ trn_cell_extension_field_getconstarray_field(field),
+ trn_cell_extension_field_getlen_field(field));
+ tt_int_op(ret, OP_EQ, 19);
+ /* Rate per sec param. */
+ const trn_cell_extension_dos_param_t *param =
+ trn_cell_extension_dos_getconst_params(dos, 0);
+ tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
+ TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC);
+ tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 0);
+ /* Burst per sec param. */
+ param = trn_cell_extension_dos_getconst_params(dos, 1);
+ tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
+ TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC);
+ tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 0);
+ trn_cell_extension_dos_free(dos); dos = NULL;
+ trn_cell_extension_free(extensions); extensions = NULL;
+
+ /* Case 3: Enable the DoS extension. Parameter set to some normal values. */
+ config.has_dos_defense_enabled = 1;
+ config.intro_dos_rate_per_sec = 42;
+ config.intro_dos_burst_per_sec = 250;
+ extensions = build_establish_intro_extensions(&config, ip);
+ tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 1);
+ /* Validate the extension. */
+ field = trn_cell_extension_getconst_fields(extensions, 0);
+ tt_int_op(trn_cell_extension_field_get_field_type(field), OP_EQ,
+ TRUNNEL_CELL_EXTENSION_TYPE_DOS);
+ ret = trn_cell_extension_dos_parse(&dos,
+ trn_cell_extension_field_getconstarray_field(field),
+ trn_cell_extension_field_getlen_field(field));
+ tt_int_op(ret, OP_EQ, 19);
+ /* Rate per sec param. */
+ param = trn_cell_extension_dos_getconst_params(dos, 0);
+ tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
+ TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC);
+ tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 42);
+ /* Burst per sec param. */
+ param = trn_cell_extension_dos_getconst_params(dos, 1);
+ tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
+ TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC);
+ tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 250);
+ trn_cell_extension_dos_free(dos); dos = NULL;
+ trn_cell_extension_free(extensions); extensions = NULL;
+
+ done:
+ service_intro_point_free(ip);
+ trn_cell_extension_dos_free(dos);
+ trn_cell_extension_free(extensions);
+}
+
struct testcase_t hs_cell_tests[] = {
{ "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK,
NULL, NULL },
{ "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK,
NULL, NULL },
+ { "gen_establish_intro_dos_ext", test_gen_establish_intro_dos_ext, TT_FORK,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index 2f2bb45581..b777dafdfb 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -14,6 +14,7 @@
#define CIRCUITBUILD_PRIVATE
#define CIRCUITLIST_PRIVATE
#define CONNECTION_PRIVATE
+#define CRYPT_PATH_PRIVATE
#include "test/test.h"
#include "test/test_helpers.h"
@@ -36,6 +37,7 @@
#include "feature/hs/hs_config.h"
#include "feature/hs/hs_ident.h"
#include "feature/hs/hs_cache.h"
+#include "feature/rend/rendcache.h"
#include "core/or/circuitlist.h"
#include "core/or/circuitbuild.h"
#include "core/mainloop/connection.h"
@@ -44,6 +46,7 @@
#include "core/or/cpath_build_state_st.h"
#include "core/or/crypt_path_st.h"
+#include "core/or/crypt_path.h"
#include "feature/dircommon/dir_connection_st.h"
#include "core/or/entry_connection_st.h"
#include "core/or/extend_info_st.h"
@@ -157,8 +160,7 @@ helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
or_circ->rend_data = rend_data_dup(conn_rend_data);
} else {
/* prop224: Setup hs ident on the circuit */
- or_circ->hs_ident = hs_ident_circuit_new(&service_pk,
- HS_IDENT_CIRCUIT_RENDEZVOUS);
+ or_circ->hs_ident = hs_ident_circuit_new(&service_pk);
}
TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
@@ -241,12 +243,14 @@ test_e2e_rend_circuit_setup_legacy(void *arg)
tt_int_op(retval, OP_EQ, 1);
/* Check the digest algo */
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.f_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest),
OP_EQ, DIGEST_SHA1);
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.b_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest),
OP_EQ, DIGEST_SHA1);
- tt_assert(or_circ->cpath->crypto.f_crypto);
- tt_assert(or_circ->cpath->crypto.b_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.f_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.b_crypto);
/* Ensure that circ purpose was changed */
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
@@ -311,12 +315,14 @@ test_e2e_rend_circuit_setup(void *arg)
tt_int_op(retval, OP_EQ, 1);
/* Check that the crypt path has prop224 algorithm parameters */
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.f_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest),
OP_EQ, DIGEST_SHA3_256);
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.b_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest),
OP_EQ, DIGEST_SHA3_256);
- tt_assert(or_circ->cpath->crypto.f_crypto);
- tt_assert(or_circ->cpath->crypto.b_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.f_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.b_crypto);
/* Ensure that circ purpose was changed */
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
@@ -395,7 +401,7 @@ test_client_pick_intro(void *arg)
tt_assert(fetched_desc);
tt_mem_op(fetched_desc->subcredential, OP_EQ, desc->subcredential,
DIGEST256_LEN);
- tt_assert(!tor_mem_is_zero((char*)fetched_desc->subcredential,
+ tt_assert(!fast_mem_is_zero((char*)fetched_desc->subcredential,
DIGEST256_LEN));
tor_free(encoded);
}
@@ -403,6 +409,9 @@ test_client_pick_intro(void *arg)
/* 2) Mark all intro points except _the chosen one_ as failed. Then query the
* desc and get a random intro: check that we got _the chosen one_. */
{
+ /* Tell hs_get_extend_info_from_lspecs() to skip the private address check.
+ */
+ get_options_mutable()->ExtendAllowPrivateAddresses = 1;
/* Pick the chosen intro point and get its ei */
hs_desc_intro_point_t *chosen_intro_point =
smartlist_get(desc->encrypted_data.intro_points, 0);
@@ -430,7 +439,7 @@ test_client_pick_intro(void *arg)
for (int i = 0; i < 64; ++i) {
extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
tor_assert(ip);
- tt_assert(!tor_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN));
+ tt_assert(!fast_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN));
tt_mem_op(ip->identity_digest, OP_EQ, chosen_intro_ei->identity_digest,
DIGEST_LEN);
extend_info_free(ip);
@@ -476,6 +485,18 @@ test_client_pick_intro(void *arg)
SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
hs_desc_intro_point_t *, ip) {
extend_info_t *intro_ei = desc_intro_point_to_extend_info(ip);
+ /* desc_intro_point_to_extend_info() doesn't return IPv6 intro points
+ * yet, because we can't extend to them. See #24404, #24451, and #24181.
+ */
+ if (intro_ei == NULL) {
+ /* Pretend we're making a direct connection, and that we can use IPv6
+ */
+ get_options_mutable()->ClientUseIPv6 = 1;
+ intro_ei = hs_get_extend_info_from_lspecs(ip->link_specifiers,
+ &ip->onion_key, 1);
+ tt_assert(tor_addr_family(&intro_ei->addr) == AF_INET6);
+ }
+ tt_assert(intro_ei);
if (intro_ei) {
const char *ptr;
char ip_addr[TOR_ADDR_BUF_LEN];
@@ -942,8 +963,7 @@ test_close_intro_circuits_new_desc(void *arg)
const hs_desc_intro_point_t *ip =
smartlist_get(desc1->encrypted_data.intro_points, 0);
tt_assert(ip);
- ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey,
- HS_IDENT_CIRCUIT_INTRO);
+ ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk,
&ip->auth_key_cert->signed_key);
}
@@ -986,6 +1006,91 @@ test_close_intro_circuits_new_desc(void *arg)
UNMOCK(networkstatus_get_live_consensus);
}
+static void
+test_close_intro_circuits_cache_clean(void *arg)
+{
+ int ret;
+ ed25519_keypair_t service_kp;
+ circuit_t *circ = NULL;
+ origin_circuit_t *ocirc = NULL;
+ hs_descriptor_t *desc1 = NULL;
+
+ (void) arg;
+
+ hs_init();
+ rend_cache_init();
+
+ /* This is needed because of the client cache expiration timestamp is based
+ * on having a consensus. See cached_client_descriptor_has_expired(). */
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ /* Set consensus time */
+ parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.valid_after);
+ parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
+ &mock_ns.fresh_until);
+ parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC",
+ &mock_ns.valid_until);
+
+ /* Generate service keypair */
+ tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
+
+ /* Create and add to the global list a dummy client introduction circuits.
+ * We'll then make sure the hs_ident is attached to a dummy descriptor. */
+ circ = dummy_origin_circuit_new(0);
+ tt_assert(circ);
+ circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
+
+ /* Build the first descriptor and cache it. */
+ {
+ char *encoded;
+ desc1 = hs_helper_build_hs_desc_with_ip(&service_kp);
+ tt_assert(desc1);
+ ret = hs_desc_encode_descriptor(desc1, &service_kp, NULL, &encoded);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(encoded);
+
+ /* Store it */
+ ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(encoded);
+ tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey));
+ }
+
+ /* We'll pick one introduction point and associate it with the circuit. */
+ {
+ const hs_desc_intro_point_t *ip =
+ smartlist_get(desc1->encrypted_data.intro_points, 0);
+ tt_assert(ip);
+ ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
+ ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk,
+ &ip->auth_key_cert->signed_key);
+ }
+
+ /* Before we are about to clean up the intro circuits, make sure it is
+ * actually there. */
+ tt_assert(circuit_get_next_intro_circ(NULL, true));
+
+ /* Cleanup the client cache. The ns valid after time is what decides if the
+ * descriptor has expired so put it in the future enough (72h) so we are
+ * sure to always expire. */
+ mock_ns.valid_after = approx_time() + (72 * 24 * 60 * 60);
+ hs_cache_clean_as_client(0);
+
+ /* Once stored, our intro circuit should be closed because it is related to
+ * an old introduction point that doesn't exists anymore. */
+ tt_assert(!circuit_get_next_intro_circ(NULL, true));
+
+ done:
+ circuit_free(circ);
+ hs_descriptor_free(desc1);
+ hs_free_all();
+ rend_cache_free_all();
+ UNMOCK(networkstatus_get_live_consensus);
+}
+
struct testcase_t hs_client_tests[] = {
{ "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
TT_FORK, NULL, NULL },
@@ -1005,6 +1110,8 @@ struct testcase_t hs_client_tests[] = {
TT_FORK, NULL, NULL },
{ "close_intro_circuits_new_desc", test_close_intro_circuits_new_desc,
TT_FORK, NULL, NULL },
+ { "close_intro_circuits_cache_clean", test_close_intro_circuits_cache_clean,
+ TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index 2aff179687..de3f7e04f7 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -275,7 +275,7 @@ test_start_time_of_next_time_period(void *arg)
static void
cleanup_nodelist(void)
{
- smartlist_t *nodelist = nodelist_get_list();
+ const smartlist_t *nodelist = nodelist_get_list();
SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
tor_free(node->md);
node->md = NULL;
@@ -502,6 +502,7 @@ test_desc_reupload_logic(void *arg)
pubkey_hex, strlen(pubkey_hex));
hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr);
service = tor_malloc_zero(sizeof(hs_service_t));
+ tt_assert(service);
memcpy(service->onion_address, onion_addr, sizeof(service->onion_address));
ed25519_secret_key_generate(&service->keys.identity_sk, 0);
ed25519_public_key_generate(&service->keys.identity_pk,
@@ -603,6 +604,10 @@ test_desc_reupload_logic(void *arg)
SMARTLIST_FOREACH(ns->routerstatus_list,
routerstatus_t *, rs, routerstatus_free(rs));
smartlist_clear(ns->routerstatus_list);
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
networkstatus_vote_free(ns);
cleanup_nodelist();
hs_free_all();
@@ -630,7 +635,7 @@ test_disaster_srv(void *arg)
get_disaster_srv(1, srv_one);
get_disaster_srv(2, srv_two);
- /* Check that the cached ones where updated */
+ /* Check that the cached ones were updated */
tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_one, DIGEST256_LEN);
tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN);
diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c
index c2c556307d..71e1529216 100644
--- a/src/test/test_hs_config.c
+++ b/src/test/test_hs_config.c
@@ -12,6 +12,7 @@
#include "test/test.h"
#include "test/test_helpers.h"
#include "test/log_test_helpers.h"
+#include "test/resolve_test_helpers.h"
#include "app/config/config.h"
#include "feature/hs/hs_common.h"
@@ -272,6 +273,7 @@ test_valid_service_v2(void *arg)
int ret;
(void) arg;
+ mock_hostname_resolver();
/* Valid complex configuration. Basic client authorization. */
{
@@ -314,7 +316,7 @@ test_valid_service_v2(void *arg)
}
done:
- ;
+ unmock_hostname_resolver();
}
static void
@@ -392,6 +394,7 @@ test_valid_service_v3(void *arg)
int ret;
(void) arg;
+ mock_hostname_resolver();
/* Valid complex configuration. */
{
@@ -448,7 +451,7 @@ test_valid_service_v3(void *arg)
}
done:
- ;
+ unmock_hostname_resolver();
}
static void
@@ -489,6 +492,111 @@ test_staging_service_v3(void *arg)
hs_free_all();
}
+static void
+test_dos_parameters(void *arg)
+{
+ int ret;
+
+ (void) arg;
+
+ hs_init();
+
+ /* Valid configuration. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 22 1.1.1.1:22\n"
+ "HiddenServiceEnableIntroDoSDefense 1\n"
+ "HiddenServiceEnableIntroDoSRatePerSec 42\n"
+ "HiddenServiceEnableIntroDoSBurstPerSec 87\n";
+
+ setup_full_capture_of_logs(LOG_INFO);
+ ret = helper_config_service(conf, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("Service INTRO2 DoS defenses rate set to: 42");
+ expect_log_msg_containing("Service INTRO2 DoS defenses burst set to: 87");
+ teardown_capture_of_logs();
+ }
+
+ /* Invalid rate. Value of 2^37. Max allowed is 2^31. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 22 1.1.1.1:22\n"
+ "HiddenServiceEnableIntroDoSDefense 1\n"
+ "HiddenServiceEnableIntroDoSRatePerSec 137438953472\n"
+ "HiddenServiceEnableIntroDoSBurstPerSec 87\n";
+
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 0);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceEnableIntroDoSRatePerSec must "
+ "be between 0 and 2147483647, "
+ "not 137438953472");
+ teardown_capture_of_logs();
+ }
+
+ /* Invalid burst. Value of 2^38. Max allowed is 2^31. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 22 1.1.1.1:22\n"
+ "HiddenServiceEnableIntroDoSDefense 1\n"
+ "HiddenServiceEnableIntroDoSRatePerSec 42\n"
+ "HiddenServiceEnableIntroDoSBurstPerSec 274877906944\n";
+
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 0);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceEnableIntroDoSBurstPerSec must "
+ "be between 0 and 2147483647, "
+ "not 274877906944");
+ teardown_capture_of_logs();
+ }
+
+ /* Burst is smaller than rate. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 22 1.1.1.1:22\n"
+ "HiddenServiceEnableIntroDoSDefense 1\n"
+ "HiddenServiceEnableIntroDoSRatePerSec 42\n"
+ "HiddenServiceEnableIntroDoSBurstPerSec 27\n";
+
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 0);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Hidden service DoS defenses burst (27) can "
+ "not be smaller than the rate value (42).");
+ teardown_capture_of_logs();
+ }
+
+ /* Negative value. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 22 1.1.1.1:22\n"
+ "HiddenServiceEnableIntroDoSDefense 1\n"
+ "HiddenServiceEnableIntroDoSRatePerSec -1\n"
+ "HiddenServiceEnableIntroDoSBurstPerSec 42\n";
+
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 0);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceEnableIntroDoSRatePerSec must be "
+ "between 0 and 2147483647, not -1");
+ teardown_capture_of_logs();
+ }
+
+ done:
+ hs_free_all();
+}
+
struct testcase_t hs_config_tests[] = {
/* Invalid service not specific to any version. */
{ "invalid_service", test_invalid_service, TT_FORK,
@@ -512,6 +620,9 @@ struct testcase_t hs_config_tests[] = {
{ "staging_service_v3", test_staging_service_v3, TT_FORK,
NULL, NULL },
+ /* Test HS DoS parameters. */
+ { "dos_parameters", test_dos_parameters, TT_FORK,
+ NULL, NULL },
+
END_OF_TESTCASES
};
-
diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c
index ba67712f1b..7cedc987bb 100644
--- a/src/test/test_hs_control.c
+++ b/src/test/test_hs_control.c
@@ -6,11 +6,13 @@
* \brief Unit tests for hidden service control port event and command.
**/
-#define CONTROL_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
#include "core/or/or.h"
#include "test/test.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
+#include "feature/control/control_fmt.h"
#include "app/config/config.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_control.h"
@@ -105,8 +107,7 @@ test_hs_desc_event(void *arg)
memset(&blinded_pk, 'B', sizeof(blinded_pk));
memset(&hsdir_rs, 0, sizeof(hsdir_rs));
memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN);
- ret = ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
- tt_int_op(ret, OP_EQ, 0);
+ ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
memcpy(&ident.identity_pk, &identity_kp.pubkey,
sizeof(ed25519_public_key_t));
memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk));
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index de584ed47a..6fe5573c0f 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -21,6 +21,7 @@
#include "test/hs_test_helpers.h"
#include "test/test_helpers.h"
#include "test/log_test_helpers.h"
+#include "test/rng_test_helpers.h"
#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
DISABLE_GCC_WARNING(overlength-strings)
@@ -30,13 +31,6 @@ DISABLE_GCC_WARNING(overlength-strings)
#include "test_hs_descriptor.inc"
ENABLE_GCC_WARNING(overlength-strings)
-/* Mock function to fill all bytes with 1 */
-static void
-mock_crypto_strongest_rand(uint8_t *out, size_t out_len)
-{
- memset(out, 1, out_len);
-}
-
/* Test certificate encoding put in a descriptor. */
static void
test_cert_encoding(void *arg)
@@ -132,7 +126,7 @@ test_descriptor_padding(void *arg)
tt_assert(padded_plaintext);
tor_free(plaintext);
/* Make sure our padding has been zeroed. */
- tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len,
+ tt_int_op(fast_mem_is_zero((char *) padded_plaintext + plaintext_len,
padded_len - plaintext_len), OP_EQ, 1);
tor_free(padded_plaintext);
/* Never never have a padded length smaller than the plaintext. */
@@ -149,7 +143,7 @@ test_descriptor_padding(void *arg)
tt_assert(padded_plaintext);
tor_free(plaintext);
/* Make sure our padding has been zeroed. */
- tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len,
+ tt_int_op(fast_mem_is_zero((char *) padded_plaintext + plaintext_len,
padded_len - plaintext_len), OP_EQ, 1);
tor_free(padded_plaintext);
/* Never never have a padded length smaller than the plaintext. */
@@ -166,7 +160,7 @@ test_descriptor_padding(void *arg)
tt_assert(padded_plaintext);
tor_free(plaintext);
/* Make sure our padding has been zeroed. */
- tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len,
+ tt_int_op(fast_mem_is_zero((char *) padded_plaintext + plaintext_len,
padded_len - plaintext_len), OP_EQ, 1);
tor_free(padded_plaintext);
/* Never never have a padded length smaller than the plaintext. */
@@ -179,115 +173,6 @@ test_descriptor_padding(void *arg)
}
static void
-test_link_specifier(void *arg)
-{
- ssize_t ret;
- hs_desc_link_specifier_t spec;
- smartlist_t *link_specifiers = smartlist_new();
- char buf[256];
- char *b64 = NULL;
- link_specifier_t *ls = NULL;
-
- (void) arg;
-
- /* Always this port. */
- spec.u.ap.port = 42;
- smartlist_add(link_specifiers, &spec);
-
- /* Test IPv4 for starter. */
- {
- uint32_t ipv4;
-
- spec.type = LS_IPV4;
- ret = tor_addr_parse(&spec.u.ap.addr, "1.2.3.4");
- tt_int_op(ret, OP_EQ, AF_INET);
- b64 = encode_link_specifiers(link_specifiers);
- tt_assert(b64);
-
- /* Decode it and validate the format. */
- ret = base64_decode(buf, sizeof(buf), b64, strlen(b64));
- tt_int_op(ret, OP_GT, 0);
- /* First byte is the number of link specifier. */
- tt_int_op(get_uint8(buf), OP_EQ, 1);
- ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1);
- tt_int_op(ret, OP_EQ, 8);
- /* Should be 2 bytes for port and 4 bytes for IPv4. */
- tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, 6);
- ipv4 = link_specifier_get_un_ipv4_addr(ls);
- tt_int_op(tor_addr_to_ipv4h(&spec.u.ap.addr), OP_EQ, ipv4);
- tt_int_op(link_specifier_get_un_ipv4_port(ls), OP_EQ, spec.u.ap.port);
-
- link_specifier_free(ls);
- ls = NULL;
- tor_free(b64);
- }
-
- /* Test IPv6. */
- {
- uint8_t ipv6[16];
-
- spec.type = LS_IPV6;
- ret = tor_addr_parse(&spec.u.ap.addr, "[1:2:3:4::]");
- tt_int_op(ret, OP_EQ, AF_INET6);
- b64 = encode_link_specifiers(link_specifiers);
- tt_assert(b64);
-
- /* Decode it and validate the format. */
- ret = base64_decode(buf, sizeof(buf), b64, strlen(b64));
- tt_int_op(ret, OP_GT, 0);
- /* First byte is the number of link specifier. */
- tt_int_op(get_uint8(buf), OP_EQ, 1);
- ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1);
- tt_int_op(ret, OP_EQ, 20);
- /* Should be 2 bytes for port and 16 bytes for IPv6. */
- tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, 18);
- for (unsigned int i = 0; i < sizeof(ipv6); i++) {
- ipv6[i] = link_specifier_get_un_ipv6_addr(ls, i);
- }
- tt_mem_op(tor_addr_to_in6_addr8(&spec.u.ap.addr), OP_EQ, ipv6,
- sizeof(ipv6));
- tt_int_op(link_specifier_get_un_ipv6_port(ls), OP_EQ, spec.u.ap.port);
-
- link_specifier_free(ls);
- ls = NULL;
- tor_free(b64);
- }
-
- /* Test legacy. */
- {
- uint8_t *id;
-
- spec.type = LS_LEGACY_ID;
- memset(spec.u.legacy_id, 'Y', sizeof(spec.u.legacy_id));
- b64 = encode_link_specifiers(link_specifiers);
- tt_assert(b64);
-
- /* Decode it and validate the format. */
- ret = base64_decode(buf, sizeof(buf), b64, strlen(b64));
- tt_int_op(ret, OP_GT, 0);
- /* First byte is the number of link specifier. */
- tt_int_op(get_uint8(buf), OP_EQ, 1);
- ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1);
- /* 20 bytes digest + 1 byte type + 1 byte len. */
- tt_int_op(ret, OP_EQ, 22);
- tt_int_op(link_specifier_getlen_un_legacy_id(ls), OP_EQ, DIGEST_LEN);
- /* Digest length is 20 bytes. */
- tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, DIGEST_LEN);
- id = link_specifier_getarray_un_legacy_id(ls);
- tt_mem_op(spec.u.legacy_id, OP_EQ, id, DIGEST_LEN);
-
- link_specifier_free(ls);
- ls = NULL;
- tor_free(b64);
- }
-
- done:
- link_specifier_free(ls);
- tor_free(b64);
- smartlist_free(link_specifiers);
-}
-
-static void
test_encode_descriptor(void *arg)
{
int ret;
@@ -848,8 +733,7 @@ test_desc_signature(void *arg)
ret = ed25519_sign_prefixed(&sig, (const uint8_t *) data, strlen(data),
"Tor onion service descriptor sig v3", &kp);
tt_int_op(ret, OP_EQ, 0);
- ret = ed25519_signature_to_base64(sig_b64, &sig);
- tt_int_op(ret, OP_EQ, 0);
+ ed25519_signature_to_base64(sig_b64, &sig);
/* Build the descriptor that should be valid. */
tor_asprintf(&desc, "%ssignature %s\n", data, sig_b64);
ret = desc_sig_is_valid(sig_b64, &kp.pubkey, desc, strlen(desc));
@@ -909,7 +793,7 @@ test_build_authorized_client(void *arg)
client_pubkey_b16,
strlen(client_pubkey_b16));
- MOCK(crypto_strongest_rand_, mock_crypto_strongest_rand);
+ testing_enable_prefilled_rng("\x01", 1);
hs_desc_build_authorized_client(subcredential,
&client_auth_pk, &auth_ephemeral_sk,
@@ -925,15 +809,13 @@ test_build_authorized_client(void *arg)
done:
tor_free(desc_client);
tor_free(mem_op_hex_tmp);
- UNMOCK(crypto_strongest_rand_);
+ testing_disable_prefilled_rng();
}
struct testcase_t hs_descriptor[] = {
/* Encoding tests. */
{ "cert_encoding", test_cert_encoding, TT_FORK,
NULL, NULL },
- { "link_specifier", test_link_specifier, TT_FORK,
- NULL, NULL },
{ "encode_descriptor", test_encode_descriptor, TT_FORK,
NULL, NULL },
{ "descriptor_padding", test_descriptor_padding, TT_FORK,
diff --git a/src/test/test_hs_dos.c b/src/test/test_hs_dos.c
new file mode 100644
index 0000000000..f68639e24a
--- /dev/null
+++ b/src/test/test_hs_dos.c
@@ -0,0 +1,176 @@
+/* Copyright (c) 2017-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_cell.c
+ * \brief Test hidden service cell functionality.
+ */
+
+#define CIRCUITLIST_PRIVATE
+#define NETWORKSTATUS_PRIVATE
+#define HS_DOS_PRIVATE
+#define HS_INTROPOINT_PRIVATE
+
+#include "test/test.h"
+#include "test/test_helpers.h"
+#include "test/log_test_helpers.h"
+
+#include "app/config/config.h"
+
+#include "core/or/circuitlist.h"
+#include "core/or/circuituse.h"
+#include "core/or/or_circuit_st.h"
+
+#include "feature/hs/hs_dos.h"
+#include "feature/hs/hs_intropoint.h"
+#include "feature/nodelist/networkstatus.h"
+
+static void
+setup_mock_consensus(void)
+{
+ current_ns_consensus = tor_malloc_zero(sizeof(networkstatus_t));
+ current_ns_consensus->net_params = smartlist_new();
+ smartlist_add(current_ns_consensus->net_params,
+ (void *) "HiddenServiceEnableIntroDoSDefense=1");
+ hs_dos_consensus_has_changed(current_ns_consensus);
+}
+
+static void
+free_mock_consensus(void)
+{
+ smartlist_free(current_ns_consensus->net_params);
+ tor_free(current_ns_consensus);
+}
+
+static void
+test_can_send_intro2(void *arg)
+{
+ uint32_t now = (uint32_t) approx_time();
+ or_circuit_t *or_circ = NULL;
+
+ (void) arg;
+
+ hs_init();
+ hs_dos_init();
+
+ get_options_mutable()->ORPort_set = 1;
+ setup_mock_consensus();
+
+ or_circ = or_circuit_new(1, NULL);
+
+ /* Make that circuit a service intro point. */
+ circuit_change_purpose(TO_CIRCUIT(or_circ), CIRCUIT_PURPOSE_INTRO_POINT);
+ hs_dos_setup_default_intro2_defenses(or_circ);
+ or_circ->introduce2_dos_defense_enabled = 1;
+
+ /* Brand new circuit, we should be able to send INTRODUCE2 cells. */
+ tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
+
+ /* Simulate that 10 cells have arrived in 1 second. There should be no
+ * refill since the bucket is already at maximum on the first cell. */
+ update_approx_time(++now);
+ for (int i = 0; i < 10; i++) {
+ tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
+ }
+ tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
+ get_intro2_burst_consensus_param(NULL) - 10);
+
+ /* Fully refill the bucket minus 1 cell. */
+ update_approx_time(++now);
+ tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
+ tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
+ get_intro2_burst_consensus_param(NULL) - 1);
+
+ /* Receive an INTRODUCE2 at each second. We should have the bucket full
+ * since at every second it gets refilled. */
+ for (int i = 0; i < 10; i++) {
+ update_approx_time(++now);
+ tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
+ }
+ /* Last check if we can send the cell decrements the bucket so minus 1. */
+ tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
+ get_intro2_burst_consensus_param(NULL) - 1);
+
+ /* Manually reset bucket for next test. */
+ token_bucket_ctr_reset(&or_circ->introduce2_bucket, now);
+ tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
+ get_intro2_burst_consensus_param(NULL));
+
+ /* Do a full burst in the current second which should empty the bucket and
+ * we shouldn't be allowed to send one more cell after that. We go minus 1
+ * cell else the very last check if we can send the INTRO2 cell returns
+ * false because the bucket goes down to 0. */
+ for (uint32_t i = 0; i < get_intro2_burst_consensus_param(NULL) - 1; i++) {
+ tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
+ }
+ tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 1);
+ /* Get the last remaining cell, we shouldn't be allowed to send it. */
+ tt_int_op(false, OP_EQ, hs_dos_can_send_intro2(or_circ));
+ tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 0);
+
+ /* Make sure the next 100 cells aren't allowed and bucket stays at 0. */
+ for (int i = 0; i < 100; i++) {
+ tt_int_op(false, OP_EQ, hs_dos_can_send_intro2(or_circ));
+ tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 0);
+ }
+
+ /* One second has passed, we should have the rate minus 1 cell added. */
+ update_approx_time(++now);
+ tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
+ tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
+ get_intro2_rate_consensus_param(NULL) - 1);
+
+ done:
+ circuit_free_(TO_CIRCUIT(or_circ));
+
+ hs_free_all();
+ free_mock_consensus();
+}
+
+static void
+test_validate_dos_extension_params(void *arg)
+{
+ bool ret;
+
+ (void) arg;
+
+ /* Validate the default values. */
+ ret = cell_dos_extension_parameters_are_valid(
+ get_intro2_rate_consensus_param(NULL),
+ get_intro2_burst_consensus_param(NULL));
+ tt_assert(ret);
+
+ /* Valid custom rate/burst. */
+ ret = cell_dos_extension_parameters_are_valid(17, 42);
+ tt_assert(ret);
+ ret = cell_dos_extension_parameters_are_valid(INT32_MAX, INT32_MAX);
+ tt_assert(ret);
+
+ /* Invalid rate. */
+ ret = cell_dos_extension_parameters_are_valid(UINT64_MAX, 42);
+ tt_assert(!ret);
+
+ /* Invalid burst. */
+ ret = cell_dos_extension_parameters_are_valid(42, UINT64_MAX);
+ tt_assert(!ret);
+
+ /* Value of 0 is valid (but should disable defenses) */
+ ret = cell_dos_extension_parameters_are_valid(0, 0);
+ tt_assert(ret);
+
+ /* Can't have burst smaller than rate. */
+ ret = cell_dos_extension_parameters_are_valid(42, 40);
+ tt_assert(!ret);
+
+ done:
+ return;
+}
+
+struct testcase_t hs_dos_tests[] = {
+ { "can_send_intro2", test_can_send_intro2, TT_FORK,
+ NULL, NULL },
+ { "validate_dos_extension_params", test_validate_dos_extension_params,
+ TT_FORK, NULL, NULL },
+
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c
index 558fc32c54..feb934d93c 100644
--- a/src/test/test_hs_intropoint.c
+++ b/src/test/test_hs_intropoint.c
@@ -16,6 +16,7 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "core/or/or.h"
+#include "core/or/channel.h"
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
#include "ht.h"
@@ -25,6 +26,8 @@
#include "feature/hs/hs_cell.h"
#include "feature/hs/hs_circuitmap.h"
#include "feature/hs/hs_common.h"
+#include "feature/hs/hs_config.h"
+#include "feature/hs/hs_dos.h"
#include "feature/hs/hs_intropoint.h"
#include "feature/hs/hs_service.h"
@@ -43,6 +46,9 @@ new_establish_intro_cell(const char *circ_nonce,
uint8_t buf[RELAY_PAYLOAD_SIZE] = {0};
trn_cell_establish_intro_t *cell = NULL;
hs_service_intro_point_t *ip = NULL;
+ hs_service_config_t config;
+
+ memset(&config, 0, sizeof(config));
/* Ensure that *cell_out is NULL such that we can use to check if we need to
* free `cell` in case of an error. */
@@ -50,9 +56,9 @@ new_establish_intro_cell(const char *circ_nonce,
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
- ip = service_intro_point_new(NULL, 0, 0);
+ ip = service_intro_point_new(NULL);
tt_assert(ip);
- cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, buf);
tt_i64_op(cell_len, OP_GT, 0);
cell_len = trn_cell_establish_intro_parse(&cell, buf, sizeof(buf));
@@ -73,12 +79,15 @@ new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out)
{
ssize_t cell_len = 0;
hs_service_intro_point_t *ip = NULL;
+ hs_service_config_t config;
+
+ memset(&config, 0, sizeof(config));
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
- ip = service_intro_point_new(NULL, 0, 0);
+ ip = service_intro_point_new(NULL);
tt_assert(ip);
- cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell_out);
tt_i64_op(cell_len, OP_GT, 0);
done:
@@ -118,6 +127,8 @@ helper_create_intro_circuit(void)
or_circuit_t *circ = or_circuit_new(0, NULL);
tt_assert(circ);
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR);
+ token_bucket_ctr_init(&circ->introduce2_bucket, 100, 100,
+ (uint32_t) approx_time());
done:
return circ;
}
@@ -693,6 +704,17 @@ test_introduce1_suitable_circuit(void *arg)
tt_int_op(ret, OP_EQ, 0);
}
+ /* Single hop circuit should not be allowed. */
+ {
+ circ = or_circuit_new(0, NULL);
+ circ->p_chan = tor_malloc_zero(sizeof(channel_t));
+ circ->p_chan->is_client = 1;
+ ret = circuit_is_suitable_for_introduce1(circ);
+ tor_free(circ->p_chan);
+ circuit_free_(TO_CIRCUIT(circ));
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
done:
;
}
@@ -888,43 +910,213 @@ test_received_introduce1_handling(void *arg)
UNMOCK(relay_send_command_from_edge_);
}
+static void
+test_received_establish_intro_dos_ext(void *arg)
+{
+ int ret;
+ ssize_t cell_len = 0;
+ uint8_t cell[RELAY_PAYLOAD_SIZE] = {0};
+ char circ_nonce[DIGEST_LEN] = {0};
+ hs_service_intro_point_t *ip = NULL;
+ hs_service_config_t config;
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);
+
+ (void) arg;
+
+ MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
+
+ hs_circuitmap_init();
+
+ /* Setup. */
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ ip = service_intro_point_new(NULL);
+ tt_assert(ip);
+ ip->support_intro2_dos_defense = 1;
+ memset(&config, 0, sizeof(config));
+ config.has_dos_defense_enabled = 1;
+ config.intro_dos_rate_per_sec = 13;
+ config.intro_dos_burst_per_sec = 42;
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
+ /* The INTRO2 bucket should be 0 at this point. */
+ tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, 0);
+ tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, 0);
+ tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, 0);
+ tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, 0);
+
+ /* Case 1: Build encoded cell. Usable DoS parameters. */
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
+ tt_size_op(cell_len, OP_GT, 0);
+ /* Pass it to the intro point. */
+ ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
+ tt_int_op(ret, OP_EQ, 0);
+ /* Should be set to the burst value. */
+ tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, 42);
+ /* Validate the config of the intro2 bucket. */
+ tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, 13);
+ tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, 42);
+ tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, 1);
+
+ /* Need to reset the circuit in between test cases. */
+ circuit_free_(TO_CIRCUIT(intro_circ));
+ intro_circ = or_circuit_new(0,NULL);
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
+
+ /* Case 2: Build encoded cell. Bad DoS parameters. */
+ config.has_dos_defense_enabled = 1;
+ config.intro_dos_rate_per_sec = UINT_MAX;
+ config.intro_dos_burst_per_sec = 13;
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
+ tt_size_op(cell_len, OP_GT, 0);
+ /* Pass it to the intro point. */
+ ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
+ tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT);
+ tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
+ tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_DEFAULT);
+
+ /* Need to reset the circuit in between test cases. */
+ circuit_free_(TO_CIRCUIT(intro_circ));
+ intro_circ = or_circuit_new(0,NULL);
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
+
+ /* Case 3: Build encoded cell. Burst is smaller than rate. Not allowed. */
+ config.has_dos_defense_enabled = 1;
+ config.intro_dos_rate_per_sec = 87;
+ config.intro_dos_burst_per_sec = 45;
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
+ tt_size_op(cell_len, OP_GT, 0);
+ /* Pass it to the intro point. */
+ ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
+ tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT);
+ tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
+ tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_DEFAULT);
+
+ /* Need to reset the circuit in between test cases. */
+ circuit_free_(TO_CIRCUIT(intro_circ));
+ intro_circ = or_circuit_new(0,NULL);
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
+
+ /* Case 4: Build encoded cell. Rate is 0 but burst is not 0. Disables the
+ * defense. */
+ config.has_dos_defense_enabled = 1;
+ config.intro_dos_rate_per_sec = 0;
+ config.intro_dos_burst_per_sec = 45;
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
+ tt_size_op(cell_len, OP_GT, 0);
+ /* Pass it to the intro point. */
+ ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
+ tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT);
+ tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
+ tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_DEFAULT);
+
+ /* Need to reset the circuit in between test cases. */
+ circuit_free_(TO_CIRCUIT(intro_circ));
+ intro_circ = or_circuit_new(0,NULL);
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
+
+ /* Case 5: Build encoded cell. Burst is 0 but rate is not 0. Disables the
+ * defense. */
+ config.has_dos_defense_enabled = 1;
+ config.intro_dos_rate_per_sec = 45;
+ config.intro_dos_burst_per_sec = 0;
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
+ tt_size_op(cell_len, OP_GT, 0);
+ /* Pass it to the intro point. */
+ ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
+ tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT);
+ tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
+ tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ,
+ HS_CONFIG_V3_DOS_DEFENSE_DEFAULT);
+
+ done:
+ circuit_free_(TO_CIRCUIT(intro_circ));
+ service_intro_point_free(ip);
+ hs_circuitmap_free_all();
+ UNMOCK(relay_send_command_from_edge_);
+}
+
+static void *
+hs_subsystem_setup_fn(const struct testcase_t *tc)
+{
+ (void) tc;
+
+ return NULL;
+}
+
+static int
+hs_subsystem_cleanup_fn(const struct testcase_t *tc, void *arg)
+{
+ (void) tc;
+ (void) arg;
+
+ return 1;
+}
+
+static struct testcase_setup_t test_setup = {
+ hs_subsystem_setup_fn, hs_subsystem_cleanup_fn
+};
+
struct testcase_t hs_intropoint_tests[] = {
{ "intro_point_registration",
- test_intro_point_registration, TT_FORK, NULL, NULL },
+ test_intro_point_registration, TT_FORK, NULL, &test_setup},
{ "receive_establish_intro_wrong_keytype",
- test_establish_intro_wrong_keytype, TT_FORK, NULL, NULL },
+ test_establish_intro_wrong_keytype, TT_FORK, NULL, &test_setup},
{ "receive_establish_intro_wrong_keytype2",
- test_establish_intro_wrong_keytype2, TT_FORK, NULL, NULL },
+ test_establish_intro_wrong_keytype2, TT_FORK, NULL, &test_setup},
{ "receive_establish_intro_wrong_purpose",
- test_establish_intro_wrong_purpose, TT_FORK, NULL, NULL },
+ test_establish_intro_wrong_purpose, TT_FORK, NULL, &test_setup},
{ "receive_establish_intro_wrong_sig",
- test_establish_intro_wrong_sig, TT_FORK, NULL, NULL },
+ test_establish_intro_wrong_sig, TT_FORK, NULL, &test_setup},
{ "receive_establish_intro_wrong_sig_len",
- test_establish_intro_wrong_sig_len, TT_FORK, NULL, NULL },
+ test_establish_intro_wrong_sig_len, TT_FORK, NULL, &test_setup},
{ "receive_establish_intro_wrong_auth_key_len",
- test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, NULL },
+ test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, &test_setup},
{ "receive_establish_intro_wrong_mac",
- test_establish_intro_wrong_mac, TT_FORK, NULL, NULL },
+ test_establish_intro_wrong_mac, TT_FORK, NULL, &test_setup},
{ "introduce1_suitable_circuit",
- test_introduce1_suitable_circuit, TT_FORK, NULL, NULL },
+ test_introduce1_suitable_circuit, TT_FORK, NULL, &test_setup},
{ "introduce1_is_legacy",
- test_introduce1_is_legacy, TT_FORK, NULL, NULL },
+ test_introduce1_is_legacy, TT_FORK, NULL, &test_setup},
{ "introduce1_validation",
- test_introduce1_validation, TT_FORK, NULL, NULL },
+ test_introduce1_validation, TT_FORK, NULL, &test_setup},
{ "received_introduce1_handling",
- test_received_introduce1_handling, TT_FORK, NULL, NULL },
+ test_received_introduce1_handling, TT_FORK, NULL, &test_setup},
+
+ { "received_establish_intro_dos_ext",
+ test_received_establish_intro_dos_ext, TT_FORK, NULL, &test_setup},
END_OF_TESTCASES
};
-
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index 32b08ecf37..66194cee3d 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -21,6 +21,7 @@
#define STATEFILE_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#define HS_CLIENT_PRIVATE
+#define CRYPT_PATH_PRIVATE
#include "test/test.h"
#include "test/test_helpers.h"
@@ -60,6 +61,7 @@
#include "core/or/cpath_build_state_st.h"
#include "core/or/crypt_path_st.h"
+#include "core/or/crypt_path.h"
#include "feature/nodelist/networkstatus_st.h"
#include "feature/nodelist/node_st.h"
#include "core/or/origin_circuit_st.h"
@@ -169,8 +171,7 @@ test_e2e_rend_circuit_setup(void *arg)
tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));
- or_circ->hs_ident = hs_ident_circuit_new(&service_pk,
- HS_IDENT_CIRCUIT_RENDEZVOUS);
+ or_circ->hs_ident = hs_ident_circuit_new(&service_pk);
TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
}
@@ -193,12 +194,14 @@ test_e2e_rend_circuit_setup(void *arg)
tt_int_op(retval, OP_EQ, 1);
/* Check the digest algo */
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.f_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest),
OP_EQ, DIGEST_SHA3_256);
- tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.b_digest),
+ tt_int_op(
+ crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest),
OP_EQ, DIGEST_SHA3_256);
- tt_assert(or_circ->cpath->crypto.f_crypto);
- tt_assert(or_circ->cpath->crypto.b_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.f_crypto);
+ tt_assert(or_circ->cpath->pvt_crypto.b_crypto);
/* Ensure that circ purpose was changed */
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED);
@@ -267,7 +270,7 @@ helper_clone_authorized_client(const hs_service_authorized_client_t *client)
/* Helper: Return a newly allocated service object with the identity keypair
* sets and the current descriptor. Then register it to the global map.
- * Caller should us hs_free_all() to free this service or remove it from the
+ * Caller should use hs_free_all() to free this service or remove it from the
* global map before freeing. */
static hs_service_t *
helper_create_service(void)
@@ -289,6 +292,20 @@ helper_create_service(void)
return service;
}
+/* Helper: Deallocate a given service object, its child objects and
+ * remove it from onion service map.
+ * */
+static void
+helper_destroy_service(hs_service_t *service)
+{
+ if (!service)
+ return;
+
+ remove_service(get_hs_service_map(), service);
+
+ hs_service_free(service);
+}
+
/* Helper: Return a newly allocated service object with clients. */
static hs_service_t *
helper_create_service_with_clients(int num_clients)
@@ -314,17 +331,18 @@ helper_create_service_with_clients(int num_clients)
static hs_service_intro_point_t *
helper_create_service_ip(void)
{
- hs_desc_link_specifier_t *ls;
- hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0, 0);
+ link_specifier_t *ls;
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL);
tor_assert(ip);
/* Add a first unused link specifier. */
- ls = tor_malloc_zero(sizeof(*ls));
- ls->type = LS_IPV4;
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_IPV4);
smartlist_add(ip->base.link_specifiers, ls);
/* Add a second link specifier used by a test. */
- ls = tor_malloc_zero(sizeof(*ls));
- ls->type = LS_LEGACY_ID;
- memset(ls->u.legacy_id, 'A', sizeof(ls->u.legacy_id));
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_LEGACY_ID);
+ memset(link_specifier_getarray_un_legacy_id(ls), 'A',
+ link_specifier_getlen_un_legacy_id(ls));
smartlist_add(ip->base.link_specifiers, ls);
return ip;
@@ -378,11 +396,11 @@ test_load_keys(void *arg)
tt_assert(s);
/* Ok we have the service object. Validate few things. */
- tt_assert(!tor_mem_is_zero(s->onion_address, sizeof(s->onion_address)));
+ tt_assert(!fast_mem_is_zero(s->onion_address, sizeof(s->onion_address)));
tt_int_op(hs_address_is_valid(s->onion_address), OP_EQ, 1);
- tt_assert(!tor_mem_is_zero((char *) s->keys.identity_sk.seckey,
+ tt_assert(!fast_mem_is_zero((char *) s->keys.identity_sk.seckey,
ED25519_SECKEY_LEN));
- tt_assert(!tor_mem_is_zero((char *) s->keys.identity_pk.pubkey,
+ tt_assert(!fast_mem_is_zero((char *) s->keys.identity_pk.pubkey,
ED25519_PUBKEY_LEN));
/* Check onion address from identity key. */
hs_build_address(&s->keys.identity_pk, s->config.version, addr);
@@ -656,13 +674,15 @@ test_service_intro_point(void *arg)
(void) arg;
+ update_approx_time(1481621834);
+
/* Test simple creation of an object. */
{
- time_t now = time(NULL);
+ time_t now = approx_time();
ip = helper_create_service_ip();
tt_assert(ip);
/* Make sure the authentication keypair is not zeroes. */
- tt_int_op(tor_mem_is_zero((const char *) &ip->auth_key_kp,
+ tt_int_op(fast_mem_is_zero((const char *) &ip->auth_key_kp,
sizeof(ed25519_keypair_t)), OP_EQ, 0);
/* The introduce2_max MUST be in that range. */
tt_u64_op(ip->introduce2_max, OP_GE,
@@ -797,10 +817,11 @@ test_helper_functions(void *arg)
const node_t *node = get_node_from_intro_point(ip);
tt_ptr_op(node, OP_EQ, &mock_node);
SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
- hs_desc_link_specifier_t *, ls) {
- if (ls->type == LS_LEGACY_ID) {
+ link_specifier_t *, ls) {
+ if (link_specifier_get_ls_type(ls) == LS_LEGACY_ID) {
/* Change legacy id in link specifier which is not the mock node. */
- memset(ls->u.legacy_id, 'B', sizeof(ls->u.legacy_id));
+ memset(link_specifier_getarray_un_legacy_id(ls), 'B',
+ link_specifier_getlen_un_legacy_id(ls));
}
} SMARTLIST_FOREACH_END(ls);
node = get_node_from_intro_point(ip);
@@ -858,6 +879,10 @@ test_helper_functions(void *arg)
done:
/* This will free the service and all objects associated to it. */
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_service_free_all();
UNMOCK(node_get_by_id);
}
@@ -867,7 +892,7 @@ static void
test_intro_circuit_opened(void *arg)
{
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
- hs_service_t *service;
+ hs_service_t *service = NULL;
origin_circuit_t *circ = NULL;
(void) arg;
@@ -915,6 +940,10 @@ test_intro_circuit_opened(void *arg)
done:
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
UNMOCK(relay_send_command_from_edge_);
@@ -929,7 +958,7 @@ test_intro_established(void *arg)
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
origin_circuit_t *circ = NULL;
- hs_service_t *service;
+ hs_service_t *service = NULL;
hs_service_intro_point_t *ip = NULL;
(void) arg;
@@ -990,6 +1019,10 @@ test_intro_established(void *arg)
done:
if (circ)
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
}
@@ -1001,7 +1034,7 @@ test_rdv_circuit_opened(void *arg)
{
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
origin_circuit_t *circ = NULL;
- hs_service_t *service;
+ hs_service_t *service = NULL;
(void) arg;
@@ -1032,6 +1065,10 @@ test_rdv_circuit_opened(void *arg)
done:
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
UNMOCK(relay_send_command_from_edge_);
@@ -1069,8 +1106,7 @@ test_closing_intro_circs(void *arg)
/* Initialize intro circuit */
intro_circ = origin_circuit_init(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, flags);
- intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk,
- HS_IDENT_CIRCUIT_INTRO);
+ intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk);
/* Register circuit in the circuitmap . */
hs_circuitmap_register_intro_circ_v3_service_side(intro_circ,
&ip->auth_key_kp.pubkey);
@@ -1096,8 +1132,7 @@ test_closing_intro_circs(void *arg)
/* Now pretend that a new intro point circ was launched and opened. Check
* that the intro point will be established correctly. */
intro_circ = origin_circuit_init(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, flags);
- intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk,
- HS_IDENT_CIRCUIT_INTRO);
+ intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk);
ed25519_pubkey_copy(&intro_circ->hs_ident->intro_auth_pk,
&ip->auth_key_kp.pubkey);
/* Register circuit in the circuitmap . */
@@ -1119,6 +1154,10 @@ test_closing_intro_circs(void *arg)
circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Frees the service object. */
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(assert_circuit_ok);
}
@@ -1131,7 +1170,7 @@ test_introduce2(void *arg)
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
origin_circuit_t *circ = NULL;
- hs_service_t *service;
+ hs_service_t *service = NULL;
hs_service_intro_point_t *ip = NULL;
(void) arg;
@@ -1141,7 +1180,7 @@ test_introduce2(void *arg)
MOCK(get_or_state,
get_or_state_replacement);
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_INTRO, flags);
tt_assert(circ);
@@ -1198,6 +1237,10 @@ test_introduce2(void *arg)
dummy_state = NULL;
if (circ)
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
}
@@ -1221,6 +1264,7 @@ test_service_event(void *arg)
/* Set a service for this circuit. */
service = helper_create_service();
+ tt_assert(service);
ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
&service->keys.identity_pk);
@@ -1256,8 +1300,14 @@ test_service_event(void *arg)
run_housekeeping_event(now);
tt_int_op(digest256map_size(service->desc_current->intro_points.map),
OP_EQ, 1);
+ /* No removal if we have an established circuit after retries. */
+ ip->circuit_retries = MAX_INTRO_POINT_CIRCUIT_RETRIES + 1;
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 1);
/* Remove the IP object at once for the next test. */
ip->circuit_retries = MAX_INTRO_POINT_CIRCUIT_RETRIES + 1;
+ ip->circuit_established = 0;
run_housekeeping_event(now);
tt_int_op(digest256map_size(service->desc_current->intro_points.map),
OP_EQ, 0);
@@ -1282,6 +1332,10 @@ test_service_event(void *arg)
done:
hs_circuitmap_remove_circuit(TO_CIRCUIT(circ));
circuit_free_(TO_CIRCUIT(circ));
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(circuit_mark_for_close_);
}
@@ -1292,12 +1346,12 @@ test_rotate_descriptors(void *arg)
{
int ret;
time_t next_rotation_time, now;
- hs_service_t *service;
+ hs_service_t *service = NULL;
hs_service_descriptor_t *desc_next;
(void) arg;
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
hs_init();
MOCK(get_or_state, get_or_state_replacement);
@@ -1384,6 +1438,10 @@ test_rotate_descriptors(void *arg)
tt_assert(service->desc_next);
done:
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
UNMOCK(get_or_state);
UNMOCK(circuit_mark_for_close_);
@@ -1396,9 +1454,8 @@ static void
test_build_update_descriptors(void *arg)
{
int ret;
- time_t now = time(NULL);
node_t *node;
- hs_service_t *service;
+ hs_service_t *service = NULL;
hs_service_intro_point_t *ip_cur, *ip_next;
routerinfo_t ri;
@@ -1411,7 +1468,7 @@ test_build_update_descriptors(void *arg)
MOCK(networkstatus_get_live_consensus,
mock_networkstatus_get_live_consensus);
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
ret = parse_rfc1123_time("Sat, 26 Oct 1985 03:00:00 UTC",
&mock_ns.valid_after);
@@ -1422,7 +1479,8 @@ test_build_update_descriptors(void *arg)
voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after);
update_approx_time(mock_ns.valid_after+1);
- now = mock_ns.valid_after+1;
+
+ time_t now = mock_ns.valid_after+1;
/* Create a service without a current descriptor to trigger a build. */
service = helper_create_service();
@@ -1546,9 +1604,9 @@ test_build_update_descriptors(void *arg)
tt_int_op(smartlist_len(ip_cur->base.link_specifiers), OP_EQ, 3);
/* Make sure we have a valid encryption keypair generated when we pick an
* intro point in the update process. */
- tt_assert(!tor_mem_is_zero((char *) ip_cur->enc_key_kp.seckey.secret_key,
+ tt_assert(!fast_mem_is_zero((char *) ip_cur->enc_key_kp.seckey.secret_key,
CURVE25519_SECKEY_LEN));
- tt_assert(!tor_mem_is_zero((char *) ip_cur->enc_key_kp.pubkey.public_key,
+ tt_assert(!fast_mem_is_zero((char *) ip_cur->enc_key_kp.pubkey.public_key,
CURVE25519_PUBKEY_LEN));
tt_u64_op(ip_cur->time_to_expire, OP_GE, now +
INTRO_POINT_LIFETIME_MIN_SECONDS);
@@ -1614,6 +1672,10 @@ test_build_update_descriptors(void *arg)
tt_u64_op(service->desc_next->next_upload_time, OP_EQ, 0);
done:
+ if (service) {
+ remove_service(get_hs_service_map(), service);
+ hs_service_free(service);
+ }
hs_free_all();
nodelist_free_all();
}
@@ -1626,6 +1688,7 @@ test_build_descriptors(void *arg)
{
int ret;
time_t now = time(NULL);
+ hs_service_t *last_service = NULL;
(void) arg;
@@ -1636,7 +1699,7 @@ test_build_descriptors(void *arg)
MOCK(networkstatus_get_live_consensus,
mock_networkstatus_get_live_consensus);
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
ret = parse_rfc1123_time("Sat, 26 Oct 1985 03:00:00 UTC",
&mock_ns.valid_after);
@@ -1650,19 +1713,27 @@ test_build_descriptors(void *arg)
* is disabled. */
{
hs_service_t *service = helper_create_service();
+ last_service = service;
service_descriptor_free(service->desc_current);
service->desc_current = NULL;
build_all_descriptors(now);
+ tt_assert(service->desc_current);
+ tt_assert(service->desc_current->desc);
+
hs_desc_superencrypted_data_t *superencrypted;
superencrypted = &service->desc_current->desc->superencrypted_data;
tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 16);
+
+ helper_destroy_service(service);
+ last_service = NULL;
}
/* Generate a valid number of fake auth clients when the number of
* clients is zero. */
{
hs_service_t *service = helper_create_service_with_clients(0);
+ last_service = service;
service_descriptor_free(service->desc_current);
service->desc_current = NULL;
@@ -1670,12 +1741,16 @@ test_build_descriptors(void *arg)
hs_desc_superencrypted_data_t *superencrypted;
superencrypted = &service->desc_current->desc->superencrypted_data;
tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 16);
+
+ helper_destroy_service(service);
+ last_service = NULL;
}
/* Generate a valid number of fake auth clients when the number of
* clients is not a multiple of 16. */
{
hs_service_t *service = helper_create_service_with_clients(20);
+ last_service = service;
service_descriptor_free(service->desc_current);
service->desc_current = NULL;
@@ -1683,12 +1758,16 @@ test_build_descriptors(void *arg)
hs_desc_superencrypted_data_t *superencrypted;
superencrypted = &service->desc_current->desc->superencrypted_data;
tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 32);
+
+ helper_destroy_service(service);
+ last_service = NULL;
}
/* Do not generate any fake desc client when the number of clients is
* a multiple of 16 but not zero. */
{
hs_service_t *service = helper_create_service_with_clients(32);
+ last_service = service;
service_descriptor_free(service->desc_current);
service->desc_current = NULL;
@@ -1696,9 +1775,13 @@ test_build_descriptors(void *arg)
hs_desc_superencrypted_data_t *superencrypted;
superencrypted = &service->desc_current->desc->superencrypted_data;
tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 32);
+
+ helper_destroy_service(service);
+ last_service = NULL;
}
done:
+ helper_destroy_service(last_service);
hs_free_all();
}
@@ -1717,7 +1800,7 @@ test_upload_descriptors(void *arg)
MOCK(networkstatus_get_live_consensus,
mock_networkstatus_get_live_consensus);
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
&mock_ns.valid_after);
@@ -1847,9 +1930,9 @@ test_rendezvous1_parsing(void *arg)
}
/* Send out the RENDEZVOUS1 and make sure that our mock func worked */
- tt_assert(tor_mem_is_zero(rend1_payload, 32));
+ tt_assert(fast_mem_is_zero(rend1_payload, 32));
hs_circ_service_rp_has_opened(service, service_circ);
- tt_assert(!tor_mem_is_zero(rend1_payload, 32));
+ tt_assert(!fast_mem_is_zero(rend1_payload, 32));
tt_int_op(rend1_payload_len, OP_EQ, HS_LEGACY_RENDEZVOUS_CELL_SIZE);
/******************************/
diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c
index 4a6d90d97e..104e973b1f 100644
--- a/src/test/test_introduce.c
+++ b/src/test/test_introduce.c
@@ -383,8 +383,10 @@ make_intro_from_plaintext(
/* Output the cell */
*cell_out = cell;
+ cell = NULL;
done:
+ tor_free(cell);
return cell_len;
}
@@ -535,4 +537,3 @@ struct testcase_t introduce_tests[] = {
INTRODUCE_LEGACY(late_parse_v3),
END_OF_TESTCASES
};
-
diff --git a/src/test/test_key_expiration.sh b/src/test/test_key_expiration.sh
index 3474210607..54abb4a2fa 100755
--- a/src/test/test_key_expiration.sh
+++ b/src/test/test_key_expiration.sh
@@ -6,14 +6,14 @@
umask 077
set -e
-if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then
+if [ $# -eq 0 ] || [ ! -f "${1}" ] || [ ! -x "${1}" ]; then
if [ "$TESTING_TOR_BINARY" = "" ] ; then
echo "Usage: ${0} PATH_TO_TOR [case-number]"
exit 1
fi
fi
-UNAME_OS=`uname -s | cut -d_ -f1`
+UNAME_OS=$(uname -s | cut -d_ -f1)
if test "$UNAME_OS" = 'CYGWIN' || \
test "$UNAME_OS" = 'MSYS' || \
test "$UNAME_OS" = 'MINGW'; then
@@ -47,11 +47,11 @@ dump() { xxd -p "$1" | tr -d '\n '; }
die() { echo "$1" >&2 ; exit 5; }
check_dir() { [ -d "$1" ] || die "$1 did not exist"; }
check_file() { [ -e "$1" ] || die "$1 did not exist"; }
-check_no_file() { [ -e "$1" ] && die "$1 was not supposed to exist" || true; }
-check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: `dump $1` vs `dump $2`"; }
+check_no_file() { if [ -e "$1" ]; then die "$1 was not supposed to exist"; fi }
+check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: $(dump "$1") vs $(dump "$2")"; }
check_keys_eq() { check_files_eq "${SRC}/keys/${1}" "${ME}/keys/${1}"; }
-DATA_DIR=`mktemp -d -t tor_key_expiration_tests.XXXXXX`
+DATA_DIR=$(mktemp -d -t tor_key_expiration_tests.XXXXXX)
if [ -z "$DATA_DIR" ]; then
echo "Failure: mktemp invocation returned empty string" >&2
exit 3
@@ -60,10 +60,10 @@ if [ ! -d "$DATA_DIR" ]; then
echo "Failure: mktemp invocation result doesn't point to directory" >&2
exit 3
fi
-trap "rm -rf '$DATA_DIR'" 0
+trap 'rm -rf "$DATA_DIR"' 0
# Use an absolute path for this or Tor will complain
-DATA_DIR=`cd "${DATA_DIR}" && pwd`
+DATA_DIR=$(cd "${DATA_DIR}" && pwd)
touch "${DATA_DIR}/empty_torrc"
touch "${DATA_DIR}/empty_defaults_torrc"
diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh
index 7afff271cb..cbdfd1909c 100755
--- a/src/test/test_keygen.sh
+++ b/src/test/test_keygen.sh
@@ -6,14 +6,14 @@
umask 077
set -e
-if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then
+if [ $# -eq 0 ] || [ ! -f "${1}" ] || [ ! -x "${1}" ]; then
if [ "$TESTING_TOR_BINARY" = "" ] ; then
echo "Usage: ${0} PATH_TO_TOR [case-number]"
exit 1
fi
fi
-UNAME_OS=`uname -s | cut -d_ -f1`
+UNAME_OS=$(uname -s | cut -d_ -f1)
if test "$UNAME_OS" = 'CYGWIN' || \
test "$UNAME_OS" = 'MSYS' || \
test "$UNAME_OS" = 'MINGW'; then
@@ -64,11 +64,11 @@ dump() { xxd -p "$1" | tr -d '\n '; }
die() { echo "$1" >&2 ; exit 5; }
check_dir() { [ -d "$1" ] || die "$1 did not exist"; }
check_file() { [ -e "$1" ] || die "$1 did not exist"; }
-check_no_file() { [ -e "$1" ] && die "$1 was not supposed to exist" || true; }
-check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: `dump $1` vs `dump $2`"; }
+check_no_file() { if [ -e "$1" ]; then die "$1 was not supposed to exist"; fi }
+check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: $(dump "$1") vs $(dump "$2")"; }
check_keys_eq() { check_files_eq "${SRC}/keys/${1}" "${ME}/keys/${1}"; }
-DATA_DIR=`mktemp -d -t tor_keygen_tests.XXXXXX`
+DATA_DIR=$(mktemp -d -t tor_keygen_tests.XXXXXX)
if [ -z "$DATA_DIR" ]; then
echo "Failure: mktemp invocation returned empty string" >&2
exit 3
@@ -77,10 +77,10 @@ if [ ! -d "$DATA_DIR" ]; then
echo "Failure: mktemp invocation result doesn't point to directory" >&2
exit 3
fi
-trap "rm -rf '$DATA_DIR'" 0
+trap 'rm -rf "$DATA_DIR"' 0
# Use an absolute path for this or Tor will complain
-DATA_DIR=`cd "${DATA_DIR}" && pwd`
+DATA_DIR=$(cd "${DATA_DIR}" && pwd)
touch "${DATA_DIR}/empty_torrc"
touch "${DATA_DIR}/empty_defaults_torrc"
@@ -144,7 +144,9 @@ ME="${DATA_DIR}/case2a"
SRC="${DATA_DIR}/orig"
mkdir -p "${ME}/keys"
cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout" && die "Somehow succeeded when missing secret key, certs: `cat ${ME}/stdout`" || true
+if ${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout"; then
+ die "Somehow succeeded when missing secret key, certs: $(cat "${ME}/stdout")"
+fi
check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key"
grep "We needed to load a secret key.*but couldn't find it" "${ME}/stdout" >/dev/null || die "Tor didn't declare that it was missing a secret key"
@@ -281,7 +283,9 @@ SRC="${DATA_DIR}/encrypted"
mkdir -p "${ME}/keys"
cp "${SRC}/keys/ed25519_master_id_secret_key_encrypted" "${ME}/keys/"
cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout" && die "Tor started with encrypted secret key and no certs" || true
+if ${TOR} --DataDirectory "${ME}" --list-fingerprint > "${ME}/stdout"; then
+ die "Tor started with encrypted secret key and no certs"
+fi
check_no_file "${ME}/keys/ed25519_signing_cert"
check_no_file "${ME}/keys/ed25519_signing_secret_key"
@@ -370,7 +374,9 @@ mkdir -p "${ME}/keys"
cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/"
cp "${OTHER}/keys/ed25519_master_id_secret_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint >"${ME}/stdout" && die "Successfully started with mismatched keys!?" || true
+if ${TOR} --DataDirectory "${ME}" --list-fingerprint >"${ME}/stdout"; then
+ die "Successfully started with mismatched keys!?"
+fi
grep "public_key does not match.*secret_key" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a key mismatch"
@@ -386,7 +392,9 @@ ME="${DATA_DIR}/case11a"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --passphrase-fd 1 > "${ME}/stdout" && die "Successfully started with passphrase-fd but no keygen?" || true
+if ${TOR} --DataDirectory "${ME}" --passphrase-fd 1 > "${ME}/stdout"; then
+ die "Successfully started with passphrase-fd but no keygen?"
+fi
grep "passphrase-fd specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
@@ -402,7 +410,9 @@ ME="${DATA_DIR}/case11b"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --no-passphrase > "${ME}/stdout" && die "Successfully started with no-passphrase but no keygen?" || true
+if ${TOR} --DataDirectory "${ME}" --no-passphrase > "${ME}/stdout"; then
+ die "Successfully started with no-passphrase but no keygen?"
+fi
grep "no-passphrase specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
@@ -418,7 +428,9 @@ ME="${DATA_DIR}/case11C"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --newpass > "${ME}/stdout" && die "Successfully started with newpass but no keygen?" || true
+if ${TOR} --DataDirectory "${ME}" --newpass > "${ME}/stdout"; then
+ die "Successfully started with newpass but no keygen?"
+fi
grep "newpass specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
@@ -456,7 +468,9 @@ ME="${DATA_DIR}/case11E"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd ewigeblumenkraft > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd?" || true
+if ${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd ewigeblumenkraft > "${ME}/stdout"; then
+ die "Successfully started with bogus passphrase-fd?"
+fi
grep "Invalid --passphrase-fd value" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
@@ -473,7 +487,9 @@ ME="${DATA_DIR}/case11F"
mkdir -p "${ME}/keys"
-${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd 1 --no-passphrase > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd combination?" || true
+if ${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd 1 --no-passphrase > "${ME}/stdout"; then
+ die "Successfully started with bogus passphrase-fd combination?"
+fi
grep "no-passphrase specified with --passphrase-fd" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index 34f59f26cd..5e78e1ce4d 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -263,7 +263,7 @@ test_link_handshake_certs_ok(void *arg)
tt_assert(c1->handshake_state->authenticated_rsa);
tt_assert(! c1->handshake_state->authenticated_ed25519);
}
- tt_assert(! tor_mem_is_zero(
+ tt_assert(! fast_mem_is_zero(
(char*)c1->handshake_state->authenticated_rsa_peer_id, 20));
chan2 = tor_malloc_zero(sizeof(*chan2));
@@ -290,7 +290,7 @@ test_link_handshake_certs_ok(void *arg)
tt_ptr_op(c2->handshake_state->certs->ed_id_sign, OP_EQ, NULL);
}
tt_assert(c2->handshake_state->certs->id_cert);
- tt_assert(tor_mem_is_zero(
+ tt_assert(fast_mem_is_zero(
(char*)c2->handshake_state->authenticated_rsa_peer_id, 20));
/* no authentication has happened yet, since we haen't gotten an AUTH cell.
*/
@@ -948,7 +948,7 @@ test_link_handshake_send_authchallenge(void *arg)
#else
tt_int_op(36, OP_EQ, cell1->payload_len);
tt_int_op(36, OP_EQ, cell2->payload_len);
-#endif
+#endif /* defined(HAVE_WORKING_TOR_TLS_GET_TLSSECRETS) */
tt_int_op(0, OP_EQ, cell1->circ_id);
tt_int_op(0, OP_EQ, cell2->circ_id);
tt_int_op(CELL_AUTH_CHALLENGE, OP_EQ, cell1->command);
@@ -960,7 +960,7 @@ test_link_handshake_send_authchallenge(void *arg)
#else
tt_mem_op("\x00\x01\x00\x03", OP_EQ, cell1->payload + 32, 4);
tt_mem_op("\x00\x01\x00\x03", OP_EQ, cell2->payload + 32, 4);
-#endif
+#endif /* defined(HAVE_WORKING_TOR_TLS_GET_TLSSECRETS) */
tt_mem_op(cell1->payload, OP_NE, cell2->payload, 32);
done:
diff --git a/src/test/test_logging.c b/src/test/test_logging.c
index 95a2fce757..203ce64e32 100644
--- a/src/test/test_logging.c
+++ b/src/test/test_logging.c
@@ -9,14 +9,13 @@
#include "lib/err/torerr.h"
#include "lib/log/log.h"
#include "test/test.h"
-#include "lib/process/subprocess.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
static void
-dummy_cb_fn(int severity, uint32_t domain, const char *msg)
+dummy_cb_fn(int severity, log_domain_mask_t domain, const char *msg)
{
(void)severity; (void)domain; (void)msg;
}
@@ -36,7 +35,7 @@ test_get_sigsafe_err_fds(void *arg)
set_log_severity_config(LOG_WARN, LOG_ERR, &include_bug);
set_log_severity_config(LOG_WARN, LOG_ERR, &no_bug);
- no_bug.masks[0] &= ~(LD_BUG|LD_GENERAL);
+ no_bug.masks[SEVERITY_MASK_IDX(LOG_ERR)] &= ~(LD_BUG|LD_GENERAL);
set_log_severity_config(LOG_INFO, LOG_NOTICE, &no_bug2);
/* Add some logs; make sure the output is as expected. */
@@ -117,22 +116,27 @@ test_sigsafe_err(void *arg)
content = read_file_to_str(fn, 0, NULL);
tt_ptr_op(content, OP_NE, NULL);
- tor_split_lines(lines, content, (int)strlen(content));
+ smartlist_split_string(lines, content, "\n", 0, 0);
tt_int_op(smartlist_len(lines), OP_GE, 5);
- if (strstr(smartlist_get(lines, 0), "opening new log file"))
+ if (strstr(smartlist_get(lines, 0), "opening new log file")) {
+ void *item = smartlist_get(lines, 0);
smartlist_del_keeporder(lines, 0);
+ tor_free(item);
+ }
+
tt_assert(strstr(smartlist_get(lines, 0), "Say, this isn't too cool"));
- /* Next line is blank. */
- tt_assert(!strcmpstart(smartlist_get(lines, 1), "=============="));
- tt_assert(!strcmpstart(smartlist_get(lines, 2), "Minimal."));
- /* Next line is blank. */
- tt_assert(!strcmpstart(smartlist_get(lines, 3), "=============="));
- tt_str_op(smartlist_get(lines, 4), OP_EQ,
+ tt_str_op(smartlist_get(lines, 1), OP_EQ, "");
+ tt_assert(!strcmpstart(smartlist_get(lines, 2), "=============="));
+ tt_assert(!strcmpstart(smartlist_get(lines, 3), "Minimal."));
+ tt_str_op(smartlist_get(lines, 4), OP_EQ, "");
+ tt_assert(!strcmpstart(smartlist_get(lines, 5), "=============="));
+ tt_str_op(smartlist_get(lines, 6), OP_EQ,
"Testing any attempt to manually log from a signal.");
done:
tor_free(content);
+ SMARTLIST_FOREACH(lines, char *, x, tor_free(x));
smartlist_free(lines);
}
diff --git a/src/test/test_mainloop.c b/src/test/test_mainloop.c
index 089ea812cf..ed6b8a9b66 100644
--- a/src/test/test_mainloop.c
+++ b/src/test/test_mainloop.c
@@ -6,11 +6,23 @@
* \brief Tests for functions closely related to the Tor main loop
*/
+#define CONFIG_PRIVATE
+#define MAINLOOP_PRIVATE
+#define STATEFILE_PRIVATE
+
#include "test/test.h"
#include "test/log_test_helpers.h"
#include "core/or/or.h"
+#include "core/mainloop/connection.h"
#include "core/mainloop/mainloop.h"
+#include "core/mainloop/netstatus.h"
+
+#include "feature/hs/hs_service.h"
+
+#include "app/config/config.h"
+#include "app/config/statefile.h"
+#include "app/config/or_state_st.h"
static const uint64_t BILLION = 1000000000;
@@ -131,12 +143,227 @@ test_mainloop_update_time_jumps(void *arg)
monotime_disable_test_mocking();
}
+static int schedule_rescan_called = 0;
+static void
+mock_schedule_rescan_periodic_events(void)
+{
+ ++schedule_rescan_called;
+}
+
+static void
+test_mainloop_user_activity(void *arg)
+{
+ (void)arg;
+ const time_t start = 1542658829;
+ update_approx_time(start);
+
+ MOCK(schedule_rescan_periodic_events, mock_schedule_rescan_periodic_events);
+
+ reset_user_activity(start);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start);
+
+ set_network_participation(false);
+
+ // reset can move backwards and forwards, but does not change network
+ // participation.
+ reset_user_activity(start-10);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start-10);
+ reset_user_activity(start+10);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10);
+
+ tt_int_op(schedule_rescan_called, OP_EQ, 0);
+ tt_int_op(false, OP_EQ, is_participating_on_network());
+
+ // "note" can only move forward. Calling it from a non-participating
+ // state makes us rescan the periodic callbacks and set participation.
+ note_user_activity(start+20);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+20);
+ tt_int_op(true, OP_EQ, is_participating_on_network());
+ tt_int_op(schedule_rescan_called, OP_EQ, 1);
+
+ // Calling it again will move us forward, but not call rescan again.
+ note_user_activity(start+25);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25);
+ tt_int_op(true, OP_EQ, is_participating_on_network());
+ tt_int_op(schedule_rescan_called, OP_EQ, 1);
+
+ // We won't move backwards.
+ note_user_activity(start+20);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25);
+ tt_int_op(true, OP_EQ, is_participating_on_network());
+ tt_int_op(schedule_rescan_called, OP_EQ, 1);
+
+ // We _will_ adjust if the clock jumps though.
+ netstatus_note_clock_jumped(500);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+525);
+
+ netstatus_note_clock_jumped(-400);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+125);
+
+ done:
+ UNMOCK(schedule_rescan_periodic_events);
+}
+
+static unsigned int
+mock_get_num_services(void)
+{
+ return 1;
+}
+
+static connection_t *
+mock_connection_gbtu(int type)
+{
+ (void) type;
+ return (void *)"hello fellow connections";
+}
+
+static void
+test_mainloop_check_participation(void *arg)
+{
+ (void)arg;
+ or_options_t *options = options_new();
+ const time_t start = 1542658829;
+ const time_t ONE_DAY = 24*60*60;
+
+ // Suppose we've been idle for a day or two
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ check_network_participation_callback(start, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, false);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
+
+ // suppose we've been idle for 2 days... but we are a server.
+ reset_user_activity(start - 2*ONE_DAY);
+ options->ORPort_set = 1;
+ set_network_participation(true);
+ check_network_participation_callback(start+2, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, true);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+2);
+ options->ORPort_set = 0;
+
+ // idle for 2 days, but we have a hidden service.
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ MOCK(hs_service_get_num_services, mock_get_num_services);
+ check_network_participation_callback(start+3, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, true);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+3);
+ UNMOCK(hs_service_get_num_services);
+
+ // idle for 2 days but we have at least one user connection
+ MOCK(connection_get_by_type_nonlinked, mock_connection_gbtu);
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ options->DormantTimeoutDisabledByIdleStreams = 1;
+ check_network_participation_callback(start+10, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, true);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10);
+
+ // as above, but DormantTimeoutDisabledByIdleStreams is not set
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ options->DormantTimeoutDisabledByIdleStreams = 0;
+ check_network_participation_callback(start+13, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, false);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
+ UNMOCK(connection_get_by_type_nonlinked);
+ options->DormantTimeoutDisabledByIdleStreams = 1;
+
+ // idle for 2 days but DormantClientTimeout is 3 days
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ options->DormantClientTimeout = ONE_DAY * 3;
+ check_network_participation_callback(start+30, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, true);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
+
+ done:
+ or_options_free(options);
+ UNMOCK(hs_service_get_num_services);
+ UNMOCK(connection_get_by_type_nonlinked);
+}
+
+static void
+test_mainloop_dormant_load_state(void *arg)
+{
+ (void)arg;
+ or_state_t *state = or_state_new();
+ const time_t start = 1543956575;
+
+ reset_user_activity(0);
+ set_network_participation(false);
+
+ // When we construct a new state, it starts out in "auto" mode.
+ tt_int_op(state->Dormant, OP_EQ, -1);
+
+ // Initializing from "auto" makes us start out (by default) non-Dormant,
+ // with activity right now.
+ netstatus_load_from_state(state, start);
+ tt_assert(is_participating_on_network());
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start);
+
+ // Initializing from dormant clears the last user activity time, and
+ // makes us dormant.
+ state->Dormant = 1;
+ netstatus_load_from_state(state, start);
+ tt_assert(! is_participating_on_network());
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, 0);
+
+ // Initializing from non-dormant sets the last user activity time, and
+ // makes us non-dormant.
+ state->Dormant = 0;
+ state->MinutesSinceUserActivity = 123;
+ netstatus_load_from_state(state, start);
+ tt_assert(is_participating_on_network());
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start - 123*60);
+
+ // If we would start dormant, but DormantCanceledByStartup is set, then
+ // we start up non-dormant.
+ state->Dormant = 1;
+ get_options_mutable()->DormantCanceledByStartup = 1;
+ netstatus_load_from_state(state, start);
+ tt_assert(is_participating_on_network());
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start);
+
+ done:
+ or_state_free(state);
+}
+
+static void
+test_mainloop_dormant_save_state(void *arg)
+{
+ (void)arg;
+ or_state_t *state = or_state_new();
+ const time_t start = 1543956575;
+
+ // Can we save a non-dormant state correctly?
+ reset_user_activity(start - 1000);
+ set_network_participation(true);
+ netstatus_flush_to_state(state, start);
+
+ tt_int_op(state->Dormant, OP_EQ, 0);
+ tt_int_op(state->MinutesSinceUserActivity, OP_EQ, 1000 / 60);
+
+ // Can we save a dormant state correctly?
+ set_network_participation(false);
+ netstatus_flush_to_state(state, start);
+
+ tt_int_op(state->Dormant, OP_EQ, 1);
+ tt_int_op(state->MinutesSinceUserActivity, OP_EQ, 0);
+
+ done:
+ or_state_free(state);
+}
+
#define MAINLOOP_TEST(name) \
{ #name, test_mainloop_## name , TT_FORK, NULL, NULL }
struct testcase_t mainloop_tests[] = {
MAINLOOP_TEST(update_time_normal),
MAINLOOP_TEST(update_time_jumps),
+ MAINLOOP_TEST(user_activity),
+ MAINLOOP_TEST(check_participation),
+ MAINLOOP_TEST(dormant_load_state),
+ MAINLOOP_TEST(dormant_save_state),
END_OF_TESTCASES
};
-
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
index 4c4317d81a..804e6c546a 100644
--- a/src/test/test_microdesc.c
+++ b/src/test/test_microdesc.c
@@ -11,6 +11,7 @@
#include "feature/dirparse/routerparse.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodefamily.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/torcert.h"
@@ -20,6 +21,7 @@
#include "feature/nodelist/routerstatus_st.h"
#include "test/test.h"
+#include "test/log_test_helpers.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
@@ -70,6 +72,7 @@ test_md_cache(void *data)
const char *test_md3_noannotation = strchr(test_md3, '\n')+1;
time_t time1, time2, time3;
char *fn = NULL, *s = NULL;
+ char *encoded_family = NULL;
(void)data;
options = get_options_mutable();
@@ -172,8 +175,9 @@ test_md_cache(void *data)
tt_ptr_op(md1->family, OP_EQ, NULL);
tt_ptr_op(md3->family, OP_NE, NULL);
- tt_int_op(smartlist_len(md3->family), OP_EQ, 3);
- tt_str_op(smartlist_get(md3->family, 0), OP_EQ, "nodeX");
+
+ encoded_family = nodefamily_format(md3->family);
+ tt_str_op(encoded_family, OP_EQ, "nodex nodey nodez");
/* Now rebuild the cache! */
tt_int_op(microdesc_cache_rebuild(mc, 1), OP_EQ, 0);
@@ -254,6 +258,7 @@ test_md_cache(void *data)
smartlist_free(wanted);
tor_free(s);
tor_free(fn);
+ tor_free(encoded_family);
}
static const char truncated_md[] =
@@ -417,6 +422,28 @@ static const char test_md2_21[] =
"ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n"
"id ed25519 wqfLzgfCtRfYNg88LsL1QpzxS0itapJ1aj6TbnByx/Q\n";
+static const char test_md2_withfamily_28[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAL2R8EfubUcahxha4u02P4VAR0llQIMwFAmrHPjzcK7apcQgDOf2ovOA\n"
+ "+YQnJFxlpBmCoCZC6ssCi+9G0mqo650lFuTMP5I90BdtjotfzESfTykHLiChyvhd\n"
+ "l0dlqclb2SU/GKem/fLRXH16aNi72CdSUu/1slKs/70ILi34QixRAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n"
+ "family OtherNode !Strange\n"
+ "id ed25519 wqfLzgfCtRfYNg88LsL1QpzxS0itapJ1aj6TbnByx/Q\n";
+
+static const char test_md2_withfamily_29[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAL2R8EfubUcahxha4u02P4VAR0llQIMwFAmrHPjzcK7apcQgDOf2ovOA\n"
+ "+YQnJFxlpBmCoCZC6ssCi+9G0mqo650lFuTMP5I90BdtjotfzESfTykHLiChyvhd\n"
+ "l0dlqclb2SU/GKem/fLRXH16aNi72CdSUu/1slKs/70ILi34QixRAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n"
+ "family !Strange $B7E27F104213C36F13E7E9829182845E495997A0 othernode\n"
+ "id ed25519 wqfLzgfCtRfYNg88LsL1QpzxS0itapJ1aj6TbnByx/Q\n";
+
static void
test_md_generate(void *arg)
{
@@ -447,6 +474,17 @@ test_md_generate(void *arg)
tt_assert(ed25519_pubkey_eq(md->ed25519_identity_pkey,
&ri->cache_info.signing_key_cert->signing_key));
+ // Try family encoding.
+ microdesc_free(md);
+ ri->declared_family = smartlist_new();
+ smartlist_add_strdup(ri->declared_family, "OtherNode !Strange");
+ md = dirvote_create_microdescriptor(ri, 28);
+ tt_str_op(md->body, OP_EQ, test_md2_withfamily_28);
+
+ microdesc_free(md);
+ md = dirvote_create_microdescriptor(ri, 29);
+ tt_str_op(md->body, OP_EQ, test_md2_withfamily_29);
+
done:
microdesc_free(md);
routerinfo_free(ri);
@@ -611,6 +649,41 @@ static const char MD_PARSE_TEST_DATA[] =
"ntor-onion-key k2yFqTU2vzMCQDEiE/j9UcEHxKrXMLpB3IL0or09sik=\n"
"id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n"
"p6 allow 80\n"
+ /* Good 11: Normal, non-exit relay with ipv6 address */
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n"
+ "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n"
+ "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "a [::1:2:3:4]:9090\n"
+ "a 18.0.0.1:9999\n"
+ "ntor-onion-key k2yFqTU2vzMCQDEiE/j9UcEHxKrXMLpB3IL0or09sik=\n"
+ "id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n"
+ /* Good 12: Normal, exit relay with ipv6 address */
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n"
+ "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n"
+ "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "a [::1:2:3:4]:9090\n"
+ "a 18.0.0.1:9999\n"
+ "ntor-onion-key k2yFqTU2vzMCQDEiE/j9UcEHxKrXMLpB3IL0or09sik=\n"
+ "p accept 20-23,43,53,79-81,88,110,143,194,220,389,443,464,531,543-544\n"
+ "id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n"
+ /* Good 13: Normal, exit relay with only ipv6 exit policy */
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n"
+ "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n"
+ "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "a [::1:2:3:4]:9090\n"
+ "a 18.0.0.1:9999\n"
+ "ntor-onion-key k2yFqTU2vzMCQDEiE/j9UcEHxKrXMLpB3IL0or09sik=\n"
+ "p6 accept 20-23,43,53,79-81,88,110,143,194,220,389,443,464,531,543-544\n"
+ "id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n"
;
#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
ENABLE_GCC_WARNING(overlength-strings)
@@ -628,7 +701,7 @@ test_md_parse(void *arg)
smartlist_t *mds = microdescs_parse_from_string(MD_PARSE_TEST_DATA,
NULL, 1, SAVED_NOWHERE,
invalid);
- tt_int_op(smartlist_len(mds), OP_EQ, 11);
+ tt_int_op(smartlist_len(mds), OP_EQ, 14);
tt_int_op(smartlist_len(invalid), OP_EQ, 4);
test_memeq_hex(smartlist_get(invalid,0),
@@ -675,6 +748,21 @@ test_md_parse(void *arg)
tt_assert(tor_addr_family(&md->ipv6_addr) == AF_INET6);
tt_int_op(md->ipv6_orport, OP_EQ, 9090);
+ md = smartlist_get(mds, 11);
+ tt_assert(tor_addr_family(&md->ipv6_addr) == AF_INET6);
+ tt_int_op(md->ipv6_orport, OP_EQ, 9090);
+ tt_int_op(md->policy_is_reject_star, OP_EQ, 1);
+
+ md = smartlist_get(mds, 12);
+ tt_assert(tor_addr_family(&md->ipv6_addr) == AF_INET6);
+ tt_int_op(md->ipv6_orport, OP_EQ, 9090);
+ tt_int_op(md->policy_is_reject_star, OP_EQ, 0);
+
+ md = smartlist_get(mds, 13);
+ tt_assert(tor_addr_family(&md->ipv6_addr) == AF_INET6);
+ tt_int_op(md->ipv6_orport, OP_EQ, 9090);
+ tt_int_op(md->policy_is_reject_star, OP_EQ, 0);
+
done:
SMARTLIST_FOREACH(mds, microdesc_t *, mdsc, microdesc_free(mdsc));
smartlist_free(mds);
@@ -683,6 +771,80 @@ test_md_parse(void *arg)
tor_free(mem_op_hex_tmp);
}
+static void
+test_md_parse_id_ed25519(void *arg)
+{
+ (void)arg;
+
+ /* A correct MD with an ed25519 ID ... and an unspecified ID type,
+ * which is permitted. */
+ const char GOOD_MD[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n"
+ "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n"
+ "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "id ed25519 VGhpcyBpc24ndCBhY3R1YWxseSBhIHB1YmxpYyBrZXk\n"
+ "id wumpus dodecahedron\n";
+
+ smartlist_t *mds = NULL;
+ const microdesc_t *md;
+
+ mds = microdescs_parse_from_string(GOOD_MD,
+ NULL, 1, SAVED_NOWHERE, NULL);
+ tt_assert(mds);
+ tt_int_op(smartlist_len(mds), OP_EQ, 1);
+ md = smartlist_get(mds, 0);
+ tt_mem_op(md->ed25519_identity_pkey, OP_EQ,
+ "This isn't actually a public key", ED25519_PUBKEY_LEN);
+ SMARTLIST_FOREACH(mds, microdesc_t *, m, microdesc_free(m));
+ smartlist_free(mds);
+
+ /* As above, but ed25519 ID key appears twice. */
+ const char DUPLICATE_KEY[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n"
+ "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n"
+ "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "id ed25519 VGhpcyBpc24ndCBhY3R1YWxseSBhIHB1YmxpYyBrZXk\n"
+ "id ed25519 VGhpcyBpc24ndCBhY3R1YWxseSBhIHB1YmxpYyBrZXk\n";
+
+ setup_capture_of_logs(LOG_WARN);
+ mds = microdescs_parse_from_string(DUPLICATE_KEY,
+ NULL, 1, SAVED_NOWHERE, NULL);
+ tt_assert(mds);
+ tt_int_op(smartlist_len(mds), OP_EQ, 0); // no entries.
+ expect_single_log_msg_containing("Extra ed25519 key");
+ mock_clean_saved_logs();
+ smartlist_free(mds);
+
+ /* As above, but ed25519 ID key is invalid. */
+ const char BOGUS_KEY[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n"
+ "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n"
+ "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "id ed25519 VGhpcyBpc24ndCBhY3R1YWxseSBhIHB1YmxpYyZZZZZZZZZZZ\n";
+
+ mds = microdescs_parse_from_string(BOGUS_KEY,
+ NULL, 1, SAVED_NOWHERE, NULL);
+ tt_assert(mds);
+ tt_int_op(smartlist_len(mds), OP_EQ, 0); // no entries.
+ expect_single_log_msg_containing("Bogus ed25519 key");
+
+ done:
+ if (mds) {
+ SMARTLIST_FOREACH(mds, microdesc_t *, m, microdesc_free(m));
+ smartlist_free(mds);
+ }
+ teardown_capture_of_logs();
+}
+
static int mock_rgsbd_called = 0;
static routerstatus_t *mock_rgsbd_val_a = NULL;
static routerstatus_t *mock_rgsbd_val_b = NULL;
@@ -816,6 +978,7 @@ struct testcase_t microdesc_tests[] = {
{ "broken_cache", test_md_cache_broken, TT_FORK, NULL, NULL },
{ "generate", test_md_generate, 0, NULL, NULL },
{ "parse", test_md_parse, 0, NULL, NULL },
+ { "parse_id_ed25519", test_md_parse_id_ed25519, 0, NULL, NULL },
{ "reject_cache", test_md_reject_cache, TT_FORK, NULL, NULL },
{ "corrupt_desc", test_md_corrupt_desc, TT_FORK, NULL, NULL },
END_OF_TESTCASES
diff --git a/src/test/test_namemap.c b/src/test/test_namemap.c
new file mode 100644
index 0000000000..df77d4e2de
--- /dev/null
+++ b/src/test/test_namemap.c
@@ -0,0 +1,174 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "test/test.h"
+
+#include "lib/cc/torint.h"
+#include "lib/container/namemap.h"
+#include "lib/container/namemap_st.h"
+#include "lib/malloc/malloc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static void
+test_namemap_empty(void *arg)
+{
+ (void)arg;
+
+ namemap_t m;
+ namemap_init(&m);
+ namemap_t m2 = NAMEMAP_INIT();
+
+ tt_uint_op(0, OP_EQ, namemap_get_size(&m));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, "hello"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, "hello"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, "hello128"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, ""));
+ tt_uint_op(0, OP_EQ, namemap_get_size(&m));
+
+ tt_uint_op(0, OP_EQ, namemap_get_size(&m2));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello128"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, ""));
+ tt_uint_op(0, OP_EQ, namemap_get_size(&m));
+
+ done:
+ namemap_clear(&m);
+ namemap_clear(&m2);
+}
+
+static void
+test_namemap_toolong(void *arg)
+{
+ (void)arg;
+ namemap_t m;
+ char *ok = NULL;
+ char *toolong = NULL;
+ namemap_init(&m);
+
+ ok = tor_malloc_zero(MAX_NAMEMAP_NAME_LEN+1);
+ memset(ok, 'x', MAX_NAMEMAP_NAME_LEN);
+
+ toolong = tor_malloc_zero(MAX_NAMEMAP_NAME_LEN+2);
+ memset(toolong, 'x', MAX_NAMEMAP_NAME_LEN+1);
+
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, ok));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, toolong));
+ unsigned u1 = namemap_get_or_create_id(&m, toolong);
+ unsigned u2 = namemap_get_or_create_id(&m, ok);
+ tt_uint_op(u1, OP_EQ, NAMEMAP_ERR);
+ tt_uint_op(u2, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(u2, OP_EQ, namemap_get_id(&m, ok));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m, toolong));
+
+ tt_str_op(ok, OP_EQ, namemap_get_name(&m, u2));
+ tt_ptr_op(NULL, OP_EQ, namemap_get_name(&m, u1));
+
+ done:
+ tor_free(ok);
+ tor_free(toolong);
+ namemap_clear(&m);
+}
+
+static void
+test_namemap_blackbox(void *arg)
+{
+ (void)arg;
+
+ namemap_t m1, m2;
+ namemap_init(&m1);
+ namemap_init(&m2);
+
+ unsigned u1 = namemap_get_or_create_id(&m1, "hello");
+ unsigned u2 = namemap_get_or_create_id(&m1, "world");
+ tt_uint_op(u1, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(u2, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(u1, OP_NE, u2);
+
+ tt_uint_op(u1, OP_EQ, namemap_get_id(&m1, "hello"));
+ tt_uint_op(u1, OP_EQ, namemap_get_or_create_id(&m1, "hello"));
+ tt_uint_op(u2, OP_EQ, namemap_get_id(&m1, "world"));
+ tt_uint_op(u2, OP_EQ, namemap_get_or_create_id(&m1, "world"));
+
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m1, "HELLO"));
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m2, "hello"));
+
+ unsigned u3 = namemap_get_or_create_id(&m2, "hola");
+ tt_uint_op(u3, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(NAMEMAP_ERR, OP_EQ, namemap_get_id(&m1, "hola"));
+ tt_uint_op(u3, OP_EQ, namemap_get_or_create_id(&m2, "hola"));
+ tt_uint_op(u3, OP_EQ, namemap_get_id(&m2, "hola"));
+
+ unsigned int u4 = namemap_get_or_create_id(&m1, "hola");
+ tt_uint_op(u4, OP_NE, NAMEMAP_ERR);
+ tt_uint_op(u4, OP_EQ, namemap_get_id(&m1, "hola"));
+ tt_uint_op(u3, OP_EQ, namemap_get_id(&m2, "hola"));
+
+ tt_str_op("hello", OP_EQ, namemap_get_name(&m1, u1));
+ tt_str_op("world", OP_EQ, namemap_get_name(&m1, u2));
+ tt_str_op("hola", OP_EQ, namemap_get_name(&m2, u3));
+ tt_str_op("hola", OP_EQ, namemap_get_name(&m1, u4));
+
+ tt_ptr_op(NULL, OP_EQ, namemap_get_name(&m2, u3 + 10));
+
+ done:
+ namemap_clear(&m1);
+ namemap_clear(&m2);
+}
+
+static void
+test_namemap_internals(void *arg)
+{
+ (void)arg;
+ // This test actually assumes know something about the identity layout.
+ namemap_t m;
+ namemap_init(&m);
+
+ tt_uint_op(0, OP_EQ, namemap_get_or_create_id(&m, "that"));
+ tt_uint_op(0, OP_EQ, namemap_get_or_create_id(&m, "that"));
+ tt_uint_op(1, OP_EQ, namemap_get_or_create_id(&m, "is"));
+ tt_uint_op(1, OP_EQ, namemap_get_or_create_id(&m, "is"));
+
+ tt_uint_op(0, OP_EQ, namemap_get_id(&m, "that"));
+ tt_uint_op(0, OP_EQ, namemap_get_id(&m, "that"));
+ tt_uint_op(1, OP_EQ, namemap_get_id(&m, "is"));
+ tt_uint_op(2, OP_EQ, namemap_get_or_create_id(&m, "not"));
+ tt_uint_op(1, OP_EQ, namemap_get_or_create_id(&m, "is"));
+ tt_uint_op(2, OP_EQ, namemap_get_or_create_id(&m, "not"));
+
+ done:
+ namemap_clear(&m);
+}
+
+static void
+test_namemap_fmt(void *arg)
+{
+ (void)arg;
+ namemap_t m = NAMEMAP_INIT();
+
+ unsigned a = namemap_get_or_create_id(&m, "greetings");
+ unsigned b = namemap_get_or_create_id(&m, "earthlings");
+
+ tt_str_op(namemap_fmt_name(&m, a), OP_EQ, "greetings");
+ tt_str_op(namemap_fmt_name(&m, b), OP_EQ, "earthlings");
+ tt_int_op(a, OP_NE, 100);
+ tt_int_op(b, OP_NE, 100);
+ tt_str_op(namemap_fmt_name(&m, 100), OP_EQ, "{100}");
+
+ done:
+ namemap_clear(&m);
+}
+
+#define T(name) \
+ { #name, test_namemap_ ## name , 0, NULL, NULL }
+
+struct testcase_t namemap_tests[] = {
+ T(empty),
+ T(toolong),
+ T(blackbox),
+ T(internals),
+ T(fmt),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_netinfo.c b/src/test/test_netinfo.c
new file mode 100644
index 0000000000..27d276d42f
--- /dev/null
+++ b/src/test/test_netinfo.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "trunnel/netinfo.h"
+#include "test/test.h"
+
+static void
+test_netinfo_unsupported_addr(void *arg)
+{
+ const uint8_t wire_data[] =
+ { // TIME
+ 0x00, 0x00, 0x00, 0x01,
+ // OTHERADDR
+ 0x04, // ATYPE
+ 0x04, // ALEN
+ 0x08, 0x08, 0x08, 0x08, // AVAL
+ 0x01, // NMYADDR
+ 0x03, // ATYPE (unsupported)
+ 0x05, // ALEN
+ 'a', 'd', 'r', 'r', '!' // AVAL (unsupported)
+ };
+
+ (void)arg;
+
+ netinfo_cell_t *parsed_cell = NULL;
+
+ ssize_t parsed = netinfo_cell_parse(&parsed_cell, wire_data,
+ sizeof(wire_data));
+
+ tt_assert(parsed == sizeof(wire_data));
+
+ netinfo_addr_t *addr = netinfo_cell_get_my_addrs(parsed_cell, 0);
+ tt_assert(addr);
+
+ tt_int_op(3, OP_EQ, netinfo_addr_get_addr_type(addr));
+ tt_int_op(5, OP_EQ, netinfo_addr_get_len(addr));
+
+ done:
+ netinfo_cell_free(parsed_cell);
+}
+
+struct testcase_t netinfo_tests[] = {
+ { "unsupported_addr", test_netinfo_unsupported_addr, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c
index 53eb0413e5..b8ca56bb81 100644
--- a/src/test/test_nodelist.c
+++ b/src/test/test_nodelist.c
@@ -6,15 +6,21 @@
* \brief Unit tests for nodelist related functions.
**/
+#define NODELIST_PRIVATE
+
#include "core/or/or.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include "feature/nodelist/describe.h"
#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodefamily.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/torcert.h"
+#include "core/or/extend_info_st.h"
#include "feature/nodelist/microdesc_st.h"
#include "feature/nodelist/networkstatus_st.h"
#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/nodefamily_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerstatus_st.h"
@@ -72,7 +78,7 @@ test_nodelist_node_get_verbose_nickname_not_named(void *arg)
}
/** A node should be considered a directory server if it has an open dirport
- * of it accepts tunnelled directory requests.
+ * or it accepts tunnelled directory requests.
*/
static void
test_nodelist_node_is_dir(void *arg)
@@ -231,6 +237,1015 @@ test_nodelist_ed_id(void *arg)
#undef N_NODES
}
+static void
+test_nodelist_nodefamily(void *arg)
+{
+ (void)arg;
+ /* hex ID digests */
+ const char h1[] = "5B435D6869206861206C65207363617270652070";
+ const char h2[] = "75C3B220616E6461726520696E206769726F2061";
+ const char h3[] = "2074726F766172206461206D616E67696172652C";
+ const char h4[] = "206D656E747265206E6F6E2076616C65206C2769";
+ const char h5[] = "6E766572736F2E202D2D5072696D6F204C657669";
+
+ /* binary ID digests */
+ uint8_t d1[DIGEST_LEN], d2[DIGEST_LEN], d3[DIGEST_LEN], d4[DIGEST_LEN],
+ d5[DIGEST_LEN];
+ base16_decode((char*)d1, sizeof(d1), h1, strlen(h1));
+ base16_decode((char*)d2, sizeof(d2), h2, strlen(h2));
+ base16_decode((char*)d3, sizeof(d3), h3, strlen(h3));
+ base16_decode((char*)d4, sizeof(d4), h4, strlen(h4));
+ base16_decode((char*)d5, sizeof(d5), h5, strlen(h5));
+
+ char *enc=NULL, *enc2=NULL;
+
+ nodefamily_t *nf1 = NULL;
+ nodefamily_t *nf2 = NULL;
+ nodefamily_t *nf3 = NULL;
+
+ enc = nodefamily_format(NULL);
+ tt_str_op(enc, OP_EQ, "");
+ tor_free(enc);
+
+ /* Make sure that sorting and de-duplication work. */
+ tor_asprintf(&enc, "$%s hello", h1);
+ nf1 = nodefamily_parse(enc, NULL, 0);
+ tt_assert(nf1);
+ tor_free(enc);
+
+ tor_asprintf(&enc, "hello hello $%s hello", h1);
+ nf2 = nodefamily_parse(enc, NULL, 0);
+ tt_assert(nf2);
+ tt_ptr_op(nf1, OP_EQ, nf2);
+ tor_free(enc);
+
+ tor_asprintf(&enc, "%s $%s hello", h1, h1);
+ nf3 = nodefamily_parse(enc, NULL, 0);
+ tt_assert(nf3);
+ tt_ptr_op(nf1, OP_EQ, nf3);
+ tor_free(enc);
+
+ tt_assert(nodefamily_contains_rsa_id(nf1, d1));
+ tt_assert(! nodefamily_contains_rsa_id(nf1, d2));
+ tt_assert(nodefamily_contains_nickname(nf1, "hello"));
+ tt_assert(nodefamily_contains_nickname(nf1, "HELLO"));
+ tt_assert(! nodefamily_contains_nickname(nf1, "goodbye"));
+
+ tt_int_op(nf1->refcnt, OP_EQ, 3);
+ nodefamily_free(nf3);
+ tt_int_op(nf1->refcnt, OP_EQ, 2);
+
+ /* Try parsing with a provided self RSA digest. */
+ nf3 = nodefamily_parse("hello ", d1, 0);
+ tt_assert(nf3);
+ tt_ptr_op(nf1, OP_EQ, nf3);
+
+ /* Do we get the expected result when we re-encode? */
+ tor_asprintf(&enc, "$%s hello", h1);
+ enc2 = nodefamily_format(nf1);
+ tt_str_op(enc2, OP_EQ, enc);
+ tor_free(enc2);
+ tor_free(enc);
+
+ /* Make sure that we get a different result if we give a different digest. */
+ nodefamily_free(nf3);
+ tor_asprintf(&enc, "hello $%s hello", h3);
+ nf3 = nodefamily_parse(enc, NULL, 0);
+ tt_assert(nf3);
+ tt_ptr_op(nf1, OP_NE, nf3);
+ tor_free(enc);
+
+ tt_assert(nodefamily_contains_rsa_id(nf3, d3));
+ tt_assert(! nodefamily_contains_rsa_id(nf3, d2));
+ tt_assert(! nodefamily_contains_rsa_id(nf3, d1));
+ tt_assert(nodefamily_contains_nickname(nf3, "hello"));
+ tt_assert(! nodefamily_contains_nickname(nf3, "goodbye"));
+
+ nodefamily_free(nf1);
+ nodefamily_free(nf2);
+ nodefamily_free(nf3);
+
+ /* Try one with several digests, all with nicknames appended, in different
+ formats. */
+ tor_asprintf(&enc, "%s $%s $%s=res $%s~ist", h1, h2, h3, h4);
+ nf1 = nodefamily_parse(enc, d5, 0);
+ tt_assert(nf1);
+ tt_assert(nodefamily_contains_rsa_id(nf1, d1));
+ tt_assert(nodefamily_contains_rsa_id(nf1, d2));
+ tt_assert(nodefamily_contains_rsa_id(nf1, d3));
+ tt_assert(nodefamily_contains_rsa_id(nf1, d4));
+ tt_assert(nodefamily_contains_rsa_id(nf1, d5));
+ /* Nicknames aren't preserved when ids are present, since node naming is
+ * deprecated */
+ tt_assert(! nodefamily_contains_nickname(nf3, "res"));
+ tor_free(enc);
+ tor_asprintf(&enc, "$%s $%s $%s $%s $%s", h4, h3, h1, h5, h2);
+ enc2 = nodefamily_format(nf1);
+ tt_str_op(enc, OP_EQ, enc2);
+ tor_free(enc);
+ tor_free(enc2);
+
+ /* Try ones where we parse the empty string. */
+ nf2 = nodefamily_parse("", NULL, 0);
+ nf3 = nodefamily_parse("", d4, 0);
+ tt_assert(nf2);
+ tt_assert(nf3);
+ tt_ptr_op(nf2, OP_NE, nf3);
+
+ tt_assert(! nodefamily_contains_rsa_id(nf2, d4));
+ tt_assert(nodefamily_contains_rsa_id(nf3, d4));
+ tt_assert(! nodefamily_contains_rsa_id(nf2, d5));
+ tt_assert(! nodefamily_contains_rsa_id(nf3, d5));
+ tt_assert(! nodefamily_contains_nickname(nf2, "fred"));
+ tt_assert(! nodefamily_contains_nickname(nf3, "bosco"));
+
+ /* The NULL family should contain nothing. */
+ tt_assert(! nodefamily_contains_rsa_id(NULL, d4));
+ tt_assert(! nodefamily_contains_rsa_id(NULL, d5));
+
+ done:
+ tor_free(enc);
+ tor_free(enc2);
+ nodefamily_free(nf1);
+ nodefamily_free(nf2);
+ nodefamily_free(nf3);
+ nodefamily_free_all();
+}
+
+static void
+test_nodelist_nodefamily_parse_err(void *arg)
+{
+ (void)arg;
+ nodefamily_t *nf1 = NULL;
+ char *enc = NULL;
+ const char *semibogus =
+ "sdakljfdslkfjdsaklfjdkl9sdf " // too long for nickname
+ "$jkASDFLkjsadfjhkl " // not hex
+ "$7468696e67732d696e2d7468656d73656c766573 " // ok
+ "reticulatogranulate "// ok
+ "$73656d69616e7468726f706f6c6f676963616c6c79 " // too long for hex
+ "$616273656e746d696e6465646e6573736573" // too short for hex
+ ;
+
+ setup_capture_of_logs(LOG_WARN);
+
+ // We only get two items when we parse this.
+ for (int reject = 0; reject <= 1; ++reject) {
+ for (int log_at_warn = 0; log_at_warn <= 1; ++log_at_warn) {
+ unsigned flags = log_at_warn ? NF_WARN_MALFORMED : 0;
+ flags |= reject ? NF_REJECT_MALFORMED : 0;
+ nf1 = nodefamily_parse(semibogus, NULL, flags);
+ if (reject) {
+ tt_assert(nf1 == NULL);
+ } else {
+ tt_assert(nf1);
+ enc = nodefamily_format(nf1);
+ tt_str_op(enc, OP_EQ,
+ "$7468696E67732D696E2D7468656D73656C766573 "
+ "reticulatogranulate");
+ tor_free(enc);
+ }
+
+ if (log_at_warn) {
+ expect_log_msg_containing("$616273656e746d696e6465646e6573736573");
+ expect_log_msg_containing("sdakljfdslkfjdsaklfjdkl9sdf");
+ } else {
+ tt_int_op(mock_saved_log_n_entries(), OP_EQ, 0);
+ }
+ mock_clean_saved_logs();
+ }
+ }
+
+ done:
+ tor_free(enc);
+ nodefamily_free(nf1);
+ teardown_capture_of_logs();
+}
+
+static const node_t *
+mock_node_get_by_id(const char *id)
+{
+ if (fast_memeq(id, "!!!!!!!!!!!!!!!!!!!!", DIGEST_LEN))
+ return NULL;
+
+ // use tor_free, not node_free.
+ node_t *fake_node = tor_malloc_zero(sizeof(node_t));
+ memcpy(fake_node->identity, id, DIGEST_LEN);
+ return fake_node;
+}
+
+static const node_t *
+mock_node_get_by_nickname(const char *nn, unsigned flags)
+{
+ (void)flags;
+ if (!strcmp(nn, "nonesuch"))
+ return NULL;
+
+ // use tor_free, not node_free.
+ node_t *fake_node = tor_malloc_zero(sizeof(node_t));
+ strlcpy(fake_node->identity, nn, DIGEST_LEN);
+ return fake_node;
+}
+
+static void
+test_nodelist_nodefamily_lookup(void *arg)
+{
+ (void)arg;
+ MOCK(node_get_by_nickname, mock_node_get_by_nickname);
+ MOCK(node_get_by_id, mock_node_get_by_id);
+ smartlist_t *sl = smartlist_new();
+ nodefamily_t *nf1 = NULL;
+ char *mem_op_hex_tmp = NULL;
+
+ // 'null' is allowed.
+ nodefamily_add_nodes_to_smartlist(NULL, sl);
+ tt_int_op(smartlist_len(sl), OP_EQ, 0);
+
+ // Try a real family
+ nf1 = nodefamily_parse("$EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE "
+ "$2121212121212121212121212121212121212121 "
+ "$3333333333333333333333333333333333333333 "
+ "erewhon nonesuch", NULL, 0);
+ tt_assert(nf1);
+ nodefamily_add_nodes_to_smartlist(nf1, sl);
+ // There were 5 elements; 2 were dropped because the mocked lookup failed.
+ tt_int_op(smartlist_len(sl), OP_EQ, 3);
+
+ const node_t *n = smartlist_get(sl, 0);
+ test_memeq_hex(n->identity, "3333333333333333333333333333333333333333");
+ n = smartlist_get(sl, 1);
+ test_memeq_hex(n->identity, "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE");
+ n = smartlist_get(sl, 2);
+ tt_str_op(n->identity, OP_EQ, "erewhon");
+
+ done:
+ UNMOCK(node_get_by_nickname);
+ UNMOCK(node_get_by_id);
+ SMARTLIST_FOREACH(sl, node_t *, fake_node, tor_free(fake_node));
+ smartlist_free(sl);
+ nodefamily_free(nf1);
+ tor_free(mem_op_hex_tmp);
+}
+
+static void
+test_nodelist_nickname_matches(void *arg)
+{
+ (void)arg;
+ node_t mock_node;
+ routerstatus_t mock_rs;
+ memset(&mock_node, 0, sizeof(mock_node));
+ memset(&mock_rs, 0, sizeof(mock_rs));
+
+ strlcpy(mock_rs.nickname, "evilgeniuses", sizeof(mock_rs.nickname));
+ mock_node.rs = &mock_rs;
+ memcpy(mock_node.identity, ".forabettertomorrow.", DIGEST_LEN);
+
+#define match(x) tt_assert(node_nickname_matches(&mock_node, (x)))
+#define no_match(x) tt_assert(! node_nickname_matches(&mock_node, (x)))
+
+ match("evilgeniuses");
+ match("EvilGeniuses");
+ match("EvilGeniuses");
+ match("2e666f7261626574746572746f6d6f72726f772e");
+ match("2E666F7261626574746572746F6D6F72726F772E");
+ match("$2e666f7261626574746572746f6d6f72726f772e");
+ match("$2E666F7261626574746572746F6D6F72726F772E");
+ match("$2E666F7261626574746572746F6D6F72726F772E~evilgeniuses");
+ match("$2E666F7261626574746572746F6D6F72726F772E~EVILGENIUSES");
+
+ no_match("evilgenius");
+ no_match("evilgeniuseses");
+ no_match("evil.genius");
+ no_match("$2E666F7261626574746572746F6D6F72726FFFFF");
+ no_match("2E666F7261626574746572746F6D6F72726FFFFF");
+ no_match("$2E666F7261626574746572746F6D6F72726F772E~fred");
+ no_match("$2E666F7261626574746572746F6D6F72726F772E=EVILGENIUSES");
+ done:
+ ;
+}
+
+static void
+test_nodelist_node_nodefamily(void *arg)
+{
+ (void)arg;
+ node_t mock_node1;
+ routerstatus_t mock_rs;
+ microdesc_t mock_md;
+
+ node_t mock_node2;
+ routerinfo_t mock_ri;
+
+ smartlist_t *nodes=smartlist_new();
+
+ memset(&mock_node1, 0, sizeof(mock_node1));
+ memset(&mock_node2, 0, sizeof(mock_node2));
+ memset(&mock_rs, 0, sizeof(mock_rs));
+ memset(&mock_md, 0, sizeof(mock_md));
+ memset(&mock_ri, 0, sizeof(mock_ri));
+
+ mock_node1.rs = &mock_rs;
+ mock_node1.md = &mock_md;
+
+ mock_node2.ri = &mock_ri;
+
+ strlcpy(mock_rs.nickname, "nodeone", sizeof(mock_rs.nickname));
+ mock_ri.nickname = tor_strdup("nodetwo");
+
+ memcpy(mock_node1.identity, "NodeOneNode1NodeOne1", DIGEST_LEN);
+ memcpy(mock_node2.identity, "SecondNodeWe'reTestn", DIGEST_LEN);
+
+ // empty families.
+ tt_assert(! node_family_contains(&mock_node1, &mock_node2));
+ tt_assert(! node_family_contains(&mock_node2, &mock_node1));
+
+ // Families contain nodes, but not these nodes
+ mock_ri.declared_family = smartlist_new();
+ smartlist_add(mock_ri.declared_family, (char*)"NodeThree");
+ mock_md.family = nodefamily_parse("NodeFour", NULL, 0);
+ tt_assert(! node_family_contains(&mock_node1, &mock_node2));
+ tt_assert(! node_family_contains(&mock_node2, &mock_node1));
+
+ // Families contain one another.
+ smartlist_add(mock_ri.declared_family, (char*)
+ "4e6f64654f6e654e6f6465314e6f64654f6e6531");
+ tt_assert(! node_family_contains(&mock_node1, &mock_node2));
+ tt_assert(node_family_contains(&mock_node2, &mock_node1));
+
+ nodefamily_free(mock_md.family);
+ mock_md.family = nodefamily_parse(
+ "NodeFour "
+ "5365636f6e644e6f64655765277265546573746e", NULL, 0);
+ tt_assert(node_family_contains(&mock_node1, &mock_node2));
+ tt_assert(node_family_contains(&mock_node2, &mock_node1));
+
+ // Try looking up families now.
+ MOCK(node_get_by_nickname, mock_node_get_by_nickname);
+ MOCK(node_get_by_id, mock_node_get_by_id);
+
+ node_lookup_declared_family(nodes, &mock_node1);
+ tt_int_op(smartlist_len(nodes), OP_EQ, 2);
+ const node_t *n = smartlist_get(nodes, 0);
+ tt_mem_op(n->identity, OP_EQ, "SecondNodeWe'reTestn", DIGEST_LEN);
+ n = smartlist_get(nodes, 1);
+ tt_str_op(n->identity, OP_EQ, "nodefour");
+
+ // free, try the other one.
+ SMARTLIST_FOREACH(nodes, node_t *, x, tor_free(x));
+ smartlist_clear(nodes);
+
+ node_lookup_declared_family(nodes, &mock_node2);
+ tt_int_op(smartlist_len(nodes), OP_EQ, 2);
+ n = smartlist_get(nodes, 0);
+ // This gets a truncated hex hex ID since it was looked up by name
+ tt_str_op(n->identity, OP_EQ, "NodeThree");
+ n = smartlist_get(nodes, 1);
+ tt_str_op(n->identity, OP_EQ, "4e6f64654f6e654e6f6");
+
+ done:
+ UNMOCK(node_get_by_nickname);
+ UNMOCK(node_get_by_id);
+ smartlist_free(mock_ri.declared_family);
+ nodefamily_free(mock_md.family);
+ tor_free(mock_ri.nickname);
+ // use tor_free, these aren't real nodes
+ SMARTLIST_FOREACH(nodes, node_t *, x, tor_free(x));
+ smartlist_free(nodes);
+}
+
+static void
+test_nodelist_nodefamily_canonicalize(void *arg)
+{
+ (void)arg;
+ char *c = NULL;
+
+ c = nodefamily_canonicalize("", NULL, 0);
+ tt_str_op(c, OP_EQ, "");
+ tor_free(c);
+
+ uint8_t own_id[20];
+ memset(own_id, 0, sizeof(own_id));
+ c = nodefamily_canonicalize(
+ "alice BOB caroL %potrzebie !!!@#@# "
+ "$bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=fred "
+ "ffffffffffffffffffffffffffffffffffffffff "
+ "$cccccccccccccccccccccccccccccccccccccccc ", own_id, 0);
+ tt_str_op(c, OP_EQ,
+ "!!!@#@# "
+ "$0000000000000000000000000000000000000000 "
+ "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB "
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC "
+ "$FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF "
+ "%potrzebie "
+ "alice bob carol");
+
+ done:
+ tor_free(c);
+}
+
+/** format_node_description() should return
+ * "Fingerprint~Nickname at IPv4 and [IPv6]".
+ * The nickname and addresses are optional.
+ */
+static void
+test_nodelist_format_node_description(void *arg)
+{
+ char mock_digest[DIGEST_LEN];
+ char mock_nickname[MAX_NICKNAME_LEN+1];
+ tor_addr_t mock_null_ip;
+ tor_addr_t mock_ipv4;
+ tor_addr_t mock_ipv6;
+
+ char ndesc[NODE_DESC_BUF_LEN];
+ const char *rv = NULL;
+
+ (void) arg;
+
+ /* Clear variables */
+ memset(ndesc, 0, sizeof(ndesc));
+ memset(mock_digest, 0, sizeof(mock_digest));
+ memset(mock_nickname, 0, sizeof(mock_nickname));
+ memset(&mock_null_ip, 0, sizeof(mock_null_ip));
+ memset(&mock_ipv4, 0, sizeof(mock_ipv4));
+ memset(&mock_ipv6, 0, sizeof(mock_ipv6));
+
+ /* Set variables */
+ memcpy(mock_digest,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ sizeof(mock_digest));
+ strlcpy(mock_nickname, "TestOR7890123456789", sizeof(mock_nickname));
+ tor_addr_parse(&mock_ipv4, "111.222.233.244");
+ tor_addr_parse(&mock_ipv6, "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ /* Test function with variables */
+ rv = format_node_description(ndesc,
+ mock_digest,
+ NULL,
+ NULL,
+ 0);
+ tt_ptr_op(rv, OP_EQ, ndesc);
+ tt_str_op(ndesc, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+
+ /* format node description should use ~ because named is deprecated */
+ rv = format_node_description(ndesc,
+ mock_digest,
+ mock_nickname,
+ NULL,
+ 0);
+ tt_ptr_op(rv, OP_EQ, ndesc);
+ tt_str_op(ndesc, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~""TestOR7890123456789");
+
+ /* Try a null IP address, rather than NULL */
+ rv = format_node_description(ndesc,
+ mock_digest,
+ mock_nickname,
+ &mock_null_ip,
+ 0);
+ tt_ptr_op(rv, OP_EQ, ndesc);
+ tt_str_op(ndesc, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789");
+
+ /* Try some real IP addresses */
+ rv = format_node_description(ndesc,
+ mock_digest,
+ NULL,
+ &mock_ipv4,
+ 0);
+ tt_ptr_op(rv, OP_EQ, ndesc);
+ tt_str_op(ndesc, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA at 111.222.233.244");
+
+ rv = format_node_description(ndesc,
+ mock_digest,
+ mock_nickname,
+ &mock_ipv6,
+ 0);
+ tt_ptr_op(rv, OP_EQ, ndesc);
+ tt_str_op(ndesc, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ rv = format_node_description(ndesc,
+ mock_digest,
+ mock_nickname,
+ &mock_ipv6,
+ tor_addr_to_ipv4h(&mock_ipv4));
+ tt_ptr_op(rv, OP_EQ, ndesc);
+ tt_str_op(ndesc, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ /* test NULL handling */
+ rv = format_node_description(NULL, NULL, NULL, NULL, 0);
+ tt_str_op(rv, OP_EQ, "<NULL BUFFER>");
+
+ rv = format_node_description(ndesc, NULL, NULL, NULL, 0);
+ tt_ptr_op(rv, OP_EQ, ndesc);
+ tt_str_op(rv, OP_EQ, "<NULL ID DIGEST>");
+
+ done:
+ return;
+}
+
+/** router_describe() is a wrapper for format_node_description(), see that
+ * test for details.
+ *
+ * The routerinfo-only node_describe() tests are in this function,
+ * so we can re-use the same mocked variables.
+ */
+static void
+test_nodelist_router_describe(void *arg)
+{
+ char mock_nickname[MAX_NICKNAME_LEN+1];
+ tor_addr_t mock_ipv4;
+ routerinfo_t mock_ri_ipv4;
+ routerinfo_t mock_ri_ipv6;
+ routerinfo_t mock_ri_dual;
+
+ const char *rv = NULL;
+
+ (void) arg;
+
+ /* Clear variables */
+ memset(mock_nickname, 0, sizeof(mock_nickname));
+ memset(&mock_ipv4, 0, sizeof(mock_ipv4));
+ memset(&mock_ri_ipv4, 0, sizeof(mock_ri_ipv4));
+ memset(&mock_ri_ipv6, 0, sizeof(mock_ri_ipv6));
+ memset(&mock_ri_dual, 0, sizeof(mock_ri_dual));
+
+ /* Set up the dual-stack routerinfo */
+ memcpy(mock_ri_dual.cache_info.identity_digest,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ sizeof(mock_ri_dual.cache_info.identity_digest));
+ strlcpy(mock_nickname, "TestOR7890123456789", sizeof(mock_nickname));
+ mock_ri_dual.nickname = mock_nickname;
+ tor_addr_parse(&mock_ipv4, "111.222.233.244");
+ mock_ri_dual.addr = tor_addr_to_ipv4h(&mock_ipv4);
+ tor_addr_parse(&mock_ri_dual.ipv6_addr,
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ /* Create and modify the other routerinfos.
+ * mock_nickname is referenced from all 3 routerinfos.
+ * That's ok, all their memory is static. */
+ memcpy(&mock_ri_ipv4, &mock_ri_dual, sizeof(mock_ri_ipv4));
+ memcpy(&mock_ri_ipv6, &mock_ri_dual, sizeof(mock_ri_ipv6));
+ /* Clear the unnecessary addresses */
+ memset(&mock_ri_ipv4.ipv6_addr, 0, sizeof(mock_ri_ipv4.ipv6_addr));
+ mock_ri_ipv6.addr = 0;
+
+ /* We don't test the no-nickname and no-IP cases, because they're covered by
+ * format_node_description(), and we don't expect to see them in Tor code. */
+
+ /* Try some real IP addresses */
+ rv = router_describe(&mock_ri_ipv4);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244");
+
+ rv = router_describe(&mock_ri_ipv6);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ rv = router_describe(&mock_ri_dual);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ /* test NULL handling */
+ rv = router_describe(NULL);
+ tt_str_op(rv, OP_EQ, "<null>");
+
+ /* Now test a node with only these routerinfos */
+ node_t mock_node;
+ memset(&mock_node, 0, sizeof(mock_node));
+ memcpy(mock_node.identity,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ sizeof(mock_node.identity));
+
+ /* Try some real IP addresses */
+ mock_node.ri = &mock_ri_ipv4;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244");
+
+ mock_node.ri = &mock_ri_ipv6;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ mock_node.ri = &mock_ri_dual;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ done:
+ return;
+}
+
+/** node_describe() is a wrapper for format_node_description(), see that
+ * test for details.
+ *
+ * The routerinfo-only and routerstatus-only node_describe() tests are in
+ * test_nodelist_router_describe() and test_nodelist_routerstatus_describe(),
+ * so we can re-use their mocked variables.
+ */
+static void
+test_nodelist_node_describe(void *arg)
+{
+ char mock_nickname[MAX_NICKNAME_LEN+1];
+ tor_addr_t mock_ipv4;
+
+ const char *rv = NULL;
+
+ (void) arg;
+
+ /* Routerinfos */
+ routerinfo_t mock_ri_dual;
+
+ /* Clear variables */
+ memset(mock_nickname, 0, sizeof(mock_nickname));
+ memset(&mock_ipv4, 0, sizeof(mock_ipv4));
+ memset(&mock_ri_dual, 0, sizeof(mock_ri_dual));
+
+ /* Set up the dual-stack routerinfo */
+ memcpy(mock_ri_dual.cache_info.identity_digest,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ sizeof(mock_ri_dual.cache_info.identity_digest));
+ strlcpy(mock_nickname, "TestOR7890123456789", sizeof(mock_nickname));
+ mock_ri_dual.nickname = mock_nickname;
+ tor_addr_parse(&mock_ipv4, "111.222.233.244");
+ mock_ri_dual.addr = tor_addr_to_ipv4h(&mock_ipv4);
+ tor_addr_parse(&mock_ri_dual.ipv6_addr,
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ /* Routerstatuses */
+ routerstatus_t mock_rs_ipv4;
+ routerstatus_t mock_rs_dual;
+
+ /* Clear variables */
+ memset(&mock_ipv4, 0, sizeof(mock_ipv4));
+ memset(&mock_rs_ipv4, 0, sizeof(mock_rs_ipv4));
+ memset(&mock_rs_dual, 0, sizeof(mock_rs_dual));
+
+ /* Set up the dual-stack routerstatus */
+ memcpy(mock_rs_dual.identity_digest,
+ "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB"
+ "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB",
+ sizeof(mock_rs_dual.identity_digest));
+ strlcpy(mock_rs_dual.nickname, "Bbb",
+ sizeof(mock_rs_dual.nickname));
+ tor_addr_parse(&mock_ipv4, "2.2.2.2");
+ mock_rs_dual.addr = tor_addr_to_ipv4h(&mock_ipv4);
+ tor_addr_parse(&mock_rs_dual.ipv6_addr,
+ "[bbbb::bbbb]");
+
+ /* Create and modify the other routerstatus. */
+ memcpy(&mock_rs_ipv4, &mock_rs_dual, sizeof(mock_rs_ipv4));
+ /* Clear the unnecessary IPv6 address */
+ memset(&mock_rs_ipv4.ipv6_addr, 0, sizeof(mock_rs_ipv4.ipv6_addr));
+
+ /* Microdescs */
+ microdesc_t mock_md_null;
+ microdesc_t mock_md_ipv6;
+
+ /* Clear variables */
+ memset(&mock_md_null, 0, sizeof(mock_md_null));
+ memset(&mock_md_ipv6, 0, sizeof(mock_md_ipv6));
+
+ /* Set up the microdesc */
+ tor_addr_parse(&mock_md_ipv6.ipv6_addr,
+ "[eeee::6000:6000]");
+
+ /* Set up the node */
+ node_t mock_node;
+ memset(&mock_node, 0, sizeof(mock_node));
+ memcpy(mock_node.identity,
+ "\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC"
+ "\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC",
+ sizeof(mock_node.identity));
+
+ /* Test that the routerinfo and routerstatus work separately, but the
+ * identity comes from the node */
+ mock_node.ri = &mock_ri_dual;
+ mock_node.rs = NULL;
+ mock_node.md = NULL;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~TestOR7890123456789 at "
+ "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ mock_node.ri = NULL;
+ mock_node.rs = &mock_rs_ipv4;
+ mock_node.md = NULL;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2");
+
+ mock_node.ri = NULL;
+ mock_node.rs = &mock_rs_dual;
+ mock_node.md = NULL;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2 and [bbbb::bbbb]");
+
+ /* Test that the routerstatus overrides the routerinfo */
+ mock_node.ri = &mock_ri_dual;
+ mock_node.rs = &mock_rs_ipv4;
+ mock_node.md = NULL;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2");
+
+ mock_node.ri = &mock_ri_dual;
+ mock_node.rs = &mock_rs_dual;
+ mock_node.md = NULL;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2 and [bbbb::bbbb]");
+
+ /* Test that the microdesc IPv6 is used if the routerinfo doesn't have IPv6
+ */
+ mock_node.ri = NULL;
+ mock_node.rs = &mock_rs_ipv4;
+ mock_node.md = &mock_md_ipv6;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2 and [eeee::6000:6000]");
+
+ mock_node.ri = NULL;
+ mock_node.rs = &mock_rs_ipv4;
+ mock_node.md = &mock_md_null;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2");
+
+ mock_node.ri = NULL;
+ mock_node.rs = &mock_rs_dual;
+ mock_node.md = &mock_md_ipv6;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2 and [bbbb::bbbb]");
+
+ mock_node.ri = NULL;
+ mock_node.rs = &mock_rs_dual;
+ mock_node.md = &mock_md_null;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2 and [bbbb::bbbb]");
+
+ /* Test that the routerinfo doesn't change the results above
+ */
+ mock_node.ri = &mock_ri_dual;
+ mock_node.rs = &mock_rs_ipv4;
+ mock_node.md = &mock_md_ipv6;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2 and [eeee::6000:6000]");
+
+ mock_node.ri = &mock_ri_dual;
+ mock_node.rs = &mock_rs_ipv4;
+ mock_node.md = &mock_md_null;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2");
+
+ mock_node.ri = &mock_ri_dual;
+ mock_node.rs = &mock_rs_dual;
+ mock_node.md = &mock_md_ipv6;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2 and [bbbb::bbbb]");
+
+ mock_node.ri = &mock_ri_dual;
+ mock_node.rs = &mock_rs_dual;
+ mock_node.md = &mock_md_null;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at "
+ "2.2.2.2 and [bbbb::bbbb]");
+
+ /* test NULL handling */
+ rv = node_describe(NULL);
+ tt_str_op(rv, OP_EQ, "<null>");
+
+ mock_node.ri = NULL;
+ mock_node.rs = NULL;
+ mock_node.md = NULL;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "<null rs and ri>");
+
+ done:
+ return;
+}
+
+/** routerstatus_describe() is a wrapper for format_node_description(), see
+ * that test for details.
+ *
+ * The routerstatus-only node_describe() tests are in this function,
+ * so we can re-use the same mocked variables.
+ */
+static void
+test_nodelist_routerstatus_describe(void *arg)
+{
+ tor_addr_t mock_ipv4;
+ routerstatus_t mock_rs_ipv4;
+ routerstatus_t mock_rs_ipv6;
+ routerstatus_t mock_rs_dual;
+
+ const char *rv = NULL;
+
+ (void) arg;
+
+ /* Clear variables */
+ memset(&mock_ipv4, 0, sizeof(mock_ipv4));
+ memset(&mock_rs_ipv4, 0, sizeof(mock_rs_ipv4));
+ memset(&mock_rs_ipv6, 0, sizeof(mock_rs_ipv6));
+ memset(&mock_rs_dual, 0, sizeof(mock_rs_dual));
+
+ /* Set up the dual-stack routerstatus */
+ memcpy(mock_rs_dual.identity_digest,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ sizeof(mock_rs_dual.identity_digest));
+ strlcpy(mock_rs_dual.nickname, "TestOR7890123456789",
+ sizeof(mock_rs_dual.nickname));
+ tor_addr_parse(&mock_ipv4, "111.222.233.244");
+ mock_rs_dual.addr = tor_addr_to_ipv4h(&mock_ipv4);
+ tor_addr_parse(&mock_rs_dual.ipv6_addr,
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ /* Create and modify the other routerstatuses. */
+ memcpy(&mock_rs_ipv4, &mock_rs_dual, sizeof(mock_rs_ipv4));
+ memcpy(&mock_rs_ipv6, &mock_rs_dual, sizeof(mock_rs_ipv6));
+ /* Clear the unnecessary addresses */
+ memset(&mock_rs_ipv4.ipv6_addr, 0, sizeof(mock_rs_ipv4.ipv6_addr));
+ mock_rs_ipv6.addr = 0;
+
+ /* We don't test the no-nickname and no-IP cases, because they're covered by
+ * format_node_description(), and we don't expect to see them in Tor code. */
+
+ /* Try some real IP addresses */
+ rv = routerstatus_describe(&mock_rs_ipv4);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244");
+
+ rv = routerstatus_describe(&mock_rs_ipv6);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ rv = routerstatus_describe(&mock_rs_dual);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ /* test NULL handling */
+ rv = routerstatus_describe(NULL);
+ tt_str_op(rv, OP_EQ, "<null>");
+
+ /* Now test a node with only these routerstatuses */
+ node_t mock_node;
+ memset(&mock_node, 0, sizeof(mock_node));
+ memcpy(mock_node.identity,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ sizeof(mock_node.identity));
+
+ /* Try some real IP addresses */
+ mock_node.rs = &mock_rs_ipv4;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244");
+
+ mock_node.rs = &mock_rs_ipv6;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ mock_node.rs = &mock_rs_dual;
+ rv = node_describe(&mock_node);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ done:
+ return;
+}
+
+/** extend_info_describe() is a wrapper for format_node_description(), see
+ * that test for details.
+ */
+static void
+test_nodelist_extend_info_describe(void *arg)
+{
+ extend_info_t mock_ei_ipv4;
+ extend_info_t mock_ei_ipv6;
+
+ const char *rv = NULL;
+
+ (void) arg;
+
+ /* Clear variables */
+ memset(&mock_ei_ipv4, 0, sizeof(mock_ei_ipv4));
+ memset(&mock_ei_ipv6, 0, sizeof(mock_ei_ipv6));
+
+ /* Set up the IPv4 extend info */
+ memcpy(mock_ei_ipv4.identity_digest,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ sizeof(mock_ei_ipv4.identity_digest));
+ strlcpy(mock_ei_ipv4.nickname, "TestOR7890123456789",
+ sizeof(mock_ei_ipv4.nickname));
+ tor_addr_parse(&mock_ei_ipv4.addr, "111.222.233.244");
+
+ /* Create and modify the other extend info. */
+ memcpy(&mock_ei_ipv6, &mock_ei_ipv4, sizeof(mock_ei_ipv6));
+ tor_addr_parse(&mock_ei_ipv6.addr,
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ /* We don't test the no-nickname and no-IP cases, because they're covered by
+ * format_node_description(), and we don't expect to see them in Tor code. */
+
+ /* Try some real IP addresses */
+ rv = extend_info_describe(&mock_ei_ipv4);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "111.222.233.244");
+
+ rv = extend_info_describe(&mock_ei_ipv6);
+ tt_str_op(rv, OP_EQ,
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at "
+ "[1111:2222:3333:4444:5555:6666:7777:8888]");
+
+ /* Extend infos only have one IP address, so there is no dual case */
+
+ /* test NULL handling */
+ rv = extend_info_describe(NULL);
+ tt_str_op(rv, OP_EQ, "<null>");
+
+ done:
+ return;
+}
+
+/** router_get_verbose_nickname() should return "Fingerprint~Nickname"
+ */
+static void
+test_nodelist_router_get_verbose_nickname(void *arg)
+{
+ routerinfo_t mock_ri;
+ char mock_nickname[MAX_NICKNAME_LEN+1];
+
+ char vname[MAX_VERBOSE_NICKNAME_LEN+1];
+
+ (void) arg;
+
+ memset(&mock_ri, 0, sizeof(routerinfo_t));
+ memset(mock_nickname, 0, sizeof(mock_nickname));
+ mock_ri.nickname = mock_nickname;
+
+ /* verbose nickname should use ~ because named is deprecated */
+ strlcpy(mock_nickname, "TestOR", sizeof(mock_nickname));
+ memcpy(mock_ri.cache_info.identity_digest,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA",
+ DIGEST_LEN);
+ router_get_verbose_nickname(vname, &mock_ri);
+ tt_str_op(vname, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR");
+
+ /* test NULL router handling */
+ router_get_verbose_nickname(vname, NULL);
+ tt_str_op(vname, OP_EQ, "<null>");
+
+ router_get_verbose_nickname(NULL, &mock_ri);
+ router_get_verbose_nickname(NULL, NULL);
+
+ done:
+ return;
+}
+
#define NODE(name, flags) \
{ #name, test_nodelist_##name, (flags), NULL, NULL }
@@ -239,6 +1254,17 @@ struct testcase_t nodelist_tests[] = {
NODE(node_get_verbose_nickname_not_named, TT_FORK),
NODE(node_is_dir, TT_FORK),
NODE(ed_id, TT_FORK),
+ NODE(nodefamily, TT_FORK),
+ NODE(nodefamily_parse_err, TT_FORK),
+ NODE(nodefamily_lookup, TT_FORK),
+ NODE(nickname_matches, 0),
+ NODE(node_nodefamily, TT_FORK),
+ NODE(nodefamily_canonicalize, 0),
+ NODE(format_node_description, 0),
+ NODE(router_describe, 0),
+ NODE(node_describe, 0),
+ NODE(routerstatus_describe, 0),
+ NODE(extend_info_describe, 0),
+ NODE(router_get_verbose_nickname, 0),
END_OF_TESTCASES
};
-
diff --git a/src/test/test_oom.c b/src/test/test_oom.c
index b813fa43a3..da6b2ee14d 100644
--- a/src/test/test_oom.c
+++ b/src/test/test_oom.c
@@ -8,7 +8,7 @@
#define CIRCUITLIST_PRIVATE
#define CONNECTION_PRIVATE
#include "core/or/or.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "core/or/circuitlist.h"
#include "lib/evloop/compat_libevent.h"
#include "core/mainloop/connection.h"
diff --git a/src/test/test_options.c b/src/test/test_options.c
index 0e52967a23..a2641425fe 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -5,7 +5,7 @@
#define CONFIG_PRIVATE
#include "core/or/or.h"
-#include "app/config/confparse.h"
+#include "lib/confmgt/confparse.h"
#include "app/config/config.h"
#include "test/test.h"
#include "lib/geoip/geoip.h"
@@ -14,6 +14,7 @@
#include "feature/nodelist/routerset.h"
#include "core/mainloop/mainloop.h"
#include "test/log_test_helpers.h"
+#include "test/resolve_test_helpers.h"
#include "lib/sandbox/sandbox.h"
#include "lib/memarea/memarea.h"
@@ -31,14 +32,14 @@
typedef struct {
int severity;
- uint32_t domain;
+ log_domain_mask_t domain;
char *msg;
} logmsg_t;
static smartlist_t *messages = NULL;
static void
-log_cback(int severity, uint32_t domain, const char *msg)
+log_cback(int severity, log_domain_mask_t domain, const char *msg)
{
logmsg_t *x = tor_malloc(sizeof(*x));
x->severity = severity;
@@ -54,9 +55,9 @@ setup_log_callback(void)
{
log_severity_list_t lst;
memset(&lst, 0, sizeof(lst));
- lst.masks[LOG_ERR - LOG_ERR] = ~0;
- lst.masks[LOG_WARN - LOG_ERR] = ~0;
- lst.masks[LOG_NOTICE - LOG_ERR] = ~0;
+ lst.masks[SEVERITY_MASK_IDX(LOG_ERR)] = LD_ALL_DOMAINS;
+ lst.masks[SEVERITY_MASK_IDX(LOG_WARN)] = LD_ALL_DOMAINS;
+ lst.masks[SEVERITY_MASK_IDX(LOG_NOTICE)] = LD_ALL_DOMAINS;
add_callback_log(&lst, log_cback);
mark_logs_temp();
}
@@ -96,7 +97,7 @@ clear_log_messages(void)
opt->command = CMD_RUN_TOR; \
options_init(opt); \
\
- dflt = config_dup(&options_format, opt); \
+ dflt = config_dup(get_options_mgr(), opt); \
clear_log_messages(); \
} while (0)
@@ -196,7 +197,7 @@ test_options_validate_impl(const char *configuration,
if (r)
goto done;
- r = config_assign(&options_format, opt, cl, 0, &msg);
+ r = config_assign(get_options_mgr(), opt, cl, 0, &msg);
if (phase == PH_ASSIGN) {
if (test_options_checkmsgs(configuration, expect_errmsg,
expect_log_severity,
@@ -241,6 +242,7 @@ test_options_validate(void *arg)
(void)arg;
setup_log_callback();
sandbox_disable_getaddrinfo_cache();
+ mock_hostname_resolver();
WANT_ERR("ExtORPort 500000", "Invalid ExtORPort", PH_VALIDATE);
@@ -258,13 +260,17 @@ test_options_validate(void *arg)
WANT_ERR("BridgeRelay 1\nDirCache 0",
"We're a bridge but DirCache is disabled.", PH_VALIDATE);
+ // XXXX We should replace this with a more full error message once #29211
+ // XXXX is done. It is truncated for now because at the current stage
+ // XXXX of refactoring, we can't give a full error message like before.
WANT_ERR_LOG("HeartbeatPeriod 21 snarks",
- "Interval 'HeartbeatPeriod 21 snarks' is malformed or"
- " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.",
+ "malformed or out of bounds", LOG_WARN,
+ "Unknown unit 'snarks'.",
PH_ASSIGN);
+ // XXXX As above.
WANT_ERR_LOG("LogTimeGranularity 21 snarks",
- "Msec interval 'LogTimeGranularity 21 snarks' is malformed or"
- " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.",
+ "malformed or out of bounds", LOG_WARN,
+ "Unknown unit 'snarks'.",
PH_ASSIGN);
OK("HeartbeatPeriod 1 hour", PH_VALIDATE);
OK("LogTimeGranularity 100 milliseconds", PH_VALIDATE);
@@ -278,6 +284,7 @@ test_options_validate(void *arg)
close_temp_logs();
clear_log_messages();
+ unmock_hostname_resolver();
return;
}
@@ -300,7 +307,7 @@ test_have_enough_mem_for_dircache(void *arg)
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, &msg);
+ r = config_assign(get_options_mgr(), opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
/* 300 MB RAM available, DirCache enabled */
@@ -323,7 +330,7 @@ test_have_enough_mem_for_dircache(void *arg)
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, &msg);
+ r = config_assign(get_options_mgr(), opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
/* 300 MB RAM available, DirCache enabled, Bridge */
@@ -346,7 +353,7 @@ test_have_enough_mem_for_dircache(void *arg)
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, &msg);
+ r = config_assign(get_options_mgr(), opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
/* 200 MB RAM available, DirCache disabled */
@@ -429,10 +436,12 @@ get_options_test_data(const char *conf)
// with options_init(), but about a dozen tests break when I do that.
// Being kinda lame and just fixing the immedate breakage for now..
result->opt->ConnectionPadding = -1; // default must be "auto"
+ result->opt->DormantClientTimeout = 1800; // must be over 600.
+ result->opt->CircuitPadding = 1; // default must be "1"
rv = config_get_lines(conf, &cl, 1);
tt_int_op(rv, OP_EQ, 0);
- rv = config_assign(&options_format, result->opt, cl, 0, &msg);
+ rv = config_assign(get_options_mgr(), result->opt, cl, 0, &msg);
if (msg) {
/* Display the parse error message by comparing it with an empty string */
tt_str_op(msg, OP_EQ, "");
@@ -443,7 +452,7 @@ get_options_test_data(const char *conf)
result->opt->TokenBucketRefillInterval = 1;
rv = config_get_lines(TEST_OPTIONS_OLD_VALUES, &cl, 1);
tt_int_op(rv, OP_EQ, 0);
- rv = config_assign(&options_format, result->def_opt, cl, 0, &msg);
+ rv = config_assign(get_options_mgr(), result->def_opt, cl, 0, &msg);
if (msg) {
/* Display the parse error message by comparing it with an empty string */
tt_str_op(msg, OP_EQ, "");
@@ -1171,7 +1180,7 @@ test_options_validate__transproxy(void *ignored)
// Assert that a test has run for some TransProxyType
tt_assert(tdata);
-#else /* !(defined(USE_TRANSPARENT)) */
+#else /* !defined(USE_TRANSPARENT) */
tdata = get_options_test_data("TransPort 127.0.0.1:555\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
@@ -1346,29 +1355,6 @@ test_options_validate__token_bucket(void *ignored)
}
static void
-test_options_validate__recommended_packages(void *ignored)
-{
- (void)ignored;
- int ret;
- char *msg;
- setup_capture_of_logs(LOG_WARN);
- options_test_data_t *tdata = get_options_test_data(
- "RecommendedPackages foo 1.2 http://foo.com sha1=123123123123\n"
- "RecommendedPackages invalid-package-line\n");
-
- ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
- tt_int_op(ret, OP_EQ, -1);
- expect_no_log_msg("Invalid RecommendedPackage line "
- "invalid-package-line will be ignored\n");
-
- done:
- escaped(NULL); // This will free the leaking memory from the previous escaped
- teardown_capture_of_logs();
- free_options_test_data(tdata);
- tor_free(msg);
-}
-
-static void
test_options_validate__fetch_dir(void *ignored)
{
(void)ignored;
@@ -4203,7 +4189,6 @@ struct testcase_t options_tests[] = {
LOCAL_VALIDATE_TEST(exclude_nodes),
LOCAL_VALIDATE_TEST(node_families),
LOCAL_VALIDATE_TEST(token_bucket),
- LOCAL_VALIDATE_TEST(recommended_packages),
LOCAL_VALIDATE_TEST(fetch_dir),
LOCAL_VALIDATE_TEST(conn_limit),
LOCAL_VALIDATE_TEST(paths_needed),
diff --git a/src/test/test_parsecommon.c b/src/test/test_parsecommon.c
new file mode 100644
index 0000000000..0c8f467a45
--- /dev/null
+++ b/src/test/test_parsecommon.c
@@ -0,0 +1,594 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "core/or/or.h"
+#include "test/test.h"
+#include "lib/memarea/memarea.h"
+#include "lib/encoding/binascii.h"
+#include "feature/dirparse/parsecommon.h"
+#include "test/log_test_helpers.h"
+
+static void
+test_parsecommon_tokenize_string_null(void *arg)
+{
+
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ const char *str_with_null = "a\0bccccccccc";
+
+ int retval =
+ tokenize_string(area, str_with_null,
+ str_with_null + 3,
+ tokens, NULL, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_multiple_lines(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T01("uptime", K_UPTIME, GE(1), NO_OBJ),
+ T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ),
+ T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ char *str = tor_strdup(
+ "hibernating 0\nuptime 1024\n"
+ "published 2018-10-15 10:00:00\n");
+
+ int retval =
+ tokenize_string(area, str, NULL,
+ tokens, table, 0);
+
+ tt_int_op(smartlist_len(tokens), OP_EQ, 3);
+ directory_token_t *token = smartlist_get(tokens, 0);
+
+ tt_int_op(token->tp, OP_EQ, K_HIBERNATING);
+
+ token = smartlist_get(tokens, 1);
+
+ tt_int_op(token->tp, OP_EQ, K_UPTIME);
+
+ token = smartlist_get(tokens, 2);
+
+ tt_int_op(token->tp, OP_EQ, K_PUBLISHED);
+
+ tt_int_op(retval, OP_EQ, 0);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_min_cnt(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T01("uptime", K_UPTIME, EQ(2), NO_OBJ),
+ T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ // Missing "uptime"
+ char *str = tor_strdup("uptime 1024\nhibernating 0\n");
+
+ int retval =
+ tokenize_string(area, str, NULL,
+ tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_max_cnt(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T01("uptime", K_UPTIME, EQ(1), NO_OBJ),
+ T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ // "uptime" expected once, but occurs twice in input.
+ char *str = tor_strdup(
+ "uptime 1024\nuptime 2048\nhibernating 0\n");
+
+ int retval =
+ tokenize_string(area, str, NULL,
+ tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_at_start(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T1_START("client-name", C_CLIENT_NAME, CONCAT_ARGS, NO_OBJ),
+ T01("uptime", K_UPTIME, EQ(1), NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ // "client-name" is not the first line.
+ char *str = tor_strdup(
+ "uptime 1024\nclient-name Alice\n");
+
+ int retval =
+ tokenize_string(area, str, NULL, tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_at_end(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T1_END("client-name", C_CLIENT_NAME, CONCAT_ARGS, NO_OBJ),
+ T01("uptime", K_UPTIME, EQ(1), NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ // "client-name" is not the last line.
+ char *str = tor_strdup(
+ "client-name Alice\nuptime 1024\n");
+
+ int retval =
+ tokenize_string(area, str, NULL, tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_no_annotations(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ char *str = tor_strdup("@last-listed 2018-09-21 15:30:03\n");
+
+ int retval =
+ tokenize_string(area, str, NULL, tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_success(void *arg)
+{
+ memarea_t *area = memarea_new();
+ const char *str = "uptime 1024";
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t table = T01("uptime", K_UPTIME, GE(1), NO_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &table);
+
+ tt_int_op(token->tp, OP_EQ, K_UPTIME);
+ tt_int_op(token->n_args, OP_EQ, 1);
+ tt_str_op(*(token->args), OP_EQ, "1024");
+ tt_assert(!token->object_type);
+ tt_int_op(token->object_size, OP_EQ, 0);
+ tt_assert(!token->object_body);
+
+ tt_ptr_op(*s, OP_EQ, end);
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_concat_args(void *arg)
+{
+ memarea_t *area = memarea_new();
+ const char *str = "proto A=1 B=2";
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T01("proto", K_PROTO, CONCAT_ARGS, NO_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, K_PROTO);
+ tt_int_op(token->n_args, OP_EQ, 1);
+ tt_str_op(*(token->args), OP_EQ, "A=1 B=2");
+
+ done:
+ memarea_drop_all(area);
+}
+
+static void
+test_parsecommon_get_next_token_parse_keys(void *arg)
+{
+ (void)arg;
+
+ memarea_t *area = memarea_new();
+ const char *str =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMDdIya33BfNlHOkzoTKSTT8EjD64waMfUr372syVHiFjHhObwKwGA5u\n"
+ "sHaMIe9r+Ij/4C1dKyuXkcz3DOl6gWNhTD7dZ89I+Okoh1jWe30jxCiAcywC22p5\n"
+ "XLhrDkX1A63Z7XCH9ltwU2WMqWsVM98N2GR6MTujP7wtqdLExYN1AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = (const char **)&str;
+ directory_token_t *token = NULL;
+ directory_token_t *token2 = NULL;
+
+ token_rule_t rule = T1("onion-key", R_IPO_ONION_KEY, NO_ARGS, NEED_KEY_1024);
+
+ token = get_next_token(area, s, end, &rule);
+ tt_assert(token);
+
+ tt_int_op(token->tp, OP_EQ, R_IPO_ONION_KEY);
+ tt_int_op(token->n_args, OP_EQ, 0);
+ tt_str_op(token->object_type, OP_EQ, "RSA PUBLIC KEY");
+ tt_int_op(token->object_size, OP_EQ, 140);
+ tt_assert(token->object_body);
+ tt_assert(token->key);
+ tt_assert(!token->error);
+
+ const char *str2 =
+ "client-key\n"
+ "-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIICXAIBAAKBgQCwS810a2auH2PQchOBz9smNgjlDu31aq0IYlUohSYbhcv5AJ+d\n"
+ "DY0nfZWzS+mZPwzL3UiEnTt6PVv7AgoZ5V9ZJWJTKIURjJpkK0mstfJKHKIZhf84\n"
+ "pmFfRej9GQViB6NLtp1obOXJgJixSlMfw9doDI4NoAnEISCyH/tD77Qs2wIDAQAB\n"
+ "AoGAbDg8CKkdQOnX9c7xFpCnsE8fKqz9eddgHHNwXw1NFTwOt+2gDWKSMZmv2X5S\n"
+ "CVZg3owZxf5W0nT0D6Ny2+6nliak7foYAvkD0BsCiBhgftwC0zAo6k5rIbUKB3PJ\n"
+ "QLFXgpJhqWuXkODyt/hS/GTernR437WVSEGp1bnALqiFabECQQDaqHOxzoWY/nvH\n"
+ "KrfUi8EhqCnqERlRHwrW0MQZ1RPvF16OPPma+xa+ht/amfh3vYN5tZY82Zm43gGl\n"
+ "XWL5cZhNAkEAzmdSootYVnqLLLRMfHKXnO1XbaEcA/08MDNKGlSclBJixFenE8jX\n"
+ "iQsUbHwMJuGONvzWpRGPBP2f8xBd28ZtxwJARY+LZshtpfNniz/ixYJESaHG28je\n"
+ "xfjbKOW3TQSFV+2WTifFvHEeljQwKMoMyoMGvYRwLCGJjs9JtMLVxsdFjQJBAKwD\n"
+ "3BBvBQ39TuPQ1zWX4tb7zjMlY83HTFP3Sriq71tP/1QWoL2SUl56B2lp8E6vB/C3\n"
+ "wsMK4SCNprHRYAd7VZ0CQDKn6Zhd11P94PLs0msybFEh1VXr6CEW/BrxBgbL4ls6\n"
+ "dbX5XO0z4Ra8gYXgObgimhyMDYO98Idt5+Z3HIdyrSc=\n"
+ "-----END RSA PRIVATE KEY-----\n";
+
+ const char *end2 = str2 + strlen(str2);
+ const char **s2 = (const char **)&str2;
+
+ token_rule_t rule2 = T01("client-key", C_CLIENT_KEY, NO_ARGS,
+ NEED_SKEY_1024);
+
+ token2 = get_next_token(area, s2, end2, &rule2);
+ tt_assert(token2);
+
+ tt_int_op(token2->tp, OP_EQ, C_CLIENT_KEY);
+ tt_int_op(token2->n_args, OP_EQ, 0);
+ tt_str_op(token2->object_type, OP_EQ, "RSA PRIVATE KEY");
+ tt_int_op(token2->object_size, OP_EQ, 608);
+ tt_assert(token2->object_body);
+ tt_assert(token2->key);
+ tt_assert(!token->error);
+
+ done:
+ if (token) token_clear(token);
+ if (token2) token_clear(token2);
+ memarea_drop_all(area);
+}
+
+static void
+test_parsecommon_get_next_token_object(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n"
+ "-----END SIGNATURE-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, K_DIRECTORY_SIGNATURE);
+ tt_int_op(token->n_args, OP_EQ, 2);
+ tt_str_op(token->args[0], OP_EQ,
+ "0232AF901C31A04EE9848595AF9BB7620D4C5B2E");
+ tt_str_op(token->args[1], OP_EQ,
+ "CD1FD971855430880D3C31E0331C5C55800C2F79");
+
+ tt_assert(!token->error);
+
+ char decoded[256];
+ const char *signature =
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n";
+ tt_assert(signature);
+ size_t signature_len = strlen(signature);
+ base64_decode(decoded, sizeof(decoded), signature, signature_len);
+
+ tt_str_op(token->object_type, OP_EQ, "SIGNATURE");
+ tt_int_op(token->object_size, OP_EQ, 256);
+ tt_mem_op(token->object_body, OP_EQ, decoded, 256);
+
+ tt_assert(!token->key);
+
+ done:
+ memarea_drop_all(area);
+}
+
+static void
+test_parsecommon_get_next_token_err_too_many_args(void *arg)
+{
+ memarea_t *area = memarea_new();
+ const char *str = "uptime 1024 1024 1024";
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t table = T01("uptime", K_UPTIME, EQ(1), NO_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &table);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Too many arguments to uptime");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_too_few_args(void *arg)
+{
+ memarea_t *area = memarea_new();
+ const char *str = "uptime";
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t table = T01("uptime", K_UPTIME, EQ(1), NO_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &table);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Too few arguments to uptime");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_obj_missing_endline(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Malformed object: missing object end line");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_bad_beginline(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-Z---\n"
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n"
+ "-----END SIGNATURE-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Malformed object: bad begin line");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_tag_mismatch(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n"
+ "-----END SOMETHINGELSE-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ,
+ "Malformed object: mismatched end tag SIGNATURE");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_bad_base64(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "%%@%%%%%%%!!!'\n"
+ "-----END SIGNATURE-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Malformed object: bad base64-encoded data");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+#define PARSECOMMON_TEST(name) \
+ { #name, test_parsecommon_ ## name, 0, NULL, NULL }
+
+struct testcase_t parsecommon_tests[] = {
+ PARSECOMMON_TEST(tokenize_string_null),
+ PARSECOMMON_TEST(tokenize_string_multiple_lines),
+ PARSECOMMON_TEST(tokenize_string_min_cnt),
+ PARSECOMMON_TEST(tokenize_string_max_cnt),
+ PARSECOMMON_TEST(tokenize_string_at_start),
+ PARSECOMMON_TEST(tokenize_string_at_end),
+ PARSECOMMON_TEST(tokenize_string_no_annotations),
+ PARSECOMMON_TEST(get_next_token_success),
+ PARSECOMMON_TEST(get_next_token_concat_args),
+ PARSECOMMON_TEST(get_next_token_parse_keys),
+ PARSECOMMON_TEST(get_next_token_object),
+ PARSECOMMON_TEST(get_next_token_err_too_many_args),
+ PARSECOMMON_TEST(get_next_token_err_too_few_args),
+ PARSECOMMON_TEST(get_next_token_err_obj_missing_endline),
+ PARSECOMMON_TEST(get_next_token_err_bad_beginline),
+ PARSECOMMON_TEST(get_next_token_err_tag_mismatch),
+ PARSECOMMON_TEST(get_next_token_err_bad_base64),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_parseconf.sh b/src/test/test_parseconf.sh
new file mode 100755
index 0000000000..eeb80cdfa7
--- /dev/null
+++ b/src/test/test_parseconf.sh
@@ -0,0 +1,204 @@
+#!/bin/sh
+# Copyright 2019, The Tor Project, Inc.
+# See LICENSE for licensing information
+
+# Integration test script for verifying that Tor configurations are parsed as
+# we expect.
+#
+# Valid configurations are tested with --dump-config, which parses and
+# validates the configuration before writing it out. We then make sure that
+# the result is what we expect, before parsing and dumping it again to make
+# sure that there is no change.
+#
+# Invalid configurations are tested with --verify-config, which parses
+# and validates the configuration. We capture its output and make sure that
+# it contains the error message we expect.
+
+# This script looks for its test cases as individual directories in
+# src/test/conf_examples/. Each test may have these files:
+#
+# torrc -- Usually needed. This file is passed to Tor on the command line
+# with the "-f" flag. (If you omit it, you'll test Tor's behavior when
+# it receives a nonexistent configuration file.)
+#
+# torrc.defaults -- Optional. If present, it is passed to Tor on the command
+# line with the --defaults-torrc option. If this file is absent, an empty
+# file is passed instead to prevent Tor from reading the system defaults.
+#
+# cmdline -- Optional. If present, it contains command-line arguments that
+# will be passed to Tor.
+#
+# expected -- If this file is present, then it should be the expected result
+# of "--dump-config short" for this test case. Exactly one of
+# "expected" or "error" must be present, or the test will fail.
+#
+# error -- If this file is present, then it contains a regex that must be
+# matched by some line in the output of "--verify-config", which must
+# fail. Exactly one of "expected" or "error" must be present, or the
+# test will fail.
+
+umask 077
+set -e
+
+# emulate realpath(), in case coreutils or equivalent is not installed.
+abspath() {
+ f="$*"
+ if [ -d "$f" ]; then
+ dir="$f"
+ base=""
+ else
+ dir="$(dirname "$f")"
+ base="/$(basename "$f")"
+ fi
+ dir="$(cd "$dir" && pwd)"
+ echo "$dir$base"
+}
+
+# find the tor binary
+if [ $# -ge 1 ]; then
+ TOR_BINARY="${1}"
+ shift
+else
+ TOR_BINARY="${TESTING_TOR_BINARY:-./src/app/tor}"
+fi
+
+TOR_BINARY="$(abspath "$TOR_BINARY")"
+
+# make a safe space for temporary files
+DATA_DIR=$(mktemp -d -t tor_parseconf_tests.XXXXXX)
+trap 'rm -rf "$DATA_DIR"' 0
+
+# This is where we look for examples
+EXAMPLEDIR="$(dirname "$0")"/conf_examples
+
+case "$(uname -s)" in
+ CYGWIN*) WINDOWS=1;;
+ MINGW*) WINDOWS=1;;
+ MSYS*) WINDOWS=1;;
+ *) WINDOWS=0;;
+esac
+
+####
+# BUG WORKAROUND FOR 31757:
+# On Appveyor, it seems that Tor sometimes randomly fails to produce
+# output with --dump-config. Whil we are figuring this out, do not treat
+# windows errors as hard failures.
+####
+if test "$WINDOWS" = 1; then
+ EXITCODE=0
+else
+ EXITCODE=1
+fi
+
+die() { echo "$1" >&2 ; exit "$EXITCODE"; }
+
+if test "$WINDOWS" = 1; then
+ FILTER="dos2unix"
+else
+ FILTER="cat"
+fi
+
+touch "${DATA_DIR}/EMPTY" || die "Couldn't create empty file."
+
+for dir in "${EXAMPLEDIR}"/*; do
+ if ! test -d "${dir}"; then
+ # Only count directories.
+ continue
+ fi
+
+ testname="$(basename "${dir}")"
+ # We use printf since "echo -n" is not standard
+ printf "%s: " "$testname"
+
+ PREV_DIR="$(pwd)"
+ cd "${dir}"
+
+ if test -f "./torrc.defaults"; then
+ DEFAULTS="./torrc.defaults"
+ else
+ DEFAULTS="${DATA_DIR}/EMPTY"
+ fi
+
+ if test -f "./cmdline"; then
+ CMDLINE="$(cat ./cmdline)"
+ else
+ CMDLINE=""
+ fi
+
+ if test -f "./expected"; then
+ if test -f "./error"; then
+ echo "FAIL: Found both ${dir}/expected and ${dir}/error."
+ echo "(Only one of these files should exist.)"
+ exit $EXITCODE
+ fi
+
+ # This case should succeed: run dump-config and see if it does.
+
+ "${TOR_BINARY}" -f "./torrc" \
+ --defaults-torrc "${DEFAULTS}" \
+ --dump-config short \
+ ${CMDLINE} \
+ | "${FILTER}" > "${DATA_DIR}/output.${testname}" \
+ || die "Failure: Tor exited."
+
+ if cmp "./expected" "${DATA_DIR}/output.${testname}">/dev/null ; then
+ # Check round-trip.
+ "${TOR_BINARY}" -f "${DATA_DIR}/output.${testname}" \
+ --defaults-torrc "${DATA_DIR}/empty" \
+ --dump-config short \
+ | "${FILTER}" \
+ > "${DATA_DIR}/output_2.${testname}" \
+ || die "Failure: Tor exited on round-trip."
+
+ if ! cmp "${DATA_DIR}/output.${testname}" \
+ "${DATA_DIR}/output_2.${testname}"; then
+ echo "Failure: did not match on round-trip."
+ exit $EXITCODE
+ fi
+
+ echo "OK"
+ else
+ echo "FAIL"
+ if test "$(wc -c < "${DATA_DIR}/output.${testname}")" = 0; then
+ # There was no output -- probably we failed.
+ "${TOR_BINARY}" -f "./torrc" \
+ --defaults-torrc "${DEFAULTS}" \
+ --verify-config \
+ ${CMDLINE} || true
+ fi
+ diff -u "./expected" "${DATA_DIR}/output.${testname}" || /bin/true
+ exit $EXITCODE
+ fi
+
+ elif test -f "./error"; then
+ # This case should fail: run verify-config and see if it does.
+
+ "${TOR_BINARY}" --verify-config \
+ -f ./torrc \
+ --defaults-torrc "${DEFAULTS}" \
+ ${CMDLINE} \
+ > "${DATA_DIR}/output.${testname}" \
+ && die "Failure: Tor did not report an error."
+
+ expect_err="$(cat ./error)"
+ if grep "${expect_err}" "${DATA_DIR}/output.${testname}" >/dev/null; then
+ echo "OK"
+ else
+ echo "FAIL"
+ echo "Expected error: ${expect_err}"
+ echo "Tor said:"
+ cat "${DATA_DIR}/output.${testname}"
+ exit $EXITCODE
+ fi
+
+ else
+ # This case is not actually configured with a success or a failure.
+ # call that an error.
+
+ echo "FAIL: Did not find ${dir}/expected or ${dir}/error."
+ exit $EXITCODE
+ fi
+
+ cd "${PREV_DIR}"
+
+done
diff --git a/src/test/test_periodic_event.c b/src/test/test_periodic_event.c
index 4a53639dad..267156a908 100644
--- a/src/test/test_periodic_event.c
+++ b/src/test/test_periodic_event.c
@@ -19,6 +19,7 @@
#include "feature/hibernate/hibernate.h"
#include "feature/hs/hs_service.h"
#include "core/mainloop/mainloop.h"
+#include "core/mainloop/netstatus.h"
#include "core/mainloop/periodic.h"
/** Helper function: This is replaced in some tests for the event callbacks so
@@ -50,16 +51,21 @@ test_pe_initialize(void *arg)
* need to run the main loop and then wait for a second delaying the unit
* tests. Instead, we'll test the callback work indepedently elsewhere. */
initialize_periodic_events();
+ periodic_events_connect_all();
+ set_network_participation(false);
+ rescan_periodic_events(get_options());
/* Validate that all events have been set up. */
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
tt_assert(item->ev);
tt_assert(item->fn);
tt_u64_op(item->last_action_time, OP_EQ, 0);
/* Every event must have role(s) assign to it. This is done statically. */
tt_u64_op(item->roles, OP_NE, 0);
- tt_uint_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
+ !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
+ tt_uint_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
}
done:
@@ -79,17 +85,20 @@ test_pe_launch(void *arg)
* network gets enabled. */
consider_hibernation(time(NULL));
+ set_network_participation(true);
+
/* Hack: We'll set a dumb fn() of each events so they don't get called when
* dispatching them. We just want to test the state of the callbacks, not
* the whole code path. */
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
item->fn = dumb_event_fn;
}
options = get_options_mutable();
options->SocksPort_set = 1;
periodic_events_on_new_options(options);
+
#if 0
/* Lets make sure that before intialization, we can't scan the periodic
* events list and launch them. Lets try by being a Client. */
@@ -99,20 +108,20 @@ test_pe_launch(void *arg)
periodic_event_item_t *item = &periodic_events[i];
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
}
-#endif
+#endif /* 0 */
initialize_periodic_events();
+ periodic_events_connect_all();
/* Now that we've initialized, rescan the list to launch. */
periodic_events_on_new_options(options);
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
- if (item->roles & PERIODIC_EVENT_ROLE_CLIENT) {
- tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
- } else {
- tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
- }
+ int mask = PERIODIC_EVENT_ROLE_CLIENT|PERIODIC_EVENT_ROLE_ALL|
+ PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
+ int should_be_enabled = !!(item->roles & mask);
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
// enabled or not, the event has not yet been run.
tt_u64_op(item->last_action_time, OP_EQ, 0);
}
@@ -124,10 +133,11 @@ test_pe_launch(void *arg)
unsigned roles = get_my_roles(options);
tt_uint_op(roles, OP_EQ,
- PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER);
+ PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER|
+ PERIODIC_EVENT_ROLE_ALL|PERIODIC_EVENT_ROLE_NET_PARTICIPANT);
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
/* Only Client role should be disabled. */
if (item->roles == PERIODIC_EVENT_ROLE_CLIENT) {
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
@@ -144,17 +154,23 @@ test_pe_launch(void *arg)
/* Disable everything and we'll enable them ALL. */
options->SocksPort_set = 0;
options->ORPort_set = 0;
+ options->DisableNetwork = 1;
+ set_network_participation(false);
periodic_events_on_new_options(options);
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
- tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
+ int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
+ !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
}
/* Enable everything. */
options->SocksPort_set = 1; options->ORPort_set = 1;
options->BridgeRelay = 1; options->AuthoritativeDir = 1;
options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1;
+ options->DisableNetwork = 0;
+ set_network_participation(true);
register_dummy_hidden_service(&service);
periodic_events_on_new_options(options);
/* Note down the reference because we need to remove this service from the
@@ -163,9 +179,10 @@ test_pe_launch(void *arg)
* trigger a rescan of the event disabling the HS service event. */
to_remove = &service;
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
- tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ,
+ (item->roles != PERIODIC_EVENT_ROLE_CONTROLEV));
}
done:
@@ -187,42 +204,49 @@ test_pe_get_roles(void *arg)
or_options_t *options = get_options_mutable();
tt_assert(options);
+ set_network_participation(true);
+
+ const int ALL = PERIODIC_EVENT_ROLE_ALL |
+ PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
/* Nothing configured, should be no roles. */
+ tt_assert(net_is_disabled());
roles = get_my_roles(options);
- tt_int_op(roles, OP_EQ, 0);
+ tt_int_op(roles, OP_EQ, ALL);
/* Indicate we have a SocksPort, roles should be come Client. */
options->SocksPort_set = 1;
roles = get_my_roles(options);
- tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT);
+ tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT|ALL);
/* Now, we'll add a ORPort so should now be a Relay + Client. */
options->ORPort_set = 1;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
(PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
- PERIODIC_EVENT_ROLE_DIRSERVER));
+ PERIODIC_EVENT_ROLE_DIRSERVER | ALL));
/* Now add a Bridge. */
options->BridgeRelay = 1;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
(PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
- PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER));
+ PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER |
+ ALL));
tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER);
/* Unset client so we can solely test Router role. */
options->SocksPort_set = 0;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
- PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER);
+ PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER |
+ ALL);
/* Reset options so we can test authorities. */
options->SocksPort_set = 0;
options->ORPort_set = 0;
options->BridgeRelay = 0;
roles = get_my_roles(options);
- tt_int_op(roles, OP_EQ, 0);
+ tt_int_op(roles, OP_EQ, ALL);
/* Now upgrade to Dirauth. */
options->DirPort_set = 1;
@@ -230,7 +254,7 @@ test_pe_get_roles(void *arg)
options->V3AuthoritativeDir = 1;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
- PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER);
+ PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
/* Now Bridge Authority. */
@@ -238,7 +262,7 @@ test_pe_get_roles(void *arg)
options->BridgeAuthoritativeDir = 1;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
- PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER);
+ PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
/* Move that bridge auth to become a relay. */
@@ -246,7 +270,7 @@ test_pe_get_roles(void *arg)
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
(PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY
- | PERIODIC_EVENT_ROLE_DIRSERVER));
+ | PERIODIC_EVENT_ROLE_DIRSERVER|ALL));
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
/* And now an Hidden service. */
@@ -257,7 +281,8 @@ test_pe_get_roles(void *arg)
remove_service(get_hs_service_map(), &service);
tt_int_op(roles, OP_EQ,
(PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY |
- PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER));
+ PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER |
+ ALL));
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
done:
@@ -277,12 +302,13 @@ test_pe_hs_service(void *arg)
consider_hibernation(time(NULL));
/* Initialize the events so we can enable them */
initialize_periodic_events();
+ periodic_events_connect_all();
/* Hack: We'll set a dumb fn() of each events so they don't get called when
* dispatching them. We just want to test the state of the callbacks, not
* the whole code path. */
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
item->fn = dumb_event_fn;
}
@@ -295,8 +321,8 @@ test_pe_hs_service(void *arg)
* trigger a rescan of the event disabling the HS service event. */
to_remove = &service;
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
}
@@ -306,8 +332,8 @@ test_pe_hs_service(void *arg)
/* Remove the service from the global map, it should trigger a rescan and
* disable the HS service events. */
remove_service(get_hs_service_map(), &service);
- for (int i = 0; periodic_events[i].name; ++i) {
- periodic_event_item_t *item = &periodic_events[i];
+ for (int i = 0; mainloop_periodic_events[i].name; ++i) {
+ periodic_event_item_t *item = &mainloop_periodic_events[i];
if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
}
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index 9c001c294a..e58bb3d174 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -6,13 +6,18 @@
#include "core/or/or.h"
#include "app/config/config.h"
+#include "core/or/circuitbuild.h"
#include "core/or/policies.h"
#include "feature/dirparse/policy_parse.h"
+#include "feature/hs/hs_common.h"
+#include "feature/hs/hs_descriptor.h"
#include "feature/relay/router.h"
#include "lib/encoding/confline.h"
#include "test/test.h"
+#include "test/log_test_helpers.h"
#include "core/or/addr_policy_st.h"
+#include "core/or/extend_info_st.h"
#include "core/or/port_cfg_st.h"
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerinfo_st.h"
@@ -2024,6 +2029,115 @@ test_policies_fascist_firewall_allows_address(void *arg)
expect_ap); \
STMT_END
+/* Check that fascist_firewall_choose_address_ls() returns the expected
+ * results. */
+#define CHECK_CHOSEN_ADDR_NULL_LS() \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_ls_ap; \
+ tor_addr_make_null(&chosen_ls_ap.addr, AF_UNSPEC); \
+ chosen_ls_ap.port = 0; \
+ setup_full_capture_of_logs(LOG_WARN); \
+ fascist_firewall_choose_address_ls(NULL, 1, &chosen_ls_ap); \
+ expect_single_log_msg("Unknown or missing link specifiers"); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_CHOSEN_ADDR_LS(fake_ls, pref_only, expect_rv, expect_ap) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_ls_ap; \
+ tor_addr_make_null(&chosen_ls_ap.addr, AF_UNSPEC); \
+ chosen_ls_ap.port = 0; \
+ setup_full_capture_of_logs(LOG_WARN); \
+ fascist_firewall_choose_address_ls(fake_ls, pref_only, &chosen_ls_ap); \
+ if (smartlist_len(fake_ls) == 0) { \
+ expect_single_log_msg("Link specifiers are empty"); \
+ } else { \
+ expect_no_log_entry(); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_ls_ap.addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, chosen_ls_ap.port); \
+ } \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_LS_LEGACY_ONLY(fake_ls) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_ls_ap; \
+ tor_addr_make_null(&chosen_ls_ap.addr, AF_UNSPEC); \
+ chosen_ls_ap.port = 0; \
+ setup_full_capture_of_logs(LOG_WARN); \
+ fascist_firewall_choose_address_ls(fake_ls, 0, &chosen_ls_ap); \
+ expect_single_log_msg("None of our link specifiers have IPv4 or IPv6"); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS(fake_ls, direct_conn, expect_ap) \
+ STMT_BEGIN \
+ curve25519_secret_key_t seckey; \
+ curve25519_secret_key_generate(&seckey, 0); \
+ curve25519_public_key_t pubkey; \
+ curve25519_public_key_generate(&pubkey, &seckey); \
+ setup_full_capture_of_logs(LOG_WARN); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, &pubkey, \
+ direct_conn); \
+ if (fake_ls == NULL) { \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ expect_single_log_msg("Specified link specifiers is null"); \
+ } else { \
+ expect_no_log_entry(); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &ei->addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, ei->port); \
+ extend_info_free(ei); \
+ } \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS_NULL_KEY(fake_ls) \
+ STMT_BEGIN \
+ setup_full_capture_of_logs(LOG_WARN); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, NULL, 0); \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ expect_single_log_msg("Specified onion key is null"); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(fake_ls, direct_conn) \
+ STMT_BEGIN \
+ curve25519_secret_key_t seckey; \
+ curve25519_secret_key_generate(&seckey, 0); \
+ curve25519_public_key_t pubkey; \
+ curve25519_public_key_generate(&pubkey, &seckey); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, &pubkey, \
+ direct_conn); \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_MSG(fake_ls, msg_level, msg) \
+ STMT_BEGIN \
+ curve25519_secret_key_t seckey; \
+ curve25519_secret_key_generate(&seckey, 0); \
+ curve25519_public_key_t pubkey; \
+ curve25519_public_key_generate(&pubkey, &seckey); \
+ setup_full_capture_of_logs(msg_level); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, &pubkey, 0); \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ expect_single_log_msg(msg); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+/** Mock the preferred address function to return zero (prefer IPv4). */
+static int
+mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv4(void)
+{
+ return 0;
+}
+
+/** Mock the preferred address function to return one (prefer IPv6). */
+static int
+mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv6(void)
+{
+ return 1;
+}
+
/** Run unit tests for fascist_firewall_choose_address */
static void
test_policies_fascist_firewall_choose_address(void *arg)
@@ -2422,6 +2536,177 @@ test_policies_fascist_firewall_choose_address(void *arg)
CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
ipv4_dir_ap);
+ /* Test ClientAutoIPv6ORPort and pretend we prefer IPv4. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientAutoIPv6ORPort = 1;
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ MOCK(fascist_firewall_rand_prefer_ipv6_addr,
+ mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv4);
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+
+ UNMOCK(fascist_firewall_rand_prefer_ipv6_addr);
+
+ /* Test ClientAutoIPv6ORPort and pretend we prefer IPv6. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientAutoIPv6ORPort = 1;
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ MOCK(fascist_firewall_rand_prefer_ipv6_addr,
+ mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv6);
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+
+ UNMOCK(fascist_firewall_rand_prefer_ipv6_addr);
+
+ /* Test firewall_choose_address_ls(). To do this, we make a fake link
+ * specifier. */
+ smartlist_t *lspecs = smartlist_new(),
+ *lspecs_blank = smartlist_new(),
+ *lspecs_v4 = smartlist_new(),
+ *lspecs_v6 = smartlist_new(),
+ *lspecs_no_legacy = smartlist_new(),
+ *lspecs_legacy_only = smartlist_new();
+ link_specifier_t *fake_ls;
+
+ /* IPv4 link specifier */
+ fake_ls = link_specifier_new();
+ link_specifier_set_ls_type(fake_ls, LS_IPV4);
+ link_specifier_set_un_ipv4_addr(fake_ls,
+ tor_addr_to_ipv4h(&ipv4_or_ap.addr));
+ link_specifier_set_un_ipv4_port(fake_ls, ipv4_or_ap.port);
+ link_specifier_set_ls_len(fake_ls, sizeof(ipv4_or_ap.addr.addr.in_addr) +
+ sizeof(ipv4_or_ap.port));
+ smartlist_add(lspecs, fake_ls);
+ smartlist_add(lspecs_v4, fake_ls);
+ smartlist_add(lspecs_no_legacy, fake_ls);
+
+ /* IPv6 link specifier */
+ fake_ls = link_specifier_new();
+ link_specifier_set_ls_type(fake_ls, LS_IPV6);
+ size_t addr_len = link_specifier_getlen_un_ipv6_addr(fake_ls);
+ const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ipv6_or_ap.addr);
+ uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(fake_ls);
+ memcpy(ipv6_array, in6_addr, addr_len);
+ link_specifier_set_un_ipv6_port(fake_ls, ipv6_or_ap.port);
+ link_specifier_set_ls_len(fake_ls, addr_len + sizeof(ipv6_or_ap.port));
+ smartlist_add(lspecs, fake_ls);
+ smartlist_add(lspecs_v6, fake_ls);
+
+ /* Legacy ID link specifier */
+ fake_ls = link_specifier_new();
+ link_specifier_set_ls_type(fake_ls, LS_LEGACY_ID);
+ uint8_t *legacy_id = link_specifier_getarray_un_legacy_id(fake_ls);
+ memset(legacy_id, 'A', sizeof(*legacy_id));
+ link_specifier_set_ls_len(fake_ls,
+ link_specifier_getlen_un_legacy_id(fake_ls));
+ smartlist_add(lspecs, fake_ls);
+ smartlist_add(lspecs_legacy_only, fake_ls);
+ smartlist_add(lspecs_v4, fake_ls);
+ smartlist_add(lspecs_v6, fake_ls);
+
+ /* Check with bogus requests. */
+ tor_addr_port_t null_ap; \
+ tor_addr_make_null(&null_ap.addr, AF_UNSPEC); \
+ null_ap.port = 0; \
+
+ /* Check for a null link state. */
+ CHECK_CHOSEN_ADDR_NULL_LS();
+ CHECK_HS_EXTEND_INFO_ADDR_LS(NULL, 1, null_ap);
+
+ /* Check for a blank link state. */
+ CHECK_CHOSEN_ADDR_LS(lspecs_blank, 0, 0, null_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_blank, 0);
+
+ /* Check for a link state with only a Legacy ID. */
+ CHECK_LS_LEGACY_ONLY(lspecs_legacy_only);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_legacy_only, 0);
+ smartlist_free(lspecs_legacy_only);
+
+ /* Check with a null onion_key. */
+ CHECK_HS_EXTEND_INFO_ADDR_LS_NULL_KEY(lspecs_blank);
+ smartlist_free(lspecs_blank);
+
+ /* Check with a null onion_key. */
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_MSG(lspecs_no_legacy, LOG_WARN,
+ "Missing Legacy ID in link state");
+ smartlist_free(lspecs_no_legacy);
+
+ /* Enable both IPv4 and IPv6. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+
+ /* Prefer IPv4, enable both IPv4 and IPv6. */
+ mock_options.ClientPreferIPv6ORPort = 0;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv4_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv4_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ /* Prefer IPv6, enable both IPv4 and IPv6. */
+ mock_options.ClientPreferIPv6ORPort = 1;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv6_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv6_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ /* IPv4-only. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 0;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv4_or_ap);
+
+ CHECK_CHOSEN_ADDR_LS(lspecs_v6, 0, 0, null_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv4_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v6, 0);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v6, 1);
+
+ /* IPv6-only. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 1;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv6_or_ap);
+
+ CHECK_CHOSEN_ADDR_LS(lspecs_v4, 0, 0, null_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv6_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v4, 1);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v6, 0);
+
+ smartlist_free(lspecs_v4);
+ smartlist_free(lspecs_v6);
+
+ SMARTLIST_FOREACH(lspecs, link_specifier_t *, lspec, \
+ link_specifier_free(lspec)); \
+ smartlist_free(lspecs);
+
done:
UNMOCK(get_options);
}
diff --git a/src/test/test_prob_distr.c b/src/test/test_prob_distr.c
new file mode 100644
index 0000000000..0ecbf65f41
--- /dev/null
+++ b/src/test/test_prob_distr.c
@@ -0,0 +1,1402 @@
+/* Copyright (c) 2018-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_prob_distr.c
+ * \brief Test probability distributions.
+ * \detail
+ *
+ * For each probability distribution we do two kinds of tests:
+ *
+ * a) We do numerical deterministic testing of their cdf/icdf/sf/isf functions
+ * and the various relationships between them for each distribution. We also
+ * do deterministic tests on their sampling functions. Test vectors for
+ * these tests were computed from alternative implementations and were
+ * eyeballed to make sure they make sense
+ * (e.g. src/test/prob_distr_mpfr_ref.c computes logit(p) using GNU mpfr
+ * with 200-bit precision and is then tested in test_logit_logistic()).
+ *
+ * b) We do stochastic hypothesis testing (G-test) to ensure that sampling from
+ * the given distributions is distributed properly. The stochastic tests are
+ * slow and their false positive rate is not well suited for CI, so they are
+ * currently disabled-by-default and put into 'tests-slow'.
+ */
+
+#define PROB_DISTR_PRIVATE
+
+#include "orconfig.h"
+
+#include "test/test.h"
+
+#include "core/or/or.h"
+
+#include "lib/math/prob_distr.h"
+#include "lib/math/fp.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "test/rng_test_helpers.h"
+
+#include <float.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * Return floor(d) converted to size_t, as a workaround for complaints
+ * under -Wbad-function-cast for (size_t)floor(d).
+ */
+static size_t
+floor_to_size_t(double d)
+{
+ double integral_d = floor(d);
+ return (size_t)integral_d;
+}
+
+/**
+ * Return ceil(d) converted to size_t, as a workaround for complaints
+ * under -Wbad-function-cast for (size_t)ceil(d).
+ */
+static size_t
+ceil_to_size_t(double d)
+{
+ double integral_d = ceil(d);
+ return (size_t)integral_d;
+}
+
+/*
+ * Geometric(p) distribution, supported on {1, 2, 3, ...}.
+ *
+ * Compute the probability mass function Geom(n; p) of the number of
+ * trials before the first success when success has probability p.
+ */
+static double
+logpmf_geometric(unsigned n, double p)
+{
+ /* This is actually a check against 1, but we do >= so that the compiler
+ does not raise a -Wfloat-equal */
+ if (p >= 1) {
+ if (n == 1)
+ return 0;
+ else
+ return -HUGE_VAL;
+ }
+ return (n - 1)*log1p(-p) + log(p);
+}
+
+/**
+ * Compute the logistic function, translated in output by 1/2:
+ * logistichalf(x) = logistic(x) - 1/2. Well-conditioned on the entire
+ * real plane, with maximum condition number 1 at 0.
+ *
+ * This implementation gives relative error bounded by 5 eps.
+ */
+static double
+logistichalf(double x)
+{
+ /*
+ * Rewrite this with the identity
+ *
+ * 1/(1 + e^{-x}) - 1/2
+ * = (1 - 1/2 - e^{-x}/2)/(1 + e^{-x})
+ * = (1/2 - e^{-x}/2)/(1 + e^{-x})
+ * = (1 - e^{-x})/[2 (1 + e^{-x})]
+ * = -(e^{-x} - 1)/[2 (1 + e^{-x})],
+ *
+ * which we can evaluate by -expm1(-x)/[2 (1 + exp(-x))].
+ *
+ * Suppose exp has error d0, + has error d1, expm1 has error
+ * d2, and / has error d3, so we evaluate
+ *
+ * -(1 + d2) (1 + d3) (e^{-x} - 1)
+ * / [2 (1 + d1) (1 + (1 + d0) e^{-x})].
+ *
+ * In the denominator,
+ *
+ * 1 + (1 + d0) e^{-x}
+ * = 1 + e^{-x} + d0 e^{-x}
+ * = (1 + e^{-x}) (1 + d0 e^{-x}/(1 + e^{-x})),
+ *
+ * so the relative error of the numerator is
+ *
+ * d' = d2 + d3 + d2 d3,
+ * and of the denominator,
+ * d'' = d1 + d0 e^{-x}/(1 + e^{-x}) + d0 d1 e^{-x}/(1 + e^{-x})
+ * = d1 + d0 L(-x) + d0 d1 L(-x),
+ *
+ * where L(-x) is logistic(-x). By Lemma 1 the relative error
+ * of the quotient is bounded by
+ *
+ * 2|d2 + d3 + d2 d3 - d1 - d0 L(x) + d0 d1 L(x)|,
+ *
+ * Since 0 < L(x) < 1, this is bounded by
+ *
+ * 2|d2| + 2|d3| + 2|d2 d3| + 2|d1| + 2|d0| + 2|d0 d1|
+ * <= 4 eps + 2 eps^2.
+ */
+ if (x < log(DBL_EPSILON/8)) {
+ /*
+ * Avoid overflow in e^{-x}. When x < log(eps/4), we
+ * we further have x < logit(eps/4), so that
+ * logistic(x) < eps/4. Hence the relative error of
+ * logistic(x) - 1/2 from -1/2 is bounded by eps/2, and
+ * so the relative error of -1/2 from logistic(x) - 1/2
+ * is bounded by eps.
+ */
+ return -0.5;
+ } else {
+ return -expm1(-x)/(2*(1 + exp(-x)));
+ }
+}
+
+/**
+ * Compute the log of the sum of the exps. Caller should arrange the
+ * array in descending order to minimize error because I don't want to
+ * deal with using temporary space and the one caller in this file
+ * arranges that anyway.
+ *
+ * Warning: This implementation does not handle infinite or NaN inputs
+ * sensibly, because I don't need that here at the moment. (NaN, or
+ * -inf and +inf together, should yield NaN; +inf and finite should
+ * yield +inf; otherwise all -inf should be ignored because exp(-inf) =
+ * 0.)
+ */
+static double
+logsumexp(double *A, size_t n)
+{
+ double maximum, sum;
+ size_t i;
+
+ if (n == 0)
+ return log(0);
+
+ maximum = A[0];
+ for (i = 1; i < n; i++) {
+ if (A[i] > maximum)
+ maximum = A[i];
+ }
+
+ sum = 0;
+ for (i = n; i --> 0;)
+ sum += exp(A[i] - maximum);
+
+ return log(sum) + maximum;
+}
+
+/**
+ * Compute log(1 - e^x). Defined only for negative x so that e^x < 1.
+ * This is the complement of a probability in log space.
+ */
+static double
+log1mexp(double x)
+{
+
+ /*
+ * We want to compute log on [0, 1/2) but log1p on [1/2, +inf),
+ * so partition x at -log(2) = log(1/2).
+ */
+ if (-log(2) < x)
+ return log(-expm1(x));
+ else
+ return log1p(-exp(x));
+}
+
+/*
+ * Tests of numerical errors in computing logit, logistic, and the
+ * various cdfs, sfs, icdfs, and isfs.
+ */
+
+#define arraycount(A) (sizeof(A)/sizeof(A[0]))
+
+/** Return relative error between <b>actual</b> and <b>expected</b>.
+ * Special cases: If <b>expected</b> is zero or infinite, return 1 if
+ * <b>actual</b> is equal to <b>expected</b> and 0 if not, since the
+ * usual notion of relative error is undefined but we only use this
+ * for testing relerr(e, a) <= bound. If either is NaN, return NaN,
+ * which has the property that NaN <= bound is false no matter what
+ * bound is.
+ *
+ * Beware: if you test !(relerr(e, a) > bound), then then the result
+ * is true when a is NaN because NaN > bound is false too. See
+ * CHECK_RELERR for correct use to decide when to report failure.
+ */
+static double
+relerr(double expected, double actual)
+{
+ /*
+ * To silence -Wfloat-equal, we have to test for equality using
+ * inequalities: we have (fabs(expected) <= 0) iff (expected == 0),
+ * and (actual <= expected && actual >= expected) iff actual ==
+ * expected whether expected is zero or infinite.
+ */
+ if (fabs(expected) <= 0 || tor_isinf(expected)) {
+ if (actual <= expected && actual >= expected)
+ return 0;
+ else
+ return 1;
+ } else {
+ return fabs((expected - actual)/expected);
+ }
+}
+
+/** Check that relative error of <b>expected</b> and <b>actual</b> is within
+ * <b>relerr_bound</b>. Caller must arrange to have i and relerr_bound in
+ * scope. */
+#define CHECK_RELERR(expected, actual) do { \
+ double check_expected = (expected); \
+ double check_actual = (actual); \
+ const char *str_expected = #expected; \
+ const char *str_actual = #actual; \
+ double check_relerr = relerr(expected, actual); \
+ if (!(relerr(check_expected, check_actual) <= relerr_bound)) { \
+ log_warn(LD_GENERAL, "%s:%d: case %u: relerr(%s=%.17e, %s=%.17e)" \
+ " = %.17e > %.17e\n", \
+ __func__, __LINE__, (unsigned) i, \
+ str_expected, check_expected, \
+ str_actual, check_actual, \
+ check_relerr, relerr_bound); \
+ ok = false; \
+ } \
+} while (0)
+
+/* Check that a <= b.
+ * Caller must arrange to have i in scope. */
+#define CHECK_LE(a, b) do { \
+ double check_a = (a); \
+ double check_b = (b); \
+ const char *str_a = #a; \
+ const char *str_b = #b; \
+ if (!(check_a <= check_b)) { \
+ log_warn(LD_GENERAL, "%s:%d: case %u: %s=%.17e > %s=%.17e\n", \
+ __func__, __LINE__, (unsigned) i, \
+ str_a, check_a, str_b, check_b); \
+ ok = false; \
+ } \
+} while (0)
+
+/**
+ * Test the logit and logistic functions. Confirm that they agree with
+ * the cdf, sf, icdf, and isf of the standard Logistic distribution.
+ * Confirm that the sampler for the standard logistic distribution maps
+ * [0, 1] into the right subinterval for the inverse transform, for
+ * this implementation.
+ */
+static void
+test_logit_logistic(void *arg)
+{
+ (void) arg;
+
+ static const struct {
+ double x; /* x = logit(p) */
+ double p; /* p = logistic(x) */
+ double phalf; /* p - 1/2 = logistic(x) - 1/2 */
+ } cases[] = {
+ { -HUGE_VAL, 0, -0.5 },
+ { -1000, 0, -0.5 },
+ { -710, 4.47628622567513e-309, -0.5 },
+ { -708, 3.307553003638408e-308, -0.5 },
+ { -2, .11920292202211755, -.3807970779778824 },
+ { -1.0000001, .2689414017088022, -.23105859829119776 },
+ { -1, .2689414213699951, -.23105857863000487 },
+ { -0.9999999, .26894144103118883, -.2310585589688111 },
+ /* see src/test/prob_distr_mpfr_ref.c for computation */
+ { -4.000000000537333e-5, .49999, -1.0000000000010001e-5 },
+ { -4.000000000533334e-5, .49999, -.00001 },
+ { -4.000000108916878e-9, .499999999, -1.0000000272292198e-9 },
+ { -4e-9, .499999999, -1e-9 },
+ { -4e-16, .5, -1e-16 },
+ { -4e-300, .5, -1e-300 },
+ { 0, .5, 0 },
+ { 4e-300, .5, 1e-300 },
+ { 4e-16, .5, 1e-16 },
+ { 3.999999886872274e-9, .500000001, 9.999999717180685e-10 },
+ { 4e-9, .500000001, 1e-9 },
+ { 4.0000000005333336e-5, .50001, .00001 },
+ { 8.000042667076272e-3, .502, .002 },
+ { 0.9999999, .7310585589688111, .2310585589688111 },
+ { 1, .7310585786300049, .23105857863000487 },
+ { 1.0000001, .7310585982911977, .23105859829119774 },
+ { 2, .8807970779778823, .3807970779778824 },
+ { 708, 1, .5 },
+ { 710, 1, .5 },
+ { 1000, 1, .5 },
+ { HUGE_VAL, 1, .5 },
+ };
+ double relerr_bound = 3e-15; /* >10eps */
+ size_t i;
+ bool ok = true;
+
+ for (i = 0; i < arraycount(cases); i++) {
+ double x = cases[i].x;
+ double p = cases[i].p;
+ double phalf = cases[i].phalf;
+
+ /*
+ * cdf is logistic, icdf is logit, and symmetry for
+ * sf/isf.
+ */
+ CHECK_RELERR(logistic(x), cdf_logistic(x, 0, 1));
+ CHECK_RELERR(logistic(-x), sf_logistic(x, 0, 1));
+ CHECK_RELERR(logit(p), icdf_logistic(p, 0, 1));
+ CHECK_RELERR(-logit(p), isf_logistic(p, 0, 1));
+
+ CHECK_RELERR(cdf_logistic(x, 0, 1), cdf_logistic(x*2, 0, 2));
+ CHECK_RELERR(sf_logistic(x, 0, 1), sf_logistic(x*2, 0, 2));
+ CHECK_RELERR(icdf_logistic(p, 0, 1), icdf_logistic(p, 0, 2)/2);
+ CHECK_RELERR(isf_logistic(p, 0, 1), isf_logistic(p, 0, 2)/2);
+
+ CHECK_RELERR(cdf_logistic(x, 0, 1), cdf_logistic(x/2, 0, .5));
+ CHECK_RELERR(sf_logistic(x, 0, 1), sf_logistic(x/2, 0, .5));
+ CHECK_RELERR(icdf_logistic(p, 0, 1), icdf_logistic(p, 0,.5)*2);
+ CHECK_RELERR(isf_logistic(p, 0, 1), isf_logistic(p, 0, .5)*2);
+
+ CHECK_RELERR(cdf_logistic(x, 0, 1), cdf_logistic(x*2 + 1, 1, 2));
+ CHECK_RELERR(sf_logistic(x, 0, 1), sf_logistic(x*2 + 1, 1, 2));
+
+ /*
+ * For p near 0 and p near 1/2, the arithmetic of
+ * translating by 1 loses precision.
+ */
+ if (fabs(p) > DBL_EPSILON && fabs(p) < 0.4) {
+ CHECK_RELERR(icdf_logistic(p, 0, 1),
+ (icdf_logistic(p, 1, 2) - 1)/2);
+ CHECK_RELERR(isf_logistic(p, 0, 1),
+ (isf_logistic(p, 1, 2) - 1)/2);
+ }
+
+ CHECK_RELERR(p, logistic(x));
+ CHECK_RELERR(phalf, logistichalf(x));
+
+ /*
+ * On the interior floating-point numbers, either logit or
+ * logithalf had better give the correct answer.
+ *
+ * For probabilities near 0, we can get much finer resolution with
+ * logit, and for probabilities near 1/2, we can get much finer
+ * resolution with logithalf by representing them using p - 1/2.
+ *
+ * E.g., we can write -.00001 for phalf, and .49999 for p, but the
+ * difference 1/2 - .00001 gives 1.0000000000010001e-5 in binary64
+ * arithmetic. So test logit(.49999) which should give the same
+ * answer as logithalf(-1.0000000000010001e-5), namely
+ * -4.000000000537333e-5, and also test logithalf(-.00001) which
+ * gives -4.000000000533334e-5 instead -- but don't expect
+ * logit(.49999) to give -4.000000000533334e-5 even though it looks
+ * like 1/2 - .00001.
+ *
+ * A naive implementation of logit will just use log(p/(1 - p)) and
+ * give the answer -4.000000000551673e-05 for .49999, which is
+ * wrong in a lot of digits, which happens because log is
+ * ill-conditioned near 1 and thus amplifies whatever relative
+ * error we made in computing p/(1 - p).
+ */
+ if ((0 < p && p < 1) || tor_isinf(x)) {
+ if (phalf >= p - 0.5 && phalf <= p - 0.5)
+ CHECK_RELERR(x, logit(p));
+ if (p >= 0.5 + phalf && p <= 0.5 + phalf)
+ CHECK_RELERR(x, logithalf(phalf));
+ }
+
+ CHECK_RELERR(-phalf, logistichalf(-x));
+ if (fabs(phalf) < 0.5 || tor_isinf(x))
+ CHECK_RELERR(-x, logithalf(-phalf));
+ if (p < 1 || tor_isinf(x)) {
+ CHECK_RELERR(1 - p, logistic(-x));
+ if (p > .75 || tor_isinf(x))
+ CHECK_RELERR(-x, logit(1 - p));
+ } else {
+ CHECK_LE(logistic(-x), 1e-300);
+ }
+ }
+
+ for (i = 0; i <= 100; i++) {
+ double p0 = (double)i/100;
+
+ CHECK_RELERR(logit(p0/(1 + M_E)), sample_logistic(0, 0, p0));
+ CHECK_RELERR(-logit(p0/(1 + M_E)), sample_logistic(1, 0, p0));
+ CHECK_RELERR(logithalf(p0*(0.5 - 1/(1 + M_E))),
+ sample_logistic(0, 1, p0));
+ CHECK_RELERR(-logithalf(p0*(0.5 - 1/(1 + M_E))),
+ sample_logistic(1, 1, p0));
+ }
+
+ if (!ok)
+ printf("fail logit/logistic / logistic cdf/sf\n");
+
+ tt_assert(ok);
+
+ done:
+ ;
+}
+
+/**
+ * Test the cdf, sf, icdf, and isf of the LogLogistic distribution.
+ */
+static void
+test_log_logistic(void *arg)
+{
+ (void) arg;
+
+ static const struct {
+ /* x is a point in the support of the LogLogistic distribution */
+ double x;
+ /* 'p' is the probability that a random variable X for a given LogLogistic
+ * probability ditribution will take value less-or-equal to x */
+ double p;
+ /* 'np' is the probability that a random variable X for a given LogLogistic
+ * probability distribution will take value greater-or-equal to x. */
+ double np;
+ } cases[] = {
+ { 0, 0, 1 },
+ { 1e-300, 1e-300, 1 },
+ { 1e-17, 1e-17, 1 },
+ { 1e-15, 1e-15, .999999999999999 },
+ { .1, .09090909090909091, .90909090909090909 },
+ { .25, .2, .8 },
+ { .5, .33333333333333333, .66666666666666667 },
+ { .75, .42857142857142855, .5714285714285714 },
+ { .9999, .49997499874993756, .5000250012500626 },
+ { .99999999, .49999999749999996, .5000000025 },
+ { .999999999999999, .49999999999999994, .5000000000000002 },
+ { 1, .5, .5 },
+ };
+ double relerr_bound = 3e-15;
+ size_t i;
+ bool ok = true;
+
+ for (i = 0; i < arraycount(cases); i++) {
+ double x = cases[i].x;
+ double p = cases[i].p;
+ double np = cases[i].np;
+
+ CHECK_RELERR(p, cdf_log_logistic(x, 1, 1));
+ CHECK_RELERR(p, cdf_log_logistic(x/2, .5, 1));
+ CHECK_RELERR(p, cdf_log_logistic(x*2, 2, 1));
+ CHECK_RELERR(p, cdf_log_logistic(sqrt(x), 1, 2));
+ CHECK_RELERR(p, cdf_log_logistic(sqrt(x)/2, .5, 2));
+ CHECK_RELERR(p, cdf_log_logistic(sqrt(x)*2, 2, 2));
+ if (2*sqrt(DBL_MIN) < x) {
+ CHECK_RELERR(p, cdf_log_logistic(x*x, 1, .5));
+ CHECK_RELERR(p, cdf_log_logistic(x*x/2, .5, .5));
+ CHECK_RELERR(p, cdf_log_logistic(x*x*2, 2, .5));
+ }
+
+ CHECK_RELERR(np, sf_log_logistic(x, 1, 1));
+ CHECK_RELERR(np, sf_log_logistic(x/2, .5, 1));
+ CHECK_RELERR(np, sf_log_logistic(x*2, 2, 1));
+ CHECK_RELERR(np, sf_log_logistic(sqrt(x), 1, 2));
+ CHECK_RELERR(np, sf_log_logistic(sqrt(x)/2, .5, 2));
+ CHECK_RELERR(np, sf_log_logistic(sqrt(x)*2, 2, 2));
+ if (2*sqrt(DBL_MIN) < x) {
+ CHECK_RELERR(np, sf_log_logistic(x*x, 1, .5));
+ CHECK_RELERR(np, sf_log_logistic(x*x/2, .5, .5));
+ CHECK_RELERR(np, sf_log_logistic(x*x*2, 2, .5));
+ }
+
+ CHECK_RELERR(np, cdf_log_logistic(1/x, 1, 1));
+ CHECK_RELERR(np, cdf_log_logistic(1/(2*x), .5, 1));
+ CHECK_RELERR(np, cdf_log_logistic(2/x, 2, 1));
+ CHECK_RELERR(np, cdf_log_logistic(1/sqrt(x), 1, 2));
+ CHECK_RELERR(np, cdf_log_logistic(1/(2*sqrt(x)), .5, 2));
+ CHECK_RELERR(np, cdf_log_logistic(2/sqrt(x), 2, 2));
+ if (2*sqrt(DBL_MIN) < x && x < 1/(2*sqrt(DBL_MIN))) {
+ CHECK_RELERR(np, cdf_log_logistic(1/(x*x), 1, .5));
+ CHECK_RELERR(np, cdf_log_logistic(1/(2*x*x), .5, .5));
+ CHECK_RELERR(np, cdf_log_logistic(2/(x*x), 2, .5));
+ }
+
+ CHECK_RELERR(p, sf_log_logistic(1/x, 1, 1));
+ CHECK_RELERR(p, sf_log_logistic(1/(2*x), .5, 1));
+ CHECK_RELERR(p, sf_log_logistic(2/x, 2, 1));
+ CHECK_RELERR(p, sf_log_logistic(1/sqrt(x), 1, 2));
+ CHECK_RELERR(p, sf_log_logistic(1/(2*sqrt(x)), .5, 2));
+ CHECK_RELERR(p, sf_log_logistic(2/sqrt(x), 2, 2));
+ if (2*sqrt(DBL_MIN) < x && x < 1/(2*sqrt(DBL_MIN))) {
+ CHECK_RELERR(p, sf_log_logistic(1/(x*x), 1, .5));
+ CHECK_RELERR(p, sf_log_logistic(1/(2*x*x), .5, .5));
+ CHECK_RELERR(p, sf_log_logistic(2/(x*x), 2, .5));
+ }
+
+ CHECK_RELERR(x, icdf_log_logistic(p, 1, 1));
+ CHECK_RELERR(x/2, icdf_log_logistic(p, .5, 1));
+ CHECK_RELERR(x*2, icdf_log_logistic(p, 2, 1));
+ CHECK_RELERR(x, icdf_log_logistic(p, 1, 1));
+ CHECK_RELERR(sqrt(x)/2, icdf_log_logistic(p, .5, 2));
+ CHECK_RELERR(sqrt(x)*2, icdf_log_logistic(p, 2, 2));
+ CHECK_RELERR(sqrt(x), icdf_log_logistic(p, 1, 2));
+ CHECK_RELERR(x*x/2, icdf_log_logistic(p, .5, .5));
+ CHECK_RELERR(x*x*2, icdf_log_logistic(p, 2, .5));
+
+ if (np < .9) {
+ CHECK_RELERR(x, isf_log_logistic(np, 1, 1));
+ CHECK_RELERR(x/2, isf_log_logistic(np, .5, 1));
+ CHECK_RELERR(x*2, isf_log_logistic(np, 2, 1));
+ CHECK_RELERR(sqrt(x), isf_log_logistic(np, 1, 2));
+ CHECK_RELERR(sqrt(x)/2, isf_log_logistic(np, .5, 2));
+ CHECK_RELERR(sqrt(x)*2, isf_log_logistic(np, 2, 2));
+ CHECK_RELERR(x*x, isf_log_logistic(np, 1, .5));
+ CHECK_RELERR(x*x/2, isf_log_logistic(np, .5, .5));
+ CHECK_RELERR(x*x*2, isf_log_logistic(np, 2, .5));
+
+ CHECK_RELERR(1/x, icdf_log_logistic(np, 1, 1));
+ CHECK_RELERR(1/(2*x), icdf_log_logistic(np, .5, 1));
+ CHECK_RELERR(2/x, icdf_log_logistic(np, 2, 1));
+ CHECK_RELERR(1/sqrt(x), icdf_log_logistic(np, 1, 2));
+ CHECK_RELERR(1/(2*sqrt(x)),
+ icdf_log_logistic(np, .5, 2));
+ CHECK_RELERR(2/sqrt(x), icdf_log_logistic(np, 2, 2));
+ CHECK_RELERR(1/(x*x), icdf_log_logistic(np, 1, .5));
+ CHECK_RELERR(1/(2*x*x), icdf_log_logistic(np, .5, .5));
+ CHECK_RELERR(2/(x*x), icdf_log_logistic(np, 2, .5));
+ }
+
+ CHECK_RELERR(1/x, isf_log_logistic(p, 1, 1));
+ CHECK_RELERR(1/(2*x), isf_log_logistic(p, .5, 1));
+ CHECK_RELERR(2/x, isf_log_logistic(p, 2, 1));
+ CHECK_RELERR(1/sqrt(x), isf_log_logistic(p, 1, 2));
+ CHECK_RELERR(1/(2*sqrt(x)), isf_log_logistic(p, .5, 2));
+ CHECK_RELERR(2/sqrt(x), isf_log_logistic(p, 2, 2));
+ CHECK_RELERR(1/(x*x), isf_log_logistic(p, 1, .5));
+ CHECK_RELERR(1/(2*x*x), isf_log_logistic(p, .5, .5));
+ CHECK_RELERR(2/(x*x), isf_log_logistic(p, 2, .5));
+ }
+
+ for (i = 0; i <= 100; i++) {
+ double p0 = (double)i/100;
+
+ CHECK_RELERR(0.5*p0/(1 - 0.5*p0), sample_log_logistic(0, p0));
+ CHECK_RELERR((1 - 0.5*p0)/(0.5*p0),
+ sample_log_logistic(1, p0));
+ }
+
+ if (!ok)
+ printf("fail log logistic cdf/sf\n");
+
+ tt_assert(ok);
+
+ done:
+ ;
+}
+
+/**
+ * Test the cdf, sf, icdf, isf of the Weibull distribution.
+ */
+static void
+test_weibull(void *arg)
+{
+ (void) arg;
+
+ static const struct {
+ /* x is a point in the support of the Weibull distribution */
+ double x;
+ /* 'p' is the probability that a random variable X for a given Weibull
+ * probability ditribution will take value less-or-equal to x */
+ double p;
+ /* 'np' is the probability that a random variable X for a given Weibull
+ * probability distribution will take value greater-or-equal to x. */
+ double np;
+ } cases[] = {
+ { 0, 0, 1 },
+ { 1e-300, 1e-300, 1 },
+ { 1e-17, 1e-17, 1 },
+ { .1, .09516258196404043, .9048374180359595 },
+ { .5, .3934693402873666, .6065306597126334 },
+ { .6931471805599453, .5, .5 },
+ { 1, .6321205588285577, .36787944117144233 },
+ { 10, .9999546000702375, 4.5399929762484854e-5 },
+ { 36, .9999999999999998, 2.319522830243569e-16 },
+ { 37, .9999999999999999, 8.533047625744066e-17 },
+ { 38, 1, 3.1391327920480296e-17 },
+ { 100, 1, 3.720075976020836e-44 },
+ { 708, 1, 3.307553003638408e-308 },
+ { 710, 1, 4.47628622567513e-309 },
+ { 1000, 1, 0 },
+ { HUGE_VAL, 1, 0 },
+ };
+ double relerr_bound = 3e-15;
+ size_t i;
+ bool ok = true;
+
+ for (i = 0; i < arraycount(cases); i++) {
+ double x = cases[i].x;
+ double p = cases[i].p;
+ double np = cases[i].np;
+
+ CHECK_RELERR(p, cdf_weibull(x, 1, 1));
+ CHECK_RELERR(p, cdf_weibull(x/2, .5, 1));
+ CHECK_RELERR(p, cdf_weibull(x*2, 2, 1));
+ /* For 0 < x < sqrt(DBL_MIN), x^2 loses lots of bits. */
+ if (x <= 0 ||
+ sqrt(DBL_MIN) <= x) {
+ CHECK_RELERR(p, cdf_weibull(x*x, 1, .5));
+ CHECK_RELERR(p, cdf_weibull(x*x/2, .5, .5));
+ CHECK_RELERR(p, cdf_weibull(x*x*2, 2, .5));
+ }
+ CHECK_RELERR(p, cdf_weibull(sqrt(x), 1, 2));
+ CHECK_RELERR(p, cdf_weibull(sqrt(x)/2, .5, 2));
+ CHECK_RELERR(p, cdf_weibull(sqrt(x)*2, 2, 2));
+ CHECK_RELERR(np, sf_weibull(x, 1, 1));
+ CHECK_RELERR(np, sf_weibull(x/2, .5, 1));
+ CHECK_RELERR(np, sf_weibull(x*2, 2, 1));
+ CHECK_RELERR(np, sf_weibull(x*x, 1, .5));
+ CHECK_RELERR(np, sf_weibull(x*x/2, .5, .5));
+ CHECK_RELERR(np, sf_weibull(x*x*2, 2, .5));
+ if (x >= 10) {
+ /*
+ * exp amplifies the error of sqrt(x)^2
+ * proportionally to exp(x); for large inputs
+ * this is significant.
+ */
+ double t = -expm1(-x*(2*DBL_EPSILON + DBL_EPSILON));
+ relerr_bound = t + DBL_EPSILON + t*DBL_EPSILON;
+ if (relerr_bound < 3e-15)
+ /*
+ * The tests are written only to 16
+ * decimal places anyway even if your
+ * `double' is, say, i387 binary80, for
+ * whatever reason.
+ */
+ relerr_bound = 3e-15;
+ CHECK_RELERR(np, sf_weibull(sqrt(x), 1, 2));
+ CHECK_RELERR(np, sf_weibull(sqrt(x)/2, .5, 2));
+ CHECK_RELERR(np, sf_weibull(sqrt(x)*2, 2, 2));
+ }
+
+ if (p <= 0.75) {
+ /*
+ * For p near 1, not enough precision near 1 to
+ * recover x.
+ */
+ CHECK_RELERR(x, icdf_weibull(p, 1, 1));
+ CHECK_RELERR(x/2, icdf_weibull(p, .5, 1));
+ CHECK_RELERR(x*2, icdf_weibull(p, 2, 1));
+ }
+ if (p >= 0.25 && !tor_isinf(x) && np > 0) {
+ /*
+ * For p near 0, not enough precision in np
+ * near 1 to recover x. For 0, isf gives inf,
+ * even if p is precise enough for the icdf to
+ * work.
+ */
+ CHECK_RELERR(x, isf_weibull(np, 1, 1));
+ CHECK_RELERR(x/2, isf_weibull(np, .5, 1));
+ CHECK_RELERR(x*2, isf_weibull(np, 2, 1));
+ }
+ }
+
+ for (i = 0; i <= 100; i++) {
+ double p0 = (double)i/100;
+
+ CHECK_RELERR(3*sqrt(-log(p0/2)), sample_weibull(0, p0, 3, 2));
+ CHECK_RELERR(3*sqrt(-log1p(-p0/2)),
+ sample_weibull(1, p0, 3, 2));
+ }
+
+ if (!ok)
+ printf("fail Weibull cdf/sf\n");
+
+ tt_assert(ok);
+
+ done:
+ ;
+}
+
+/**
+ * Test the cdf, sf, icdf, and isf of the generalized Pareto
+ * distribution.
+ */
+static void
+test_genpareto(void *arg)
+{
+ (void) arg;
+
+ struct {
+ /* xi is the 'xi' parameter of the generalized Pareto distribution, and the
+ * rest are the same as in the above tests */
+ double xi, x, p, np;
+ } cases[] = {
+ { 0, 0, 0, 1 },
+ { 1e-300, .004, 3.992010656008528e-3, .9960079893439915 },
+ { 1e-300, .1, .09516258196404043, .9048374180359595 },
+ { 1e-300, 1, .6321205588285577, .36787944117144233 },
+ { 1e-300, 10, .9999546000702375, 4.5399929762484854e-5 },
+ { 1e-200, 1e-16, 9.999999999999999e-17, .9999999999999999 },
+ { 1e-16, 1e-200, 9.999999999999998e-201, 1 },
+ { 1e-16, 1e-16, 1e-16, 1 },
+ { 1e-16, .004, 3.992010656008528e-3, .9960079893439915 },
+ { 1e-16, .1, .09516258196404043, .9048374180359595 },
+ { 1e-16, 1, .6321205588285577, .36787944117144233 },
+ { 1e-16, 10, .9999546000702375, 4.539992976248509e-5 },
+ { 1e-10, 1e-6, 9.999995000001667e-7, .9999990000005 },
+ { 1e-8, 1e-8, 9.999999950000001e-9, .9999999900000001 },
+ { 1, 1e-300, 1e-300, 1 },
+ { 1, 1e-16, 1e-16, .9999999999999999 },
+ { 1, .1, .09090909090909091, .9090909090909091 },
+ { 1, 1, .5, .5 },
+ { 1, 10, .9090909090909091, .0909090909090909 },
+ { 1, 100, .9900990099009901, .0099009900990099 },
+ { 1, 1000, .999000999000999, 9.990009990009992e-4 },
+ { 10, 1e-300, 1e-300, 1 },
+ { 10, 1e-16, 9.999999999999995e-17, .9999999999999999 },
+ { 10, .1, .06696700846319258, .9330329915368074 },
+ { 10, 1, .21320655780322778, .7867934421967723 },
+ { 10, 10, .3696701667040189, .6303298332959811 },
+ { 10, 100, .49886285755007337, .5011371424499267 },
+ { 10, 1000, .6018968102992647, .3981031897007353 },
+ };
+ double xi_array[] = { -1.5, -1, -1e-30, 0, 1e-30, 1, 1.5 };
+ size_t i, j;
+ double relerr_bound = 3e-15;
+ bool ok = true;
+
+ for (i = 0; i < arraycount(cases); i++) {
+ double xi = cases[i].xi;
+ double x = cases[i].x;
+ double p = cases[i].p;
+ double np = cases[i].np;
+
+ CHECK_RELERR(p, cdf_genpareto(x, 0, 1, xi));
+ CHECK_RELERR(p, cdf_genpareto(x*2, 0, 2, xi));
+ CHECK_RELERR(p, cdf_genpareto(x/2, 0, .5, xi));
+ CHECK_RELERR(np, sf_genpareto(x, 0, 1, xi));
+ CHECK_RELERR(np, sf_genpareto(x*2, 0, 2, xi));
+ CHECK_RELERR(np, sf_genpareto(x/2, 0, .5, xi));
+
+ if (p < .5) {
+ CHECK_RELERR(x, icdf_genpareto(p, 0, 1, xi));
+ CHECK_RELERR(x*2, icdf_genpareto(p, 0, 2, xi));
+ CHECK_RELERR(x/2, icdf_genpareto(p, 0, .5, xi));
+ }
+ if (np < .5) {
+ CHECK_RELERR(x, isf_genpareto(np, 0, 1, xi));
+ CHECK_RELERR(x*2, isf_genpareto(np, 0, 2, xi));
+ CHECK_RELERR(x/2, isf_genpareto(np, 0, .5, xi));
+ }
+ }
+
+ for (i = 0; i < arraycount(xi_array); i++) {
+ for (j = 0; j <= 100; j++) {
+ double p0 = (j == 0 ? 2*DBL_MIN : (double)j/100);
+
+ /* This is actually a check against 0, but we do <= so that the compiler
+ does not raise a -Wfloat-equal */
+ if (fabs(xi_array[i]) <= 0) {
+ /*
+ * When xi == 0, the generalized Pareto
+ * distribution reduces to an
+ * exponential distribution.
+ */
+ CHECK_RELERR(-log(p0/2),
+ sample_genpareto(0, p0, 0));
+ CHECK_RELERR(-log1p(-p0/2),
+ sample_genpareto(1, p0, 0));
+ } else {
+ CHECK_RELERR(expm1(-xi_array[i]*log(p0/2))/xi_array[i],
+ sample_genpareto(0, p0, xi_array[i]));
+ CHECK_RELERR((j == 0 ? DBL_MIN :
+ expm1(-xi_array[i]*log1p(-p0/2))/xi_array[i]),
+ sample_genpareto(1, p0, xi_array[i]));
+ }
+
+ CHECK_RELERR(isf_genpareto(p0/2, 0, 1, xi_array[i]),
+ sample_genpareto(0, p0, xi_array[i]));
+ CHECK_RELERR(icdf_genpareto(p0/2, 0, 1, xi_array[i]),
+ sample_genpareto(1, p0, xi_array[i]));
+ }
+ }
+
+ tt_assert(ok);
+
+ done:
+ ;
+}
+
+/**
+ * Test the deterministic sampler for uniform distribution on [a, b].
+ *
+ * This currently only tests whether the outcome lies within [a, b].
+ */
+static void
+test_uniform_interval(void *arg)
+{
+ (void) arg;
+ struct {
+ /* Sample from a uniform distribution with parameters 'a' and 'b', using
+ * 't' as the sampling index. */
+ double t, a, b;
+ } cases[] = {
+ { 0, 0, 0 },
+ { 0, 0, 1 },
+ { 0, 1.0000000000000007, 3.999999999999995 },
+ { 0, 4000, 4000 },
+ { 0.42475836677491291, 4000, 4000 },
+ { 0, -DBL_MAX, DBL_MAX },
+ { 0.25, -DBL_MAX, DBL_MAX },
+ { 0.5, -DBL_MAX, DBL_MAX },
+ };
+ size_t i = 0;
+ bool ok = true;
+
+ for (i = 0; i < arraycount(cases); i++) {
+ double t = cases[i].t;
+ double a = cases[i].a;
+ double b = cases[i].b;
+
+ CHECK_LE(a, sample_uniform_interval(t, a, b));
+ CHECK_LE(sample_uniform_interval(t, a, b), b);
+
+ CHECK_LE(a, sample_uniform_interval(1 - t, a, b));
+ CHECK_LE(sample_uniform_interval(1 - t, a, b), b);
+
+ CHECK_LE(sample_uniform_interval(t, -b, -a), -a);
+ CHECK_LE(-b, sample_uniform_interval(t, -b, -a));
+
+ CHECK_LE(sample_uniform_interval(1 - t, -b, -a), -a);
+ CHECK_LE(-b, sample_uniform_interval(1 - t, -b, -a));
+ }
+
+ tt_assert(ok);
+
+ done:
+ ;
+}
+
+/********************** Stochastic tests ****************************/
+
+/*
+ * Psi test, sometimes also called G-test. The psi test statistic,
+ * suitably scaled, has chi^2 distribution, but the psi test tends to
+ * have better statistical power in practice to detect deviations than
+ * the chi^2 test does. (The chi^2 test statistic is the first term of
+ * the Taylor expansion of the psi test statistic.) The psi test is
+ * generic, for any CDF; particular distributions might have higher-
+ * power tests to distinguish them from predictable deviations or bugs.
+ *
+ * We choose the psi critical value so that a single psi test has
+ * probability below alpha = 1% of spuriously failing even if all the
+ * code is correct. But the false positive rate for a suite of n tests
+ * is higher: 1 - Binom(0; n, alpha) = 1 - (1 - alpha)^n. For n = 10,
+ * this is about 10%, and for n = 100 it is well over 50%.
+ *
+ * Given that these tests will run with every CI job, we want to drive down the
+ * false positive rate. We can drive it down by running each test four times,
+ * and accepting it if it passes at least once; in that case, it is as if we
+ * used Binom(4; 2, alpha) = alpha^4 as the false positive rate for each test,
+ * and for n = 10 tests, it would be 9.99999959506e-08. If each CI build has 14
+ * jobs, then the chance of a CI build failing is 1.39999903326e-06, which
+ * means that a CI build will break with probability 50% after about 495106
+ * builds.
+ *
+ * The critical value for a chi^2 distribution with 100 degrees of
+ * freedom and false positive rate alpha = 1% was taken from:
+ *
+ * NIST/SEMATECH e-Handbook of Statistical Methods, Section
+ * 1.3.6.7.4 `Critical Values of the Chi-Square Distribution',
+ * <http://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm>,
+ * retrieved 2018-10-28.
+ */
+
+static const size_t NSAMPLES = 100000;
+/* Number of chances we give to the test to succeed. */
+static const unsigned NTRIALS = 4;
+/* Number of times we want the test to pass per NTRIALS. */
+static const unsigned NPASSES_MIN = 1;
+
+#define PSI_DF 100 /* degrees of freedom */
+static const double PSI_CRITICAL = 135.807; /* critical value, alpha = .01 */
+
+/**
+ * Perform a psi test on an array of sample counts, C, adding up to N
+ * samples, and an array of log expected probabilities, logP,
+ * representing the null hypothesis for the distribution of samples
+ * counted. Return false if the psi test rejects the null hypothesis,
+ * true if otherwise.
+ */
+static bool
+psi_test(const size_t C[PSI_DF], const double logP[PSI_DF], size_t N)
+{
+ double psi = 0;
+ double c = 0; /* Kahan compensation */
+ double t, u;
+ size_t i;
+
+ for (i = 0; i < PSI_DF; i++) {
+ /*
+ * c*log(c/(n*p)) = (1/n) * f*log(f/p) where f = c/n is
+ * the frequency, and f*log(f/p) ---> 0 as f ---> 0, so
+ * this is a reasonable choice. Further, any mass that
+ * _fails_ to turn up in this bin will inflate another
+ * bin instead, so we don't really lose anything by
+ * ignoring empty bins even if they have high
+ * probability.
+ */
+ if (C[i] == 0)
+ continue;
+ t = C[i]*(log((double)C[i]/N) - logP[i]) - c;
+ u = psi + t;
+ c = (u - psi) - t;
+ psi = u;
+ }
+ psi *= 2;
+
+ return psi <= PSI_CRITICAL;
+}
+
+static bool
+test_stochastic_geometric_impl(double p)
+{
+ const struct geometric geometric = {
+ .base = GEOMETRIC(geometric),
+ .p = p,
+ };
+ double logP[PSI_DF] = {0};
+ unsigned ntry = NTRIALS, npass = 0;
+ unsigned i;
+ size_t j;
+
+ /* Compute logP[i] = Geom(i + 1; p). */
+ for (i = 0; i < PSI_DF - 1; i++)
+ logP[i] = logpmf_geometric(i + 1, p);
+
+ /* Compute logP[n-1] = log (1 - (P[0] + P[1] + ... + P[n-2])). */
+ logP[PSI_DF - 1] = log1mexp(logsumexp(logP, PSI_DF - 1));
+
+ while (ntry --> 0) {
+ size_t C[PSI_DF] = {0};
+
+ for (j = 0; j < NSAMPLES; j++) {
+ double n_tmp = dist_sample(&geometric.base);
+
+ /* Must be an integer. (XXX -Wfloat-equal) */
+ tor_assert(ceil(n_tmp) <= n_tmp && ceil(n_tmp) >= n_tmp);
+
+ /* Must be a positive integer. */
+ tor_assert(n_tmp >= 1);
+
+ /* Probability of getting a value in the billions is negligible. */
+ tor_assert(n_tmp <= (double)UINT_MAX);
+
+ unsigned n = (unsigned) n_tmp;
+
+ if (n > PSI_DF)
+ n = PSI_DF;
+ C[n - 1]++;
+ }
+
+ if (psi_test(C, logP, NSAMPLES)) {
+ if (++npass >= NPASSES_MIN)
+ break;
+ }
+ }
+
+ if (npass >= NPASSES_MIN) {
+ /* printf("pass %s sampler\n", "geometric"); */
+ return true;
+ } else {
+ printf("fail %s sampler\n", "geometric");
+ return false;
+ }
+}
+
+/**
+ * Divide the support of <b>dist</b> into histogram bins in <b>logP</b>. Start
+ * at the 1st percentile and ending at the 99th percentile. Pick the bin
+ * boundaries using linear interpolation so that they are uniformly spaced.
+ *
+ * In each bin logP[i] we insert the expected log-probability that a sampled
+ * value will fall into that bin. We will use this as the null hypothesis of
+ * the psi test.
+ *
+ * Set logP[i] = log(CDF(x_i) - CDF(x_{i-1})), where x_-1 = -inf, x_n =
+ * +inf, and x_i = i*(hi - lo)/(n - 2).
+ */
+static void
+bin_cdfs(const struct dist *dist, double lo, double hi, double *logP, size_t n)
+{
+#define CDF(x) dist_cdf(dist, x)
+#define SF(x) dist_sf(dist, x)
+ const double w = (hi - lo)/(n - 2);
+ double halfway = dist_icdf(dist, 0.5);
+ double x_0, x_1;
+ size_t i;
+ size_t n2 = ceil_to_size_t((halfway - lo)/w);
+
+ tor_assert(lo <= halfway);
+ tor_assert(halfway <= hi);
+ tor_assert(n2 <= n);
+
+ x_1 = lo;
+ logP[0] = log(CDF(x_1) - 0); /* 0 = CDF(-inf) */
+ for (i = 1; i < n2; i++) {
+ x_0 = x_1;
+ /* do the linear interpolation */
+ x_1 = (i <= n/2 ? lo + i*w : hi - (n - 2 - i)*w);
+ /* set the expected log-probability */
+ logP[i] = log(CDF(x_1) - CDF(x_0));
+ }
+ x_0 = hi;
+ logP[n - 1] = log(SF(x_0) - 0); /* 0 = SF(+inf) = 1 - CDF(+inf) */
+
+ /* In this loop we are filling out the high part of the array. We are using
+ * SF because in these cases the CDF is near 1 where precision is lower. So
+ * instead we are using SF near 0 where the precision is higher. We have
+ * SF(t) = 1 - CDF(t). */
+ for (i = 1; i < n - n2; i++) {
+ x_1 = x_0;
+ /* do the linear interpolation */
+ x_0 = (i <= n/2 ? hi - i*w : lo + (n - 2 - i)*w);
+ /* set the expected log-probability */
+ logP[n - i - 1] = log(SF(x_0) - SF(x_1));
+ }
+#undef SF
+#undef CDF
+}
+
+/**
+ * Draw NSAMPLES samples from dist, counting the number of samples x in
+ * the ith bin C[i] if x_{i-1} <= x < x_i, where x_-1 = -inf, x_n =
+ * +inf, and x_i = i*(hi - lo)/(n - 2).
+ */
+static void
+bin_samples(const struct dist *dist, double lo, double hi, size_t *C, size_t n)
+{
+ const double w = (hi - lo)/(n - 2);
+ size_t i;
+
+ for (i = 0; i < NSAMPLES; i++) {
+ double x = dist_sample(dist);
+ size_t bin;
+
+ if (x < lo)
+ bin = 0;
+ else if (x < hi)
+ bin = 1 + floor_to_size_t((x - lo)/w);
+ else
+ bin = n - 1;
+ tor_assert(bin < n);
+ C[bin]++;
+ }
+}
+
+/**
+ * Carry out a Psi test on <b>dist</b>.
+ *
+ * Sample NSAMPLES from dist, putting them in bins from -inf to lo to
+ * hi to +inf, and apply up to two psi tests. True if at least one psi
+ * test passes; false if not. False positive rate should be bounded by
+ * 0.01^2 = 0.0001.
+ */
+static bool
+test_psi_dist_sample(const struct dist *dist)
+{
+ double logP[PSI_DF] = {0};
+ unsigned ntry = NTRIALS, npass = 0;
+ double lo = dist_icdf(dist, 1/(double)(PSI_DF + 2));
+ double hi = dist_isf(dist, 1/(double)(PSI_DF + 2));
+
+ /* Create the null hypothesis in logP */
+ bin_cdfs(dist, lo, hi, logP, PSI_DF);
+
+ /* Now run the test */
+ while (ntry --> 0) {
+ size_t C[PSI_DF] = {0};
+ bin_samples(dist, lo, hi, C, PSI_DF);
+ if (psi_test(C, logP, NSAMPLES)) {
+ if (++npass >= NPASSES_MIN)
+ break;
+ }
+ }
+
+ /* Did we fail or succeed? */
+ if (npass >= NPASSES_MIN) {
+ /* printf("pass %s sampler\n", dist_name(dist));*/
+ return true;
+ } else {
+ printf("fail %s sampler\n", dist_name(dist));
+ return false;
+ }
+}
+
+static void
+write_stochastic_warning(void)
+{
+ if (tinytest_cur_test_has_failed()) {
+ printf("\n"
+ "NOTE: This is a stochastic test, and we expect it to fail from\n"
+ "time to time, with some low probability. If you see it fail more\n"
+ "than one trial in 100, though, please tell us.\n\n");
+ }
+}
+
+static void
+test_stochastic_uniform(void *arg)
+{
+ (void) arg;
+
+ const struct uniform uniform01 = {
+ .base = UNIFORM(uniform01),
+ .a = 0,
+ .b = 1,
+ };
+ const struct uniform uniform_pos = {
+ .base = UNIFORM(uniform_pos),
+ .a = 1.23,
+ .b = 4.56,
+ };
+ const struct uniform uniform_neg = {
+ .base = UNIFORM(uniform_neg),
+ .a = -10,
+ .b = -1,
+ };
+ const struct uniform uniform_cross = {
+ .base = UNIFORM(uniform_cross),
+ .a = -1.23,
+ .b = 4.56,
+ };
+ const struct uniform uniform_subnormal = {
+ .base = UNIFORM(uniform_subnormal),
+ .a = 4e-324,
+ .b = 4e-310,
+ };
+ const struct uniform uniform_subnormal_cross = {
+ .base = UNIFORM(uniform_subnormal_cross),
+ .a = -4e-324,
+ .b = 4e-310,
+ };
+ bool ok = true, tests_failed = true;
+
+ testing_enable_reproducible_rng();
+
+ ok &= test_psi_dist_sample(&uniform01.base);
+ ok &= test_psi_dist_sample(&uniform_pos.base);
+ ok &= test_psi_dist_sample(&uniform_neg.base);
+ ok &= test_psi_dist_sample(&uniform_cross.base);
+ ok &= test_psi_dist_sample(&uniform_subnormal.base);
+ ok &= test_psi_dist_sample(&uniform_subnormal_cross.base);
+
+ tt_assert(ok);
+
+ tests_failed = false;
+
+ done:
+ if (tests_failed) {
+ write_stochastic_warning();
+ }
+ testing_disable_reproducible_rng();
+}
+
+static bool
+test_stochastic_logistic_impl(double mu, double sigma)
+{
+ const struct logistic dist = {
+ .base = LOGISTIC(dist),
+ .mu = mu,
+ .sigma = sigma,
+ };
+
+ /* XXX Consider some fancier logistic test. */
+ return test_psi_dist_sample(&dist.base);
+}
+
+static bool
+test_stochastic_log_logistic_impl(double alpha, double beta)
+{
+ const struct log_logistic dist = {
+ .base = LOG_LOGISTIC(dist),
+ .alpha = alpha,
+ .beta = beta,
+ };
+
+ /* XXX Consider some fancier log logistic test. */
+ return test_psi_dist_sample(&dist.base);
+}
+
+static bool
+test_stochastic_weibull_impl(double lambda, double k)
+{
+ const struct weibull dist = {
+ .base = WEIBULL(dist),
+ .lambda = lambda,
+ .k = k,
+ };
+
+/*
+ * XXX Consider applying a Tiku-Singh test:
+ *
+ * M.L. Tiku and M. Singh, `Testing the two-parameter
+ * Weibull distribution', Communications in Statistics --
+ * Theory and Methods A10(9), 1981, 907--918.
+ *https://www.tandfonline.com/doi/pdf/10.1080/03610928108828082?needAccess=true
+ */
+ return test_psi_dist_sample(&dist.base);
+}
+
+static bool
+test_stochastic_genpareto_impl(double mu, double sigma, double xi)
+{
+ const struct genpareto dist = {
+ .base = GENPARETO(dist),
+ .mu = mu,
+ .sigma = sigma,
+ .xi = xi,
+ };
+
+ /* XXX Consider some fancier GPD test. */
+ return test_psi_dist_sample(&dist.base);
+}
+
+static void
+test_stochastic_genpareto(void *arg)
+{
+ bool ok = 0;
+ bool tests_failed = true;
+ (void) arg;
+
+ testing_enable_reproducible_rng();
+
+ ok = test_stochastic_genpareto_impl(0, 1, -0.25);
+ tt_assert(ok);
+ ok = test_stochastic_genpareto_impl(0, 1, -1e-30);
+ tt_assert(ok);
+ ok = test_stochastic_genpareto_impl(0, 1, 0);
+ tt_assert(ok);
+ ok = test_stochastic_genpareto_impl(0, 1, 1e-30);
+ tt_assert(ok);
+ ok = test_stochastic_genpareto_impl(0, 1, 0.25);
+ tt_assert(ok);
+ ok = test_stochastic_genpareto_impl(-1, 1, -0.25);
+ tt_assert(ok);
+ ok = test_stochastic_genpareto_impl(1, 2, 0.25);
+ tt_assert(ok);
+
+ tests_failed = false;
+
+ done:
+ if (tests_failed) {
+ write_stochastic_warning();
+ }
+ testing_disable_reproducible_rng();
+}
+
+static void
+test_stochastic_geometric(void *arg)
+{
+ bool ok = 0;
+ bool tests_failed = true;
+
+ (void) arg;
+
+ testing_enable_reproducible_rng();
+
+ ok = test_stochastic_geometric_impl(0.1);
+ tt_assert(ok);
+ ok = test_stochastic_geometric_impl(0.5);
+ tt_assert(ok);
+ ok = test_stochastic_geometric_impl(0.9);
+ tt_assert(ok);
+ ok = test_stochastic_geometric_impl(1);
+ tt_assert(ok);
+
+ tests_failed = false;
+
+ done:
+ if (tests_failed) {
+ write_stochastic_warning();
+ }
+ testing_disable_reproducible_rng();
+}
+
+static void
+test_stochastic_logistic(void *arg)
+{
+ bool ok = 0;
+ bool tests_failed = true;
+ (void) arg;
+
+ testing_enable_reproducible_rng();
+
+ ok = test_stochastic_logistic_impl(0, 1);
+ tt_assert(ok);
+ ok = test_stochastic_logistic_impl(0, 1e-16);
+ tt_assert(ok);
+ ok = test_stochastic_logistic_impl(1, 10);
+ tt_assert(ok);
+ ok = test_stochastic_logistic_impl(-10, 100);
+ tt_assert(ok);
+
+ tests_failed = false;
+
+ done:
+ if (tests_failed) {
+ write_stochastic_warning();
+ }
+ testing_disable_reproducible_rng();
+}
+
+static void
+test_stochastic_log_logistic(void *arg)
+{
+ bool ok = 0;
+ (void) arg;
+
+ testing_enable_reproducible_rng();
+
+ ok = test_stochastic_log_logistic_impl(1, 1);
+ tt_assert(ok);
+ ok = test_stochastic_log_logistic_impl(1, 10);
+ tt_assert(ok);
+ ok = test_stochastic_log_logistic_impl(M_E, 1e-1);
+ tt_assert(ok);
+ ok = test_stochastic_log_logistic_impl(exp(-10), 1e-2);
+ tt_assert(ok);
+
+ done:
+ write_stochastic_warning();
+ testing_disable_reproducible_rng();
+}
+
+static void
+test_stochastic_weibull(void *arg)
+{
+ bool ok = 0;
+ (void) arg;
+
+ testing_enable_reproducible_rng();
+
+ ok = test_stochastic_weibull_impl(1, 0.5);
+ tt_assert(ok);
+ ok = test_stochastic_weibull_impl(1, 1);
+ tt_assert(ok);
+ ok = test_stochastic_weibull_impl(1, 1.5);
+ tt_assert(ok);
+ ok = test_stochastic_weibull_impl(1, 2);
+ tt_assert(ok);
+ ok = test_stochastic_weibull_impl(10, 1);
+ tt_assert(ok);
+
+ done:
+ write_stochastic_warning();
+ testing_disable_reproducible_rng();
+ UNMOCK(crypto_rand);
+}
+
+struct testcase_t prob_distr_tests[] = {
+ { "logit_logistics", test_logit_logistic, TT_FORK, NULL, NULL },
+ { "log_logistic", test_log_logistic, TT_FORK, NULL, NULL },
+ { "weibull", test_weibull, TT_FORK, NULL, NULL },
+ { "genpareto", test_genpareto, TT_FORK, NULL, NULL },
+ { "uniform_interval", test_uniform_interval, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
+struct testcase_t slow_stochastic_prob_distr_tests[] = {
+ { "stochastic_genpareto", test_stochastic_genpareto, TT_FORK, NULL, NULL },
+ { "stochastic_geometric", test_stochastic_geometric, TT_FORK, NULL, NULL },
+ { "stochastic_uniform", test_stochastic_uniform, TT_FORK, NULL, NULL },
+ { "stochastic_logistic", test_stochastic_logistic, TT_FORK, NULL, NULL },
+ { "stochastic_log_logistic", test_stochastic_log_logistic, TT_FORK, NULL,
+ NULL },
+ { "stochastic_weibull", test_stochastic_weibull, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_process.c b/src/test/test_process.c
new file mode 100644
index 0000000000..7836312761
--- /dev/null
+++ b/src/test/test_process.c
@@ -0,0 +1,669 @@
+/* Copyright (c) 2018-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_process.c
+ * \brief Test cases for the Process API.
+ */
+
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "test/test.h"
+#include "lib/process/env.h"
+
+#define PROCESS_PRIVATE
+#include "lib/process/process.h"
+#define PROCESS_UNIX_PRIVATE
+#include "lib/process/process_unix.h"
+#define PROCESS_WIN32_PRIVATE
+#include "lib/process/process_win32.h"
+
+static const char *stdout_read_buffer;
+static const char *stderr_read_buffer;
+
+struct process_data_t {
+ smartlist_t *stdout_data;
+ smartlist_t *stderr_data;
+ smartlist_t *stdin_data;
+ process_exit_code_t exit_code;
+};
+
+typedef struct process_data_t process_data_t;
+
+static process_data_t *
+process_data_new(void)
+{
+ process_data_t *process_data = tor_malloc_zero(sizeof(process_data_t));
+ process_data->stdout_data = smartlist_new();
+ process_data->stderr_data = smartlist_new();
+ process_data->stdin_data = smartlist_new();
+ return process_data;
+}
+
+static void
+process_data_free(process_data_t *process_data)
+{
+ if (process_data == NULL)
+ return;
+
+ SMARTLIST_FOREACH(process_data->stdout_data, char *, x, tor_free(x));
+ SMARTLIST_FOREACH(process_data->stderr_data, char *, x, tor_free(x));
+ SMARTLIST_FOREACH(process_data->stdin_data, char *, x, tor_free(x));
+
+ smartlist_free(process_data->stdout_data);
+ smartlist_free(process_data->stderr_data);
+ smartlist_free(process_data->stdin_data);
+ tor_free(process_data);
+}
+
+static int
+process_mocked_read_stdout(process_t *process, buf_t *buffer)
+{
+ (void)process;
+
+ if (stdout_read_buffer != NULL) {
+ buf_add_string(buffer, stdout_read_buffer);
+ stdout_read_buffer = NULL;
+ }
+
+ return (int)buf_datalen(buffer);
+}
+
+static int
+process_mocked_read_stderr(process_t *process, buf_t *buffer)
+{
+ (void)process;
+
+ if (stderr_read_buffer != NULL) {
+ buf_add_string(buffer, stderr_read_buffer);
+ stderr_read_buffer = NULL;
+ }
+
+ return (int)buf_datalen(buffer);
+}
+
+static void
+process_mocked_write_stdin(process_t *process, buf_t *buffer)
+{
+ const size_t size = buf_datalen(buffer);
+
+ if (size == 0)
+ return;
+
+ char *data = tor_malloc_zero(size + 1);
+ process_data_t *process_data = process_get_data(process);
+
+ buf_get_bytes(buffer, data, size);
+ smartlist_add(process_data->stdin_data, data);
+}
+
+static void
+process_stdout_callback(process_t *process, const char *data, size_t size)
+{
+ tt_ptr_op(process, OP_NE, NULL);
+ tt_ptr_op(data, OP_NE, NULL);
+ tt_int_op(strlen(data), OP_EQ, size);
+
+ process_data_t *process_data = process_get_data(process);
+ smartlist_add(process_data->stdout_data, tor_strdup(data));
+
+ done:
+ return;
+}
+
+static void
+process_stderr_callback(process_t *process, const char *data, size_t size)
+{
+ tt_ptr_op(process, OP_NE, NULL);
+ tt_ptr_op(data, OP_NE, NULL);
+ tt_int_op(strlen(data), OP_EQ, size);
+
+ process_data_t *process_data = process_get_data(process);
+ smartlist_add(process_data->stderr_data, tor_strdup(data));
+
+ done:
+ return;
+}
+
+static bool
+process_exit_callback(process_t *process, process_exit_code_t exit_code)
+{
+ tt_ptr_op(process, OP_NE, NULL);
+
+ process_data_t *process_data = process_get_data(process);
+ process_data->exit_code = exit_code;
+
+ done:
+ /* Do not free up our process_t. */
+ return false;
+}
+
+static void
+test_default_values(void *arg)
+{
+ (void)arg;
+ process_t *process = process_new("/path/to/nothing");
+
+ /* We are not running by default. */
+ tt_int_op(PROCESS_STATUS_NOT_RUNNING, OP_EQ, process_get_status(process));
+
+ /* We use the line protocol by default. */
+ tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
+
+ /* We don't set any custom data by default. */
+ tt_ptr_op(NULL, OP_EQ, process_get_data(process));
+
+ /* Our command was given to the process_t's constructor in process_new(). */
+ tt_str_op("/path/to/nothing", OP_EQ, process_get_command(process));
+
+ /* Make sure we are listed in the list of proccesses. */
+ tt_assert(smartlist_contains(process_get_all_processes(),
+ process));
+
+ /* Default PID is 0. */
+ tt_u64_op(0, OP_EQ, process_get_pid(process));
+
+ /* Our arguments should be empty. */
+ tt_int_op(0, OP_EQ,
+ smartlist_len(process_get_arguments(process)));
+
+ done:
+ process_free(process);
+}
+
+static void
+test_environment(void *arg)
+{
+ (void)arg;
+
+ process_t *process = process_new("");
+ process_environment_t *env = NULL;
+
+ process_set_environment(process, "E", "F");
+ process_set_environment(process, "C", "D");
+ process_set_environment(process, "A", "B");
+
+ env = process_get_environment(process);
+ tt_mem_op(env->windows_environment_block, OP_EQ,
+ "A=B\0C=D\0E=F\0", 12);
+ tt_str_op(env->unixoid_environment_block[0], OP_EQ,
+ "A=B");
+ tt_str_op(env->unixoid_environment_block[1], OP_EQ,
+ "C=D");
+ tt_str_op(env->unixoid_environment_block[2], OP_EQ,
+ "E=F");
+ tt_ptr_op(env->unixoid_environment_block[3], OP_EQ,
+ NULL);
+ process_environment_free(env);
+
+ /* Reset our environment. */
+ smartlist_t *new_env = smartlist_new();
+ smartlist_add(new_env, (char *)"FOO=bar");
+ smartlist_add(new_env, (char *)"HELLO=world");
+
+ process_reset_environment(process, new_env);
+ smartlist_free(new_env);
+
+ env = process_get_environment(process);
+ tt_mem_op(env->windows_environment_block, OP_EQ,
+ "FOO=bar\0HELLO=world\0", 20);
+ tt_str_op(env->unixoid_environment_block[0], OP_EQ,
+ "FOO=bar");
+ tt_str_op(env->unixoid_environment_block[1], OP_EQ,
+ "HELLO=world");
+ tt_ptr_op(env->unixoid_environment_block[2], OP_EQ,
+ NULL);
+
+ done:
+ process_environment_free(env);
+ process_free(process);
+}
+
+static void
+test_stringified_types(void *arg)
+{
+ (void)arg;
+
+ /* process_protocol_t values. */
+ tt_str_op("Raw", OP_EQ, process_protocol_to_string(PROCESS_PROTOCOL_RAW));
+ tt_str_op("Line", OP_EQ, process_protocol_to_string(PROCESS_PROTOCOL_LINE));
+
+ /* process_status_t values. */
+ tt_str_op("not running", OP_EQ,
+ process_status_to_string(PROCESS_STATUS_NOT_RUNNING));
+ tt_str_op("running", OP_EQ,
+ process_status_to_string(PROCESS_STATUS_RUNNING));
+ tt_str_op("error", OP_EQ,
+ process_status_to_string(PROCESS_STATUS_ERROR));
+
+ done:
+ return;
+}
+
+static void
+test_line_protocol_simple(void *arg)
+{
+ (void)arg;
+
+ process_data_t *process_data = process_data_new();
+
+ process_t *process = process_new("");
+ process_set_data(process, process_data);
+
+ process_set_stdout_read_callback(process, process_stdout_callback);
+ process_set_stderr_read_callback(process, process_stderr_callback);
+
+ MOCK(process_read_stdout, process_mocked_read_stdout);
+ MOCK(process_read_stderr, process_mocked_read_stderr);
+
+ /* Make sure we are running with the line protocol. */
+ tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
+
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ stdout_read_buffer = "Hello stdout\n";
+ process_notify_event_stdout(process);
+ tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
+
+ stderr_read_buffer = "Hello stderr\r\n";
+ process_notify_event_stderr(process);
+ tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
+
+ /* Data should be ready. */
+ tt_int_op(1, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(1, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ /* Check if the data is correct. */
+ tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
+ "Hello stdout");
+ tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
+ "Hello stderr");
+
+ done:
+ process_data_free(process_data);
+ process_free(process);
+
+ UNMOCK(process_read_stdout);
+ UNMOCK(process_read_stderr);
+}
+
+static void
+test_line_protocol_multi(void *arg)
+{
+ (void)arg;
+
+ process_data_t *process_data = process_data_new();
+
+ process_t *process = process_new("");
+ process_set_data(process, process_data);
+ process_set_stdout_read_callback(process, process_stdout_callback);
+ process_set_stderr_read_callback(process, process_stderr_callback);
+
+ MOCK(process_read_stdout, process_mocked_read_stdout);
+ MOCK(process_read_stderr, process_mocked_read_stderr);
+
+ /* Make sure we are running with the line protocol. */
+ tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
+
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ stdout_read_buffer = "Hello stdout\r\nOnion Onion Onion\nA B C D\r\n\r\n";
+ process_notify_event_stdout(process);
+ tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
+
+ stderr_read_buffer = "Hello stderr\nFoo bar baz\nOnion Onion Onion\n";
+ process_notify_event_stderr(process);
+ tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
+
+ /* Data should be ready. */
+ tt_int_op(4, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(3, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ /* Check if the data is correct. */
+ tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
+ "Hello stdout");
+ tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
+ "Onion Onion Onion");
+ tt_str_op(smartlist_get(process_data->stdout_data, 2), OP_EQ,
+ "A B C D");
+ tt_str_op(smartlist_get(process_data->stdout_data, 3), OP_EQ,
+ "");
+
+ tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
+ "Hello stderr");
+ tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
+ "Foo bar baz");
+ tt_str_op(smartlist_get(process_data->stderr_data, 2), OP_EQ,
+ "Onion Onion Onion");
+
+ done:
+ process_data_free(process_data);
+ process_free(process);
+
+ UNMOCK(process_read_stdout);
+ UNMOCK(process_read_stderr);
+}
+
+static void
+test_line_protocol_partial(void *arg)
+{
+ (void)arg;
+
+ process_data_t *process_data = process_data_new();
+
+ process_t *process = process_new("");
+ process_set_data(process, process_data);
+ process_set_stdout_read_callback(process, process_stdout_callback);
+ process_set_stderr_read_callback(process, process_stderr_callback);
+
+ MOCK(process_read_stdout, process_mocked_read_stdout);
+ MOCK(process_read_stderr, process_mocked_read_stderr);
+
+ /* Make sure we are running with the line protocol. */
+ tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
+
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ stdout_read_buffer = "Hello stdout this is a partial line ...";
+ process_notify_event_stdout(process);
+ tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
+
+ stderr_read_buffer = "Hello stderr this is a partial line ...";
+ process_notify_event_stderr(process);
+ tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
+
+ /* Data should NOT be ready. */
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ stdout_read_buffer = " the end\nAnother partial string goes here ...";
+ process_notify_event_stdout(process);
+ tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
+
+ stderr_read_buffer = " the end\nAnother partial string goes here ...";
+ process_notify_event_stderr(process);
+ tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
+
+ /* Some data should be ready. */
+ tt_int_op(1, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(1, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ stdout_read_buffer = " the end\nFoo bar baz\n";
+ process_notify_event_stdout(process);
+ tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
+
+ stderr_read_buffer = " the end\nFoo bar baz\n";
+ process_notify_event_stderr(process);
+ tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
+
+ /* Some data should be ready. */
+ tt_int_op(3, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(3, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ /* Check if the data is correct. */
+ tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
+ "Hello stdout this is a partial line ... the end");
+ tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
+ "Another partial string goes here ... the end");
+ tt_str_op(smartlist_get(process_data->stdout_data, 2), OP_EQ,
+ "Foo bar baz");
+
+ tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
+ "Hello stderr this is a partial line ... the end");
+ tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
+ "Another partial string goes here ... the end");
+ tt_str_op(smartlist_get(process_data->stderr_data, 2), OP_EQ,
+ "Foo bar baz");
+
+ done:
+ process_data_free(process_data);
+ process_free(process);
+
+ UNMOCK(process_read_stdout);
+ UNMOCK(process_read_stderr);
+}
+
+static void
+test_raw_protocol_simple(void *arg)
+{
+ (void)arg;
+
+ process_data_t *process_data = process_data_new();
+
+ process_t *process = process_new("");
+ process_set_data(process, process_data);
+ process_set_protocol(process, PROCESS_PROTOCOL_RAW);
+
+ process_set_stdout_read_callback(process, process_stdout_callback);
+ process_set_stderr_read_callback(process, process_stderr_callback);
+
+ MOCK(process_read_stdout, process_mocked_read_stdout);
+ MOCK(process_read_stderr, process_mocked_read_stderr);
+
+ /* Make sure we are running with the raw protocol. */
+ tt_int_op(PROCESS_PROTOCOL_RAW, OP_EQ, process_get_protocol(process));
+
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ stdout_read_buffer = "Hello stdout\n";
+ process_notify_event_stdout(process);
+ tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
+
+ stderr_read_buffer = "Hello stderr\n";
+ process_notify_event_stderr(process);
+ tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
+
+ /* Data should be ready. */
+ tt_int_op(1, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(1, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ stdout_read_buffer = "Hello, again, stdout\nThis contains multiple lines";
+ process_notify_event_stdout(process);
+ tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
+
+ stderr_read_buffer = "Hello, again, stderr\nThis contains multiple lines";
+ process_notify_event_stderr(process);
+ tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
+
+ /* Data should be ready. */
+ tt_int_op(2, OP_EQ, smartlist_len(process_data->stdout_data));
+ tt_int_op(2, OP_EQ, smartlist_len(process_data->stderr_data));
+
+ /* Check if the data is correct. */
+ tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
+ "Hello stdout\n");
+ tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
+ "Hello, again, stdout\nThis contains multiple lines");
+
+ tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
+ "Hello stderr\n");
+ tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
+ "Hello, again, stderr\nThis contains multiple lines");
+
+ done:
+ process_data_free(process_data);
+ process_free(process);
+
+ UNMOCK(process_read_stdout);
+ UNMOCK(process_read_stderr);
+}
+
+static void
+test_write_simple(void *arg)
+{
+ (void)arg;
+
+ process_data_t *process_data = process_data_new();
+
+ process_t *process = process_new("");
+ process_set_data(process, process_data);
+
+ MOCK(process_write_stdin, process_mocked_write_stdin);
+
+ process_write(process, (uint8_t *)"Hello world\n", 12);
+ process_notify_event_stdin(process);
+ tt_int_op(1, OP_EQ, smartlist_len(process_data->stdin_data));
+
+ process_printf(process, "Hello %s !\n", "moon");
+ process_notify_event_stdin(process);
+ tt_int_op(2, OP_EQ, smartlist_len(process_data->stdin_data));
+
+ done:
+ process_data_free(process_data);
+ process_free(process);
+
+ UNMOCK(process_write_stdin);
+}
+
+static void
+test_exit_simple(void *arg)
+{
+ (void)arg;
+
+ process_data_t *process_data = process_data_new();
+
+ process_t *process = process_new("");
+ process_set_data(process, process_data);
+ process_set_exit_callback(process, process_exit_callback);
+
+ /* Our default is 0. */
+ tt_u64_op(0, OP_EQ, process_data->exit_code);
+
+ /* Fake that we are a running process. */
+ process_set_status(process, PROCESS_STATUS_RUNNING);
+ tt_int_op(process_get_status(process), OP_EQ, PROCESS_STATUS_RUNNING);
+
+ /* Fake an exit. */
+ process_notify_event_exit(process, 1337);
+
+ /* Check if our state changed and if our callback fired. */
+ tt_int_op(process_get_status(process), OP_EQ, PROCESS_STATUS_NOT_RUNNING);
+ tt_u64_op(1337, OP_EQ, process_data->exit_code);
+
+ done:
+ process_set_data(process, process_data);
+ process_data_free(process_data);
+ process_free(process);
+}
+
+static void
+test_argv_simple(void *arg)
+{
+ (void)arg;
+
+ process_t *process = process_new("/bin/cat");
+ char **argv = NULL;
+
+ /* Setup some arguments. */
+ process_append_argument(process, "foo");
+ process_append_argument(process, "bar");
+ process_append_argument(process, "baz");
+
+ /* Check the number of elements. */
+ tt_int_op(3, OP_EQ,
+ smartlist_len(process_get_arguments(process)));
+
+ /* Let's try to convert it into a Unix style char **argv. */
+ argv = process_get_argv(process);
+
+ /* Check our values. */
+ tt_str_op(argv[0], OP_EQ, "/bin/cat");
+ tt_str_op(argv[1], OP_EQ, "foo");
+ tt_str_op(argv[2], OP_EQ, "bar");
+ tt_str_op(argv[3], OP_EQ, "baz");
+ tt_ptr_op(argv[4], OP_EQ, NULL);
+
+ done:
+ tor_free(argv);
+ process_free(process);
+}
+
+static void
+test_unix(void *arg)
+{
+ (void)arg;
+#ifndef _WIN32
+ process_t *process = process_new("");
+
+ /* On Unix all processes should have a Unix process handle. */
+ tt_ptr_op(NULL, OP_NE, process_get_unix_process(process));
+
+ done:
+ process_free(process);
+#endif /* !defined(_WIN32) */
+}
+
+static void
+test_win32(void *arg)
+{
+ (void)arg;
+#ifdef _WIN32
+ process_t *process = process_new("");
+ char *joined_argv = NULL;
+
+ /* On Win32 all processes should have a Win32 process handle. */
+ tt_ptr_op(NULL, OP_NE, process_get_win32_process(process));
+
+ /* Based on some test cases from "Parsing C++ Command-Line Arguments" in
+ * MSDN but we don't exercise all quoting rules because tor_join_win_cmdline
+ * will try to only generate simple cases for the child process to parse;
+ * i.e. we never embed quoted strings in arguments. */
+
+ const char *argvs[][4] = {
+ {"a", "bb", "CCC", NULL}, // Normal
+ {NULL, NULL, NULL, NULL}, // Empty argument list
+ {"", NULL, NULL, NULL}, // Empty argument
+ {"\"a", "b\"b", "CCC\"", NULL}, // Quotes
+ {"a\tbc", "dd dd", "E", NULL}, // Whitespace
+ {"a\\\\\\b", "de fg", "H", NULL}, // Backslashes
+ {"a\\\"b", "\\c", "D\\", NULL}, // Backslashes before quote
+ {"a\\\\b c", "d", "E", NULL}, // Backslashes not before quote
+ { NULL } // Terminator
+ };
+
+ const char *cmdlines[] = {
+ "a bb CCC",
+ "",
+ "\"\"",
+ "\\\"a b\\\"b CCC\\\"",
+ "\"a\tbc\" \"dd dd\" E",
+ "a\\\\\\b \"de fg\" H",
+ "a\\\\\\\"b \\c D\\",
+ "\"a\\\\b c\" d E",
+ NULL // Terminator
+ };
+
+ int i;
+
+ for (i=0; cmdlines[i]!=NULL; i++) {
+ log_info(LD_GENERAL, "Joining argvs[%d], expecting <%s>", i, cmdlines[i]);
+ joined_argv = tor_join_win_cmdline(argvs[i]);
+ tt_str_op(cmdlines[i],OP_EQ, joined_argv);
+ tor_free(joined_argv);
+ }
+
+ done:
+ tor_free(joined_argv);
+ process_free(process);
+#endif /* defined(_WIN32) */
+}
+
+struct testcase_t process_tests[] = {
+ { "default_values", test_default_values, TT_FORK, NULL, NULL },
+ { "environment", test_environment, TT_FORK, NULL, NULL },
+ { "stringified_types", test_stringified_types, TT_FORK, NULL, NULL },
+ { "line_protocol_simple", test_line_protocol_simple, TT_FORK, NULL, NULL },
+ { "line_protocol_multi", test_line_protocol_multi, TT_FORK, NULL, NULL },
+ { "line_protocol_partial", test_line_protocol_partial, TT_FORK, NULL, NULL },
+ { "raw_protocol_simple", test_raw_protocol_simple, TT_FORK, NULL, NULL },
+ { "write_simple", test_write_simple, TT_FORK, NULL, NULL },
+ { "exit_simple", test_exit_simple, TT_FORK, NULL, NULL },
+ { "argv_simple", test_argv_simple, TT_FORK, NULL, NULL },
+ { "unix", test_unix, TT_FORK, NULL, NULL },
+ { "win32", test_win32, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_process_descs.c b/src/test/test_process_descs.c
new file mode 100644
index 0000000000..7dc9abde31
--- /dev/null
+++ b/src/test/test_process_descs.c
@@ -0,0 +1,67 @@
+/* Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#include "core/or/or.h"
+#include "feature/dirauth/process_descs.h"
+
+#include "test/test.h"
+
+static void
+test_process_descs_versions(void *arg)
+{
+ (void)arg;
+ struct {
+ const char *version;
+ bool should_reject;
+ } cases[] = {
+ // a very old version: reject.
+ { "Tor 0.1.2.3-alpha", true },
+ // a non-tor program: don't reject.
+ { "Wombat 0.1.2.3-alpha", false },
+ // a slightly old version: reject
+ { "Tor 0.2.9.4-alpha", true },
+ // a slightly old version: just new enough to support.
+ { "Tor 0.2.9.5-alpha", false },
+ // a newer 0.2.9 version: supported.
+ { "Tor 0.2.9.100", false },
+ // some unsupported versions: reject.
+ { "Tor 0.3.0.0-alpha-dev", true },
+ { "Tor 0.3.0.2-alpha", true },
+ { "Tor 0.3.0.5", true },
+ { "Tor 0.3.1.4", true },
+ { "Tor 0.3.2.4", true },
+ { "Tor 0.3.3.4", true },
+ { "Tor 0.3.4.1-alpha", true },
+ { "Tor 0.3.4.100", true },
+ { "Tor 0.3.5.1-alpha", true },
+ { "Tor 0.3.5.6-rc", true},
+ // new enough to be supported
+ { "Tor 0.3.5.7", false },
+ { "Tor 0.3.5.8", false },
+ { "Tor 0.4.0.1-alpha", false },
+ { "Tor 0.4.1.5", false },
+ // Very far in the future
+ { "Tor 100.100.1.5", false },
+ };
+ size_t n_cases = ARRAY_LENGTH(cases);
+
+ for (unsigned i = 0; i < n_cases; ++i) {
+ const char *msg = NULL;
+ bool rejected = dirserv_rejects_tor_version(cases[i].version, &msg);
+ tt_int_op(rejected, OP_EQ, cases[i].should_reject);
+ tt_int_op(msg == NULL, OP_EQ, rejected == false);
+ }
+
+ done:
+ ;
+}
+
+#define T(name,flags) \
+ { #name, test_process_descs_##name, (flags), NULL, NULL }
+
+struct testcase_t process_descs_tests[] = {
+ T(versions,0),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_process_slow.c b/src/test/test_process_slow.c
new file mode 100644
index 0000000000..f311e8b293
--- /dev/null
+++ b/src/test/test_process_slow.c
@@ -0,0 +1,365 @@
+/* Copyright (c) 2018-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_process_slow.c
+ * \brief Slow test cases for the Process API.
+ */
+
+#define MAINLOOP_PRIVATE
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "core/mainloop/mainloop.h"
+#include "lib/evloop/compat_libevent.h"
+#include "lib/process/process.h"
+#include "lib/process/waitpid.h"
+#include "test/test.h"
+
+#ifndef BUILDDIR
+#define BUILDDIR "."
+#endif
+
+#ifdef _WIN32
+#define TEST_PROCESS "test-process.exe"
+#else
+#define TEST_PROCESS BUILDDIR "/src/test/test-process"
+#endif /* defined(_WIN32) */
+
+/** Timer that ticks once a second and stop the event loop after 5 ticks. */
+static periodic_timer_t *main_loop_timeout_timer;
+
+/** How many times have our timer ticked? */
+static int timer_tick_count;
+
+struct process_data_t {
+ smartlist_t *stdout_data;
+ smartlist_t *stderr_data;
+ smartlist_t *stdin_data;
+ process_exit_code_t exit_code;
+ bool did_exit;
+};
+
+typedef struct process_data_t process_data_t;
+
+static process_data_t *
+process_data_new(void)
+{
+ process_data_t *process_data = tor_malloc_zero(sizeof(process_data_t));
+ process_data->stdout_data = smartlist_new();
+ process_data->stderr_data = smartlist_new();
+ process_data->stdin_data = smartlist_new();
+ return process_data;
+}
+
+static void
+process_data_free(process_data_t *process_data)
+{
+ if (process_data == NULL)
+ return;
+
+ SMARTLIST_FOREACH(process_data->stdout_data, char *, x, tor_free(x));
+ SMARTLIST_FOREACH(process_data->stderr_data, char *, x, tor_free(x));
+ SMARTLIST_FOREACH(process_data->stdin_data, char *, x, tor_free(x));
+
+ smartlist_free(process_data->stdout_data);
+ smartlist_free(process_data->stderr_data);
+ smartlist_free(process_data->stdin_data);
+ tor_free(process_data);
+}
+
+static void
+process_stdout_callback(process_t *process, const char *data, size_t size)
+{
+ tt_ptr_op(process, OP_NE, NULL);
+ tt_ptr_op(data, OP_NE, NULL);
+ tt_int_op(strlen(data), OP_EQ, size);
+
+ process_data_t *process_data = process_get_data(process);
+ smartlist_add(process_data->stdout_data, tor_strdup(data));
+
+ done:
+ return;
+}
+
+static void
+process_stderr_callback(process_t *process, const char *data, size_t size)
+{
+ tt_ptr_op(process, OP_NE, NULL);
+ tt_ptr_op(data, OP_NE, NULL);
+ tt_int_op(strlen(data), OP_EQ, size);
+
+ process_data_t *process_data = process_get_data(process);
+ smartlist_add(process_data->stderr_data, tor_strdup(data));
+
+ done:
+ return;
+}
+
+static bool
+process_exit_callback(process_t *process, process_exit_code_t exit_code)
+{
+ process_status_t status;
+
+ tt_ptr_op(process, OP_NE, NULL);
+
+ process_data_t *process_data = process_get_data(process);
+ process_data->exit_code = exit_code;
+ process_data->did_exit = true;
+
+ /* Check if our process is still running? */
+ status = process_get_status(process);
+ tt_int_op(status, OP_EQ, PROCESS_STATUS_NOT_RUNNING);
+
+ done:
+ /* Do not free up our process_t. */
+ return false;
+}
+
+#ifdef _WIN32
+static const char *
+get_win32_test_binary_path(void)
+{
+ static char buffer[MAX_PATH];
+
+ /* Get the absolute path of our binary: \path\to\test-slow.exe. */
+ GetModuleFileNameA(GetModuleHandle(0), buffer, sizeof(buffer));
+
+ /* Find our process name. */
+ char *offset = strstr(buffer, "test-slow.exe");
+ tt_ptr_op(offset, OP_NE, NULL);
+
+ /* Change test-slow.exe to test-process.exe. */
+ memcpy(offset, TEST_PROCESS, strlen(TEST_PROCESS));
+
+ return buffer;
+ done:
+ return NULL;
+}
+#endif /* defined(_WIN32) */
+
+static void
+main_loop_timeout_cb(periodic_timer_t *timer, void *data)
+{
+ /* Sanity check. */
+ tt_ptr_op(timer, OP_EQ, main_loop_timeout_timer);
+ tt_ptr_op(data, OP_NE, NULL);
+
+ /* Our process data. */
+ process_data_t *process_data = data;
+
+ /* Our process did exit. */
+ if (process_data->did_exit)
+ tor_shutdown_event_loop_and_exit(0);
+
+ /* Have we been called 10 times we exit the main loop. */
+ timer_tick_count++;
+
+ tt_int_op(timer_tick_count, OP_LT, 10);
+
+#ifndef _WIN32
+ /* Call waitpid callbacks. */
+ notify_pending_waitpid_callbacks();
+#endif
+
+ return;
+ done:
+ /* Exit with an error. */
+ tor_shutdown_event_loop_and_exit(-1);
+}
+
+static void
+run_main_loop(process_data_t *process_data)
+{
+ int ret;
+
+ /* Wake up after 1 seconds. */
+ static const struct timeval interval = {1, 0};
+
+ timer_tick_count = 0;
+ main_loop_timeout_timer = periodic_timer_new(tor_libevent_get_base(),
+ &interval,
+ main_loop_timeout_cb,
+ process_data);
+
+ /* Run our main loop. */
+ ret = run_main_loop_until_done();
+
+ /* Clean up our main loop timeout timer. */
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ periodic_timer_free(main_loop_timeout_timer);
+}
+
+static void
+test_callbacks(void *arg)
+{
+ (void)arg;
+ const char *filename = NULL;
+
+#ifdef _WIN32
+ filename = get_win32_test_binary_path();
+#else
+ filename = TEST_PROCESS;
+#endif
+
+ /* Process callback data. */
+ process_data_t *process_data = process_data_new();
+
+ /* Setup our process. */
+ process_t *process = process_new(filename);
+ process_set_data(process, process_data);
+ process_set_stdout_read_callback(process, process_stdout_callback);
+ process_set_stderr_read_callback(process, process_stderr_callback);
+ process_set_exit_callback(process, process_exit_callback);
+
+ /* Set environment variable. */
+ process_set_environment(process, "TOR_TEST_ENV", "Hello, from Tor!");
+
+ /* Add some arguments. */
+ process_append_argument(process, "This is the first one");
+ process_append_argument(process, "Second one");
+ process_append_argument(process, "Third: Foo bar baz");
+
+ /* Run our process. */
+ process_status_t status;
+
+ status = process_exec(process);
+ tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
+
+ /* Write some lines to stdin. */
+ process_printf(process, "Hi process!\r\n");
+ process_printf(process, "Can you read more than one line?\n");
+ process_printf(process, "Can you read partial ...");
+ process_printf(process, " lines?\r\n");
+
+ /* Start our main loop. */
+ run_main_loop(process_data);
+
+ /* We returned. Let's see what our event loop said. */
+ tt_int_op(smartlist_len(process_data->stdout_data), OP_EQ, 12);
+ tt_int_op(smartlist_len(process_data->stderr_data), OP_EQ, 3);
+ tt_assert(process_data->did_exit);
+ tt_u64_op(process_data->exit_code, OP_EQ, 0);
+
+ /* Check stdout output. */
+ char argv0_expected[256];
+ tor_snprintf(argv0_expected, sizeof(argv0_expected),
+ "argv[0] = '%s'", filename);
+
+ tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
+ argv0_expected);
+ tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
+ "argv[1] = 'This is the first one'");
+ tt_str_op(smartlist_get(process_data->stdout_data, 2), OP_EQ,
+ "argv[2] = 'Second one'");
+ tt_str_op(smartlist_get(process_data->stdout_data, 3), OP_EQ,
+ "argv[3] = 'Third: Foo bar baz'");
+ tt_str_op(smartlist_get(process_data->stdout_data, 4), OP_EQ,
+ "Environment variable TOR_TEST_ENV = 'Hello, from Tor!'");
+ tt_str_op(smartlist_get(process_data->stdout_data, 5), OP_EQ,
+ "Output on stdout");
+ tt_str_op(smartlist_get(process_data->stdout_data, 6), OP_EQ,
+ "This is a new line");
+ tt_str_op(smartlist_get(process_data->stdout_data, 7), OP_EQ,
+ "Partial line on stdout ...end of partial line on stdout");
+ tt_str_op(smartlist_get(process_data->stdout_data, 8), OP_EQ,
+ "Read line from stdin: 'Hi process!'");
+ tt_str_op(smartlist_get(process_data->stdout_data, 9), OP_EQ,
+ "Read line from stdin: 'Can you read more than one line?'");
+ tt_str_op(smartlist_get(process_data->stdout_data, 10), OP_EQ,
+ "Read line from stdin: 'Can you read partial ... lines?'");
+ tt_str_op(smartlist_get(process_data->stdout_data, 11), OP_EQ,
+ "We are done for here, thank you!");
+
+ /* Check stderr output. */
+ tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
+ "Output on stderr");
+ tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
+ "This is a new line");
+ tt_str_op(smartlist_get(process_data->stderr_data, 2), OP_EQ,
+ "Partial line on stderr ...end of partial line on stderr");
+
+ done:
+ process_data_free(process_data);
+ process_free(process);
+}
+
+static void
+test_callbacks_terminate(void *arg)
+{
+ (void)arg;
+ const char *filename = NULL;
+
+#ifdef _WIN32
+ filename = get_win32_test_binary_path();
+#else
+ filename = TEST_PROCESS;
+#endif
+
+ /* Process callback data. */
+ process_data_t *process_data = process_data_new();
+
+ /* Setup our process. */
+ process_t *process = process_new(filename);
+ process_set_data(process, process_data);
+ process_set_exit_callback(process, process_exit_callback);
+
+ /* Run our process. */
+ process_status_t status;
+
+ status = process_exec(process);
+ tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
+
+ /* Zap our process. */
+ bool success;
+
+ success = process_terminate(process);
+ tt_assert(success);
+
+ /* Start our main loop. */
+ run_main_loop(process_data);
+
+ /* Check if we did exit. */
+ tt_assert(process_data->did_exit);
+
+ done:
+ process_data_free(process_data);
+ process_free(process);
+}
+
+static void
+test_nonexistent_executable(void *arg)
+{
+ (void)arg;
+
+ /* Process callback data. */
+ process_data_t *process_data = process_data_new();
+
+ /* Setup our process. */
+ process_t *process = process_new("binary-does-not-exist");
+ process_set_data(process, process_data);
+ process_set_exit_callback(process, process_exit_callback);
+
+ /* Run our process. */
+ process_exec(process);
+
+ /* Start our main loop. */
+ run_main_loop(process_data);
+
+ /* Ensure that the exit callback was actually called even though the binary
+ * did not exist.
+ */
+ tt_assert(process_data->did_exit);
+
+ done:
+ process_data_free(process_data);
+ process_free(process);
+}
+
+struct testcase_t slow_process_tests[] = {
+ { "callbacks", test_callbacks, 0, NULL, NULL },
+ { "callbacks_terminate", test_callbacks_terminate, 0, NULL, NULL },
+ { "nonexistent_executable", test_nonexistent_executable, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_proto_http.c b/src/test/test_proto_http.c
index 08990c0b6a..f9339e8dd3 100644
--- a/src/test/test_proto_http.c
+++ b/src/test/test_proto_http.c
@@ -8,7 +8,7 @@
#include "core/or/or.h"
#include "test/test.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "core/proto/proto_http.h"
#include "test/log_test_helpers.h"
diff --git a/src/test/test_proto_misc.c b/src/test/test_proto_misc.c
index af9cf7eee2..18669a7772 100644
--- a/src/test/test_proto_misc.c
+++ b/src/test/test_proto_misc.c
@@ -8,7 +8,7 @@
#include "core/or/or.h"
#include "test/test.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "core/or/connection_or.h"
#include "feature/relay/ext_orport.h"
#include "core/proto/proto_cell.h"
diff --git a/src/test/test_protover.c b/src/test/test_protover.c
index 63c508bd13..0254501165 100644
--- a/src/test/test_protover.c
+++ b/src/test/test_protover.c
@@ -22,7 +22,7 @@ test_protover_parse(void *arg)
tt_skip();
done:
;
-#else
+#else /* !defined(HAVE_RUST) */
char *re_encoded = NULL;
const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900";
@@ -89,7 +89,7 @@ test_protover_parse(void *arg)
SMARTLIST_FOREACH(elts, proto_entry_t *, ent, proto_entry_free(ent));
smartlist_free(elts);
tor_free(re_encoded);
-#endif
+#endif /* defined(HAVE_RUST) */
}
static void
@@ -133,7 +133,7 @@ test_protover_parse_fail(void *arg)
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
tt_ptr_op(elts, OP_EQ, NULL);
-#endif
+#endif /* defined(HAVE_RUST) */
done:
;
}
@@ -335,7 +335,7 @@ test_protover_all_supported(void *arg)
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaa=1-65536", &msg));
tor_end_capture_bugs_();
-#endif
+#endif /* !defined(HAVE_RUST) */
done:
tor_end_capture_bugs_();
@@ -459,7 +459,7 @@ test_protover_supported_protocols(void *arg)
tt_assert(protocol_list_supports_protocol(supported_protocols,
PRT_LINKAUTH,
PROTOVER_LINKAUTH_V1));
-#endif
+#endif /* defined(HAVE_WORKING_TOR_TLS_GET_TLSSECRETS) */
/* Latest LinkAuth is not exposed in the headers. */
tt_assert(protocol_list_supports_protocol(supported_protocols,
PRT_LINKAUTH,
diff --git a/src/test/test_pt.c b/src/test/test_pt.c
index 1f9786648a..8f3ce03c42 100644
--- a/src/test/test_pt.c
+++ b/src/test/test_pt.c
@@ -7,22 +7,25 @@
#define PT_PRIVATE
#define UTIL_PRIVATE
#define STATEFILE_PRIVATE
-#define CONTROL_PRIVATE
-#define SUBPROCESS_PRIVATE
+#define CONTROL_EVENTS_PRIVATE
+#define PROCESS_PRIVATE
#include "core/or/or.h"
#include "app/config/config.h"
-#include "app/config/confparse.h"
+#include "lib/confmgt/confparse.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/client/transports.h"
#include "core/or/circuitbuild.h"
#include "app/config/statefile.h"
#include "test/test.h"
-#include "lib/process/subprocess.h"
#include "lib/encoding/confline.h"
#include "lib/net/resolve.h"
+#include "lib/process/process.h"
#include "app/config/or_state_st.h"
+#include "test/log_test_helpers.h"
+
static void
reset_mp(managed_proxy_t *mp)
{
@@ -288,41 +291,35 @@ test_pt_get_extrainfo_string(void *arg)
tor_free(s);
}
-#ifdef _WIN32
-#define STDIN_HANDLE HANDLE*
-#else
-#define STDIN_HANDLE int
-#endif
-
-static smartlist_t *
-tor_get_lines_from_handle_replacement(STDIN_HANDLE handle,
- enum stream_status *stream_status_out)
+static int
+process_read_stdout_replacement(process_t *process, buf_t *buffer)
{
+ (void)process;
static int times_called = 0;
- smartlist_t *retval_sl = smartlist_new();
-
- (void) handle;
- (void) stream_status_out;
/* Generate some dummy CMETHOD lines the first 5 times. The 6th
time, send 'CMETHODS DONE' to finish configuring the proxy. */
- if (times_called++ != 5) {
- smartlist_add_asprintf(retval_sl, "SMETHOD mock%d 127.0.0.1:555%d",
+ times_called++;
+
+ if (times_called <= 5) {
+ buf_add_printf(buffer, "SMETHOD mock%d 127.0.0.1:555%d\n",
times_called, times_called);
- } else {
- smartlist_add_strdup(retval_sl, "SMETHODS DONE");
+ } else if (times_called <= 6) {
+ buf_add_string(buffer, "SMETHODS DONE\n");
+ } else if (times_called <= 7) {
+ buf_add_string(buffer, "LOG SEVERITY=error MESSAGE=\"Oh noes, something "
+ "bad happened. What do we do!?\"\n");
+ buf_add_string(buffer, "LOG SEVERITY=warning MESSAGE=\"warning msg\"\n");
+ buf_add_string(buffer, "LOG SEVERITY=notice MESSAGE=\"notice msg\"\n");
+ buf_add_string(buffer, "LOG SEVERITY=info MESSAGE=\"info msg\"\n");
+ buf_add_string(buffer, "LOG SEVERITY=debug MESSAGE=\"debug msg\"\n");
+ } else if (times_called <= 8) {
+ buf_add_string(buffer, "STATUS TRANSPORT=a K_1=a K_2=b K_3=\"foo bar\"\n");
+ buf_add_string(buffer, "STATUS TRANSPORT=b K_1=a K_2=b K_3=\"foo bar\"\n");
+ buf_add_string(buffer, "STATUS TRANSPORT=c K_1=a K_2=b K_3=\"foo bar\"\n");
}
- return retval_sl;
-}
-
-/* NOP mock */
-static void
-tor_process_handle_destroy_replacement(process_handle_t *process_handle,
- int also_terminate_process)
-{
- (void) process_handle;
- (void) also_terminate_process;
+ return (int)buf_datalen(buffer);
}
static or_state_t *dummy_state = NULL;
@@ -355,12 +352,9 @@ test_pt_configure_proxy(void *arg)
managed_proxy_t *mp = NULL;
(void) arg;
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
- MOCK(tor_get_lines_from_handle,
- tor_get_lines_from_handle_replacement);
- MOCK(tor_process_handle_destroy,
- tor_process_handle_destroy_replacement);
+ MOCK(process_read_stdout, process_read_stdout_replacement);
MOCK(get_or_state,
get_or_state_replacement);
MOCK(queue_control_event_string,
@@ -372,24 +366,34 @@ test_pt_configure_proxy(void *arg)
mp->conf_state = PT_PROTO_ACCEPTING_METHODS;
mp->transports = smartlist_new();
mp->transports_to_launch = smartlist_new();
- mp->process_handle = tor_malloc_zero(sizeof(process_handle_t));
mp->argv = tor_malloc_zero(sizeof(char*)*2);
mp->argv[0] = tor_strdup("<testcase>");
mp->is_server = 1;
+ /* Configure the process. */
+ mp->process = process_new("");
+ process_set_stdout_read_callback(mp->process, managed_proxy_stdout_callback);
+ process_set_data(mp->process, mp);
+
/* Test the return value of configure_proxy() by calling it some
times while it is uninitialized and then finally finalizing its
configuration. */
for (i = 0 ; i < 5 ; i++) {
+ /* force a read from our mocked stdout reader. */
+ process_notify_event_stdout(mp->process);
+ /* try to configure our proxy. */
retval = configure_proxy(mp);
/* retval should be zero because proxy hasn't finished configuring yet */
tt_int_op(retval, OP_EQ, 0);
/* check the number of registered transports */
- tt_assert(smartlist_len(mp->transports) == i+1);
+ tt_int_op(smartlist_len(mp->transports), OP_EQ, i+1);
/* check that the mp is still waiting for transports */
tt_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
}
+ /* Get the SMETHOD DONE written to the process. */
+ process_notify_event_stdout(mp->process);
+
/* this last configure_proxy() should finalize the proxy configuration. */
retval = configure_proxy(mp);
/* retval should be 1 since the proxy finished configuring */
@@ -412,6 +416,49 @@ test_pt_configure_proxy(void *arg)
tt_str_op(smartlist_get(controlevent_msgs, 4), OP_EQ,
"650 TRANSPORT_LAUNCHED server mock5 127.0.0.1 5555\r\n");
+ /* Get the log message out. */
+ setup_full_capture_of_logs(LOG_ERR);
+ process_notify_event_stdout(mp->process);
+ expect_single_log_msg_containing("Oh noes, something bad happened");
+ teardown_capture_of_logs();
+
+ tt_int_op(controlevent_n, OP_EQ, 10);
+ tt_int_op(controlevent_event, OP_EQ, EVENT_PT_LOG);
+ tt_int_op(smartlist_len(controlevent_msgs), OP_EQ, 10);
+ tt_str_op(smartlist_get(controlevent_msgs, 5), OP_EQ,
+ "650 PT_LOG PT=<testcase> SEVERITY=error "
+ "MESSAGE=\"Oh noes, "
+ "something bad happened. What do we do!?\"\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 6), OP_EQ,
+ "650 PT_LOG PT=<testcase> SEVERITY=warning "
+ "MESSAGE=\"warning msg\"\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 7), OP_EQ,
+ "650 PT_LOG PT=<testcase> SEVERITY=notice "
+ "MESSAGE=\"notice msg\"\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 8), OP_EQ,
+ "650 PT_LOG PT=<testcase> SEVERITY=info "
+ "MESSAGE=\"info msg\"\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 9), OP_EQ,
+ "650 PT_LOG PT=<testcase> SEVERITY=debug "
+ "MESSAGE=\"debug msg\"\r\n");
+
+ /* Get the STATUS messages out. */
+ process_notify_event_stdout(mp->process);
+
+ tt_int_op(controlevent_n, OP_EQ, 13);
+ tt_int_op(controlevent_event, OP_EQ, EVENT_PT_STATUS);
+ tt_int_op(smartlist_len(controlevent_msgs), OP_EQ, 13);
+
+ tt_str_op(smartlist_get(controlevent_msgs, 10), OP_EQ,
+ "650 PT_STATUS "
+ "PT=<testcase> TRANSPORT=a K_1=a K_2=b K_3=\"foo bar\"\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 11), OP_EQ,
+ "650 PT_STATUS "
+ "PT=<testcase> TRANSPORT=b K_1=a K_2=b K_3=\"foo bar\"\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 12), OP_EQ,
+ "650 PT_STATUS "
+ "PT=<testcase> TRANSPORT=c K_1=a K_2=b K_3=\"foo bar\"\r\n");
+
{ /* check that the transport info were saved properly in the tor state */
config_line_t *transport_in_state = NULL;
smartlist_t *transport_info_sl = smartlist_new();
@@ -434,9 +481,9 @@ test_pt_configure_proxy(void *arg)
}
done:
+ teardown_capture_of_logs();
or_state_free(dummy_state);
- UNMOCK(tor_get_lines_from_handle);
- UNMOCK(tor_process_handle_destroy);
+ UNMOCK(process_read_stdout);
UNMOCK(get_or_state);
UNMOCK(queue_control_event_string);
if (controlevent_msgs) {
@@ -449,7 +496,7 @@ test_pt_configure_proxy(void *arg)
smartlist_free(mp->transports);
}
smartlist_free(mp->transports_to_launch);
- tor_free(mp->process_handle);
+ process_free(mp->process);
tor_free(mp->argv[0]);
tor_free(mp->argv);
tor_free(mp);
diff --git a/src/test/test_ptr_slow.c b/src/test/test_ptr_slow.c
new file mode 100644
index 0000000000..76bdbf1891
--- /dev/null
+++ b/src/test/test_ptr_slow.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "test/test.h"
+#include "test/ptr_helpers.h"
+
+#include <stdint.h>
+#include <limits.h>
+
+/** Assert that <b>a</b> can be cast to void * and back. */
+static void
+assert_int_voidptr_roundtrip(int a)
+{
+ intptr_t ap = (intptr_t)a;
+ void *b = cast_intptr_to_voidstar(ap);
+ intptr_t c = cast_voidstar_to_intptr(b);
+ void *d = cast_intptr_to_voidstar(c);
+
+ tt_assert(ap == c);
+ tt_assert(b == d);
+
+ done:
+ return;
+}
+
+/** Test for possibility of casting `int` to `void *` and back. */
+static void
+test_int_voidstar_interop(void *arg)
+{
+ int a;
+ (void)arg;
+
+ for (a = -1024; a <= 1024; a++) {
+ assert_int_voidptr_roundtrip(a);
+ }
+
+ for (a = INT_MIN; a <= INT_MIN+1024; a++) {
+ assert_int_voidptr_roundtrip(a);
+ }
+
+ for (a = INT_MAX-1024; a < INT_MAX; a++) {
+ assert_int_voidptr_roundtrip(a);
+ }
+
+ a = 1;
+ for (unsigned long i = 0; i < sizeof(int) * 8; i++) {
+ assert_int_voidptr_roundtrip(a);
+ a = (a << 1);
+ }
+}
+
+/** Assert that <b>a</b> can be cast to void * and back. */
+static void
+assert_uint_voidptr_roundtrip(unsigned int a)
+{
+ uintptr_t ap = (uintptr_t)a;
+ void *b = cast_uintptr_to_voidstar(ap);
+ uintptr_t c = cast_voidstar_to_uintptr(b);
+ void *d = cast_uintptr_to_voidstar(c);
+
+ tt_assert(ap == c);
+ tt_assert(b == d);
+
+ done:
+ return;
+}
+
+/** Test for possibility of casting `int` to `void *` and back. */
+static void
+test_uint_voidstar_interop(void *arg)
+{
+ unsigned int a;
+ (void)arg;
+
+ for (a = 0; a <= 1024; a++) {
+ assert_uint_voidptr_roundtrip(a);
+ }
+
+ for (a = UINT_MAX-1024; a < UINT_MAX; a++) {
+ assert_uint_voidptr_roundtrip(a);
+ }
+
+ a = 1;
+ for (unsigned long i = 0; i < sizeof(int) * 8; i++) {
+ assert_uint_voidptr_roundtrip(a);
+ a = (a << 1);
+ }
+}
+
+struct testcase_t slow_ptr_tests[] = {
+ { .name = "int_voidstar_interop",
+ .fn = test_int_voidstar_interop,
+ .flags = 0,
+ .setup = NULL,
+ .setup_data = NULL },
+ { .name = "uint_voidstar_interop",
+ .fn = test_uint_voidstar_interop,
+ .flags = 0,
+ .setup = NULL,
+ .setup_data = NULL },
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_pubsub_build.c b/src/test/test_pubsub_build.c
new file mode 100644
index 0000000000..021323fbf1
--- /dev/null
+++ b/src/test/test_pubsub_build.c
@@ -0,0 +1,578 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define DISPATCH_PRIVATE
+#define PUBSUB_PRIVATE
+
+#include "test/test.h"
+
+#include "lib/cc/torint.h"
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/dispatch/dispatch_st.h"
+#include "lib/dispatch/msgtypes.h"
+#include "lib/pubsub/pubsub_macros.h"
+#include "lib/pubsub/pubsub_build.h"
+#include "lib/pubsub/pubsub_builder_st.h"
+
+#include "lib/log/escape.h"
+#include "lib/malloc/malloc.h"
+#include "lib/string/printf.h"
+
+#include "test/log_test_helpers.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static char *
+ex_int_fmt(msg_aux_data_t aux)
+{
+ int val = (int) aux.u64;
+ char *r=NULL;
+ tor_asprintf(&r, "%d", val);
+ return r;
+}
+
+static char *
+ex_str_fmt(msg_aux_data_t aux)
+{
+ return esc_for_log(aux.ptr);
+}
+
+static void
+ex_str_free(msg_aux_data_t aux)
+{
+ tor_free_(aux.ptr);
+}
+
+static dispatch_typefns_t intfns = {
+ .fmt_fn = ex_int_fmt
+};
+
+static dispatch_typefns_t stringfns = {
+ .free_fn = ex_str_free,
+ .fmt_fn = ex_str_fmt
+};
+
+DECLARE_MESSAGE_INT(bunch_of_coconuts, int, int);
+DECLARE_PUBLISH(bunch_of_coconuts);
+DECLARE_SUBSCRIBE(bunch_of_coconuts, coconut_recipient_cb);
+
+DECLARE_MESSAGE(yes_we_have_no, string, char *);
+DECLARE_PUBLISH(yes_we_have_no);
+DECLARE_SUBSCRIBE(yes_we_have_no, absent_item_cb);
+
+static void
+coconut_recipient_cb(const msg_t *m, int n_coconuts)
+{
+ (void)m;
+ (void)n_coconuts;
+}
+
+static void
+absent_item_cb(const msg_t *m, const char *fruitname)
+{
+ (void)m;
+ (void)fruitname;
+}
+
+#define FLAG_SKIP 99999
+
+static void
+seed_dispatch_builder(pubsub_builder_t *b,
+ unsigned fl1, unsigned fl2, unsigned fl3, unsigned fl4)
+{
+ pubsub_connector_t *c = NULL;
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys1"));
+ DISPATCH_REGISTER_TYPE(c, int, &intfns);
+ if (fl1 != FLAG_SKIP)
+ DISPATCH_ADD_PUB_(c, main, bunch_of_coconuts, fl1);
+ if (fl2 != FLAG_SKIP)
+ DISPATCH_ADD_SUB_(c, main, yes_we_have_no, fl2);
+ pubsub_connector_free(c);
+ }
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys2"));
+ DISPATCH_REGISTER_TYPE(c, string, &stringfns);
+ if (fl3 != FLAG_SKIP)
+ DISPATCH_ADD_PUB_(c, main, yes_we_have_no, fl3);
+ if (fl4 != FLAG_SKIP)
+ DISPATCH_ADD_SUB_(c, main, bunch_of_coconuts, fl4);
+ pubsub_connector_free(c);
+ }
+}
+
+static void
+seed_pubsub_builder_basic(pubsub_builder_t *b)
+{
+ seed_dispatch_builder(b, 0, 0, 0, 0);
+}
+
+/* Regular builder with valid types and messages.
+ */
+static void
+test_pubsub_build_types_ok(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+ pubsub_items_t *items = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+
+ dispatcher = pubsub_builder_finalize(b, &items);
+ b = NULL;
+ tt_assert(dispatcher);
+ tt_assert(items);
+ tt_int_op(smartlist_len(items->items), OP_EQ, 4);
+
+ // Make sure that the bindings got build correctly.
+ SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, item) {
+ if (item->is_publish) {
+ tt_assert(item->pub_binding);
+ tt_ptr_op(item->pub_binding->dispatch_ptr, OP_EQ, dispatcher);
+ }
+ } SMARTLIST_FOREACH_END(item);
+
+ tt_int_op(dispatcher->n_types, OP_GE, 2);
+ tt_assert(dispatcher->typefns);
+
+ tt_assert(dispatcher->typefns[get_msg_type_id("int")].fmt_fn == ex_int_fmt);
+ tt_assert(dispatcher->typefns[get_msg_type_id("string")].fmt_fn ==
+ ex_str_fmt);
+
+ // Now clear the bindings, like we would do before freeing the
+ // the dispatcher.
+ pubsub_items_clear_bindings(items);
+ SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, item) {
+ if (item->is_publish) {
+ tt_assert(item->pub_binding);
+ tt_ptr_op(item->pub_binding->dispatch_ptr, OP_EQ, NULL);
+ }
+ } SMARTLIST_FOREACH_END(item);
+
+ done:
+ pubsub_connector_free(c);
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ pubsub_items_free(items);
+}
+
+/* We fail if the same type is defined in two places with different functions.
+ */
+static void
+test_pubsub_build_types_decls_conflict(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
+ // Extra declaration of int: we don't allow this.
+ DISPATCH_REGISTER_TYPE(c, int, &stringfns);
+ pubsub_connector_free(c);
+ }
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+ // expect_log_msg_containing("(int) declared twice"); // XXXX
+
+ done:
+ pubsub_connector_free(c);
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* If a message ID exists but nobody is publishing or subscribing to it,
+ * that's okay. */
+static void
+test_pubsub_build_unused_message(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+
+ // This message isn't actually generated by anyone, but that will be fine:
+ // we just log it at info.
+ get_message_id("unused");
+ setup_capture_of_logs(LOG_INFO);
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+ expect_log_msg_containing(
+ "Nobody is publishing or subscribing to message");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* Publishing or subscribing to a message with no subscribers / publishers
+ * should fail and warn. */
+static void
+test_pubsub_build_missing_pubsub(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+
+ b = pubsub_builder_new();
+ seed_dispatch_builder(b, 0, 0, FLAG_SKIP, FLAG_SKIP);
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing(
+ "Message \"bunch_of_coconuts\" has publishers, but no subscribers.");
+ expect_log_msg_containing(
+ "Message \"yes_we_have_no\" has subscribers, but no publishers.");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* Make sure that a stub publisher or subscriber prevents an error from
+ * happening even if there are no other publishers/subscribers for a message
+ */
+static void
+test_pubsub_build_stub_pubsub(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+
+ b = pubsub_builder_new();
+ seed_dispatch_builder(b, 0, 0, DISP_FLAG_STUB, DISP_FLAG_STUB);
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+
+ // 1 subscriber.
+ tt_int_op(1, OP_EQ,
+ dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
+ // no subscribers
+ tt_ptr_op(NULL, OP_EQ,
+ dispatcher->table[get_message_id("bunch_of_coconuts")]);
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+}
+
+/* Only one channel per msg id. */
+static void
+test_pubsub_build_channels_conflict(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+ pub_binding_t btmp;
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("problems"));
+ /* Usually the DISPATCH_ADD_PUB macro would keep us from using
+ * the wrong channel */
+ pubsub_add_pub_(c, &btmp, get_channel_id("hithere"),
+ get_message_id("bunch_of_coconuts"),
+ get_msg_type_id("int"),
+ 0 /* flags */,
+ "somewhere.c", 22);
+ pubsub_connector_free(c);
+ };
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing("Message \"bunch_of_coconuts\" is associated "
+ "with multiple inconsistent channels.");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* Only one type per msg id. */
+static void
+test_pubsub_build_types_conflict(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+ pub_binding_t btmp;
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("problems"));
+ /* Usually the DISPATCH_ADD_PUB macro would keep us from using
+ * the wrong channel */
+ pubsub_add_pub_(c, &btmp, get_channel_id("hithere"),
+ get_message_id("bunch_of_coconuts"),
+ get_msg_type_id("string"),
+ 0 /* flags */,
+ "somewhere.c", 22);
+ pubsub_connector_free(c);
+ };
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing("Message \"bunch_of_coconuts\" is associated "
+ "with multiple inconsistent message types.");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* The same module can't publish and subscribe the same message */
+static void
+test_pubsub_build_pubsub_same(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys1"));
+ // already publishing this.
+ DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
+ pubsub_connector_free(c);
+ };
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing("Message \"bunch_of_coconuts\" is published "
+ "and subscribed by the same subsystem \"sys1\".");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+/* More than one subsystem may publish or subscribe, and that's okay. */
+static void
+test_pubsub_build_pubsub_multi(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+ pub_binding_t btmp;
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
+ DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
+ pubsub_add_pub_(c, &btmp, get_channel_id("main"),
+ get_message_id("yes_we_have_no"),
+ get_msg_type_id("string"),
+ 0 /* flags */,
+ "somewhere.c", 22);
+ pubsub_connector_free(c);
+ };
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+
+ // 1 subscribers
+ tt_int_op(1, OP_EQ,
+ dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
+ // 2 subscribers.
+ dtbl_entry_t *ent =
+ dispatcher->table[get_message_id("bunch_of_coconuts")];
+ tt_int_op(2, OP_EQ, ent->n_enabled);
+ tt_int_op(2, OP_EQ, ent->n_fns);
+ tt_ptr_op(ent->rcv[0].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+ tt_ptr_op(ent->rcv[1].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+}
+
+static void
+some_other_coconut_hook(const msg_t *m)
+{
+ (void)m;
+}
+
+/* Subscribe hooks should be build correctly when there are a bunch of
+ * them. */
+static void
+test_pubsub_build_sub_many(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+ char *sysname = NULL;
+ b = pubsub_builder_new();
+ seed_pubsub_builder_basic(b);
+
+ int i;
+ for (i = 1; i < 100; ++i) {
+ tor_asprintf(&sysname, "system%d",i);
+ c = pubsub_connector_for_subsystem(b, get_subsys_id(sysname));
+ if (i % 7) {
+ DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
+ } else {
+ pubsub_add_sub_(c, some_other_coconut_hook,
+ get_channel_id("main"),
+ get_message_id("bunch_of_coconuts"),
+ get_msg_type_id("int"),
+ 0 /* flags */,
+ "somewhere.c", 22);
+ }
+ pubsub_connector_free(c);
+ tor_free(sysname);
+ };
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+
+ dtbl_entry_t *ent =
+ dispatcher->table[get_message_id("bunch_of_coconuts")];
+ tt_int_op(100, OP_EQ, ent->n_enabled);
+ tt_int_op(100, OP_EQ, ent->n_fns);
+ tt_ptr_op(ent->rcv[0].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+ tt_ptr_op(ent->rcv[1].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+ tt_ptr_op(ent->rcv[76].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+ tt_ptr_op(ent->rcv[77].fn, OP_EQ, some_other_coconut_hook);
+ tt_ptr_op(ent->rcv[78].fn, OP_EQ, recv_fn__bunch_of_coconuts);
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ tor_free(sysname);
+}
+
+/* It's fine to declare the excl flag. */
+static void
+test_pubsub_build_excl_ok(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+
+ b = pubsub_builder_new();
+ // Try one excl/excl pair and one excl/non pair.
+ seed_dispatch_builder(b, DISP_FLAG_EXCL, 0,
+ DISP_FLAG_EXCL, DISP_FLAG_EXCL);
+
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher);
+
+ // 1 subscribers
+ tt_int_op(1, OP_EQ,
+ dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
+ // 1 subscriber.
+ tt_int_op(1, OP_EQ,
+ dispatcher->table[get_message_id("bunch_of_coconuts")]->n_enabled);
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+}
+
+/* but if you declare the excl flag, you need to mean it. */
+static void
+test_pubsub_build_excl_bad(void *arg)
+{
+ (void)arg;
+ pubsub_builder_t *b = NULL;
+ dispatch_t *dispatcher = NULL;
+ pubsub_connector_t *c = NULL;
+
+ b = pubsub_builder_new();
+ seed_dispatch_builder(b, DISP_FLAG_EXCL, DISP_FLAG_EXCL,
+ 0, 0);
+
+ {
+ c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
+ DISPATCH_ADD_PUB_(c, main, bunch_of_coconuts, 0);
+ DISPATCH_ADD_SUB_(c, main, yes_we_have_no, 0);
+ pubsub_connector_free(c);
+ };
+
+ setup_full_capture_of_logs(LOG_WARN);
+ dispatcher = pubsub_builder_finalize(b, NULL);
+ b = NULL;
+ tt_assert(dispatcher == NULL);
+
+ expect_log_msg_containing("has multiple publishers, but at least one is "
+ "marked as exclusive.");
+ expect_log_msg_containing("has multiple subscribers, but at least one is "
+ "marked as exclusive.");
+
+ done:
+ pubsub_builder_free(b);
+ dispatch_free(dispatcher);
+ teardown_capture_of_logs();
+}
+
+#define T(name, flags) \
+ { #name, test_pubsub_build_ ## name , (flags), NULL, NULL }
+
+struct testcase_t pubsub_build_tests[] = {
+ T(types_ok, TT_FORK),
+ T(types_decls_conflict, TT_FORK),
+ T(unused_message, TT_FORK),
+ T(missing_pubsub, TT_FORK),
+ T(stub_pubsub, TT_FORK),
+ T(channels_conflict, TT_FORK),
+ T(types_conflict, TT_FORK),
+ T(pubsub_same, TT_FORK),
+ T(pubsub_multi, TT_FORK),
+ T(sub_many, TT_FORK),
+ T(excl_ok, TT_FORK),
+ T(excl_bad, TT_FORK),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_pubsub_msg.c b/src/test/test_pubsub_msg.c
new file mode 100644
index 0000000000..73c7c9f540
--- /dev/null
+++ b/src/test/test_pubsub_msg.c
@@ -0,0 +1,305 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define DISPATCH_PRIVATE
+
+#include "test/test.h"
+
+#include "lib/dispatch/dispatch.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/dispatch/dispatch_st.h"
+#include "lib/dispatch/msgtypes.h"
+#include "lib/pubsub/pubsub_flags.h"
+#include "lib/pubsub/pub_binding_st.h"
+#include "lib/pubsub/pubsub_build.h"
+#include "lib/pubsub/pubsub_builder_st.h"
+#include "lib/pubsub/pubsub_connect.h"
+#include "lib/pubsub/pubsub_publish.h"
+
+#include "lib/log/escape.h"
+#include "lib/malloc/malloc.h"
+#include "lib/string/printf.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static char *
+ex_str_fmt(msg_aux_data_t aux)
+{
+ return esc_for_log(aux.ptr);
+}
+static void
+ex_str_free(msg_aux_data_t aux)
+{
+ tor_free_(aux.ptr);
+}
+static dispatch_typefns_t stringfns = {
+ .free_fn = ex_str_free,
+ .fmt_fn = ex_str_fmt
+};
+
+// We're using the lowest-level publish/subscribe logic here, to avoid the
+// pubsub_macros.h macros and just test the dispatch core. We'll use a string
+// type for everything.
+
+#define DECLARE_MESSAGE(suffix) \
+ static pub_binding_t pub_binding_##suffix; \
+ static int msg_received_##suffix = 0; \
+ static void recv_msg_##suffix(const msg_t *m) { \
+ (void)m; \
+ ++msg_received_##suffix; \
+ } \
+ EAT_SEMICOLON
+
+#define ADD_PUBLISH(binding_suffix, subsys, channel, msg, flags) \
+ STMT_BEGIN { \
+ con = pubsub_connector_for_subsystem(builder, \
+ get_subsys_id(#subsys)); \
+ pubsub_add_pub_(con, &pub_binding_##binding_suffix, \
+ get_channel_id(#channel), \
+ get_message_id(#msg), get_msg_type_id("string"), \
+ (flags), __FILE__, __LINE__); \
+ pubsub_connector_free(con); \
+ } STMT_END
+
+#define ADD_SUBSCRIBE(hook_suffix, subsys, channel, msg, flags) \
+ STMT_BEGIN { \
+ con = pubsub_connector_for_subsystem(builder, \
+ get_subsys_id(#subsys)); \
+ pubsub_add_sub_(con, recv_msg_##hook_suffix, \
+ get_channel_id(#channel), \
+ get_message_id(#msg), get_msg_type_id("string"), \
+ (flags), __FILE__, __LINE__); \
+ pubsub_connector_free(con); \
+ } STMT_END
+
+#define SEND(binding_suffix, val) \
+ STMT_BEGIN { \
+ msg_aux_data_t data_; \
+ data_.ptr = tor_strdup(val); \
+ pubsub_pub_(&pub_binding_##binding_suffix, data_); \
+ } STMT_END
+
+DECLARE_MESSAGE(msg1);
+DECLARE_MESSAGE(msg2);
+DECLARE_MESSAGE(msg3);
+DECLARE_MESSAGE(msg4);
+DECLARE_MESSAGE(msg5);
+
+static smartlist_t *strings_received = NULL;
+static void
+recv_msg_copy_string(const msg_t *m)
+{
+ const char *s = m->aux_data__.ptr;
+ smartlist_add(strings_received, tor_strdup(s));
+}
+
+static void *
+setup_dispatcher(const struct testcase_t *testcase)
+{
+ (void)testcase;
+ pubsub_builder_t *builder = pubsub_builder_new();
+ pubsub_connector_t *con;
+
+ {
+ con = pubsub_connector_for_subsystem(builder, get_subsys_id("types"));
+ pubsub_connector_register_type_(con,
+ get_msg_type_id("string"),
+ &stringfns,
+ "nowhere.c", 99);
+ pubsub_connector_free(con);
+ }
+ // message1 has one publisher and one subscriber.
+ ADD_PUBLISH(msg1, sys1, main, message1, 0);
+ ADD_SUBSCRIBE(msg1, sys2, main, message1, 0);
+
+ // message2 has a publisher and a stub subscriber.
+ ADD_PUBLISH(msg2, sys1, main, message2, 0);
+ ADD_SUBSCRIBE(msg2, sys2, main, message2, DISP_FLAG_STUB);
+
+ // message3 has a publisher and three subscribers.
+ ADD_PUBLISH(msg3, sys1, main, message3, 0);
+ ADD_SUBSCRIBE(msg3, sys2, main, message3, 0);
+ ADD_SUBSCRIBE(msg3, sys3, main, message3, 0);
+ ADD_SUBSCRIBE(msg3, sys4, main, message3, 0);
+
+ // message4 has one publisher and two subscribers, but it's on another
+ // channel.
+ ADD_PUBLISH(msg4, sys2, other, message4, 0);
+ ADD_SUBSCRIBE(msg4, sys1, other, message4, 0);
+ ADD_SUBSCRIBE(msg4, sys3, other, message4, 0);
+
+ // message5 has a huge number of recipients.
+ ADD_PUBLISH(msg5, sys3, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys4, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys5, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys6, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys7, main, message5, 0);
+ ADD_SUBSCRIBE(msg5, sys8, main, message5, 0);
+ for (int i = 0; i < 1000-5; ++i) {
+ char *sys;
+ tor_asprintf(&sys, "xsys-%d", i);
+ con = pubsub_connector_for_subsystem(builder, get_subsys_id(sys));
+ pubsub_add_sub_(con, recv_msg_copy_string,
+ get_channel_id("main"),
+ get_message_id("message5"),
+ get_msg_type_id("string"), 0, "here", 100);
+ pubsub_connector_free(con);
+ tor_free(sys);
+ }
+
+ return pubsub_builder_finalize(builder, NULL);
+}
+
+static int
+cleanup_dispatcher(const struct testcase_t *testcase, void *dispatcher_)
+{
+ (void)testcase;
+ dispatch_t *dispatcher = dispatcher_;
+ dispatch_free(dispatcher);
+ return 1;
+}
+
+static const struct testcase_setup_t dispatcher_setup = {
+ setup_dispatcher, cleanup_dispatcher
+};
+
+static void
+test_pubsub_msg_minimal(void *arg)
+{
+ dispatch_t *d = arg;
+
+ tt_int_op(0, OP_EQ, msg_received_msg1);
+ SEND(msg1, "hello world");
+ tt_int_op(0, OP_EQ, msg_received_msg1); // hasn't actually arrived yet.
+
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 1000));
+ tt_int_op(1, OP_EQ, msg_received_msg1); // we got the message!
+
+ done:
+ ;
+}
+
+static void
+test_pubsub_msg_send_to_stub(void *arg)
+{
+ dispatch_t *d = arg;
+
+ tt_int_op(0, OP_EQ, msg_received_msg2);
+ SEND(msg2, "hello silence");
+ tt_int_op(0, OP_EQ, msg_received_msg2); // hasn't actually arrived yet.
+
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 1000));
+ tt_int_op(0, OP_EQ, msg_received_msg2); // doesn't arrive -- stub hook.
+
+ done:
+ ;
+}
+
+static void
+test_pubsub_msg_cancel_msgs(void *arg)
+{
+ dispatch_t *d = arg;
+
+ tt_int_op(0, OP_EQ, msg_received_msg1);
+ for (int i = 0; i < 100; ++i) {
+ SEND(msg1, "hello world");
+ }
+ tt_int_op(0, OP_EQ, msg_received_msg1); // hasn't actually arrived yet.
+
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 10));
+ tt_int_op(10, OP_EQ, msg_received_msg1); // we got the message 10 times.
+
+ // At this point, the dispatcher will be freed with queued, undelivered
+ // messages.
+ done:
+ ;
+}
+
+struct alertfn_target {
+ dispatch_t *d;
+ channel_id_t ch;
+ int count;
+};
+static void
+alertfn_generic(dispatch_t *d, channel_id_t ch, void *arg)
+{
+ struct alertfn_target *t = arg;
+ tt_ptr_op(d, OP_EQ, t->d);
+ tt_int_op(ch, OP_EQ, t->ch);
+ ++t->count;
+ done:
+ ;
+}
+
+static void
+test_pubsub_msg_alertfns(void *arg)
+{
+ dispatch_t *d = arg;
+ struct alertfn_target ch1_a = { d, get_channel_id("main"), 0 };
+ struct alertfn_target ch2_a = { d, get_channel_id("other"), 0 };
+
+ tt_int_op(0, OP_EQ,
+ dispatch_set_alert_fn(d, get_channel_id("main"),
+ alertfn_generic, &ch1_a));
+ tt_int_op(0, OP_EQ,
+ dispatch_set_alert_fn(d, get_channel_id("other"),
+ alertfn_generic, &ch2_a));
+
+ SEND(msg3, "hello");
+ tt_int_op(ch1_a.count, OP_EQ, 1);
+ SEND(msg3, "world");
+ tt_int_op(ch1_a.count, OP_EQ, 1); // only the first message sends an alert
+ tt_int_op(ch2_a.count, OP_EQ, 0); // no alert for 'other'
+
+ SEND(msg4, "worse things happen in C");
+ tt_int_op(ch2_a.count, OP_EQ, 1);
+
+ // flush the first (main) channel...
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 1000));
+ tt_int_op(6, OP_EQ, msg_received_msg3); // 3 subscribers, 2 instances.
+
+ // now that the main channel is flushed, sending another message on it
+ // starts another alert.
+ tt_int_op(ch1_a.count, OP_EQ, 1);
+ SEND(msg1, "plover");
+ tt_int_op(ch1_a.count, OP_EQ, 2);
+ tt_int_op(ch2_a.count, OP_EQ, 1);
+
+ done:
+ ;
+}
+
+/* try more than N_FAST_FNS hooks on msg5 */
+static void
+test_pubsub_msg_many_hooks(void *arg)
+{
+ dispatch_t *d = arg;
+ strings_received = smartlist_new();
+
+ tt_int_op(0, OP_EQ, msg_received_msg5);
+ SEND(msg5, "hello world");
+ tt_int_op(0, OP_EQ, msg_received_msg5);
+ tt_int_op(0, OP_EQ, smartlist_len(strings_received));
+
+ tt_int_op(0, OP_EQ, dispatch_flush(d, get_channel_id("main"), 100000));
+ tt_int_op(5, OP_EQ, msg_received_msg5);
+ tt_int_op(995, OP_EQ, smartlist_len(strings_received));
+
+ done:
+ SMARTLIST_FOREACH(strings_received, char *, s, tor_free(s));
+ smartlist_free(strings_received);
+}
+
+#define T(name) \
+ { #name, test_pubsub_msg_ ## name , TT_FORK, \
+ &dispatcher_setup, NULL }
+
+struct testcase_t pubsub_msg_tests[] = {
+ T(minimal),
+ T(send_to_stub),
+ T(cancel_msgs),
+ T(alertfns),
+ T(many_hooks),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_rebind.py b/src/test/test_rebind.py
index 30a587858f..c9b9200b2d 100644
--- a/src/test/test_rebind.py
+++ b/src/test/test_rebind.py
@@ -22,9 +22,10 @@ def skip(msg):
def try_connecting_to_socksport():
socks_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- if socks_socket.connect_ex(('127.0.0.1', socks_port)):
+ e = socks_socket.connect_ex(('127.0.0.1', socks_port))
+ if e:
tor_process.terminate()
- fail('Cannot connect to SOCKSPort')
+ fail('Cannot connect to SOCKSPort: error ' + os.strerror(e))
socks_socket.close()
def wait_for_log(s):
diff --git a/src/test/test_rebind.sh b/src/test/test_rebind.sh
index ea2012957e..d6d9d86668 100755
--- a/src/test/test_rebind.sh
+++ b/src/test/test_rebind.sh
@@ -7,18 +7,21 @@ if test "$UNAME_OS" = 'CYGWIN' || \
test "$UNAME_OS" = 'MSYS' || \
test "$UNAME_OS" = 'MINGW'; then
if test "$APPVEYOR" = 'True'; then
- echo "This test is disabled on Windows CI, as it requires firewall examptions. Skipping." >&2
+ echo "This test is disabled on Windows CI, as it requires firewall exemptions. Skipping." >&2
exit 77
fi
fi
-exitcode=0
-
tmpdir=
-clean () { test -n "$tmpdir" && test -d "$tmpdir" && rm -rf "$tmpdir" || :; }
+clean () {
+ if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then
+ rm -rf "$tmpdir"
+ fi
+}
+
trap clean EXIT HUP INT TERM
-tmpdir="`mktemp -d -t tor_rebind_test.XXXXXX`"
+tmpdir="$(mktemp -d -t tor_rebind_test.XXXXXX)"
if [ -z "$tmpdir" ]; then
echo >&2 mktemp failed
exit 2
diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c
index 0623583511..c65279fb25 100644
--- a/src/test/test_relaycell.c
+++ b/src/test/test_relaycell.c
@@ -17,6 +17,7 @@
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/connection_edge.h"
+#include "core/or/sendme.h"
#include "core/or/relay.h"
#include "test/test.h"
#include "test/log_test_helpers.h"
@@ -812,7 +813,11 @@ test_circbw_relay(void *arg)
ASSERT_UNCOUNTED_BW();
/* Sendme on circuit with non-full window: counted */
- PACK_CELL(0, RELAY_COMMAND_SENDME, "Data1234");
+ PACK_CELL(0, RELAY_COMMAND_SENDME, "");
+ /* Recording a cell, the window is updated after decryption so off by one in
+ * order to record and then we process it with the proper window. */
+ circ->cpath->package_window = 901;
+ sendme_record_cell_digest_on_circ(TO_CIRCUIT(circ), circ->cpath);
circ->cpath->package_window = 900;
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
diff --git a/src/test/test_relaycrypt.c b/src/test/test_relaycrypt.c
index fe6889e521..4bbf07c3ec 100644
--- a/src/test/test_relaycrypt.c
+++ b/src/test/test_relaycrypt.c
@@ -3,6 +3,8 @@
* Copyright (c) 2007-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#define CRYPT_PATH_PRIVATE
+
#include "core/or/or.h"
#include "core/or/circuitbuild.h"
#define CIRCUITLIST_PRIVATE
@@ -10,7 +12,7 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "core/or/relay.h"
#include "core/crypto/relay_crypto.h"
-
+#include "core/or/crypt_path.h"
#include "core/or/cell_st.h"
#include "core/or/or_circuit_st.h"
#include "core/or/origin_circuit_st.h"
@@ -49,10 +51,10 @@ testing_circuitset_setup(const struct testcase_t *testcase)
cs->origin_circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL;
for (i=0; i<3; ++i) {
crypt_path_t *hop = tor_malloc_zero(sizeof(*hop));
- relay_crypto_init(&hop->crypto, KEY_MATERIAL[i], sizeof(KEY_MATERIAL[i]),
- 0, 0);
+ relay_crypto_init(&hop->pvt_crypto, KEY_MATERIAL[i],
+ sizeof(KEY_MATERIAL[i]), 0, 0);
hop->state = CPATH_STATE_OPEN;
- onion_append_to_cpath(&cs->origin_circ->cpath, hop);
+ cpath_extend_linked_list(&cs->origin_circ->cpath, hop);
tt_ptr_op(hop, OP_EQ, cs->origin_circ->cpath->prev);
}
diff --git a/src/test/test_rng.c b/src/test/test_rng.c
new file mode 100644
index 0000000000..dcf08fff1d
--- /dev/null
+++ b/src/test/test_rng.c
@@ -0,0 +1,59 @@
+/* Copyright (c) 2016-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/*
+ * Example usage:
+ *
+ * ./src/test/test-rng --emit | dieharder -g 200 -a
+ *
+ * Remember, dieharder can tell you that your RNG is completely broken, but if
+ * your RNG is not _completely_ broken, dieharder cannot tell you whether your
+ * RNG is actually secure.
+ */
+
+#include "orconfig.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "lib/crypt_ops/crypto_rand.h"
+
+int
+main(int argc, char **argv)
+{
+ uint8_t buf[0x123];
+
+ if (argc != 2 || strcmp(argv[1], "--emit")) {
+ fprintf(stderr, "If you want me to fill stdout with a bunch of random "
+ "bytes, you need to say --emit.\n");
+ return 1;
+ }
+
+ if (crypto_seed_rng() < 0) {
+ fprintf(stderr, "Can't seed RNG.\n");
+ return 1;
+ }
+
+#if 0
+ while (1) {
+ crypto_rand(buf, sizeof(buf));
+ if (write(1 /*stdout*/, buf, sizeof(buf)) != sizeof(buf)) {
+ fprintf(stderr, "write() failed: %s\n", strerror(errno));
+ return 1;
+ }
+ }
+#endif /* 0 */
+
+ crypto_fast_rng_t *rng = crypto_fast_rng_new();
+ while (1) {
+ crypto_fast_rng_getbytes(rng, buf, sizeof(buf));
+ if (write(1 /*stdout*/, buf, sizeof(buf)) != sizeof(buf)) {
+ fprintf(stderr, "write() failed: %s\n", strerror(errno));
+ return 1;
+ }
+ }
+}
diff --git a/src/test/test_router.c b/src/test/test_router.c
index 601881a124..5477ab51e9 100644
--- a/src/test/test_router.c
+++ b/src/test/test_router.c
@@ -7,16 +7,25 @@
* \brief Unittests for code in router.c
**/
+#define CONFIG_PRIVATE
+#define ROUTER_PRIVATE
+
#include "core/or/or.h"
#include "app/config/config.h"
#include "core/mainloop/mainloop.h"
#include "feature/hibernate/hibernate.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/routerstatus_st.h"
#include "feature/relay/router.h"
#include "feature/stats/rephist.h"
#include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_ed25519.h"
+#include "lib/encoding/confline.h"
/* Test suite stuff */
#include "test/test.h"
@@ -91,6 +100,9 @@ test_router_dump_router_to_string_no_bridge_distribution_method(void *arg)
router = (routerinfo_t*)router_get_my_routerinfo();
tt_ptr_op(router, !=, NULL);
+ /* The real router_get_my_routerinfo() looks up onion_curve25519_pkey using
+ * get_current_curve25519_keypair(), but we don't initialise static data in
+ * this test. */
router->onion_curve25519_pkey = &ntor_keypair.pubkey;
/* Generate our server descriptor and ensure that the substring
@@ -231,11 +243,254 @@ test_router_check_descriptor_bandwidth_changed(void *arg)
UNMOCK(we_are_hibernating);
}
+static networkstatus_t *mock_ns = NULL;
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
+{
+ (void)now;
+ return mock_ns;
+}
+
+static routerstatus_t *mock_rs = NULL;
+static const routerstatus_t *
+mock_networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest)
+{
+ (void)ns;
+ (void)digest;
+ return mock_rs;
+}
+
+static void
+test_router_mark_if_too_old(void *arg)
+{
+ (void)arg;
+ time_t now = approx_time();
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+ MOCK(networkstatus_vote_find_entry, mock_networkstatus_vote_find_entry);
+
+ routerstatus_t rs;
+ networkstatus_t ns;
+ memset(&rs, 0, sizeof(rs));
+ memset(&ns, 0, sizeof(ns));
+ mock_ns = &ns;
+ mock_ns->valid_after = now-3600;
+ mock_rs = &rs;
+ mock_rs->published_on = now - 10;
+
+ // no reason to mark this time.
+ desc_clean_since = now-10;
+ desc_dirty_reason = NULL;
+ mark_my_descriptor_dirty_if_too_old(now);
+ tt_i64_op(desc_clean_since, OP_EQ, now-10);
+
+ // Doesn't appear in consensus? Still don't mark it.
+ mock_ns = NULL;
+ mark_my_descriptor_dirty_if_too_old(now);
+ tt_i64_op(desc_clean_since, OP_EQ, now-10);
+ mock_ns = &ns;
+
+ // No new descriptor in a long time? Mark it.
+ desc_clean_since = now - 3600 * 96;
+ mark_my_descriptor_dirty_if_too_old(now);
+ tt_i64_op(desc_clean_since, OP_EQ, 0);
+ tt_str_op(desc_dirty_reason, OP_EQ, "time for new descriptor");
+
+ // Version in consensus published a long time ago? We won't mark it
+ // if it's been clean for only a short time.
+ desc_clean_since = now - 10;
+ desc_dirty_reason = NULL;
+ mock_rs->published_on = now - 3600 * 96;
+ mark_my_descriptor_dirty_if_too_old(now);
+ tt_i64_op(desc_clean_since, OP_EQ, now - 10);
+
+ // ... but if it's been clean a while, we mark.
+ desc_clean_since = now - 2 * 3600;
+ mark_my_descriptor_dirty_if_too_old(now);
+ tt_i64_op(desc_clean_since, OP_EQ, 0);
+ tt_str_op(desc_dirty_reason, OP_EQ,
+ "version listed in consensus is quite old");
+
+ // same deal if we're marked stale.
+ desc_clean_since = now - 2 * 3600;
+ desc_dirty_reason = NULL;
+ mock_rs->published_on = now - 10;
+ mock_rs->is_staledesc = 1;
+ mark_my_descriptor_dirty_if_too_old(now);
+ tt_i64_op(desc_clean_since, OP_EQ, 0);
+ tt_str_op(desc_dirty_reason, OP_EQ,
+ "listed as stale in consensus");
+
+ // same deal if we're absent from the consensus.
+ desc_clean_since = now - 2 * 3600;
+ desc_dirty_reason = NULL;
+ mock_rs = NULL;
+ mark_my_descriptor_dirty_if_too_old(now);
+ tt_i64_op(desc_clean_since, OP_EQ, 0);
+ tt_str_op(desc_dirty_reason, OP_EQ,
+ "not listed in consensus");
+
+ done:
+ UNMOCK(networkstatus_get_live_consensus);
+ UNMOCK(networkstatus_vote_find_entry);
+}
+
+static node_t fake_node;
+static const node_t *
+mock_node_get_by_nickname(const char *name, unsigned flags)
+{
+ (void)flags;
+ if (!strcasecmp(name, "crumpet"))
+ return &fake_node;
+ else
+ return NULL;
+}
+
+static void
+test_router_get_my_family(void *arg)
+{
+ (void)arg;
+ or_options_t *options = options_new();
+ smartlist_t *sl = NULL;
+ char *join = NULL;
+ // Overwrite the result of router_get_my_identity_digest(). This
+ // happens to be okay, but only for testing.
+ set_server_identity_key_digest_testing(
+ (const uint8_t*)"holeinthebottomofthe");
+
+ setup_capture_of_logs(LOG_WARN);
+
+ // No family listed -- so there's no list.
+ sl = get_my_declared_family(options);
+ tt_ptr_op(sl, OP_EQ, NULL);
+ expect_no_log_entry();
+
+#define CLEAR() do { \
+ if (sl) { \
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); \
+ smartlist_free(sl); \
+ } \
+ tor_free(join); \
+ mock_clean_saved_logs(); \
+ } while (0)
+
+ // Add a single nice friendly hex member. This should be enough
+ // to have our own ID added.
+ tt_ptr_op(options->MyFamily, OP_EQ, NULL);
+ config_line_append(&options->MyFamily, "MyFamily",
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+
+ sl = get_my_declared_family(options);
+ tt_ptr_op(sl, OP_NE, NULL);
+ tt_int_op(smartlist_len(sl), OP_EQ, 2);
+ join = smartlist_join_strings(sl, " ", 0, NULL);
+ tt_str_op(join, OP_EQ,
+ "$686F6C65696E746865626F74746F6D6F66746865 "
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ expect_no_log_entry();
+ CLEAR();
+
+ // Add a hex member with a ~. The ~ part should get removed.
+ config_line_append(&options->MyFamily, "MyFamily",
+ "$0123456789abcdef0123456789abcdef01234567~Muffin");
+ sl = get_my_declared_family(options);
+ tt_ptr_op(sl, OP_NE, NULL);
+ tt_int_op(smartlist_len(sl), OP_EQ, 3);
+ join = smartlist_join_strings(sl, " ", 0, NULL);
+ tt_str_op(join, OP_EQ,
+ "$0123456789ABCDEF0123456789ABCDEF01234567 "
+ "$686F6C65696E746865626F74746F6D6F66746865 "
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ expect_no_log_entry();
+ CLEAR();
+
+ // Nickname lookup will fail, so a nickname will appear verbatim.
+ config_line_append(&options->MyFamily, "MyFamily",
+ "BAGEL");
+ sl = get_my_declared_family(options);
+ tt_ptr_op(sl, OP_NE, NULL);
+ tt_int_op(smartlist_len(sl), OP_EQ, 4);
+ join = smartlist_join_strings(sl, " ", 0, NULL);
+ tt_str_op(join, OP_EQ,
+ "$0123456789ABCDEF0123456789ABCDEF01234567 "
+ "$686F6C65696E746865626F74746F6D6F66746865 "
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "
+ "bagel");
+ expect_single_log_msg_containing(
+ "There is a router named \"BAGEL\" in my declared family, but "
+ "I have no descriptor for it.");
+ CLEAR();
+
+ // A bogus digest should fail entirely.
+ config_line_append(&options->MyFamily, "MyFamily",
+ "$painauchocolat");
+ sl = get_my_declared_family(options);
+ tt_ptr_op(sl, OP_NE, NULL);
+ tt_int_op(smartlist_len(sl), OP_EQ, 4);
+ join = smartlist_join_strings(sl, " ", 0, NULL);
+ tt_str_op(join, OP_EQ,
+ "$0123456789ABCDEF0123456789ABCDEF01234567 "
+ "$686F6C65696E746865626F74746F6D6F66746865 "
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "
+ "bagel");
+ // "BAGEL" is still there, but it won't make a warning, because we already
+ // warned about it.
+ expect_single_log_msg_containing(
+ "There is a router named \"$painauchocolat\" in my declared "
+ "family, but that isn't a legal digest or nickname. Skipping it.");
+ CLEAR();
+
+ // Let's introduce a node we can look up by nickname
+ memset(&fake_node, 0, sizeof(fake_node));
+ memcpy(fake_node.identity, "whydoyouasknonononon", DIGEST_LEN);
+ MOCK(node_get_by_nickname, mock_node_get_by_nickname);
+
+ config_line_append(&options->MyFamily, "MyFamily",
+ "CRUmpeT");
+ sl = get_my_declared_family(options);
+ tt_ptr_op(sl, OP_NE, NULL);
+ tt_int_op(smartlist_len(sl), OP_EQ, 5);
+ join = smartlist_join_strings(sl, " ", 0, NULL);
+ tt_str_op(join, OP_EQ,
+ "$0123456789ABCDEF0123456789ABCDEF01234567 "
+ "$686F6C65696E746865626F74746F6D6F66746865 "
+ "$776879646F796F7561736B6E6F6E6F6E6F6E6F6E "
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "
+ "bagel");
+ // "BAGEL" is still there, but it won't make a warning, because we already
+ // warned about it. Some with "$painauchocolat".
+ expect_single_log_msg_containing(
+ "There is a router named \"CRUmpeT\" in my declared "
+ "family, but it wasn't listed by digest. Please consider saying "
+ "$776879646F796F7561736B6E6F6E6F6E6F6E6F6E instead, if that's "
+ "what you meant.");
+ CLEAR();
+ UNMOCK(node_get_by_nickname);
+
+ // Try a singleton list containing only us: It should give us NULL.
+ config_free_lines(options->MyFamily);
+ config_line_append(&options->MyFamily, "MyFamily",
+ "$686F6C65696E746865626F74746F6D6F66746865");
+ sl = get_my_declared_family(options);
+ tt_ptr_op(sl, OP_EQ, NULL);
+ expect_no_log_entry();
+
+ done:
+ or_options_free(options);
+ teardown_capture_of_logs();
+ CLEAR();
+ UNMOCK(node_get_by_nickname);
+
+#undef CLEAR
+}
+
#define ROUTER_TEST(name, flags) \
{ #name, test_router_ ## name, flags, NULL, NULL }
struct testcase_t router_tests[] = {
ROUTER_TEST(check_descriptor_bandwidth_changed, TT_FORK),
ROUTER_TEST(dump_router_to_string_no_bridge_distribution_method, TT_FORK),
+ ROUTER_TEST(mark_if_too_old, TT_FORK),
+ ROUTER_TEST(get_my_family, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c
index 727fa5660f..0c6b533698 100644
--- a/src/test/test_routerkeys.c
+++ b/src/test/test_routerkeys.c
@@ -399,7 +399,7 @@ test_routerkeys_ed_key_init_split(void *arg)
tt_assert(kp2 != NULL);
tt_assert(cert == NULL);
tt_mem_op(&kp1->pubkey, OP_EQ, &kp2->pubkey, sizeof(kp2->pubkey));
- tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey,
+ tt_assert(fast_mem_is_zero((char*)kp2->seckey.seckey,
sizeof(kp2->seckey.seckey)));
ed25519_keypair_free(kp2); kp2 = NULL;
@@ -409,7 +409,7 @@ test_routerkeys_ed_key_init_split(void *arg)
tt_assert(kp2 != NULL);
tt_assert(cert == NULL);
tt_mem_op(&kp1->pubkey, OP_EQ, &kp2->pubkey, sizeof(kp2->pubkey));
- tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey,
+ tt_assert(fast_mem_is_zero((char*)kp2->seckey.seckey,
sizeof(kp2->seckey.seckey)));
ed25519_keypair_free(kp2); kp2 = NULL;
@@ -455,11 +455,11 @@ test_routerkeys_ed_keys_init_all(void *arg)
options->TestingLinkKeySlop = 2*3600;
#ifdef _WIN32
- mkdir(dir);
- mkdir(keydir);
+ tt_int_op(0, OP_EQ, mkdir(dir));
+ tt_int_op(0, OP_EQ, mkdir(keydir));
#else
- mkdir(dir, 0700);
- mkdir(keydir, 0700);
+ tt_int_op(0, OP_EQ, mkdir(dir, 0700));
+ tt_int_op(0, OP_EQ, mkdir(keydir, 0700));
#endif /* defined(_WIN32) */
options->DataDirectory = dir;
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 95c9176faa..6d596e87ea 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -46,7 +46,7 @@
#include "feature/nodelist/routerstatus_st.h"
#include "lib/encoding/confline.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "test/test.h"
#include "test/test_dir_common.h"
@@ -265,7 +265,9 @@ test_router_pick_directory_server_impl(void *arg)
/* 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);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
sr_init(0);
UNMOCK(get_my_v3_authority_cert);
@@ -275,7 +277,9 @@ test_router_pick_directory_server_impl(void *arg)
construct_consensus(&consensus_text_md, now);
tt_assert(consensus_text_md);
- con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
+ con_md = networkstatus_parse_vote_from_string(consensus_text_md,
+ strlen(consensus_text_md),
+ NULL,
NS_TYPE_CONSENSUS);
tt_assert(con_md);
tt_int_op(con_md->flavor,OP_EQ, FLAV_MICRODESC);
@@ -301,7 +305,6 @@ test_router_pick_directory_server_impl(void *arg)
tt_assert(!networkstatus_consensus_is_bootstrapping(con_md->valid_until
+ 24*60*60));
/* These times are outside the test validity period */
- tt_assert(networkstatus_consensus_is_bootstrapping(now));
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));
@@ -475,7 +478,9 @@ test_directory_guard_fetch_with_no_dirinfo(void *arg)
/* Initialize the SRV subsystem */
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
- mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
sr_init(0);
UNMOCK(get_my_v3_authority_cert);
@@ -626,7 +631,7 @@ mock_clock_skew_warning(const connection_t *conn, long apparent_skew,
(void)conn;
mock_apparent_skew = apparent_skew;
tt_int_op(trusted, OP_EQ, 1);
- tt_int_op(domain, OP_EQ, LD_GENERAL);
+ tt_i64_op(domain, OP_EQ, LD_GENERAL);
tt_str_op(received, OP_EQ, "microdesc flavor consensus");
tt_str_op(source, OP_EQ, "CONSENSUS");
done:
@@ -648,7 +653,9 @@ test_skew_common(void *arg, time_t now, unsigned long *offset)
/* Initialize the SRV subsystem */
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
- mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
sr_init(0);
UNMOCK(get_my_v3_authority_cert);
@@ -662,7 +669,8 @@ test_skew_common(void *arg, time_t now, unsigned long *offset)
MOCK(clock_skew_warning, mock_clock_skew_warning);
/* Caller will call teardown_capture_of_logs() */
setup_capture_of_logs(LOG_WARN);
- retval = networkstatus_set_current_consensus(consensus, "microdesc", 0,
+ retval = networkstatus_set_current_consensus(consensus, strlen(consensus),
+ "microdesc", 0,
NULL);
done:
diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c
index c45f0e1595..cc73e6c20a 100644
--- a/src/test/test_routerset.c
+++ b/src/test/test_routerset.c
@@ -1765,7 +1765,7 @@ NS(node_get_by_nickname)(const char *nickname, unsigned flags)
* Structural test for routerset_get_all_nodes, when the nodelist has no nodes.
*/
-NS_DECL(smartlist_t *, nodelist_get_list, (void));
+NS_DECL(const smartlist_t *, nodelist_get_list, (void));
static smartlist_t *NS(mock_smartlist);
@@ -1795,7 +1795,7 @@ NS(test_main)(void *arg)
;
}
-smartlist_t *
+const smartlist_t *
NS(nodelist_get_list)(void)
{
CALLED(nodelist_get_list)++;
@@ -1811,7 +1811,7 @@ NS(nodelist_get_list)(void)
* the running_only flag is set, but the nodes are not running.
*/
-NS_DECL(smartlist_t *, nodelist_get_list, (void));
+NS_DECL(const smartlist_t *, nodelist_get_list, (void));
static smartlist_t *NS(mock_smartlist);
static node_t NS(mock_node);
@@ -1844,7 +1844,7 @@ NS(test_main)(void *arg)
;
}
-smartlist_t *
+const smartlist_t *
NS(nodelist_get_list)(void)
{
CALLED(nodelist_get_list)++;
diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh
index 00b3e88d37..804d2ada36 100755
--- a/src/test/test_rust.sh
+++ b/src/test/test_rust.sh
@@ -14,11 +14,12 @@ rustc_host=$(rustc -vV | grep host | sed 's/host: //')
for cargo_toml_dir in "${abs_top_srcdir:-../../..}"/src/rust/*; do
if [ -e "${cargo_toml_dir}/Cargo.toml" ]; then
+ # shellcheck disable=SC2086
cd "${abs_top_builddir:-../../..}/src/rust" && \
CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" \
- "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} \
+ "${CARGO:-cargo}" test "${CARGO_ONLINE-'--frozen'}" \
--features "test_linking_hack" \
- --target $rustc_host \
+ --target "$rustc_host" \
${EXTRA_CARGO_OPTIONS} \
--manifest-path "${cargo_toml_dir}/Cargo.toml" || exitcode=1
fi
diff --git a/src/test/test_sendme.c b/src/test/test_sendme.c
new file mode 100644
index 0000000000..eb402232bc
--- /dev/null
+++ b/src/test/test_sendme.c
@@ -0,0 +1,365 @@
+/* Copyright (c) 2014-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/* Unit tests for handling different kinds of relay cell */
+
+#define CIRCUITLIST_PRIVATE
+#define NETWORKSTATUS_PRIVATE
+#define SENDME_PRIVATE
+#define RELAY_PRIVATE
+
+#include "core/or/circuit_st.h"
+#include "core/or/or_circuit_st.h"
+#include "core/or/origin_circuit_st.h"
+#include "core/or/circuitlist.h"
+#include "core/or/relay.h"
+#include "core/or/sendme.h"
+
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/networkstatus_st.h"
+
+#include "lib/crypt_ops/crypto_digest.h"
+
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+
+static void
+setup_mock_consensus(void)
+{
+ current_md_consensus = current_ns_consensus =
+ tor_malloc_zero(sizeof(networkstatus_t));
+ current_md_consensus->net_params = smartlist_new();
+ current_md_consensus->routerstatus_list = smartlist_new();
+}
+
+static void
+free_mock_consensus(void)
+{
+ SMARTLIST_FOREACH(current_md_consensus->routerstatus_list, void *, r,
+ tor_free(r));
+ smartlist_free(current_md_consensus->routerstatus_list);
+ smartlist_free(current_ns_consensus->net_params);
+ tor_free(current_ns_consensus);
+}
+
+static void
+test_v1_record_digest(void *arg)
+{
+ or_circuit_t *or_circ = NULL;
+ circuit_t *circ = NULL;
+
+ (void) arg;
+
+ /* Create our dummy circuit. */
+ or_circ = or_circuit_new(1, NULL);
+ /* Points it to the OR circuit now. */
+ circ = TO_CIRCUIT(or_circ);
+
+ /* The package window has to be a multiple of CIRCWINDOW_INCREMENT minus 1
+ * in order to catched the CIRCWINDOW_INCREMENT-nth cell. Try something that
+ * shouldn't be noted. */
+ circ->package_window = CIRCWINDOW_INCREMENT;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_assert(!circ->sendme_last_digests);
+
+ /* This should work now. Package window at CIRCWINDOW_INCREMENT + 1. */
+ circ->package_window++;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_assert(circ->sendme_last_digests);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
+
+ /* Next cell in the package window shouldn't do anything. */
+ circ->package_window++;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
+
+ /* The next CIRCWINDOW_INCREMENT should add one more digest. */
+ circ->package_window = (CIRCWINDOW_INCREMENT * 2) + 1;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 2);
+
+ done:
+ circuit_free_(circ);
+}
+
+static void
+test_v1_consensus_params(void *arg)
+{
+ (void) arg;
+
+ setup_mock_consensus();
+ tt_assert(current_md_consensus);
+
+ /* Both zeroes. */
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_emit_min_version=0");
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_accept_min_version=0");
+ tt_int_op(get_emit_min_version(), OP_EQ, 0);
+ tt_int_op(get_accept_min_version(), OP_EQ, 0);
+ smartlist_clear(current_md_consensus->net_params);
+
+ /* Both ones. */
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_emit_min_version=1");
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_accept_min_version=1");
+ tt_int_op(get_emit_min_version(), OP_EQ, 1);
+ tt_int_op(get_accept_min_version(), OP_EQ, 1);
+ smartlist_clear(current_md_consensus->net_params);
+
+ /* Different values from each other. */
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_emit_min_version=1");
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_accept_min_version=0");
+ tt_int_op(get_emit_min_version(), OP_EQ, 1);
+ tt_int_op(get_accept_min_version(), OP_EQ, 0);
+ smartlist_clear(current_md_consensus->net_params);
+
+ /* Validate is the cell version is coherent with our internal default value
+ * and the one in the consensus. */
+ smartlist_add(current_md_consensus->net_params,
+ (void *) "sendme_accept_min_version=1");
+ /* Minimum acceptable value is 1. */
+ tt_int_op(cell_version_can_be_handled(1), OP_EQ, true);
+ /* Minimum acceptable value is 1 so a cell version of 0 is refused. */
+ tt_int_op(cell_version_can_be_handled(0), OP_EQ, false);
+
+ done:
+ free_mock_consensus();
+}
+
+static void
+test_v1_build_cell(void *arg)
+{
+ uint8_t payload[RELAY_PAYLOAD_SIZE], digest[DIGEST_LEN];
+ ssize_t ret;
+ crypto_digest_t *cell_digest = NULL;
+ or_circuit_t *or_circ = NULL;
+ circuit_t *circ = NULL;
+
+ (void) arg;
+
+ or_circ = or_circuit_new(1, NULL);
+ circ = TO_CIRCUIT(or_circ);
+ circ->sendme_last_digests = smartlist_new();
+
+ cell_digest = crypto_digest_new();
+ tt_assert(cell_digest);
+ crypto_digest_add_bytes(cell_digest, "AAAAAAAAAAAAAAAAAAAA", 20);
+ crypto_digest_get_digest(cell_digest, (char *) digest, sizeof(digest));
+ smartlist_add(circ->sendme_last_digests, tor_memdup(digest, sizeof(digest)));
+
+ /* SENDME v1 payload is 3 bytes + 20 bytes digest. See spec. */
+ ret = build_cell_payload_v1(digest, payload);
+ tt_int_op(ret, OP_EQ, 23);
+
+ /* Validation. */
+
+ /* An empty payload means SENDME version 0 thus valid. */
+ tt_int_op(sendme_is_valid(circ, payload, 0), OP_EQ, true);
+ /* Current phoney digest should have been popped. */
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
+
+ /* An unparseable cell means invalid. */
+ setup_full_capture_of_logs(LOG_INFO);
+ tt_int_op(sendme_is_valid(circ, (const uint8_t *) "A", 1), OP_EQ, false);
+ expect_log_msg_containing("Unparseable SENDME cell received. "
+ "Closing circuit.");
+ teardown_capture_of_logs();
+
+ /* No cell digest recorded for this. */
+ setup_full_capture_of_logs(LOG_INFO);
+ tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false);
+ expect_log_msg_containing("We received a SENDME but we have no cell digests "
+ "to match. Closing circuit.");
+ teardown_capture_of_logs();
+
+ /* Note the wrong digest in the circuit, cell should fail validation. */
+ circ->package_window = CIRCWINDOW_INCREMENT + 1;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
+ setup_full_capture_of_logs(LOG_INFO);
+ tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false);
+ /* After a validation, the last digests is always popped out. */
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
+ expect_log_msg_containing("SENDME v1 cell digest do not match.");
+ teardown_capture_of_logs();
+
+ /* Record the cell digest into the circuit, cell should validate. */
+ memcpy(or_circ->crypto.sendme_digest, digest, sizeof(digest));
+ circ->package_window = CIRCWINDOW_INCREMENT + 1;
+ sendme_record_cell_digest_on_circ(circ, NULL);
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
+ tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, true);
+ /* After a validation, the last digests is always popped out. */
+ tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
+
+ done:
+ crypto_digest_free(cell_digest);
+ circuit_free_(circ);
+}
+
+static void
+test_cell_payload_pad(void *arg)
+{
+ size_t pad_offset, payload_len, expected_offset;
+
+ (void) arg;
+
+ /* Offset should be 0, not enough room for padding. */
+ payload_len = RELAY_PAYLOAD_SIZE;
+ pad_offset = get_pad_cell_offset(payload_len);
+ tt_int_op(pad_offset, OP_EQ, 0);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ /* Still no room because we keep 4 extra bytes. */
+ pad_offset = get_pad_cell_offset(payload_len - 4);
+ tt_int_op(pad_offset, OP_EQ, 0);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ /* We should have 1 byte of padding. Meaning, the offset should be the
+ * CELL_PAYLOAD_SIZE minus 1 byte. */
+ expected_offset = CELL_PAYLOAD_SIZE - 1;
+ pad_offset = get_pad_cell_offset(payload_len - 5);
+ tt_int_op(pad_offset, OP_EQ, expected_offset);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ /* Now some arbitrary small payload length. The cell size is header + 10 +
+ * extra 4 bytes we keep so the offset should be there. */
+ expected_offset = RELAY_HEADER_SIZE + 10 + 4;
+ pad_offset = get_pad_cell_offset(10);
+ tt_int_op(pad_offset, OP_EQ, expected_offset);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ /* Data length of 0. */
+ expected_offset = RELAY_HEADER_SIZE + 4;
+ pad_offset = get_pad_cell_offset(0);
+ tt_int_op(pad_offset, OP_EQ, expected_offset);
+ tt_int_op(CELL_PAYLOAD_SIZE - pad_offset, OP_LE, CELL_PAYLOAD_SIZE);
+
+ done:
+ ;
+}
+
+static void
+test_cell_version_validation(void *arg)
+{
+ (void) arg;
+
+ /* We currently only support up to SENDME_MAX_SUPPORTED_VERSION so we are
+ * going to test the boundaries there. */
+
+ tt_assert(cell_version_can_be_handled(SENDME_MAX_SUPPORTED_VERSION));
+
+ /* Version below our supported should pass. */
+ tt_assert(cell_version_can_be_handled(SENDME_MAX_SUPPORTED_VERSION - 1));
+
+ /* Extra version from our supported should fail. */
+ tt_assert(!cell_version_can_be_handled(SENDME_MAX_SUPPORTED_VERSION + 1));
+
+ /* Simple check for version 0. */
+ tt_assert(cell_version_can_be_handled(0));
+
+ /* We MUST handle the default cell version that we emit or accept. */
+ tt_assert(cell_version_can_be_handled(SENDME_EMIT_MIN_VERSION_DEFAULT));
+ tt_assert(cell_version_can_be_handled(SENDME_ACCEPT_MIN_VERSION_DEFAULT));
+
+ done:
+ ;
+}
+
+/* check our decisions about how much stuff to put into relay cells. */
+static void
+test_package_payload_len(void *arg)
+{
+ (void)arg;
+ /* this is not a real circuit: it only has the fields needed for this
+ * test. */
+ circuit_t *c = tor_malloc_zero(sizeof(circuit_t));
+
+ /* check initial conditions. */
+ circuit_reset_sendme_randomness(c);
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_GE, CIRCWINDOW_INCREMENT / 2);
+ tt_int_op(c->send_randomness_after_n_cells, OP_LT, CIRCWINDOW_INCREMENT);
+
+ /* We have a bunch of cells before we need to send randomness, so the first
+ * few can be packaged full. */
+ int initial = c->send_randomness_after_n_cells;
+ size_t n = connection_edge_get_inbuf_bytes_to_package(10000, 0, c);
+ tt_uint_op(RELAY_PAYLOAD_SIZE, OP_EQ, n);
+ n = connection_edge_get_inbuf_bytes_to_package(95000, 1, c);
+ tt_uint_op(RELAY_PAYLOAD_SIZE, OP_EQ, n);
+ tt_int_op(c->send_randomness_after_n_cells, OP_EQ, initial - 2);
+
+ /* If package_partial isn't set, we won't package a partially full cell at
+ * all. */
+ n = connection_edge_get_inbuf_bytes_to_package(RELAY_PAYLOAD_SIZE-1, 0, c);
+ tt_int_op(n, OP_EQ, 0);
+ /* no change in our state, since nothing was sent. */
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_EQ, initial - 2);
+
+ /* If package_partial is set and the partial cell is not going to have
+ * _enough_ randomness, we package it, but we don't consider ourselves to
+ * have sent a sufficiently random cell. */
+ n = connection_edge_get_inbuf_bytes_to_package(RELAY_PAYLOAD_SIZE-1, 1, c);
+ tt_int_op(n, OP_EQ, RELAY_PAYLOAD_SIZE-1);
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_EQ, initial - 3);
+
+ /* Make sure we set have_set_sufficiently_random_cell as appropriate. */
+ n = connection_edge_get_inbuf_bytes_to_package(RELAY_PAYLOAD_SIZE-64, 1, c);
+ tt_int_op(n, OP_EQ, RELAY_PAYLOAD_SIZE-64);
+ tt_assert(c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_EQ, initial - 4);
+
+ /* Now let's look at what happens when we get down to zero. Since we have
+ * sent a sufficiently random cell, we will not force this one to have a gap.
+ */
+ c->send_randomness_after_n_cells = 0;
+ n = connection_edge_get_inbuf_bytes_to_package(10000, 1, c);
+ tt_int_op(n, OP_EQ, RELAY_PAYLOAD_SIZE);
+ /* Now these will be reset. */
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_GE,
+ CIRCWINDOW_INCREMENT / 2 - 1);
+
+ /* What would happen if we hadn't sent a sufficiently random cell? */
+ c->send_randomness_after_n_cells = 0;
+ n = connection_edge_get_inbuf_bytes_to_package(10000, 1, c);
+ const size_t reduced_payload_size = RELAY_PAYLOAD_SIZE - 4 - 16;
+ tt_int_op(n, OP_EQ, reduced_payload_size);
+ /* Now these will be reset. */
+ tt_assert(! c->have_sent_sufficiently_random_cell);
+ tt_int_op(c->send_randomness_after_n_cells, OP_GE,
+ CIRCWINDOW_INCREMENT / 2 - 1);
+
+ /* Here is a fun case: if it's time to package a small cell, then
+ * package_partial==0 should mean we accept that many bytes.
+ */
+ c->send_randomness_after_n_cells = 0;
+ n = connection_edge_get_inbuf_bytes_to_package(reduced_payload_size, 0, c);
+ tt_int_op(n, OP_EQ, reduced_payload_size);
+
+ done:
+ tor_free(c);
+}
+
+struct testcase_t sendme_tests[] = {
+ { "v1_record_digest", test_v1_record_digest, TT_FORK,
+ NULL, NULL },
+ { "v1_consensus_params", test_v1_consensus_params, TT_FORK,
+ NULL, NULL },
+ { "v1_build_cell", test_v1_build_cell, TT_FORK,
+ NULL, NULL },
+ { "cell_payload_pad", test_cell_payload_pad, TT_FORK,
+ NULL, NULL },
+ { "cell_version_validation", test_cell_version_validation, TT_FORK,
+ NULL, NULL },
+ { "package_payload_len", test_package_payload_len, 0, NULL, NULL },
+
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index b4fe6eef64..9c8703fa6f 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -58,14 +58,17 @@ trusteddirserver_get_by_v3_auth_digest_m(const char *digest)
}
/* Setup a minimal dirauth environment by initializing the SR state and
- * making sure the options are set to be an authority directory. */
+ * making sure the options are set to be an authority directory.
+ * You must only call this function once per process. */
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);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(mock_cert);
options->AuthoritativeDir = 1;
tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0);
@@ -310,6 +313,7 @@ test_get_start_time_of_current_run(void *arg)
retval = parse_rfc1123_time("Mon, 19 Apr 2015 23:00:00 UTC",
&mock_consensus.valid_after);
+ tt_int_op(retval, OP_EQ, 0);
retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:08:00 UTC",
&current_time);
@@ -424,7 +428,9 @@ test_sr_commit(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);
+ auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(auth_cert);
options->AuthoritativeDir = 1;
@@ -443,12 +449,12 @@ test_sr_commit(void *arg)
/* 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,
+ tt_assert(!fast_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, OP_EQ, our_commit->reveal_ts);
/* We should have a hashed reveal. */
- tt_assert(!tor_mem_is_zero(our_commit->hashed_reveal,
+ tt_assert(!fast_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
@@ -835,7 +841,9 @@ test_sr_setup_commits(void)
{ /* 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);
+ auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(auth_cert);
options->AuthoritativeDir = 1;
@@ -1073,73 +1081,99 @@ test_sr_get_majority_srv_from_votes(void *arg)
smartlist_free(votes);
}
+/* Testing sr_srv_dup(). */
static void
-test_utils(void *arg)
+test_sr_svr_dup(void *arg)
{
- (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, OP_EQ, srv->num_reveals);
- tt_mem_op(dup_srv->value, OP_EQ, srv->value, sizeof(srv->value));
- tor_free(srv);
- tor_free(dup_srv);
- }
+ 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 = sr_srv_dup(srv);
+ tt_assert(dup_srv);
+ tt_u64_op(dup_srv->num_reveals, OP_EQ, srv->num_reveals);
+ tt_mem_op(dup_srv->value, OP_EQ, srv->value, sizeof(srv->value));
- /* 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), OP_EQ, 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), OP_EQ, 0);
- }
+ done:
+ tor_free(srv);
+ tor_free(dup_srv);
+}
- /* Testing commit_is_authoritative(). */
- {
- crypto_pk_t *k = crypto_pk_new();
- char digest[DIGEST_LEN];
- sr_commit_t commit;
+/* 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. */
+static void
+test_commitments_are_the_same(void *arg)
+{
+ (void)arg;
+
+ /* 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), OP_EQ, 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), OP_EQ, 0);
- tt_assert(!crypto_pk_generate_key(k));
+ done:
+ return;
+}
- tt_int_op(0, OP_EQ, crypto_pk_get_digest(k, digest));
- memcpy(commit.rsa_identity, digest, sizeof(commit.rsa_identity));
- tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 1);
- /* Change the pubkey. */
- memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity));
- tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 0);
- crypto_pk_free(k);
- }
+/* Testing commit_is_authoritative(). */
+static void
+test_commit_is_authoritative(void *arg)
+{
+ (void)arg;
- /* Testing get_phase_str(). */
- {
- tt_str_op(get_phase_str(SR_PHASE_REVEAL), OP_EQ, "reveal");
- tt_str_op(get_phase_str(SR_PHASE_COMMIT), OP_EQ, "commit");
- }
+ 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, OP_EQ, crypto_pk_get_digest(k, digest));
+ memcpy(commit.rsa_identity, digest, sizeof(commit.rsa_identity));
+ tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 1);
+ /* Change the pubkey. */
+ memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity));
+ tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 0);
+
+ done:
+ crypto_pk_free(k);
+}
+
+static void
+test_get_phase_str(void *arg)
+{
+ (void)arg;
+
+ tt_str_op(get_phase_str(SR_PHASE_REVEAL), OP_EQ, "reveal");
+ tt_str_op(get_phase_str(SR_PHASE_COMMIT), OP_EQ, "commit");
+
+ done:
+ return;
+}
+
+/* Test utils that depend on authority state */
+static void
+test_utils_auth(void *arg)
+{
+ (void)arg;
+ init_authority_state();
/* Testing phase transition */
{
- init_authority_state();
set_sr_phase(SR_PHASE_COMMIT);
tt_int_op(is_phase_transition(SR_PHASE_REVEAL), OP_EQ, 1);
tt_int_op(is_phase_transition(SR_PHASE_COMMIT), OP_EQ, 0);
@@ -1150,8 +1184,193 @@ test_utils(void *arg)
tt_int_op(is_phase_transition(42), OP_EQ, 1);
}
+ /* Testing get, set, delete, clean SRVs */
+
+ {
+ /* Just set the previous SRV */
+ test_sr_setup_srv(0);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ state_del_previous_srv();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ }
+
+ {
+ /* Delete the SRVs one at a time */
+ test_sr_setup_srv(1);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ state_del_current_srv();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ state_del_previous_srv();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+
+ /* And in the opposite order */
+ test_sr_setup_srv(1);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ state_del_previous_srv();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ state_del_current_srv();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+
+ /* And both at once */
+ test_sr_setup_srv(1);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ sr_state_clean_srvs();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+
+ /* And do the gets and sets multiple times */
+ test_sr_setup_srv(1);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ state_del_previous_srv();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ state_del_previous_srv();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ sr_state_clean_srvs();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ state_del_current_srv();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ sr_state_clean_srvs();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ state_del_current_srv();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ }
+
+ {
+ /* Now set the SRVs to NULL instead */
+ test_sr_setup_srv(1);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ sr_state_set_current_srv(NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ sr_state_set_previous_srv(NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+
+ /* And in the opposite order */
+ test_sr_setup_srv(1);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ sr_state_set_previous_srv(NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ sr_state_set_current_srv(NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+
+ /* And both at once */
+ test_sr_setup_srv(1);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ sr_state_clean_srvs();
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+
+ /* And do the gets and sets multiple times */
+ test_sr_setup_srv(1);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ sr_state_set_previous_srv(NULL);
+ sr_state_set_previous_srv(NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ sr_state_set_current_srv(NULL);
+ sr_state_set_previous_srv(NULL);
+ sr_state_set_current_srv(NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ }
+
+ {
+ /* Now copy the values across */
+ test_sr_setup_srv(1);
+ /* Check that the pointers are non-NULL, and different from each other */
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE,
+ sr_state_get_current_srv());
+ /* Check that the content is different */
+ tt_mem_op(sr_state_get_previous_srv(), OP_NE,
+ sr_state_get_current_srv(), sizeof(sr_srv_t));
+ /* Set the current to the previous: the protocol goes the other way */
+ sr_state_set_current_srv(sr_srv_dup(sr_state_get_previous_srv()));
+ /* Check that the pointers are non-NULL, and different from each other */
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE,
+ sr_state_get_current_srv());
+ /* Check that the content is the same */
+ tt_mem_op(sr_state_get_previous_srv(), OP_EQ,
+ sr_state_get_current_srv(), sizeof(sr_srv_t));
+ }
+
+ {
+ /* Now copy a value onto itself */
+ test_sr_setup_srv(1);
+ /* Check that the pointers are non-NULL, and different from each other */
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE,
+ sr_state_get_current_srv());
+ /* Take a copy of the old value */
+ sr_srv_t old_current_srv;
+ memcpy(&old_current_srv, sr_state_get_current_srv(), sizeof(sr_srv_t));
+ /* Check that the content is different */
+ tt_mem_op(sr_state_get_previous_srv(), OP_NE,
+ sr_state_get_current_srv(), sizeof(sr_srv_t));
+ /* Set the current to the current: the protocol never replaces an SRV with
+ * the same value */
+ sr_state_set_current_srv(sr_srv_dup(sr_state_get_current_srv()));
+ /* Check that the pointers are non-NULL, and different from each other */
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_current_srv(), OP_NE, NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_NE,
+ sr_state_get_current_srv());
+ /* Check that the content is different between current and previous */
+ tt_mem_op(sr_state_get_previous_srv(), OP_NE,
+ sr_state_get_current_srv(), sizeof(sr_srv_t));
+ /* Check that the content is the same as the old content */
+ tt_mem_op(&old_current_srv, OP_EQ,
+ sr_state_get_current_srv(), sizeof(sr_srv_t));
+ }
+
+ /* I don't think we can say "expect a BUG()" in our tests. */
+#if 0
+ {
+ /* Now copy a value onto itself without sr_srv_dup().
+ * This should fail with a BUG() warning. */
+ test_sr_setup_srv(1);
+ sr_state_set_current_srv(sr_state_get_current_srv());
+ sr_state_set_previous_srv(sr_state_get_previous_srv());
+ }
+#endif /* 0 */
+
done:
- return;
+ sr_state_free_all();
}
static void
@@ -1159,6 +1378,7 @@ test_state_transition(void *arg)
{
sr_state_t *state = NULL;
time_t now = time(NULL);
+ sr_srv_t *cur = NULL;
(void) arg;
@@ -1197,44 +1417,47 @@ test_state_transition(void *arg)
/* 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(sr_state_get_current_srv());
+ /* Take a copy of the data, because the state owns the pointer */
+ cur = sr_srv_dup(sr_state_get_current_srv());
tt_assert(cur);
- /* After, current srv should be the previous and then set to NULL. */
+ /* After, the previous SRV should be the same as the old current SRV, and
+ * the current SRV should be set to NULL */
state_rotate_srv();
- prev = sr_state_get_previous_srv();
- tt_assert(prev == cur);
+ tt_mem_op(sr_state_get_previous_srv(), OP_EQ, cur, sizeof(sr_srv_t));
tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
sr_state_clean_srvs();
+ tor_free(cur);
}
/* 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);
+ tt_assert(sr_state_get_current_srv());
+ /* Take a copy of the data, because the state owns the pointer */
+ cur = sr_srv_dup(sr_state_get_current_srv());
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);
+ tt_mem_op(sr_state_get_previous_srv(), OP_EQ, cur, sizeof(sr_srv_t));
/* 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);
+ tt_mem_op(sr_state_get_current_srv(), OP_NE, cur, sizeof(sr_srv_t));
/* We should have our commitment alone. */
tt_int_op(digestmap_size(state->commits), OP_EQ, 1);
tt_int_op(state->n_reveal_rounds, OP_EQ, 0);
tt_int_op(state->n_commit_rounds, OP_EQ, 0);
/* 46 here since we were at 45 just before. */
tt_u64_op(state->n_protocol_runs, OP_EQ, 46);
+ tor_free(cur);
}
/* Cleanup of SRVs. */
@@ -1245,6 +1468,7 @@ test_state_transition(void *arg)
}
done:
+ tor_free(cur);
sr_state_free_all();
}
@@ -1440,7 +1664,13 @@ struct testcase_t sr_tests[] = {
{ "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 },
+ { "sr_svr_dup", test_sr_svr_dup, TT_FORK, NULL, NULL },
+ { "commitments_are_the_same", test_commitments_are_the_same, TT_FORK, NULL,
+ NULL },
+ { "commit_is_authoritative", test_commit_is_authoritative, TT_FORK, NULL,
+ NULL },
+ { "get_phase_str", test_get_phase_str, TT_FORK, NULL, NULL },
+ { "utils_auth", test_utils_auth, TT_FORK, NULL, NULL },
{ "state_transition", test_state_transition, TT_FORK, NULL, NULL },
{ "state_update", test_state_update, TT_FORK,
NULL, NULL },
diff --git a/src/test/test_slow.c b/src/test/test_slow.c
index bda67b2d92..d4d5b755a5 100644
--- a/src/test/test_slow.c
+++ b/src/test/test_slow.c
@@ -20,7 +20,9 @@
struct testgroup_t testgroups[] = {
{ "slow/crypto/", slow_crypto_tests },
- { "slow/util/", slow_util_tests },
+ { "slow/process/", slow_process_tests },
+ { "slow/prob_distr/", slow_stochastic_prob_distr_tests },
+ { "slow/ptr/", slow_ptr_tests },
END_OF_GROUPS
};
diff --git a/src/test/test_socks.c b/src/test/test_socks.c
index 3686e1036b..6d3ebd2320 100644
--- a/src/test/test_socks.c
+++ b/src/test/test_socks.c
@@ -4,7 +4,7 @@
/* See LICENSE for licensing information */
#include "core/or/or.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/proto/proto_socks.h"
diff --git a/src/test/test_status.c b/src/test/test_status.c
index 9c47469975..2fb2a7b24f 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -404,7 +404,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
{
case 0:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -412,7 +412,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 1:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -429,7 +429,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 3:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "rep_hist_log_circuit_handshake_stats"),
OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
@@ -442,13 +442,13 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 4:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "rep_hist_log_link_protocol_counts"),
OP_NE, NULL);
break;
case 5:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_str_op(format, OP_EQ, "DoS mitigation since startup:%s%s%s%s");
tt_str_op(va_arg(ap, char *), OP_EQ,
" 0 circuits killed with too many cells.");
@@ -574,7 +574,7 @@ NS(logv)(int severity, log_domain_mask_t domain, const char *funcname,
++NS(n_msgs);
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -709,7 +709,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
{
case 0:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -723,7 +723,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 1:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_accounting"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -889,7 +889,7 @@ NS(logv)(int severity, log_domain_mask_t domain, const char *funcname,
{
case 0:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -903,7 +903,7 @@ NS(logv)(int severity, log_domain_mask_t domain, const char *funcname,
break;
case 1:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -1038,7 +1038,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
{
case 0:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
@@ -1052,7 +1052,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
break;
case 1:
tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
diff --git a/src/test/test_switch_id.c b/src/test/test_switch_id.c
index baddf8d66e..19483713f7 100644
--- a/src/test/test_switch_id.c
+++ b/src/test/test_switch_id.c
@@ -87,7 +87,7 @@ main(int argc, char **argv)
fprintf(stderr, "This test is not supported on your OS.\n");
return 77;
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
const char *username;
const char *testname;
if (argc != 3) {
diff --git a/src/test/test_switch_id.sh b/src/test/test_switch_id.sh
index 79c44f2eb1..b13bf7602f 100755
--- a/src/test/test_switch_id.sh
+++ b/src/test/test_switch_id.sh
@@ -1,11 +1,11 @@
#!/bin/sh
-if test "`id -u`" != '0'; then
+if test "$(id -u)" != '0'; then
echo "This test only works when run as root. Skipping." >&2
exit 77
fi
-if test "`id -u nobody`" = ""; then
+if test "$(id -u nobody)" = ""; then
echo "This test requires that your system have a 'nobody' user. Sorry." >&2
exit 1
fi
diff --git a/src/test/test_token_bucket.c b/src/test/test_token_bucket.c
new file mode 100644
index 0000000000..31670718d9
--- /dev/null
+++ b/src/test/test_token_bucket.c
@@ -0,0 +1,152 @@
+/* Copyright (c) 2018-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_bwmgt.c
+ * \brief tests for bandwidth management / token bucket functions
+ */
+
+#define TOKEN_BUCKET_PRIVATE
+
+#include "core/or/or.h"
+#include "test/test.h"
+
+#include "lib/evloop/token_bucket.h"
+
+// an imaginary time, in timestamp units. Chosen so it will roll over.
+static const uint32_t START_TS = UINT32_MAX - 1000;
+static const uint32_t RATE = 10;
+static const uint32_t BURST = 50;
+
+static void
+test_token_bucket_ctr_init(void *arg)
+{
+ (void) arg;
+ token_bucket_ctr_t tb;
+
+ token_bucket_ctr_init(&tb, RATE, BURST, START_TS);
+ tt_uint_op(tb.cfg.rate, OP_EQ, RATE);
+ tt_uint_op(tb.cfg.burst, OP_EQ, BURST);
+ tt_uint_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS);
+ tt_int_op(tb.counter.bucket, OP_EQ, BURST);
+
+ done:
+ ;
+}
+
+static void
+test_token_bucket_ctr_adjust(void *arg)
+{
+ (void) arg;
+ token_bucket_ctr_t tb;
+
+ token_bucket_ctr_init(&tb, RATE, BURST, START_TS);
+
+ /* Increase burst. */
+ token_bucket_ctr_adjust(&tb, RATE, BURST * 2);
+ tt_uint_op(tb.cfg.rate, OP_EQ, RATE);
+ tt_uint_op(tb.counter.bucket, OP_EQ, BURST);
+ tt_uint_op(tb.cfg.burst, OP_EQ, BURST * 2);
+
+ /* Decrease burst but still above bucket value. */
+ token_bucket_ctr_adjust(&tb, RATE, BURST + 10);
+ tt_uint_op(tb.cfg.rate, OP_EQ, RATE);
+ tt_uint_op(tb.counter.bucket, OP_EQ, BURST);
+ tt_uint_op(tb.cfg.burst, OP_EQ, BURST + 10);
+
+ /* Decrease burst below bucket value. */
+ token_bucket_ctr_adjust(&tb, RATE, BURST - 1);
+ tt_uint_op(tb.cfg.rate, OP_EQ, RATE);
+ tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1);
+ tt_uint_op(tb.cfg.burst, OP_EQ, BURST - 1);
+
+ /* Change rate. */
+ token_bucket_ctr_adjust(&tb, RATE * 2, BURST);
+ tt_uint_op(tb.cfg.rate, OP_EQ, RATE * 2);
+ tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1);
+ tt_uint_op(tb.cfg.burst, OP_EQ, BURST);
+
+ done:
+ ;
+}
+
+static void
+test_token_bucket_ctr_dec(void *arg)
+{
+ (void) arg;
+ token_bucket_ctr_t tb;
+
+ token_bucket_ctr_init(&tb, RATE, BURST, START_TS);
+
+ /* Simple decrement by one. */
+ tt_uint_op(0, OP_EQ, token_bucket_ctr_dec(&tb, 1));
+ tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1);
+
+ /* Down to 0. Becomes empty. */
+ tt_uint_op(true, OP_EQ, token_bucket_ctr_dec(&tb, BURST - 1));
+ tt_uint_op(tb.counter.bucket, OP_EQ, 0);
+
+ /* Reset and try to underflow. */
+ token_bucket_ctr_init(&tb, RATE, BURST, START_TS);
+ tt_uint_op(true, OP_EQ, token_bucket_ctr_dec(&tb, BURST + 1));
+ tt_int_op(tb.counter.bucket, OP_EQ, -1);
+
+ /* Keep underflowing shouldn't flag the bucket as empty. */
+ tt_uint_op(false, OP_EQ, token_bucket_ctr_dec(&tb, BURST));
+ tt_int_op(tb.counter.bucket, OP_EQ, - (int32_t) (BURST + 1));
+
+ done:
+ ;
+}
+
+static void
+test_token_bucket_ctr_refill(void *arg)
+{
+ (void) arg;
+ token_bucket_ctr_t tb;
+
+ token_bucket_ctr_init(&tb, RATE, BURST, START_TS);
+
+ /* Reduce of half the bucket and let a single second go before refill. */
+ token_bucket_ctr_dec(&tb, BURST / 2);
+ tt_int_op(tb.counter.bucket, OP_EQ, BURST / 2);
+ token_bucket_ctr_refill(&tb, START_TS + 1);
+ tt_int_op(tb.counter.bucket, OP_EQ, (BURST / 2) + RATE);
+ tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 1);
+
+ /* No time change, nothing should move. */
+ token_bucket_ctr_refill(&tb, START_TS + 1);
+ tt_int_op(tb.counter.bucket, OP_EQ, (BURST / 2) + RATE);
+ tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 1);
+
+ /* Add 99 seconds, bucket should be back to a full BURST. */
+ token_bucket_ctr_refill(&tb, START_TS + 99);
+ tt_int_op(tb.counter.bucket, OP_EQ, BURST);
+ tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 99);
+
+ /* Empty bucket at once. */
+ token_bucket_ctr_dec(&tb, BURST);
+ tt_int_op(tb.counter.bucket, OP_EQ, 0);
+ /* On second passes. */
+ token_bucket_ctr_refill(&tb, START_TS + 100);
+ tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 100);
+ tt_int_op(tb.counter.bucket, OP_EQ, RATE);
+ /* A second second passes. */
+ token_bucket_ctr_refill(&tb, START_TS + 101);
+ tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 101);
+ tt_int_op(tb.counter.bucket, OP_EQ, RATE * 2);
+
+ done:
+ ;
+}
+
+#define TOKEN_BUCKET(name) \
+ { #name, test_token_bucket_ ## name , 0, NULL, NULL }
+
+struct testcase_t token_bucket_tests[] = {
+ TOKEN_BUCKET(ctr_init),
+ TOKEN_BUCKET(ctr_adjust),
+ TOKEN_BUCKET(ctr_dec),
+ TOKEN_BUCKET(ctr_refill),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index 853abc4f91..d59b8e001d 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -230,7 +230,7 @@ test_tortls_tor_tls_get_error(void *data)
crypto_pk_free(key2);
tor_tls_free(tls);
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
static void
test_tortls_x509_cert_get_id_digests(void *ignored)
@@ -347,7 +347,7 @@ test_tortls_server_got_renegotiate(void *ignored)
done:
tor_free(tls);
}
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
static void
test_tortls_evaluate_ecgroup_for_tls(void *ignored)
@@ -598,7 +598,7 @@ struct testcase_t tortls_tests[] = {
LOCAL_TEST_CASE(get_forced_write_size, 0),
LOCAL_TEST_CASE(used_v1_handshake, TT_FORK),
LOCAL_TEST_CASE(server_got_renegotiate, 0),
-#endif
+#endif /* defined(ENABLE_OPENSSL) */
LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0),
LOCAL_TEST_CASE(double_init, TT_FORK),
LOCAL_TEST_CASE(address, TT_FORK),
diff --git a/src/test/test_tortls.h b/src/test/test_tortls.h
index 1a8b117d0f..4567b9f6a0 100644
--- a/src/test/test_tortls.h
+++ b/src/test/test_tortls.h
@@ -10,4 +10,4 @@ extern const char *notCompletelyValidCertString;
extern const char *validCertString;
extern const char *caCertString;
-#endif
+#endif /* !defined(TEST_TORTLS_H) */
diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c
index f039980a25..93135d0b19 100644
--- a/src/test/test_tortls_openssl.c
+++ b/src/test/test_tortls_openssl.c
@@ -133,7 +133,7 @@ library_init(void)
#else
SSL_library_init();
SSL_load_error_strings();
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
}
static void
@@ -477,7 +477,7 @@ fake_x509_free(X509 *cert)
tor_free(cert);
}
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 3a0eb15157..aebefe64c5 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -6,22 +6,24 @@
#include "orconfig.h"
#define COMPAT_PRIVATE
#define COMPAT_TIME_PRIVATE
-#define CONTROL_PRIVATE
#define UTIL_PRIVATE
#define UTIL_MALLOC_PRIVATE
#define SOCKET_PRIVATE
-#define SUBPROCESS_PRIVATE
+#define PROCESS_WIN32_PRIVATE
#include "lib/testsupport/testsupport.h"
#include "core/or/or.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "app/config/config.h"
#include "feature/control/control.h"
+#include "feature/control/control_proto.h"
#include "feature/client/transports.h"
#include "lib/crypt_ops/crypto_format.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/defs/time.h"
#include "test/test.h"
#include "lib/memarea/memarea.h"
#include "lib/process/waitpid.h"
+#include "lib/process/process_win32.h"
#include "test/log_test_helpers.h"
#include "lib/compress/compress.h"
#include "lib/compress/compress_zstd.h"
@@ -30,7 +32,6 @@
#include "lib/fs/winlib.h"
#include "lib/process/env.h"
#include "lib/process/pidfile.h"
-#include "lib/process/subprocess.h"
#include "lib/intmath/weakrng.h"
#include "lib/thread/numcpus.h"
#include "lib/math/fp.h"
@@ -39,6 +40,7 @@
#include "lib/time/tvdiff.h"
#include "lib/encoding/confline.h"
#include "lib/net/socketpair.h"
+#include "lib/malloc/map_anon.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
@@ -58,6 +60,12 @@
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
#ifdef _WIN32
#include <tchar.h>
@@ -69,6 +77,28 @@
#define INFINITY_DBL ((double)INFINITY)
#define NAN_DBL ((double)NAN)
+/** Test the tor_isinf() wrapper */
+static void
+test_tor_isinf(void *arg)
+{
+ (void) arg;
+
+ tt_assert(tor_isinf(INFINITY_DBL));
+
+ tt_assert(!tor_isinf(NAN_DBL));
+ tt_assert(!tor_isinf(DBL_EPSILON));
+ tt_assert(!tor_isinf(DBL_MAX));
+ tt_assert(!tor_isinf(DBL_MIN));
+
+ tt_assert(!tor_isinf(0.0));
+ tt_assert(!tor_isinf(0.1));
+ tt_assert(!tor_isinf(3));
+ tt_assert(!tor_isinf(3.14));
+
+ done:
+ ;
+}
+
/* XXXX this is a minimal wrapper to make the unit tests compile with the
* changed tor_timegm interface. */
static time_t
@@ -404,7 +434,6 @@ test_util_time(void *arg)
/* 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 */
@@ -2058,14 +2087,14 @@ test_util_strmisc(void *arg)
/* Test mem_is_zero */
memset(buf,0,128);
buf[128] = 'x';
- tt_assert(tor_mem_is_zero(buf, 10));
- tt_assert(tor_mem_is_zero(buf, 20));
- tt_assert(tor_mem_is_zero(buf, 128));
- tt_assert(!tor_mem_is_zero(buf, 129));
+ tt_assert(fast_mem_is_zero(buf, 10));
+ tt_assert(fast_mem_is_zero(buf, 20));
+ tt_assert(fast_mem_is_zero(buf, 128));
+ tt_assert(!fast_mem_is_zero(buf, 129));
buf[60] = (char)255;
- tt_assert(!tor_mem_is_zero(buf, 128));
+ tt_assert(!fast_mem_is_zero(buf, 128));
buf[0] = (char)1;
- tt_assert(!tor_mem_is_zero(buf, 10));
+ tt_assert(!fast_mem_is_zero(buf, 10));
/* Test 'escaped' */
tt_ptr_op(escaped(NULL), OP_EQ, NULL);
@@ -2179,15 +2208,6 @@ test_util_strmisc(void *arg)
tt_int_op(strcmp_opt(NULL, "foo"), OP_LT, 0);
tt_int_op(strcmp_opt("foo", NULL), OP_GT, 0);
- /* Test strcmp_len */
- tt_int_op(strcmp_len("foo", "bar", 3), OP_GT, 0);
- tt_int_op(strcmp_len("foo", "bar", 2), OP_LT, 0);
- tt_int_op(strcmp_len("foo2", "foo1", 4), OP_GT, 0);
- tt_int_op(strcmp_len("foo2", "foo1", 3), OP_LT, 0); /* Really stop at len */
- tt_int_op(strcmp_len("foo2", "foo", 3), OP_EQ, 0); /* Really stop at len */
- tt_int_op(strcmp_len("blah", "", 4), OP_GT, 0);
- tt_int_op(strcmp_len("blah", "", 0), OP_EQ, 0);
-
done:
tor_free(cp_tmp);
}
@@ -3769,7 +3789,7 @@ test_util_memarea(void *arg)
tt_int_op(((uintptr_t)p3) % sizeof(void*),OP_EQ, 0);
tt_assert(!memarea_owns_ptr(area, p3+8192));
tt_assert(!memarea_owns_ptr(area, p3+30));
- tt_assert(tor_mem_is_zero(p2, 52));
+ tt_assert(fast_mem_is_zero(p2, 52));
/* Make sure we don't overalign. */
p1 = memarea_alloc(area, 1);
p2 = memarea_alloc(area, 1);
@@ -4050,6 +4070,13 @@ test_util_string_is_utf8(void *ptr)
tt_int_op(1, OP_EQ, string_is_utf8("ascii\x7f\n", 7));
tt_int_op(1, OP_EQ, string_is_utf8("Risqu\u00e9=1", 9));
+ /* Test the utf8_no_bom function */
+ tt_int_op(0, OP_EQ, string_is_utf8_no_bom("\uFEFF", 3));
+ tt_int_op(0, OP_EQ, string_is_utf8_no_bom("\uFFFE", 3));
+ tt_int_op(0, OP_EQ, string_is_utf8_no_bom("\uFEFFlove", 7));
+ tt_int_op(1, OP_EQ, string_is_utf8_no_bom("loveandrespect",
+ strlen("loveandrespect")));
+
// Validate exactly 'len' bytes.
tt_int_op(0, OP_EQ, string_is_utf8("\0\x80", 2));
tt_int_op(0, OP_EQ, string_is_utf8("Risqu\u00e9=1", 6));
@@ -4320,204 +4347,6 @@ test_util_load_win_lib(void *ptr)
}
#endif /* defined(_WIN32) */
-#ifndef _WIN32
-static void
-clear_hex_errno(char *hex_errno)
-{
- memset(hex_errno, '\0', HEX_ERRNO_SIZE + 1);
-}
-
-static void
-test_util_exit_status(void *ptr)
-{
- /* Leave an extra byte for a \0 so we can do string comparison */
- char hex_errno[HEX_ERRNO_SIZE + 1];
- int n;
-
- (void)ptr;
-
- clear_hex_errno(hex_errno);
- tt_str_op("",OP_EQ, hex_errno);
-
- clear_hex_errno(hex_errno);
- n = format_helper_exit_status(0, 0, hex_errno);
- tt_str_op("0/0\n",OP_EQ, hex_errno);
- tt_int_op(n,OP_EQ, strlen(hex_errno));
-
-#if SIZEOF_INT == 4
-
- clear_hex_errno(hex_errno);
- n = format_helper_exit_status(0, 0x7FFFFFFF, hex_errno);
- tt_str_op("0/7FFFFFFF\n",OP_EQ, hex_errno);
- tt_int_op(n,OP_EQ, strlen(hex_errno));
-
- clear_hex_errno(hex_errno);
- n = format_helper_exit_status(0xFF, -0x80000000, hex_errno);
- tt_str_op("FF/-80000000\n",OP_EQ, hex_errno);
- tt_int_op(n,OP_EQ, strlen(hex_errno));
- tt_int_op(n,OP_EQ, HEX_ERRNO_SIZE);
-
-#elif SIZEOF_INT == 8
-
- clear_hex_errno(hex_errno);
- n = format_helper_exit_status(0, 0x7FFFFFFFFFFFFFFF, hex_errno);
- tt_str_op("0/7FFFFFFFFFFFFFFF\n",OP_EQ, hex_errno);
- tt_int_op(n,OP_EQ, strlen(hex_errno));
-
- clear_hex_errno(hex_errno);
- n = format_helper_exit_status(0xFF, -0x8000000000000000, hex_errno);
- tt_str_op("FF/-8000000000000000\n",OP_EQ, hex_errno);
- tt_int_op(n,OP_EQ, strlen(hex_errno));
- tt_int_op(n,OP_EQ, HEX_ERRNO_SIZE);
-
-#endif /* SIZEOF_INT == 4 || ... */
-
- clear_hex_errno(hex_errno);
- n = format_helper_exit_status(0x7F, 0, hex_errno);
- tt_str_op("7F/0\n",OP_EQ, hex_errno);
- tt_int_op(n,OP_EQ, strlen(hex_errno));
-
- clear_hex_errno(hex_errno);
- n = format_helper_exit_status(0x08, -0x242, hex_errno);
- tt_str_op("8/-242\n",OP_EQ, hex_errno);
- tt_int_op(n,OP_EQ, strlen(hex_errno));
-
- clear_hex_errno(hex_errno);
- tt_str_op("",OP_EQ, hex_errno);
-
- done:
- ;
-}
-#endif /* !defined(_WIN32) */
-
-#ifndef _WIN32
-static void
-test_util_string_from_pipe(void *ptr)
-{
- int test_pipe[2] = {-1, -1};
- int retval = 0;
- enum stream_status status = IO_STREAM_TERM;
- ssize_t retlen;
- char buf[4] = { 0 };
-
- (void)ptr;
-
- errno = 0;
-
- /* Set up a pipe to test on */
- retval = pipe(test_pipe);
- tt_int_op(retval, OP_EQ, 0);
-
- /* Send in a string. */
- retlen = write(test_pipe[1], "ABC", 3);
- tt_int_op(retlen, OP_EQ, 3);
-
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
- tt_str_op(buf, OP_EQ, "ABC");
- errno = 0;
-
- /* Send in a string that contains a nul. */
- retlen = write(test_pipe[1], "AB\0", 3);
- tt_int_op(retlen, OP_EQ, 3);
-
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
- tt_str_op(buf, OP_EQ, "AB");
- errno = 0;
-
- /* Send in a string that contains a nul only. */
- retlen = write(test_pipe[1], "\0", 1);
- tt_int_op(retlen, OP_EQ, 1);
-
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
- tt_str_op(buf, OP_EQ, "");
- errno = 0;
-
- /* Send in a string that contains a trailing newline. */
- retlen = write(test_pipe[1], "AB\n", 3);
- tt_int_op(retlen, OP_EQ, 3);
-
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
- tt_str_op(buf, OP_EQ, "AB");
- errno = 0;
-
- /* Send in a string that contains a newline only. */
- retlen = write(test_pipe[1], "\n", 1);
- tt_int_op(retlen, OP_EQ, 1);
-
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
- tt_str_op(buf, OP_EQ, "");
- errno = 0;
-
- /* Send in a string and check that we nul terminate return values. */
- retlen = write(test_pipe[1], "AAA", 3);
- tt_int_op(retlen, OP_EQ, 3);
-
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
- tt_str_op(buf, OP_EQ, "AAA");
- tt_mem_op(buf, OP_EQ, "AAA\0", sizeof(buf));
- errno = 0;
-
- retlen = write(test_pipe[1], "B", 1);
- tt_int_op(retlen, OP_EQ, 1);
-
- memset(buf, '\xff', sizeof(buf));
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
- tt_str_op(buf, OP_EQ, "B");
- tt_mem_op(buf, OP_EQ, "B\0\xff\xff", sizeof(buf));
- errno = 0;
-
- /* Send in multiple lines. */
- retlen = write(test_pipe[1], "A\nB", 3);
- tt_int_op(retlen, OP_EQ, 3);
-
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
- tt_str_op(buf, OP_EQ, "A\nB");
- errno = 0;
-
- /* Send in a line and close */
- retlen = write(test_pipe[1], "AB", 2);
- tt_int_op(retlen, OP_EQ, 2);
- retval = close(test_pipe[1]);
- tt_int_op(retval, OP_EQ, 0);
- test_pipe[1] = -1;
-
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_OKAY);
- tt_str_op(buf, OP_EQ, "AB");
- errno = 0;
-
- /* Check for EOF */
- status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1);
- tt_int_op(errno, OP_EQ, 0);
- tt_int_op(status, OP_EQ, IO_STREAM_CLOSED);
- errno = 0;
-
- done:
- if (test_pipe[0] != -1)
- close(test_pipe[0]);
- if (test_pipe[1] != -1)
- close(test_pipe[1]);
-}
-
-#endif /* !defined(_WIN32) */
-
/**
* Test for format_hex_number_sigsafe()
*/
@@ -4612,57 +4441,6 @@ test_util_format_dec_number(void *ptr)
return;
}
-/**
- * Test that we can properly format a Windows command line
- */
-static void
-test_util_join_win_cmdline(void *ptr)
-{
- /* Based on some test cases from "Parsing C++ Command-Line Arguments" in
- * MSDN but we don't exercise all quoting rules because tor_join_win_cmdline
- * will try to only generate simple cases for the child process to parse;
- * i.e. we never embed quoted strings in arguments. */
-
- const char *argvs[][4] = {
- {"a", "bb", "CCC", NULL}, // Normal
- {NULL, NULL, NULL, NULL}, // Empty argument list
- {"", NULL, NULL, NULL}, // Empty argument
- {"\"a", "b\"b", "CCC\"", NULL}, // Quotes
- {"a\tbc", "dd dd", "E", NULL}, // Whitespace
- {"a\\\\\\b", "de fg", "H", NULL}, // Backslashes
- {"a\\\"b", "\\c", "D\\", NULL}, // Backslashes before quote
- {"a\\\\b c", "d", "E", NULL}, // Backslashes not before quote
- { NULL } // Terminator
- };
-
- const char *cmdlines[] = {
- "a bb CCC",
- "",
- "\"\"",
- "\\\"a b\\\"b CCC\\\"",
- "\"a\tbc\" \"dd dd\" E",
- "a\\\\\\b \"de fg\" H",
- "a\\\\\\\"b \\c D\\",
- "\"a\\\\b c\" d E",
- NULL // Terminator
- };
-
- int i;
- char *joined_argv = NULL;
-
- (void)ptr;
-
- for (i=0; cmdlines[i]!=NULL; i++) {
- log_info(LD_GENERAL, "Joining argvs[%d], expecting <%s>", i, cmdlines[i]);
- joined_argv = tor_join_win_cmdline(argvs[i]);
- tt_str_op(cmdlines[i],OP_EQ, joined_argv);
- tor_free(joined_argv);
- }
-
- done:
- tor_free(joined_argv);
-}
-
#define MAX_SPLIT_LINE_COUNT 4
struct split_lines_test_t {
const char *orig_line; // Line to be split (may contain \0's)
@@ -4670,67 +4448,6 @@ struct split_lines_test_t {
const char *split_line[MAX_SPLIT_LINE_COUNT]; // Split lines
};
-/**
- * Test that we properly split a buffer into lines
- */
-static void
-test_util_split_lines(void *ptr)
-{
- /* Test cases. orig_line of last test case must be NULL.
- * The last element of split_line[i] must be NULL. */
- struct split_lines_test_t tests[] = {
- {"", 0, {NULL}},
- {"foo", 3, {"foo", NULL}},
- {"\n\rfoo\n\rbar\r\n", 12, {"foo", "bar", NULL}},
- {"fo o\r\nb\tar", 10, {"fo o", "b.ar", NULL}},
- {"\x0f""f\0o\0\n\x01""b\0r\0\r", 12, {".f.o.", ".b.r.", NULL}},
- {"line 1\r\nline 2", 14, {"line 1", "line 2", NULL}},
- {"line 1\r\n\r\nline 2", 16, {"line 1", "line 2", NULL}},
- {"line 1\r\n\r\r\r\nline 2", 18, {"line 1", "line 2", NULL}},
- {"line 1\r\n\n\n\n\rline 2", 18, {"line 1", "line 2", NULL}},
- {"line 1\r\n\r\t\r\nline 3", 18, {"line 1", ".", "line 3", NULL}},
- {"\n\t\r\t\nline 3", 11, {".", ".", "line 3", NULL}},
- {NULL, 0, { NULL }}
- };
-
- int i, j;
- char *orig_line=NULL;
- smartlist_t *sl=NULL;
-
- (void)ptr;
-
- for (i=0; tests[i].orig_line; i++) {
- sl = smartlist_new();
- /* Allocate space for string and trailing NULL */
- orig_line = tor_memdup(tests[i].orig_line, tests[i].orig_length + 1);
- tor_split_lines(sl, orig_line, tests[i].orig_length);
-
- j = 0;
- log_info(LD_GENERAL, "Splitting test %d of length %d",
- i, tests[i].orig_length);
- SMARTLIST_FOREACH_BEGIN(sl, const char *, line) {
- /* Check we have not got too many lines */
- tt_int_op(MAX_SPLIT_LINE_COUNT, OP_GT, j);
- /* Check that there actually should be a line here */
- tt_ptr_op(tests[i].split_line[j], OP_NE, NULL);
- log_info(LD_GENERAL, "Line %d of test %d, should be <%s>",
- j, i, tests[i].split_line[j]);
- /* Check that the line is as expected */
- tt_str_op(line,OP_EQ, tests[i].split_line[j]);
- j++;
- } SMARTLIST_FOREACH_END(line);
- /* Check that we didn't miss some lines */
- tt_ptr_op(NULL,OP_EQ, tests[i].split_line[j]);
- tor_free(orig_line);
- smartlist_free(sl);
- sl = NULL;
- }
-
- done:
- tor_free(orig_line);
- smartlist_free(sl);
-}
-
static void
test_util_di_ops(void *arg)
{
@@ -5682,6 +5399,13 @@ test_util_socketpair(void *arg)
tt_skip();
}
#endif /* defined(__FreeBSD__) */
+#ifdef ENETUNREACH
+ if (ersatz && socketpair_result == -ENETUNREACH) {
+ /* We can also fail with -ENETUNREACH if we have no network stack at
+ * all. */
+ tt_skip();
+ }
+#endif /* defined(ENETUNREACH) */
tt_int_op(0, OP_EQ, socketpair_result);
tt_assert(SOCKET_OK(fds[0]));
@@ -5834,6 +5558,18 @@ test_util_ipv4_validation(void *arg)
}
static void
+test_util_ipv6_validation(void *arg)
+{
+ (void)arg;
+
+ tt_assert(string_is_valid_ipv6_address("2a00:1450:401b:800::200e"));
+ tt_assert(!string_is_valid_ipv6_address("11:22::33:44:"));
+
+ done:
+ return;
+}
+
+static void
test_util_writepid(void *arg)
{
(void) arg;
@@ -6387,9 +6123,9 @@ test_util_log_mallinfo(void *arg)
} else {
tt_u64_op(mem1, OP_LT, mem2);
}
-#else
+#else /* !defined(HAVE_MALLINFO) */
tt_skip();
-#endif
+#endif /* defined(HAVE_MALLINFO) */
done:
teardown_capture_of_logs();
tor_free(log1);
@@ -6397,6 +6133,130 @@ test_util_log_mallinfo(void *arg)
tor_free(mem);
}
+static void
+test_util_map_anon(void *arg)
+{
+ (void)arg;
+ char *ptr = NULL;
+ size_t sz = 16384;
+ unsigned inherit=0;
+
+ /* Basic checks. */
+ ptr = tor_mmap_anonymous(sz, 0, &inherit);
+ tt_ptr_op(ptr, OP_NE, 0);
+ tt_int_op(inherit, OP_EQ, INHERIT_RES_KEEP);
+ ptr[sz-1] = 3;
+ tt_int_op(ptr[0], OP_EQ, 0);
+ tt_int_op(ptr[sz-2], OP_EQ, 0);
+ tt_int_op(ptr[sz-1], OP_EQ, 3);
+
+ /* Try again, with a private (non-swappable) mapping. */
+ tor_munmap_anonymous(ptr, sz);
+ ptr = tor_mmap_anonymous(sz, ANONMAP_PRIVATE, &inherit);
+ tt_ptr_op(ptr, OP_NE, 0);
+ tt_int_op(inherit, OP_EQ, INHERIT_RES_KEEP);
+ ptr[sz-1] = 10;
+ tt_int_op(ptr[0], OP_EQ, 0);
+ tt_int_op(ptr[sz/2], OP_EQ, 0);
+ tt_int_op(ptr[sz-1], OP_EQ, 10);
+
+ /* Now let's test a drop-on-fork mapping. */
+ tor_munmap_anonymous(ptr, sz);
+ ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT, &inherit);
+ tt_ptr_op(ptr, OP_NE, 0);
+ ptr[sz-1] = 10;
+ tt_int_op(ptr[0], OP_EQ, 0);
+ tt_int_op(ptr[sz/2], OP_EQ, 0);
+ tt_int_op(ptr[sz-1], OP_EQ, 10);
+
+ done:
+ tor_munmap_anonymous(ptr, sz);
+}
+
+static void
+test_util_map_anon_nofork(void *arg)
+{
+ (void)arg;
+#ifdef _WIN32
+ /* The operating system doesn't support forking. */
+ tt_skip();
+ done:
+ ;
+#else /* !defined(_WIN32) */
+ /* We have the right OS support. We're going to try marking the buffer as
+ * either zero-on-fork or as drop-on-fork, whichever is supported. Then we
+ * will fork and send a byte back to the parent process. This will either
+ * crash, or send zero. */
+
+ char *ptr = NULL;
+ const char TEST_VALUE = 0xd0;
+ size_t sz = 16384;
+ int pipefd[2] = {-1, -1};
+ unsigned inherit=0;
+
+ tor_munmap_anonymous(ptr, sz);
+ ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT, &inherit);
+ tt_ptr_op(ptr, OP_NE, 0);
+ memset(ptr, (uint8_t)TEST_VALUE, sz);
+
+ tt_int_op(0, OP_EQ, pipe(pipefd));
+ pid_t child = fork();
+ if (child == 0) {
+ /* We're in the child. */
+ close(pipefd[0]);
+ ssize_t r = write(pipefd[1], &ptr[sz-1], 1); /* This may crash. */
+ close(pipefd[1]);
+ if (r < 0)
+ exit(1);
+ exit(0);
+ }
+ tt_int_op(child, OP_GT, 0);
+ /* In the parent. */
+ close(pipefd[1]);
+ pipefd[1] = -1;
+ char buf[1];
+ ssize_t r = read(pipefd[0], buf, 1);
+
+ if (inherit == INHERIT_RES_ZERO) {
+ // We should be seeing clear-on-fork behavior.
+ tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
+ tt_int_op(buf[0], OP_EQ, 0); // that byte should be zero.
+ } else if (inherit == INHERIT_RES_DROP) {
+ // We should be seeing noinherit behavior.
+ tt_int_op(r, OP_LE, 0); // child said nothing; it should have crashed.
+ } else {
+ // noinherit isn't implemented.
+ tt_int_op(inherit, OP_EQ, INHERIT_RES_KEEP);
+ tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
+ tt_int_op(buf[0], OP_EQ, TEST_VALUE); // that byte should be TEST_VALUE.
+ }
+
+ int ws;
+ waitpid(child, &ws, 0);
+
+#ifndef NOINHERIT_CAN_FAIL
+ /* Only if NOINHERIT_CAN_FAIL should it be possible for us to get
+ * INHERIT_KEEP behavior in this case. */
+ tt_int_op(inherit, OP_NE, INHERIT_RES_KEEP);
+#else
+ if (inherit == INHERIT_RES_KEEP) {
+ /* Call this test "skipped", not "passed", since noinherit wasn't
+ * implemented. */
+ tt_skip();
+ }
+#endif /* !defined(NOINHERIT_CAN_FAIL) */
+
+ done:
+ tor_munmap_anonymous(ptr, sz);
+ if (pipefd[0] >= 0) {
+ close(pipefd[0]);
+ }
+ if (pipefd[1] >= 0) {
+ close(pipefd[1]);
+ }
+#endif /* defined(_WIN32) */
+}
+
#define UTIL_LEGACY(name) \
{ #name, test_util_ ## name , 0, NULL, NULL }
@@ -6490,12 +6350,8 @@ struct testcase_t util_tests[] = {
UTIL_TEST(nowrap_math, 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(string_from_pipe, 0),
UTIL_TEST(format_hex_number, 0),
UTIL_TEST(format_dec_number, 0),
- UTIL_TEST(join_win_cmdline, 0),
- UTIL_TEST(split_lines, 0),
UTIL_TEST(n_bits_set, 0),
UTIL_TEST(eat_whitespace, 0),
UTIL_TEST(sl_new_from_text_lines, 0),
@@ -6512,6 +6368,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(mathlog, 0),
UTIL_TEST(fraction, 0),
UTIL_TEST(weak_random, 0),
+ { "tor_isinf", test_tor_isinf, TT_FORK, NULL, NULL },
{ "socket_ipv4", test_util_socket, TT_FORK, &passthrough_setup,
(void*)"4" },
{ "socket_ipv6", test_util_socket, TT_FORK,
@@ -6524,6 +6381,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(hostname_validation, 0),
UTIL_TEST(dest_validation_edgecase, 0),
UTIL_TEST(ipv4_validation, 0),
+ UTIL_TEST(ipv6_validation, 0),
UTIL_TEST(writepid, 0),
UTIL_TEST(get_avail_disk_space, 0),
UTIL_TEST(touch_file, 0),
@@ -6536,5 +6394,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(htonll, 0),
UTIL_TEST(get_unquoted_path, 0),
UTIL_TEST(log_mallinfo, 0),
+ UTIL_TEST(map_anon, 0),
+ UTIL_TEST(map_anon_nofork, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c
index d344d0e95c..2859da66b2 100644
--- a/src/test/test_util_format.c
+++ b/src/test/test_util_format.c
@@ -346,7 +346,7 @@ test_util_format_base32_decode(void *arg)
const char *src = "mjwgc2dcnrswqmjs";
ret = base32_decode(dst, strlen(expected), src, strlen(src));
- tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(ret, OP_EQ, 10);
tt_str_op(expected, OP_EQ, dst);
}
@@ -357,7 +357,7 @@ test_util_format_base32_decode(void *arg)
const char *src = "mjwgc2dcnrswq";
ret = base32_decode(dst, strlen(expected), src, strlen(src));
- tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(ret, OP_EQ, 8);
tt_mem_op(expected, OP_EQ, dst, strlen(expected));
}
@@ -367,7 +367,7 @@ test_util_format_base32_decode(void *arg)
ret = base32_decode(dst, real_dstlen, "#abcde", 6);
tt_int_op(ret, OP_EQ, -1);
/* Make sure the destination buffer has been zeroed even on error. */
- tt_int_op(tor_mem_is_zero(dst, real_dstlen), OP_EQ, 1);
+ tt_int_op(fast_mem_is_zero(dst, real_dstlen), OP_EQ, 1);
}
done:
@@ -392,10 +392,13 @@ test_util_format_encoded_size(void *arg)
base64_encode(outbuf, sizeof(outbuf), (char *)inbuf, i, 0);
tt_int_op(strlen(outbuf), OP_EQ, base64_encode_size(i, 0));
+ tt_int_op(i, OP_LE, base64_decode_maxsize(strlen(outbuf)));
+
base64_encode(outbuf, sizeof(outbuf), (char *)inbuf, i,
BASE64_ENCODE_MULTILINE);
tt_int_op(strlen(outbuf), OP_EQ,
base64_encode_size(i, BASE64_ENCODE_MULTILINE));
+ tt_int_op(i, OP_LE, base64_decode_maxsize(strlen(outbuf)));
}
done:
@@ -417,4 +420,3 @@ struct testcase_t util_format_tests[] = {
{ "encoded_size", test_util_format_encoded_size, 0, NULL, NULL },
END_OF_TESTCASES
};
-
diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c
deleted file mode 100644
index 29e30eaa11..0000000000
--- a/src/test/test_util_slow.c
+++ /dev/null
@@ -1,396 +0,0 @@
-/* Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-#include "orconfig.h"
-#define UTIL_PRIVATE
-#define SUBPROCESS_PRIVATE
-#include "lib/crypt_ops/crypto_cipher.h"
-#include "lib/log/log.h"
-#include "lib/process/subprocess.h"
-#include "lib/process/waitpid.h"
-#include "lib/string/printf.h"
-#include "lib/time/compat_time.h"
-#include "test/test.h"
-
-#include <errno.h>
-#include <string.h>
-
-#ifndef BUILDDIR
-#define BUILDDIR "."
-#endif
-
-#ifdef _WIN32
-#define notify_pending_waitpid_callbacks() STMT_NIL
-#define TEST_CHILD "test-child.exe"
-#define EOL "\r\n"
-#else
-#define TEST_CHILD (BUILDDIR "/src/test/test-child")
-#define EOL "\n"
-#endif /* defined(_WIN32) */
-
-#ifdef _WIN32
-/* I've assumed Windows doesn't have the gap between fork and exec
- * that causes the race condition on unix-like platforms */
-#define MATCH_PROCESS_STATUS(s1,s2) ((s1) == (s2))
-
-#else /* !(defined(_WIN32)) */
-/* work around a race condition of the timing of SIGCHLD handler updates
- * to the process_handle's fields, and checks of those fields
- *
- * TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to
- * PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */
-#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING (PROCESS_STATUS_RUNNING+1)
-#define IS_RUNNING_OR_NOTRUNNING(s) \
- ((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING)
-/* well, this is ugly */
-#define MATCH_PROCESS_STATUS(s1,s2) \
- ( (s1) == (s2) \
- ||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \
- && IS_RUNNING_OR_NOTRUNNING(s2)) \
- ||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \
- && IS_RUNNING_OR_NOTRUNNING(s1)))
-
-#endif /* defined(_WIN32) */
-
-/** Helper function for testing tor_spawn_background */
-static void
-run_util_spawn_background(const char *argv[], const char *expected_out,
- const char *expected_err, int expected_exit,
- int expected_status)
-{
- int retval, exit_code;
- ssize_t pos;
- process_handle_t *process_handle=NULL;
- char stdout_buf[100], stderr_buf[100];
- int status;
-
- /* Start the program */
-#ifdef _WIN32
- status = tor_spawn_background(NULL, argv, NULL, &process_handle);
-#else
- status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
-#endif
-
- notify_pending_waitpid_callbacks();
-
- /* the race condition doesn't affect status,
- * because status isn't updated by the SIGCHLD handler,
- * but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
- tt_assert(MATCH_PROCESS_STATUS(expected_status, status));
- if (status == PROCESS_STATUS_ERROR) {
- tt_ptr_op(process_handle, OP_EQ, NULL);
- return;
- }
-
- tt_ptr_op(process_handle, OP_NE, NULL);
-
- /* When a spawned process forks, fails, then exits very quickly,
- * (this typically occurs when exec fails)
- * there is a race condition between the SIGCHLD handler
- * updating the process_handle's fields, and this test
- * checking the process status in those fields.
- * The SIGCHLD update can occur before or after the code below executes.
- * This causes intermittent failures in spawn_background_fail(),
- * typically when the machine is under load.
- * We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */
-
- /* the race condition affects the change in
- * process_handle->status from RUNNING to NOTRUNNING */
- tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status));
-
-#ifndef _WIN32
- notify_pending_waitpid_callbacks();
- /* the race condition affects the change in
- * process_handle->waitpid_cb to NULL,
- * so we skip the check if expected_status is ambiguous,
- * that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
- tt_assert(process_handle->waitpid_cb != NULL
- || expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING);
-#endif /* !defined(_WIN32) */
-
-#ifdef _WIN32
- tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
- tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
- tt_assert(process_handle->stdin_pipe != INVALID_HANDLE_VALUE);
-#else
- tt_assert(process_handle->stdout_pipe >= 0);
- tt_assert(process_handle->stderr_pipe >= 0);
- tt_assert(process_handle->stdin_pipe >= 0);
-#endif /* defined(_WIN32) */
-
- /* Check stdout */
- pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
- sizeof(stdout_buf) - 1);
- tt_assert(pos >= 0);
- stdout_buf[pos] = '\0';
- tt_int_op(strlen(expected_out),OP_EQ, pos);
- tt_str_op(expected_out,OP_EQ, stdout_buf);
-
- notify_pending_waitpid_callbacks();
-
- /* Check it terminated correctly */
- retval = tor_get_exit_code(process_handle, 1, &exit_code);
- tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
- tt_int_op(expected_exit,OP_EQ, exit_code);
- // TODO: Make test-child exit with something other than 0
-
-#ifndef _WIN32
- notify_pending_waitpid_callbacks();
- tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
-#endif
-
- /* Check stderr */
- pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
- sizeof(stderr_buf) - 1);
- tt_assert(pos >= 0);
- stderr_buf[pos] = '\0';
- tt_str_op(expected_err,OP_EQ, stderr_buf);
- tt_int_op(strlen(expected_err),OP_EQ, pos);
-
- notify_pending_waitpid_callbacks();
-
- done:
- if (process_handle)
- tor_process_handle_destroy(process_handle, 1);
-}
-
-/** Check that we can launch a process and read the output */
-static void
-test_util_spawn_background_ok(void *ptr)
-{
- const char *argv[] = {TEST_CHILD, "--test", NULL};
- const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
- const char *expected_err = "ERR"EOL;
-
- (void)ptr;
-
- run_util_spawn_background(argv, expected_out, expected_err, 0,
- PROCESS_STATUS_RUNNING);
-}
-
-/** Check that failing to find the executable works as expected */
-static void
-test_util_spawn_background_fail(void *ptr)
-{
- const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
- const char *expected_err = "";
- char expected_out[1024];
- char code[32];
-#ifdef _WIN32
- const int expected_status = PROCESS_STATUS_ERROR;
-#else
- /* TODO: Once we can signal failure to exec, set this to be
- * PROCESS_STATUS_RUNNING_OR_ERROR */
- const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING;
-#endif /* defined(_WIN32) */
-
- memset(expected_out, 0xf0, sizeof(expected_out));
- memset(code, 0xf0, sizeof(code));
-
- (void)ptr;
-
- tor_snprintf(code, sizeof(code), "%x/%x",
- 9 /* CHILD_STATE_FAILEXEC */ , ENOENT);
- tor_snprintf(expected_out, sizeof(expected_out),
- "ERR: Failed to spawn background process - code %s\n", code);
-
- run_util_spawn_background(argv, expected_out, expected_err, 255,
- expected_status);
-}
-
-/** Test that reading from a handle returns a partial read rather than
- * blocking */
-static void
-test_util_spawn_background_partial_read_impl(int exit_early)
-{
- const int expected_exit = 0;
- const int expected_status = PROCESS_STATUS_RUNNING;
-
- int retval, exit_code;
- ssize_t pos = -1;
- process_handle_t *process_handle=NULL;
- int status;
- char stdout_buf[100], stderr_buf[100];
-
- const char *argv[] = {TEST_CHILD, "--test", NULL};
- const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
- "DONE" EOL,
- NULL };
- const char *expected_err = "ERR" EOL;
-
-#ifndef _WIN32
- int eof = 0;
-#endif
- int expected_out_ctr;
-
- if (exit_early) {
- argv[1] = "--hang";
- expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
- }
-
- /* Start the program */
-#ifdef _WIN32
- status = tor_spawn_background(NULL, argv, NULL, &process_handle);
-#else
- status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
-#endif
- tt_int_op(expected_status,OP_EQ, status);
- tt_assert(process_handle);
- tt_int_op(expected_status,OP_EQ, process_handle->status);
-
- /* Check stdout */
- for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) {
-#ifdef _WIN32
- pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
- sizeof(stdout_buf) - 1, NULL);
-#else
- /* Check that we didn't read the end of file last time */
- tt_assert(!eof);
- pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
- sizeof(stdout_buf) - 1, NULL, &eof);
-#endif /* defined(_WIN32) */
- log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
-
- /* We would have blocked, keep on trying */
- if (0 == pos)
- continue;
-
- tt_assert(pos > 0);
- stdout_buf[pos] = '\0';
- tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf);
- tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos);
- expected_out_ctr++;
- }
-
- if (exit_early) {
- tor_process_handle_destroy(process_handle, 1);
- process_handle = NULL;
- goto done;
- }
-
- /* The process should have exited without writing more */
-#ifdef _WIN32
- pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
- sizeof(stdout_buf) - 1,
- process_handle);
- tt_int_op(0,OP_EQ, pos);
-#else /* !(defined(_WIN32)) */
- if (!eof) {
- /* We should have got all the data, but maybe not the EOF flag */
- pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
- sizeof(stdout_buf) - 1,
- process_handle, &eof);
- tt_int_op(0,OP_EQ, pos);
- tt_assert(eof);
- }
- /* Otherwise, we got the EOF on the last read */
-#endif /* defined(_WIN32) */
-
- /* Check it terminated correctly */
- retval = tor_get_exit_code(process_handle, 1, &exit_code);
- tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
- tt_int_op(expected_exit,OP_EQ, exit_code);
-
- // TODO: Make test-child exit with something other than 0
-
- /* Check stderr */
- pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
- sizeof(stderr_buf) - 1);
- tt_assert(pos >= 0);
- stderr_buf[pos] = '\0';
- tt_str_op(expected_err,OP_EQ, stderr_buf);
- tt_int_op(strlen(expected_err),OP_EQ, pos);
-
- done:
- tor_process_handle_destroy(process_handle, 1);
-}
-
-static void
-test_util_spawn_background_partial_read(void *arg)
-{
- (void)arg;
- test_util_spawn_background_partial_read_impl(0);
-}
-
-static void
-test_util_spawn_background_exit_early(void *arg)
-{
- (void)arg;
- test_util_spawn_background_partial_read_impl(1);
-}
-
-static void
-test_util_spawn_background_waitpid_notify(void *arg)
-{
- int retval, exit_code;
- process_handle_t *process_handle=NULL;
- int status;
- int ms_timer;
-
- const char *argv[] = {TEST_CHILD, "--fast", NULL};
-
- (void) arg;
-
-#ifdef _WIN32
- status = tor_spawn_background(NULL, argv, NULL, &process_handle);
-#else
- status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
-#endif
-
- tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
- tt_ptr_op(process_handle, OP_NE, NULL);
-
- /* We're not going to look at the stdout/stderr output this time. Instead,
- * we're testing whether notify_pending_waitpid_calbacks() can report the
- * process exit (on unix) and/or whether tor_get_exit_code() can notice it
- * (on windows) */
-
-#ifndef _WIN32
- ms_timer = 30*1000;
- tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL);
- while (process_handle->waitpid_cb && ms_timer > 0) {
- tor_sleep_msec(100);
- ms_timer -= 100;
- notify_pending_waitpid_callbacks();
- }
- tt_int_op(ms_timer, OP_GT, 0);
- tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
-#endif /* !defined(_WIN32) */
-
- ms_timer = 30*1000;
- while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
- == PROCESS_EXIT_RUNNING) && ms_timer > 0) {
- tor_sleep_msec(100);
- ms_timer -= 100;
- }
- tt_int_op(ms_timer, OP_GT, 0);
-
- tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED);
-
- done:
- tor_process_handle_destroy(process_handle, 1);
-}
-
-#undef TEST_CHILD
-#undef EOL
-
-#undef MATCH_PROCESS_STATUS
-
-#ifndef _WIN32
-#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING
-#undef IS_RUNNING_OR_NOTRUNNING
-#endif
-
-#define UTIL_TEST(name, flags) \
- { #name, test_util_ ## name, flags, NULL, NULL }
-
-struct testcase_t slow_util_tests[] = {
- UTIL_TEST(spawn_background_ok, 0),
- UTIL_TEST(spawn_background_fail, 0),
- UTIL_TEST(spawn_background_partial_read, 0),
- UTIL_TEST(spawn_background_exit_early, 0),
- UTIL_TEST(spawn_background_waitpid_notify, 0),
- END_OF_TESTCASES
-};
diff --git a/src/test/test_voting_flags.c b/src/test/test_voting_flags.c
new file mode 100644
index 0000000000..c8111ea5df
--- /dev/null
+++ b/src/test/test_voting_flags.c
@@ -0,0 +1,191 @@
+/* Copyright (c) 2018-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#define VOTEFLAGS_PRIVATE
+
+#include "core/or/or.h"
+
+#include "feature/dirauth/voteflags.h"
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/routerstatus_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+
+#include "app/config/config.h"
+
+#include "test/test.h"
+
+typedef struct {
+ time_t now;
+ routerinfo_t ri;
+ node_t node;
+
+ routerstatus_t expected;
+} flag_vote_test_cfg_t;
+
+static void
+setup_cfg(flag_vote_test_cfg_t *c)
+{
+ memset(c, 0, sizeof(*c));
+
+ c->now = approx_time();
+
+ c->ri.nickname = (char *) "testing100";
+ strlcpy(c->expected.nickname, "testing100", sizeof(c->expected.nickname));
+
+ memset(c->ri.cache_info.identity_digest, 0xff, DIGEST_LEN);
+ memset(c->ri.cache_info.signed_descriptor_digest, 0xee, DIGEST_LEN);
+
+ c->ri.cache_info.published_on = c->now - 100;
+ c->expected.published_on = c->now - 100;
+
+ c->ri.addr = 0x7f010105;
+ c->expected.addr = 0x7f010105;
+ c->ri.or_port = 9090;
+ c->expected.or_port = 9090;
+
+ tor_addr_make_null(&c->ri.ipv6_addr, AF_INET6);
+ tor_addr_make_null(&c->expected.ipv6_addr, AF_INET6);
+
+ // By default we have no loaded information about stability or speed,
+ // so we'll default to voting "yeah sure." on these two.
+ c->expected.is_fast = 1;
+ c->expected.is_stable = 1;
+}
+
+static bool
+check_result(flag_vote_test_cfg_t *c)
+{
+ bool result = false;
+ routerstatus_t rs;
+ memset(&rs, 0, sizeof(rs));
+ dirauth_set_routerstatus_from_routerinfo(&rs, &c->node, &c->ri, c->now, 0);
+
+ tt_i64_op(rs.published_on, OP_EQ, c->expected.published_on);
+ tt_str_op(rs.nickname, OP_EQ, c->expected.nickname);
+
+ // identity_digest and descriptor_digest are not set here.
+
+ tt_uint_op(rs.addr, OP_EQ, c->expected.addr);
+ tt_uint_op(rs.or_port, OP_EQ, c->expected.or_port);
+ tt_uint_op(rs.dir_port, OP_EQ, c->expected.dir_port);
+
+ tt_assert(tor_addr_eq(&rs.ipv6_addr, &c->expected.ipv6_addr));
+ tt_uint_op(rs.ipv6_orport, OP_EQ, c->expected.ipv6_orport);
+
+#define FLAG(flagname) \
+ tt_uint_op(rs.flagname, OP_EQ, c->expected.flagname)
+
+ FLAG(is_authority);
+ FLAG(is_exit);
+ FLAG(is_stable);
+ FLAG(is_fast);
+ FLAG(is_flagged_running);
+ FLAG(is_named);
+ FLAG(is_unnamed);
+ FLAG(is_valid);
+ FLAG(is_possible_guard);
+ FLAG(is_bad_exit);
+ FLAG(is_hs_dir);
+ FLAG(is_v2_dir);
+ FLAG(is_staledesc);
+ FLAG(has_bandwidth);
+ FLAG(has_exitsummary);
+ FLAG(bw_is_unmeasured);
+
+ result = true;
+
+ done:
+ return result;
+}
+
+static void
+test_voting_flags_minimal(void *arg)
+{
+ flag_vote_test_cfg_t *cfg = arg;
+ (void) check_result(cfg);
+}
+
+static void
+test_voting_flags_ipv6(void *arg)
+{
+ flag_vote_test_cfg_t *cfg = arg;
+
+ tt_assert(tor_addr_parse(&cfg->ri.ipv6_addr, "f00::b42") == AF_INET6);
+ cfg->ri.ipv6_orport = 9091;
+ // no change in expected results, since we aren't set up with ipv6
+ // connectivity.
+ if (!check_result(cfg))
+ goto done;
+
+ get_options_mutable()->AuthDirHasIPv6Connectivity = 1;
+ // no change in expected results, since last_reachable6 won't be set.
+ if (!check_result(cfg))
+ goto done;
+
+ cfg->node.last_reachable6 = cfg->now - 10;
+ // now that lastreachable6 is set, we expect to see the result.
+ tt_assert(tor_addr_parse(&cfg->expected.ipv6_addr, "f00::b42") == AF_INET6);
+ cfg->expected.ipv6_orport = 9091;
+ if (!check_result(cfg))
+ goto done;
+ done:
+ ;
+}
+
+static void
+test_voting_flags_staledesc(void *arg)
+{
+ flag_vote_test_cfg_t *cfg = arg;
+ time_t now = cfg->now;
+
+ cfg->ri.cache_info.published_on = now - DESC_IS_STALE_INTERVAL + 10;
+ cfg->expected.published_on = now - DESC_IS_STALE_INTERVAL + 10;
+ // no change in expectations for is_staledesc
+ if (!check_result(cfg))
+ goto done;
+
+ cfg->ri.cache_info.published_on = now - DESC_IS_STALE_INTERVAL - 10;
+ cfg->expected.published_on = now - DESC_IS_STALE_INTERVAL - 10;
+ cfg->expected.is_staledesc = 1;
+ if (!check_result(cfg))
+ goto done;
+
+ done:
+ ;
+}
+
+static void *
+setup_voting_flags_test(const struct testcase_t *testcase)
+{
+ (void)testcase;
+ flag_vote_test_cfg_t *cfg = tor_malloc_zero(sizeof(*cfg));
+ setup_cfg(cfg);
+ return cfg;
+}
+
+static int
+teardown_voting_flags_test(const struct testcase_t *testcase, void *arg)
+{
+ (void)testcase;
+ flag_vote_test_cfg_t *cfg = arg;
+ tor_free(cfg);
+ return 1;
+}
+
+static const struct testcase_setup_t voting_flags_setup = {
+ .setup_fn = setup_voting_flags_test,
+ .cleanup_fn = teardown_voting_flags_test,
+};
+
+#define T(name,flags) \
+ { #name, test_voting_flags_##name, (flags), &voting_flags_setup, NULL }
+
+struct testcase_t voting_flags_tests[] = {
+ T(minimal, 0),
+ T(ipv6, TT_FORK),
+ // TODO: Add more of these tests.
+ T(staledesc, TT_FORK),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c
index c58634da5c..ba478a45a4 100644
--- a/src/test/test_workqueue.c
+++ b/src/test/test_workqueue.c
@@ -63,7 +63,7 @@ mark_handled(int serial)
tor_assert(! bitarray_is_set(handled, serial));
bitarray_set(handled, serial);
tor_mutex_release(&bitmap_mutex);
-#else /* !(defined(TRACK_RESPONSES)) */
+#else /* !defined(TRACK_RESPONSES) */
(void)serial;
#endif /* defined(TRACK_RESPONSES) */
}
diff --git a/src/test/test_workqueue_cancel.sh b/src/test/test_workqueue_cancel.sh
index f7c663171e..e50b884f26 100755
--- a/src/test/test_workqueue_cancel.sh
+++ b/src/test/test_workqueue_cancel.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue -C 1
+"${builddir:-.}/src/test/test_workqueue" -C 1
diff --git a/src/test/test_workqueue_efd.sh b/src/test/test_workqueue_efd.sh
index 4d89396819..592841fc91 100755
--- a/src/test/test_workqueue_efd.sh
+++ b/src/test/test_workqueue_efd.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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
index 7cfff45ff3..4cf1b76cbe 100755
--- a/src/test/test_workqueue_efd2.sh
+++ b/src/test/test_workqueue_efd2.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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
index afcef87853..fc3ef34c6c 100755
--- a/src/test/test_workqueue_pipe.sh
+++ b/src/test/test_workqueue_pipe.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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
index a20a1427e0..7f19ea880d 100755
--- a/src/test/test_workqueue_pipe2.sh
+++ b/src/test/test_workqueue_pipe2.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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
index 76af79746d..1ee1776447 100755
--- a/src/test/test_workqueue_socketpair.sh
+++ b/src/test/test_workqueue_socketpair.sh
@@ -1,4 +1,4 @@
#!/bin/sh
-${builddir:-.}/src/test/test_workqueue \
+"${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 2c9c4538b9..06a6f312ad 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -12,6 +12,7 @@
#include "orconfig.h"
#include "core/or/or.h"
#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "app/config/config.h"
#include "lib/crypt_ops/crypto_dh.h"
#include "lib/crypt_ops/crypto_ed25519.h"
@@ -25,6 +26,8 @@
#include "lib/compress/compress.h"
#include "lib/evloop/compat_libevent.h"
#include "lib/crypt_ops/crypto_init.h"
+#include "lib/version/torversion.h"
+#include "app/main/subsysmgr.h"
#include <stdio.h>
#ifdef HAVE_FCNTL_H
@@ -86,7 +89,7 @@ setup_directory(void)
(int)getpid(), rnd32);
r = mkdir(temp_dir);
}
-#else /* !(defined(_WIN32)) */
+#else /* !defined(_WIN32) */
tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s",
(int) getpid(), rnd32);
r = mkdir(temp_dir, 0700);
@@ -230,17 +233,17 @@ void
tinytest_prefork(void)
{
free_pregenerated_keys();
- crypto_prefork();
+ subsystems_prefork();
}
void
tinytest_postfork(void)
{
- crypto_postfork();
+ subsystems_postfork();
init_pregenerated_keys();
}
static void
-log_callback_failure(int severity, uint32_t domain, const char *msg)
+log_callback_failure(int severity, log_domain_mask_t domain, const char *msg)
{
(void)msg;
if (severity == LOG_ERR || (domain & LD_BUG)) {
@@ -259,24 +262,15 @@ main(int c, const char **v)
int loglevel = LOG_ERR;
int accel_crypto = 0;
- /* We must initialise logs before we call tor_assert() */
- init_logging(1);
+ subsystems_init_upto(SUBSYS_LEVEL_LIBS);
- update_approx_time(time(NULL));
options = options_new();
- tor_threads_init();
- tor_compress_init();
-
- network_init();
-
- monotime_init();
struct tor_libevent_cfg cfg;
memset(&cfg, 0, sizeof(cfg));
tor_libevent_initialize(&cfg);
control_initialize_event_queue();
- configure_backtrace_handler(get_version());
for (i_out = i = 1; i < c; ++i) {
if (!strcmp(v[i], "--warn")) {
@@ -301,7 +295,7 @@ main(int c, const char **v)
memset(&s, 0, sizeof(s));
set_log_severity_config(loglevel, LOG_ERR, &s);
/* ALWAYS log bug warnings. */
- s.masks[LOG_WARN-LOG_ERR] |= LD_BUG;
+ s.masks[SEVERITY_MASK_IDX(LOG_WARN)] |= LD_BUG;
add_stream_log(&s, "", fileno(stdout));
}
{
@@ -309,9 +303,10 @@ main(int c, const char **v)
log_severity_list_t s;
memset(&s, 0, sizeof(s));
set_log_severity_config(LOG_ERR, LOG_ERR, &s);
- s.masks[LOG_WARN-LOG_ERR] |= LD_BUG;
+ s.masks[SEVERITY_MASK_IDX(LOG_WARN)] |= LD_BUG;
add_callback_log(&s, log_callback_failure);
}
+ flush_log_messages_from_startup();
init_protocol_warning_severity_level();
options->command = CMD_RUN_UNITTESTS;
@@ -367,8 +362,6 @@ main(int c, const char **v)
free_pregenerated_keys();
- crypto_global_cleanup();
-
if (have_failed)
return 1;
else
diff --git a/src/test/testing_rsakeys.c b/src/test/testing_rsakeys.c
index 0f22d4e01b..727449e6eb 100644
--- a/src/test/testing_rsakeys.c
+++ b/src/test/testing_rsakeys.c
@@ -448,7 +448,8 @@ static int next_key_idx_2048;
static crypto_pk_t *
pk_generate_internal(int bits)
{
- tor_assert(bits == 2048 || bits == 1024);
+ tor_assertf(bits == 2048 || bits == 1024,
+ "Wrong key size: %d", bits);
#ifdef USE_PREGENERATED_RSA_KEYS
int *idxp;
@@ -467,7 +468,7 @@ pk_generate_internal(int bits)
*idxp += crypto_rand_int_range(1,3);
*idxp %= n_pregen;
return crypto_pk_dup_key(pregen_array[*idxp]);
-#else /* !(defined(USE_PREGENERATED_RSA_KEYS)) */
+#else /* !defined(USE_PREGENERATED_RSA_KEYS) */
crypto_pk_t *result;
int res;
result = crypto_pk_new();
diff --git a/src/test/zero_length_keys.sh b/src/test/zero_length_keys.sh
index 5635bdfd89..1702d11245 100755
--- a/src/test/zero_length_keys.sh
+++ b/src/test/zero_length_keys.sh
@@ -19,7 +19,7 @@
# 3: a command failed - the test could not be completed
#
-if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then
+if [ $# -eq 0 ] || [ ! -f "${1}" ] || [ ! -x "${1}" ]; then
echo "Usage: ${0} PATH_TO_TOR [-z|-d|-e]"
exit 1
elif [ $# -eq 1 ]; then
@@ -31,7 +31,7 @@ else #[$# -gt 1 ]; then
shift
fi
-DATA_DIR=`mktemp -d -t tor_zero_length_keys.XXXXXX`
+DATA_DIR=$(mktemp -d -t tor_zero_length_keys.XXXXXX)
if [ -z "$DATA_DIR" ]; then
echo "Failure: mktemp invocation returned empty string" >&2
exit 3
@@ -40,7 +40,7 @@ if [ ! -d "$DATA_DIR" ]; then
echo "Failure: mktemp invocation result doesn't point to directory" >&2
exit 3
fi
-trap "rm -rf '$DATA_DIR'" 0
+trap 'rm -rf "$DATA_DIR"' 0
touch "$DATA_DIR"/empty_torrc
touch "$DATA_DIR"/empty_defaults_torrc