diff options
Diffstat (limited to 'src/test')
111 files changed, 5339 insertions, 843 deletions
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake index 0ba56d7036..575198388b 100644 --- a/src/test/Makefile.nmake +++ b/src/test/Makefile.nmake @@ -12,7 +12,7 @@ LIBS = ..\..\..\build-alpha\lib\libevent.lib \ crypt32.lib gdi32.lib user32.lib TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \ - test_containers.obj \ + test_consdiff.obj test_containers.obj \ test_controller_events.obj test_crypto.obj test_data.obj test_dir.obj \ test_checkdir.obj test_microdesc.obj test_pt.obj test_util.obj \ test_config.obj test_connection.obj \ diff --git a/src/test/bench.c b/src/test/bench.c index 99bc686f30..a44dc94a61 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ extern const char tor_git_revision[]; @@ -28,6 +28,7 @@ const char tor_git_revision[] = ""; #include "crypto_curve25519.h" #include "onion_ntor.h" #include "crypto_ed25519.h" +#include "consdiff.h" #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) static uint64_t nanostart; @@ -673,6 +674,28 @@ main(int argc, const char **argv) or_options_t *options; tor_threads_init(); + tor_compress_init(); + + if (argc == 4 && !strcmp(argv[1], "diff")) { + init_logging(1); + const int N = 200; + char *f1 = read_file_to_str(argv[2], RFTS_BIN, NULL); + char *f2 = read_file_to_str(argv[3], RFTS_BIN, NULL); + if (! f1 || ! f2) { + perror("X"); + return 1; + } + for (i = 0; i < N; ++i) { + char *diff = consensus_diff_generate(f1, f2); + tor_free(diff); + } + char *diff = consensus_diff_generate(f1, f2); + printf("%s", diff); + tor_free(f1); + tor_free(f2); + tor_free(diff); + return 0; + } for (i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--list")) { diff --git a/src/test/bt_test.py b/src/test/bt_test.py index 30591453b9..4cb3326042 100755 --- a/src/test/bt_test.py +++ b/src/test/bt_test.py @@ -1,4 +1,4 @@ -# Copyright 2013-2015, The Tor Project, Inc +# Copyright 2013-2017, The Tor Project, Inc # See LICENSE for licensing information """ diff --git a/src/test/ed25519_exts_ref.py b/src/test/ed25519_exts_ref.py index d5a3a79910..af5010415e 100644 --- a/src/test/ed25519_exts_ref.py +++ b/src/test/ed25519_exts_ref.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 2014-2015, The Tor Project, Inc +# Copyright 2014-2017, The Tor Project, Inc # See LICENSE for licensing information """ diff --git a/src/test/fakechans.h b/src/test/fakechans.h index fa0e37dbe6..c0de430e3d 100644 --- a/src/test/fakechans.h +++ b/src/test/fakechans.h @@ -1,4 +1,4 @@ - /* Copyright (c) 2014-2016, The Tor Project, Inc. */ + /* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_FAKECHANS_H diff --git a/src/test/fuzz/dict/http b/src/test/fuzz/dict/http index 1a7b61e8d4..3b0531579d 100644 --- a/src/test/fuzz/dict/http +++ b/src/test/fuzz/dict/http @@ -4,7 +4,7 @@ # # Extracted from directory_handle_command() in the tor source code # -# Copyright (c) 2016, The Tor Project, Inc. +# Copyright (c) 2016-2017, The Tor Project, Inc. # See LICENSE for licensing information # # Usage: diff --git a/src/test/fuzz/fuzz_consensus.c b/src/test/fuzz/fuzz_consensus.c index f5d22f69ae..6610ade7ad 100644 --- a/src/test/fuzz/fuzz_consensus.c +++ b/src/test/fuzz/fuzz_consensus.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ROUTERPARSE_PRIVATE #include "or.h" diff --git a/src/test/fuzz/fuzz_descriptor.c b/src/test/fuzz/fuzz_descriptor.c index d19386d77f..1a50beae17 100644 --- a/src/test/fuzz/fuzz_descriptor.c +++ b/src/test/fuzz/fuzz_descriptor.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ROUTERPARSE_PRIVATE #include "or.h" diff --git a/src/test/fuzz/fuzz_diff.c b/src/test/fuzz/fuzz_diff.c new file mode 100644 index 0000000000..c241f63dc4 --- /dev/null +++ b/src/test/fuzz/fuzz_diff.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONSDIFF_PRIVATE + +#include "orconfig.h" +#include "or.h" +#include "consdiff.h" + +#include "fuzzing.h" + +static int +mock_consensus_compute_digest_(const char *c, consensus_digest_t *d) +{ + (void)c; + memset(d->sha3_256, 3, sizeof(d->sha3_256)); + return 0; +} + +int +fuzz_init(void) +{ + MOCK(consensus_compute_digest, mock_consensus_compute_digest_); + return 0; +} + +int +fuzz_cleanup(void) +{ + UNMOCK(consensus_compute_digest); + return 0; +} + +int +fuzz_main(const uint8_t *stdin_buf, size_t data_size) +{ +#define SEP "=====\n" +#define SEPLEN strlen(SEP) + const uint8_t *separator = tor_memmem(stdin_buf, data_size, SEP, SEPLEN); + if (! separator) + return 0; + size_t c1_len = separator - stdin_buf; + char *c1 = tor_memdup_nulterm(stdin_buf, c1_len); + size_t c2_len = data_size - c1_len - SEPLEN; + char *c2 = tor_memdup_nulterm(separator + SEPLEN, c2_len); + + char *c3 = consensus_diff_generate(c1, c2); + + if (c3) { + char *c4 = consensus_diff_apply(c1, c3); + tor_assert(c4); + if (strcmp(c2, c4)) { + printf("%s\n", escaped(c1)); + printf("%s\n", escaped(c2)); + printf("%s\n", escaped(c3)); + printf("%s\n", escaped(c4)); + } + tor_assert(! strcmp(c2, c4)); + tor_free(c3); + tor_free(c4); + } + tor_free(c1); + tor_free(c2); + + return 0; +} + diff --git a/src/test/fuzz/fuzz_diff_apply.c b/src/test/fuzz/fuzz_diff_apply.c new file mode 100644 index 0000000000..8d7bf751bf --- /dev/null +++ b/src/test/fuzz/fuzz_diff_apply.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONSDIFF_PRIVATE + +#include "orconfig.h" +#include "or.h" +#include "consdiff.h" + +#include "fuzzing.h" + +static int +mock_consensus_compute_digest_(const char *c, consensus_digest_t *d) +{ + (void)c; + memset(d->sha3_256, 3, sizeof(d->sha3_256)); + return 0; +} + +static int +mock_consensus_digest_eq_(const uint8_t *a, const uint8_t *b) +{ + (void)a; + (void)b; + return 1; +} + +int +fuzz_init(void) +{ + MOCK(consensus_compute_digest, mock_consensus_compute_digest_); + MOCK(consensus_digest_eq, mock_consensus_digest_eq_); + return 0; +} + +int +fuzz_cleanup(void) +{ + UNMOCK(consensus_compute_digest); + UNMOCK(consensus_digest_eq); + return 0; +} + +int +fuzz_main(const uint8_t *stdin_buf, size_t data_size) +{ +#define SEP "=====\n" +#define SEPLEN strlen(SEP) + const uint8_t *separator = tor_memmem(stdin_buf, data_size, SEP, SEPLEN); + if (! separator) + return 0; + size_t c1_len = separator - stdin_buf; + char *c1 = tor_memdup_nulterm(stdin_buf, c1_len); + size_t c2_len = data_size - c1_len - SEPLEN; + char *c2 = tor_memdup_nulterm(separator + SEPLEN, c2_len); + + char *c3 = consensus_diff_apply(c1, c2); + + tor_free(c1); + tor_free(c2); + tor_free(c3); + + return 0; +} + diff --git a/src/test/fuzz/fuzz_extrainfo.c b/src/test/fuzz/fuzz_extrainfo.c index 6251e606d0..2a3de7ecf7 100644 --- a/src/test/fuzz/fuzz_extrainfo.c +++ b/src/test/fuzz/fuzz_extrainfo.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ROUTERPARSE_PRIVATE #include "or.h" diff --git a/src/test/fuzz/fuzz_hsdescv2.c b/src/test/fuzz/fuzz_hsdescv2.c index 53b7cbe2f7..19db265716 100644 --- a/src/test/fuzz/fuzz_hsdescv2.c +++ b/src/test/fuzz/fuzz_hsdescv2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ROUTERPARSE_PRIVATE #include "or.h" diff --git a/src/test/fuzz/fuzz_http.c b/src/test/fuzz/fuzz_http.c index 01c3815f18..2ffeb60244 100644 --- a/src/test/fuzz/fuzz_http.c +++ b/src/test/fuzz/fuzz_http.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -18,10 +18,10 @@ static void mock_connection_write_to_buf_impl_(const char *string, size_t len, - connection_t *conn, int zlib) + connection_t *conn, int compressed) { log_debug(LD_GENERAL, "%sResponse:\n%u\nConnection: %p\n%s\n", - zlib ? "Compressed " : "", (unsigned)len, conn, string); + compressed ? "Compressed " : "", (unsigned)len, conn, string); } static int diff --git a/src/test/fuzz/fuzz_iptsv2.c b/src/test/fuzz/fuzz_iptsv2.c index 341d4880bd..4abde0c16d 100644 --- a/src/test/fuzz/fuzz_iptsv2.c +++ b/src/test/fuzz/fuzz_iptsv2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ROUTERPARSE_PRIVATE #include "or.h" diff --git a/src/test/fuzz/fuzz_microdesc.c b/src/test/fuzz/fuzz_microdesc.c index bb89546191..396115026e 100644 --- a/src/test/fuzz/fuzz_microdesc.c +++ b/src/test/fuzz/fuzz_microdesc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ROUTERPARSE_PRIVATE #include "or.h" diff --git a/src/test/fuzz/fuzz_vrs.c b/src/test/fuzz/fuzz_vrs.c index 9301a9bcc8..baf0610a0b 100644 --- a/src/test/fuzz/fuzz_vrs.c +++ b/src/test/fuzz/fuzz_vrs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ROUTERPARSE_PRIVATE #define NETWORKSTATUS_PRIVATE diff --git a/src/test/fuzz/fuzzing.h b/src/test/fuzz/fuzzing.h index 4295743458..aecdbb4e52 100644 --- a/src/test/fuzz/fuzzing.h +++ b/src/test/fuzz/fuzzing.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef FUZZING_H #define FUZZING_H diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c index e4920d3ee7..7aee92df63 100644 --- a/src/test/fuzz/fuzzing_common.c +++ b/src/test/fuzz/fuzzing_common.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CRYPTO_ED25519_PRIVATE #include "orconfig.h" @@ -96,6 +96,7 @@ static void global_init(void) { tor_threads_init(); + tor_compress_init(); { struct sipkey sipkey = { 1337, 7331 }; siphash_set_global_key(&sipkey); diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am index 806710879b..500377f6d7 100644 --- a/src/test/fuzz/include.am +++ b/src/test/fuzz/include.am @@ -1,4 +1,5 @@ - +# This file was generated by fuzzing_include_am.py; do not hand-edit unless +# you enjoy having your changes erased. FUZZING_CPPFLAGS = \ $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) FUZZING_CFLAGS = \ @@ -17,7 +18,9 @@ FUZZING_LIBS = \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_SYSTEMD_LIBS@ + @TOR_SYSTEMD_LIBS@ \ + @TOR_LZMA_LIBS@ \ + @TOR_ZSTD_LIBS@ oss-fuzz-prereqs: \ src/or/libtor-testing.a \ @@ -32,6 +35,16 @@ oss-fuzz-prereqs: \ noinst_HEADERS += \ src/test/fuzz/fuzzing.h +LIBFUZZER = /home/nickm/build/libfuzz/libFuzzer.a +LIBFUZZER_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ +LIBFUZZER_CFLAGS = $(FUZZING_CFLAGS) +LIBFUZZER_LDFLAG = $(FUZZING_LDFLAG) +LIBFUZZER_LIBS = $(FUZZING_LIBS) $(LIBFUZZER) -lstdc++ + +LIBOSS_FUZZ_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ +LIBOSS_FUZZ_CFLAGS = $(FUZZING_CFLAGS) + +# ===== AFL fuzzers src_test_fuzz_fuzz_consensus_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_consensus.c @@ -48,13 +61,29 @@ src_test_fuzz_fuzz_descriptor_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_descriptor_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_descriptor_LDADD = $(FUZZING_LIBS) -src_test_fuzz_fuzz_http_SOURCES = \ +src_test_fuzz_fuzz_diff_SOURCES = \ src/test/fuzz/fuzzing_common.c \ - src/test/fuzz/fuzz_http.c -src_test_fuzz_fuzz_http_CPPFLAGS = $(FUZZING_CPPFLAGS) -src_test_fuzz_fuzz_http_CFLAGS = $(FUZZING_CFLAGS) -src_test_fuzz_fuzz_http_LDFLAGS = $(FUZZING_LDFLAG) -src_test_fuzz_fuzz_http_LDADD = $(FUZZING_LIBS) + src/test/fuzz/fuzz_diff.c +src_test_fuzz_fuzz_diff_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_diff_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_diff_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_diff_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_diff_apply_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_diff_apply.c +src_test_fuzz_fuzz_diff_apply_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_diff_apply_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_diff_apply_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_diff_apply_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_extrainfo_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_extrainfo.c +src_test_fuzz_fuzz_extrainfo_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_extrainfo_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_extrainfo_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_extrainfo_LDADD = $(FUZZING_LIBS) src_test_fuzz_fuzz_hsdescv2_SOURCES = \ src/test/fuzz/fuzzing_common.c \ @@ -64,6 +93,14 @@ src_test_fuzz_fuzz_hsdescv2_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_hsdescv2_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_hsdescv2_LDADD = $(FUZZING_LIBS) +src_test_fuzz_fuzz_http_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_http.c +src_test_fuzz_fuzz_http_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_http_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_http_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_http_LDADD = $(FUZZING_LIBS) + src_test_fuzz_fuzz_iptsv2_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_iptsv2.c @@ -72,14 +109,6 @@ src_test_fuzz_fuzz_iptsv2_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_iptsv2_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_iptsv2_LDADD = $(FUZZING_LIBS) -src_test_fuzz_fuzz_extrainfo_SOURCES = \ - src/test/fuzz/fuzzing_common.c \ - src/test/fuzz/fuzz_extrainfo.c -src_test_fuzz_fuzz_extrainfo_CPPFLAGS = $(FUZZING_CPPFLAGS) -src_test_fuzz_fuzz_extrainfo_CFLAGS = $(FUZZING_CFLAGS) -src_test_fuzz_fuzz_extrainfo_LDFLAGS = $(FUZZING_LDFLAG) -src_test_fuzz_fuzz_extrainfo_LDADD = $(FUZZING_LIBS) - src_test_fuzz_fuzz_microdesc_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_microdesc.c @@ -99,19 +128,16 @@ src_test_fuzz_fuzz_vrs_LDADD = $(FUZZING_LIBS) FUZZERS = \ src/test/fuzz/fuzz-consensus \ src/test/fuzz/fuzz-descriptor \ + src/test/fuzz/fuzz-diff \ + src/test/fuzz/fuzz-diff-apply \ src/test/fuzz/fuzz-extrainfo \ - src/test/fuzz/fuzz-http \ src/test/fuzz/fuzz-hsdescv2 \ + src/test/fuzz/fuzz-http \ src/test/fuzz/fuzz-iptsv2 \ src/test/fuzz/fuzz-microdesc \ src/test/fuzz/fuzz-vrs - -LIBFUZZER = /home/nickm/build/libfuzz/libFuzzer.a -LIBFUZZER_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ -LIBFUZZER_CFLAGS = $(FUZZING_CFLAGS) -LIBFUZZER_LDFLAG = $(FUZZING_LDFLAG) -LIBFUZZER_LIBS = $(FUZZING_LIBS) $(LIBFUZZER) -lstdc++ +# ===== libfuzzer if LIBFUZZER_ENABLED src_test_fuzz_lf_fuzz_consensus_SOURCES = \ @@ -128,6 +154,20 @@ src_test_fuzz_lf_fuzz_descriptor_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_descriptor_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_descriptor_LDADD = $(LIBFUZZER_LIBS) +src_test_fuzz_lf_fuzz_diff_SOURCES = \ + $(src_test_fuzz_fuzz_diff_SOURCES) +src_test_fuzz_lf_fuzz_diff_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_diff_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_diff_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_diff_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_diff_apply_SOURCES = \ + $(src_test_fuzz_fuzz_diff_apply_SOURCES) +src_test_fuzz_lf_fuzz_diff_apply_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_diff_apply_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_diff_apply_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_diff_apply_LDADD = $(LIBFUZZER_LIBS) + src_test_fuzz_lf_fuzz_extrainfo_SOURCES = \ $(src_test_fuzz_fuzz_extrainfo_SOURCES) src_test_fuzz_lf_fuzz_extrainfo_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) @@ -135,13 +175,6 @@ src_test_fuzz_lf_fuzz_extrainfo_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_extrainfo_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_extrainfo_LDADD = $(LIBFUZZER_LIBS) -src_test_fuzz_lf_fuzz_http_SOURCES = \ - $(src_test_fuzz_fuzz_http_SOURCES) -src_test_fuzz_lf_fuzz_http_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) -src_test_fuzz_lf_fuzz_http_CFLAGS = $(LIBFUZZER_CFLAGS) -src_test_fuzz_lf_fuzz_http_LDFLAGS = $(LIBFUZZER_LDFLAG) -src_test_fuzz_lf_fuzz_http_LDADD = $(LIBFUZZER_LIBS) - src_test_fuzz_lf_fuzz_hsdescv2_SOURCES = \ $(src_test_fuzz_fuzz_hsdescv2_SOURCES) src_test_fuzz_lf_fuzz_hsdescv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) @@ -149,6 +182,13 @@ src_test_fuzz_lf_fuzz_hsdescv2_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_hsdescv2_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_hsdescv2_LDADD = $(LIBFUZZER_LIBS) +src_test_fuzz_lf_fuzz_http_SOURCES = \ + $(src_test_fuzz_fuzz_http_SOURCES) +src_test_fuzz_lf_fuzz_http_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_http_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_http_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_http_LDADD = $(LIBFUZZER_LIBS) + src_test_fuzz_lf_fuzz_iptsv2_SOURCES = \ $(src_test_fuzz_fuzz_iptsv2_SOURCES) src_test_fuzz_lf_fuzz_iptsv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) @@ -173,9 +213,11 @@ src_test_fuzz_lf_fuzz_vrs_LDADD = $(LIBFUZZER_LIBS) LIBFUZZER_FUZZERS = \ src/test/fuzz/lf-fuzz-consensus \ src/test/fuzz/lf-fuzz-descriptor \ + src/test/fuzz/lf-fuzz-diff \ + src/test/fuzz/lf-fuzz-diff-apply \ src/test/fuzz/lf-fuzz-extrainfo \ - src/test/fuzz/lf-fuzz-http \ src/test/fuzz/lf-fuzz-hsdescv2 \ + src/test/fuzz/lf-fuzz-http \ src/test/fuzz/lf-fuzz-iptsv2 \ src/test/fuzz/lf-fuzz-microdesc \ src/test/fuzz/lf-fuzz-vrs @@ -184,10 +226,9 @@ else LIBFUZZER_FUZZERS = endif -if OSS_FUZZ_ENABLED -LIBOSS_FUZZ_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ -LIBOSS_FUZZ_CFLAGS = $(FUZZING_CFLAGS) +# ===== oss-fuzz +if OSS_FUZZ_ENABLED src_test_fuzz_liboss_fuzz_consensus_a_SOURCES = \ $(src_test_fuzz_fuzz_consensus_SOURCES) src_test_fuzz_liboss_fuzz_consensus_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) @@ -198,21 +239,31 @@ src_test_fuzz_liboss_fuzz_descriptor_a_SOURCES = \ src_test_fuzz_liboss_fuzz_descriptor_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_descriptor_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +src_test_fuzz_liboss_fuzz_diff_a_SOURCES = \ + $(src_test_fuzz_fuzz_diff_SOURCES) +src_test_fuzz_liboss_fuzz_diff_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_diff_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_diff_apply_a_SOURCES = \ + $(src_test_fuzz_fuzz_diff_apply_SOURCES) +src_test_fuzz_liboss_fuzz_diff_apply_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_diff_apply_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + src_test_fuzz_liboss_fuzz_extrainfo_a_SOURCES = \ $(src_test_fuzz_fuzz_extrainfo_SOURCES) src_test_fuzz_liboss_fuzz_extrainfo_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_extrainfo_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) -src_test_fuzz_liboss_fuzz_http_a_SOURCES = \ - $(src_test_fuzz_fuzz_http_SOURCES) -src_test_fuzz_liboss_fuzz_http_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) -src_test_fuzz_liboss_fuzz_http_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) - src_test_fuzz_liboss_fuzz_hsdescv2_a_SOURCES = \ $(src_test_fuzz_fuzz_hsdescv2_SOURCES) src_test_fuzz_liboss_fuzz_hsdescv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_hsdescv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +src_test_fuzz_liboss_fuzz_http_a_SOURCES = \ + $(src_test_fuzz_fuzz_http_SOURCES) +src_test_fuzz_liboss_fuzz_http_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_http_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + src_test_fuzz_liboss_fuzz_iptsv2_a_SOURCES = \ $(src_test_fuzz_fuzz_iptsv2_SOURCES) src_test_fuzz_liboss_fuzz_iptsv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) @@ -231,12 +282,15 @@ src_test_fuzz_liboss_fuzz_vrs_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) OSS_FUZZ_FUZZERS = \ src/test/fuzz/liboss-fuzz-consensus.a \ src/test/fuzz/liboss-fuzz-descriptor.a \ + src/test/fuzz/liboss-fuzz-diff.a \ + src/test/fuzz/liboss-fuzz-diff-apply.a \ src/test/fuzz/liboss-fuzz-extrainfo.a \ - src/test/fuzz/liboss-fuzz-http.a \ src/test/fuzz/liboss-fuzz-hsdescv2.a \ + src/test/fuzz/liboss-fuzz-http.a \ src/test/fuzz/liboss-fuzz-iptsv2.a \ src/test/fuzz/liboss-fuzz-microdesc.a \ src/test/fuzz/liboss-fuzz-vrs.a + else OSS_FUZZ_FUZZERS = endif diff --git a/src/test/fuzz_static_testcases.sh b/src/test/fuzz_static_testcases.sh index bfe1677573..3cb45ad5e6 100755 --- a/src/test/fuzz_static_testcases.sh +++ b/src/test/fuzz_static_testcases.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (c) 2016, The Tor Project, Inc. +# Copyright (c) 2016-2017, The Tor Project, Inc. # See LICENSE for licensing information set -e diff --git a/src/test/hs_ntor_ref.py b/src/test/hs_ntor_ref.py new file mode 100644 index 0000000000..813e797828 --- /dev/null +++ b/src/test/hs_ntor_ref.py @@ -0,0 +1,408 @@ +#!/usr/bin/python +# Copyright 2017, The Tor Project, Inc +# See LICENSE for licensing information + +""" +hs_ntor_ref.py + +This module is a reference implementation of the modified ntor protocol +proposed for Tor hidden services in proposal 224 (Next Generation Hidden +Services) in section [NTOR-WITH-EXTRA-DATA]. + +The modified ntor protocol is a single-round protocol, with three steps in total: + + 1: Client generates keys and sends them to service via INTRODUCE cell + + 2: Service computes key material based on client's keys, and sends its own + keys to client via RENDEZVOUS cell + + 3: Client computes key material as well. + +It's meant to be used to validate Tor's HS ntor implementation by conducting +various integration tests. Specifically it conducts the following three tests: + +- Tests our Python implementation by running the whole protocol in Python and + making sure that results are consistent. + +- Tests little-t-tor ntor implementation. We use this Python code to instrument + little-t-tor and carry out the handshake by using little-t-tor code. The + small C wrapper at src/test/test-hs-ntor-cl is used for this Python module to + interface with little-t-tor. + +- Cross-tests Python and little-t-tor implementation by running half of the + protocol in Python code and the other in little-t-tor. This is actually two + tests so that all parts of the protocol are run both by little-t-tor and + Python. + +It requires the curve25519 python module from the curve25519-donna package. + +The whole logic and concept for this test suite was taken from ntor_ref.py. + + *** DO NOT USE THIS IN PRODUCTION. *** +""" + +import struct +import os, sys +import binascii +import subprocess + +try: + import curve25519 + curve25519mod = curve25519.keys +except ImportError: + curve25519 = None + import slownacl_curve25519 + curve25519mod = slownacl_curve25519 + +try: + import sha3 +except ImportError: + # error code 77 tells automake to skip this test + sys.exit(77) + +# Import Nick's ntor reference implementation in Python +# We are gonna use a few of its utilities. +from ntor_ref import hash_nil +from ntor_ref import PrivateKey + +# String constants used in this protocol +PROTOID = "tor-hs-ntor-curve25519-sha3-256-1" +T_HSENC = PROTOID + ":hs_key_extract" +T_HSVERIFY = PROTOID + ":hs_verify" +T_HSMAC = PROTOID + ":hs_mac" +M_HSEXPAND = PROTOID + ":hs_key_expand" + +INTRO_SECRET_LEN = 161 +REND_SECRET_LEN = 225 +AUTH_INPUT_LEN = 199 + +# Implements MAC(k,m) = H(htonll(len(k)) | k | m) +def mac(k,m): + def htonll(num): + return struct.pack('!q', num) + + s = sha3.SHA3256() + s.update(htonll(len(k))) + s.update(k) + s.update(m) + return s.digest() + +###################################################################### + +# Functions that implement the modified HS ntor protocol + +"""As client compute key material for INTRODUCE cell as follows: + + intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID + info = m_hsexpand | subcredential + hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) + ENC_KEY = hs_keys[0:S_KEY_LEN] + MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] +""" +def intro2_ntor_client(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential): + + dh_result = client_ephemeral_enc_privkey.get_shared_key(intro_enc_pubkey, hash_nil) + secret = dh_result + intro_auth_pubkey_str + client_ephemeral_enc_pubkey.serialize() + intro_enc_pubkey.serialize() + PROTOID + assert(len(secret) == INTRO_SECRET_LEN) + info = M_HSEXPAND + subcredential + + kdf = sha3.SHAKE256() + kdf.update(secret + T_HSENC + info) + key_material = kdf.squeeze(64*8) + + enc_key = key_material[0:32] + mac_key = key_material[32:64] + + return enc_key, mac_key + +"""Wrapper over intro2_ntor_client()""" +def client_part1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential): + enc_key, mac_key = intro2_ntor_client(intro_auth_pubkey_str, intro_enc_pubkey, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential) + assert(enc_key) + assert(mac_key) + + return enc_key, mac_key + +"""As service compute key material for INTRODUCE cell as follows: + + intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID + info = m_hsexpand | subcredential + hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) + HS_DEC_KEY = hs_keys[0:S_KEY_LEN] + HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] +""" +def intro2_ntor_service(intro_auth_pubkey_str, client_enc_pubkey, service_enc_privkey, service_enc_pubkey, subcredential): + dh_result = service_enc_privkey.get_shared_key(client_enc_pubkey, hash_nil) + secret = dh_result + intro_auth_pubkey_str + client_enc_pubkey.serialize() + service_enc_pubkey.serialize() + PROTOID + assert(len(secret) == INTRO_SECRET_LEN) + info = M_HSEXPAND + subcredential + + kdf = sha3.SHAKE256() + kdf.update(secret + T_HSENC + info) + key_material = kdf.squeeze(64*8) + + enc_key = key_material[0:32] + mac_key = key_material[32:64] + + return enc_key, mac_key + +"""As service compute key material for INTRODUCE and REDNEZVOUS cells. + + Use intro2_ntor_service() to calculate the INTRODUCE key material, and use + the following computations to do the RENDEZVOUS ones: + + rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID + NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc) + verify = MAC(rend_secret_hs_input, t_hsverify) + auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" + AUTH_INPUT_MAC = MAC(auth_input, t_hsmac) +""" +def service_part1(intro_auth_pubkey_str, client_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential): + intro_enc_key, intro_mac_key = intro2_ntor_service(intro_auth_pubkey_str, client_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential) + assert(intro_enc_key) + assert(intro_mac_key) + + service_ephemeral_privkey = PrivateKey() + service_ephemeral_pubkey = service_ephemeral_privkey.get_public() + + dh_result1 = service_ephemeral_privkey.get_shared_key(client_enc_pubkey, hash_nil) + dh_result2 = intro_enc_privkey.get_shared_key(client_enc_pubkey, hash_nil) + rend_secret_hs_input = dh_result1 + dh_result2 + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + client_enc_pubkey.serialize() + service_ephemeral_pubkey.serialize() + PROTOID + assert(len(rend_secret_hs_input) == REND_SECRET_LEN) + + ntor_key_seed = mac(rend_secret_hs_input, T_HSENC) + verify = mac(rend_secret_hs_input, T_HSVERIFY) + auth_input = verify + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + service_ephemeral_pubkey.serialize() + client_enc_pubkey.serialize() + PROTOID + "Server" + assert(len(auth_input) == AUTH_INPUT_LEN) + auth_input_mac = mac(auth_input, T_HSMAC) + + assert(ntor_key_seed) + assert(auth_input_mac) + assert(service_ephemeral_pubkey) + + return intro_enc_key, intro_mac_key, ntor_key_seed, auth_input_mac, service_ephemeral_pubkey + +"""As client compute key material for rendezvous cells as follows: + + rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID + NTOR_KEY_SEED = MAC(ntor_secret_input, t_hsenc) + verify = MAC(ntor_secret_input, t_hsverify) + auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" + AUTH_INPUT_MAC = MAC(auth_input, t_hsmac) +""" +def client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_ephemeral_rend_pubkey): + dh_result1 = client_ephemeral_enc_privkey.get_shared_key(service_ephemeral_rend_pubkey, hash_nil) + dh_result2 = client_ephemeral_enc_privkey.get_shared_key(intro_enc_pubkey, hash_nil) + rend_secret_hs_input = dh_result1 + dh_result2 + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + client_ephemeral_enc_pubkey.serialize() + service_ephemeral_rend_pubkey.serialize() + PROTOID + assert(len(rend_secret_hs_input) == REND_SECRET_LEN) + + ntor_key_seed = mac(rend_secret_hs_input, T_HSENC) + verify = mac(rend_secret_hs_input, T_HSVERIFY) + auth_input = verify + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + service_ephemeral_rend_pubkey.serialize() + client_ephemeral_enc_pubkey.serialize() + PROTOID + "Server" + assert(len(auth_input) == AUTH_INPUT_LEN) + auth_input_mac = mac(auth_input, T_HSMAC) + + assert(ntor_key_seed) + assert(auth_input_mac) + + return ntor_key_seed, auth_input_mac + +################################################################################# + +""" +Utilities for communicating with the little-t-tor ntor wrapper to conduct the +integration tests +""" + +PROG = b"./src/test/test-hs-ntor-cl" +enhex=lambda s: binascii.b2a_hex(s) +dehex=lambda s: binascii.a2b_hex(s.strip()) + +def tor_client1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_privkey, subcredential): + p = subprocess.Popen([PROG, "client1", + enhex(intro_auth_pubkey_str), + enhex(intro_enc_pubkey.serialize()), + enhex(client_ephemeral_enc_privkey.serialize()), + enhex(subcredential)], + stdout=subprocess.PIPE) + return map(dehex, p.stdout.readlines()) + +def tor_server1(intro_auth_pubkey_str, intro_enc_privkey, + client_ephemeral_enc_pubkey, subcredential): + p = subprocess.Popen([PROG, "server1", + enhex(intro_auth_pubkey_str), + enhex(intro_enc_privkey.serialize()), + enhex(client_ephemeral_enc_pubkey.serialize()), + enhex(subcredential)], + stdout=subprocess.PIPE) + return map(dehex, p.stdout.readlines()) + +def tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_ephemeral_rend_pubkey, subcredential): + p = subprocess.Popen([PROG, "client2", + enhex(intro_auth_pubkey_str), + enhex(client_ephemeral_enc_privkey.serialize()), + enhex(intro_enc_pubkey.serialize()), + enhex(service_ephemeral_rend_pubkey.serialize()), + enhex(subcredential)], + stdout=subprocess.PIPE) + return map(dehex, p.stdout.readlines()) + +################################################################################## + +# Perform a pure python ntor test +def do_pure_python_ntor_test(): + # Initialize all needed key material + client_ephemeral_enc_privkey = PrivateKey() + client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public() + intro_enc_privkey = PrivateKey() + intro_enc_pubkey = intro_enc_privkey.get_public() + intro_auth_pubkey_str = os.urandom(32) + subcredential = os.urandom(32) + + client_enc_key, client_mac_key = client_part1(intro_auth_pubkey_str, intro_enc_pubkey, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential) + + service_enc_key, service_mac_key, service_ntor_key_seed, service_auth_input_mac, service_ephemeral_pubkey = service_part1(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential) + + assert(client_enc_key == service_enc_key) + assert(client_mac_key == service_mac_key) + + client_ntor_key_seed, client_auth_input_mac = client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_ephemeral_pubkey) + + assert(client_ntor_key_seed == service_ntor_key_seed) + assert(client_auth_input_mac == service_auth_input_mac) + + print "DONE: python dance [%s]" % repr(client_auth_input_mac) + +# Perform a pure little-t-tor integration test. +def do_little_t_tor_ntor_test(): + # Initialize all needed key material + subcredential = os.urandom(32) + client_ephemeral_enc_privkey = PrivateKey() + client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public() + intro_enc_privkey = PrivateKey() + intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key + intro_auth_pubkey_str = os.urandom(32) + + client_enc_key, client_mac_key = tor_client1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_privkey, subcredential) + assert(client_enc_key) + assert(client_mac_key) + + service_enc_key, service_mac_key, service_ntor_auth_mac, service_ntor_key_seed, service_eph_pubkey = tor_server1(intro_auth_pubkey_str, + intro_enc_privkey, + client_ephemeral_enc_pubkey, + subcredential) + assert(service_enc_key) + assert(service_mac_key) + assert(service_ntor_auth_mac) + assert(service_ntor_key_seed) + + assert(client_enc_key == service_enc_key) + assert(client_mac_key == service_mac_key) + + # Turn from bytes to key + service_eph_pubkey = curve25519mod.Public(service_eph_pubkey) + + client_ntor_auth_mac, client_ntor_key_seed = tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_eph_pubkey, subcredential) + assert(client_ntor_auth_mac) + assert(client_ntor_key_seed) + + assert(client_ntor_key_seed == service_ntor_key_seed) + assert(client_ntor_auth_mac == service_ntor_auth_mac) + + print "DONE: tor dance [%s]" % repr(client_ntor_auth_mac) + +""" +Do mixed test as follows: + 1. C -> S (python mode) + 2. C <- S (tor mode) + 3. Client computes keys (python mode) +""" +def do_first_mixed_test(): + subcredential = os.urandom(32) + + client_ephemeral_enc_privkey = PrivateKey() + client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public() + intro_enc_privkey = PrivateKey() + intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key + + intro_auth_pubkey_str = os.urandom(32) + + # Let's do mixed + client_enc_key, client_mac_key = client_part1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, + subcredential) + + service_enc_key, service_mac_key, service_ntor_auth_mac, service_ntor_key_seed, service_eph_pubkey = tor_server1(intro_auth_pubkey_str, + intro_enc_privkey, + client_ephemeral_enc_pubkey, + subcredential) + assert(service_enc_key) + assert(service_mac_key) + assert(service_ntor_auth_mac) + assert(service_ntor_key_seed) + assert(service_eph_pubkey) + + assert(client_enc_key == service_enc_key) + assert(client_mac_key == service_mac_key) + + # Turn from bytes to key + service_eph_pubkey = curve25519mod.Public(service_eph_pubkey) + + client_ntor_key_seed, client_auth_input_mac = client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_eph_pubkey) + + assert(client_auth_input_mac == service_ntor_auth_mac) + assert(client_ntor_key_seed == service_ntor_key_seed) + + print "DONE: 1st mixed dance [%s]" % repr(client_auth_input_mac) + +""" +Do mixed test as follows: + 1. C -> S (tor mode) + 2. C <- S (python mode) + 3. Client computes keys (tor mode) +""" +def do_second_mixed_test(): + subcredential = os.urandom(32) + + client_ephemeral_enc_privkey = PrivateKey() + client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public() + intro_enc_privkey = PrivateKey() + intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key + + intro_auth_pubkey_str = os.urandom(32) + + # Let's do mixed + client_enc_key, client_mac_key = tor_client1(intro_auth_pubkey_str, intro_enc_pubkey, + client_ephemeral_enc_privkey, subcredential) + assert(client_enc_key) + assert(client_mac_key) + + service_enc_key, service_mac_key, service_ntor_key_seed, service_ntor_auth_mac, service_ephemeral_pubkey = service_part1(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential) + + client_ntor_auth_mac, client_ntor_key_seed = tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey, + intro_enc_pubkey, service_ephemeral_pubkey, subcredential) + assert(client_ntor_auth_mac) + assert(client_ntor_key_seed) + + assert(client_ntor_key_seed == service_ntor_key_seed) + assert(client_ntor_auth_mac == service_ntor_auth_mac) + + print "DONE: 2nd mixed dance [%s]" % repr(client_ntor_auth_mac) + +def do_mixed_tests(): + do_first_mixed_test() + do_second_mixed_test() + +if __name__ == '__main__': + do_pure_python_ntor_test() + do_little_t_tor_ntor_test() + do_mixed_tests() diff --git a/src/test/include.am b/src/test/include.am index 1c0726fd3a..230a6c8bad 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -20,7 +20,7 @@ TESTSCRIPTS = \ src/test/test_switch_id.sh if USEPYTHON -TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh +TESTSCRIPTS += src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh endif TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ @@ -82,14 +82,19 @@ src_test_test_SOURCES = \ src/test/test_checkdir.c \ src/test/test_circuitlist.c \ src/test/test_circuitmux.c \ + src/test/test_circuitbuild.c \ src/test/test_circuituse.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ src/test/test_connection.c \ + src/test/test_conscache.c \ + src/test/test_consdiff.c \ + src/test/test_consdiffmgr.c \ src/test/test_containers.c \ src/test/test_controller.c \ src/test/test_controller_events.c \ src/test/test_crypto.c \ + src/test/test_crypto_openssl.c \ src/test/test_data.c \ src/test/test_dir.c \ src/test/test_dir_common.c \ @@ -129,6 +134,7 @@ src_test_test_SOURCES = \ src/test/test_shared_random.c \ src/test/test_socks.c \ src/test/test_status.c \ + src/test/test_storagedir.c \ src/test/test_threads.c \ src/test/test_tortls.c \ src/test/test_util.c \ @@ -174,7 +180,8 @@ src_test_test_switch_id_LDFLAGS = @TOR_LDFLAGS_zlib@ src_test_test_switch_id_LDADD = \ src/common/libor-testing.a \ src/common/libor-ctime-testing.a \ - @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ @@ -186,9 +193,10 @@ src_test_test_LDADD = src/or/libtor-testing.a \ src/common/libor-ctime-testing.a \ src/common/libor-event-testing.a \ src/trunnel/libor-trunnel-testing.a \ + src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_SYSTEMD_LIBS@ + @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS) src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS) @@ -209,9 +217,10 @@ src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \ src/common/libor-ctime.a \ src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \ src/common/libor-event.a src/trunnel/libor-trunnel.a \ + src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ - @TOR_SYSTEMD_LIBS@ + @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ @@ -220,8 +229,10 @@ src_test_test_workqueue_LDADD = src/or/libtor-testing.a \ src/common/libor-ctime-testing.a \ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \ src/common/libor-event-testing.a \ + src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ src_test_test_timers_CPPFLAGS = $(src_test_test_CPPFLAGS) src_test_test_timers_CFLAGS = $(src_test_test_CFLAGS) @@ -231,7 +242,8 @@ src_test_test_timers_LDADD = \ src/common/libor-event-testing.a \ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_LZMA_LIBS@ src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS) noinst_HEADERS+= \ @@ -249,20 +261,35 @@ noinst_HEADERS+= \ src/test/vote_descriptors.inc noinst_PROGRAMS+= src/test/test-ntor-cl +noinst_PROGRAMS+= src/test/test-hs-ntor-cl src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \ src/common/libor-ctime.a \ src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \ + src/trace/libor-trace.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_LZMA_LIBS@ src_test_test_ntor_cl_AM_CPPFLAGS = \ -I"$(top_srcdir)/src/or" +src_test_test_hs_ntor_cl_SOURCES = src/test/test_hs_ntor_cl.c +src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_test_test_hs_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \ + src/common/libor-ctime.a \ + src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ +src_test_test_hs_ntor_cl_AM_CPPFLAGS = \ + -I"$(top_srcdir)/src/or" + + noinst_PROGRAMS += src/test/test-bt-cl src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c src_test_test_bt_cl_LDADD = src/common/libor-testing.a \ src/common/libor-ctime-testing.a \ + src/trace/libor-trace.a \ @TOR_LIB_MATH@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) @@ -271,12 +298,13 @@ src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) EXTRA_DIST += \ src/test/bt_test.py \ src/test/ntor_ref.py \ + src/test/hs_ntor_ref.py \ src/test/fuzz_static_testcases.sh \ src/test/slownacl_curve25519.py \ src/test/zero_length_keys.sh \ src/test/test_keygen.sh \ src/test/test_zero_length_keys.sh \ - src/test/test_ntor.sh src/test/test_bt.sh \ + src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh \ src/test/test-network.sh \ src/test/test_switch_id.sh \ src/test/test_workqueue_cancel.sh \ diff --git a/src/test/log_test_helpers.c b/src/test/log_test_helpers.c index c788a33c17..d5a39cfeee 100644 --- a/src/test/log_test_helpers.c +++ b/src/test/log_test_helpers.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define LOG_PRIVATE #include "torlog.h" diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h index 922c68b42f..f7798c0249 100644 --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" @@ -71,14 +71,14 @@ void mock_dump_saved_logs(void); \ assert_log_predicate(mock_saved_log_has_message_containing(str) && \ mock_saved_log_n_entries() == 1, \ - "expected log to contain exactly 1 message: " # str); \ + "expected log to contain exactly 1 message " # str); \ } while (0); #define expect_single_log_msg_containing(str) \ do { \ assert_log_predicate(mock_saved_log_has_message_containing(str)&& \ mock_saved_log_n_entries() == 1 , \ - "expected log to contain 1 message, containing" # str); \ + "expected log to contain 1 message, containing " # str); \ } while (0); #define expect_no_log_msg(str) \ diff --git a/src/test/ntor_ref.py b/src/test/ntor_ref.py index df065853f3..c753588f97 100755 --- a/src/test/ntor_ref.py +++ b/src/test/ntor_ref.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 2012-2015, The Tor Project, Inc +# Copyright 2012-2017, The Tor Project, Inc # See LICENSE for licensing information """ diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c index 377337bcb9..f7880046fb 100644 --- a/src/test/rend_test_helpers.c +++ b/src/test/rend_test_helpers.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h index 180a4e8fde..486adba436 100644 --- a/src/test/rend_test_helpers.h +++ b/src/test/rend_test_helpers.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test-child.c b/src/test/test-child.c index fdf3ccec0a..f0bdb3ea26 100644 --- a/src/test/test-child.c +++ b/src/test/test-child.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Tor Project, Inc. */ +/* Copyright (c) 2011-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test-network.sh b/src/test/test-network.sh index 4d9776822b..6e0f286573 100755 --- a/src/test/test-network.sh +++ b/src/test/test-network.sh @@ -1,103 +1,45 @@ -#! /bin/sh +#!/bin/sh -# Please do not modify this script, it has been moved to chutney/tools +# This script calls the equivalent script in chutney/tools -ECHO_N="/bin/echo -n" +# If we already know CHUTNEY_PATH, don't bother with argument parsing +TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh" +# Call the chutney version of this script, if it exists, and we can find it +if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then + # we can't produce any output, because we might be --quiet + # this preserves arguments with spaces correctly + exec "$TEST_NETWORK" "$@" +fi + +# We need to go looking for CHUTNEY_PATH +# Do we output anything at all? +ECHO="${ECHO:-echo}" # Output is prefixed with the name of the script myname=$(basename $0) -# We need to find CHUTNEY_PATH, so that we can call the version of this script -# in chutney/tools. And we want to pass any arguments to that script as well. -# So we source this script, which processes its arguments to find CHUTNEY_PATH. - -# Avoid recursively sourcing this script, and don't call the chutney version -# while recursing, either -if [ "$TEST_NETWORK_RECURSING" != true ]; then - # Process the arguments into environmental variables with this script - # to make sure $CHUTNEY_PATH is set - # When we switch to using test-network.sh in chutney/tools, --dry-run - # can be removed, because this script will find chutney, then pass all - # arguments to chutney's test-network.sh - echo "$myname: Parsing command-line arguments to find \$CHUTNEY_PATH" - export TEST_NETWORK_RECURSING=true - . "$0" --dry-run "$@" - - # Call the chutney version of this script, if it exists, and we can find it - if [ -d "$CHUTNEY_PATH" -a -x "$CHUTNEY_PATH/tools/test-network.sh" ]; then - unset NETWORK_DRY_RUN - echo "$myname: Calling newer chutney script \ -$CHUTNEY_PATH/tools/test-network.sh" - "$CHUTNEY_PATH/tools/test-network.sh" "$@" - exit $? - else - echo "$myname: This script has moved to chutney/tools." - echo "$myname: Please update your chutney using 'git pull'." - # When we switch to using test-network.sh in chutney/tools, we should - # exit with a very loud failure here - echo "$myname: Falling back to the old tor version of the script." - fi -fi +# Save the arguments before we destroy them +# This might not preserve arguments with spaces in them +ORIGINAL_ARGS="$@" +# We need to find CHUTNEY_PATH, so that we can call the version of this script +# in chutney/tools with the same arguments. We also need to respect --quiet. until [ -z "$1" ] do case "$1" in --chutney-path) - export CHUTNEY_PATH="$2" + CHUTNEY_PATH="$2" shift ;; --tor-path) - export TOR_DIR="$2" - shift - ;; - # When we switch to using test-network.sh in chutney/tools, only the - # --chutney-path and --tor-path arguments need to be processed by this - # script, everything else can be handled by chutney's test-network.sh - --flavor|--flavour|--network-flavor|--network-flavour) - export NETWORK_FLAVOUR="$2" - shift - ;; - --delay|--sleep|--bootstrap-time|--time) - export BOOTSTRAP_TIME="$2" - shift - ;; - # Environmental variables used by chutney verify performance tests - # Send this many bytes per client connection (10 KBytes) - --data|--data-bytes|--data-byte|--bytes|--byte) - export CHUTNEY_DATA_BYTES="$2" + TOR_DIR="$2" shift ;; - # Make this many connections per client (1) - # Note: If you create 7 or more connections to a hidden service from - # a single Tor 0.2.7 client, you'll likely get a verification failure due - # to #15937. This is fixed in 0.2.8. - --connections|--connection|--connection-count|--count) - export CHUTNEY_CONNECTIONS="$2" - shift + --quiet) + ECHO=true ;; - # Make each client connect to each HS (0) - # 0 means a single client connects to each HS - # 1 means every client connects to every HS - --hs-multi-client|--hs-multi-clients|--hs-client|--hs-clients) - export CHUTNEY_HS_MULTI_CLIENT="$2" - shift - ;; - --coverage) - export USE_COVERAGE_BINARY=true - ;; - --dry-run) - # process arguments, but don't call any other scripts - export NETWORK_DRY_RUN=true - ;; *) - echo "$myname: Sorry, I don't know what to do with '$1'." - echo "$myname: Maybe chutney's test-network.sh understands '$1'." - echo "$myname: Please update your chutney using 'git pull', and set \ -\$CHUTNEY_PATH" - # continue processing arguments during a dry run - if [ "$NETWORK_DRY_RUN" != true ]; then - exit 2 - fi + # maybe chutney's test-network.sh can handle it ;; esac shift @@ -106,22 +48,22 @@ done # optional: $TOR_DIR is the tor build directory # it's used to find the location of tor binaries # if it's not set: -# - set it ro $BUILDDIR, or +# - set it to $BUILDDIR, or # - if $PWD looks like a tor build directory, set it to $PWD, or # - unset $TOR_DIR, and let chutney fall back to finding tor binaries in $PATH if [ ! -d "$TOR_DIR" ]; then if [ -d "$BUILDDIR/src/or" -a -d "$BUILDDIR/src/tools" ]; then # Choose the build directory # But only if it looks like one - echo "$myname: \$TOR_DIR not set, trying \$BUILDDIR" - export TOR_DIR="$BUILDDIR" + $ECHO "$myname: \$TOR_DIR not set, trying \$BUILDDIR" + TOR_DIR="$BUILDDIR" elif [ -d "$PWD/src/or" -a -d "$PWD/src/tools" ]; then # Guess the tor directory is the current directory # But only if it looks like one - echo "$myname: \$TOR_DIR not set, trying \$PWD" - export TOR_DIR="$PWD" + $ECHO "$myname: \$TOR_DIR not set, trying \$PWD" + TOR_DIR="$PWD" else - echo "$myname: no \$TOR_DIR, chutney will use \$PATH for tor binaries" + $ECHO "$myname: no \$TOR_DIR, chutney will use \$PATH for tor binaries" unset TOR_DIR fi fi @@ -133,63 +75,34 @@ fi # - fail and tell the user how to clone the chutney repository if [ ! -d "$CHUTNEY_PATH" -o ! -x "$CHUTNEY_PATH/chutney" ]; then if [ -x "$PWD/chutney" ]; then - echo "$myname: \$CHUTNEY_PATH not valid, trying \$PWD" - export CHUTNEY_PATH="$PWD" + $ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$PWD" + CHUTNEY_PATH="$PWD" elif [ -d "$TOR_DIR" -a -d "$TOR_DIR/../chutney" -a \ -x "$TOR_DIR/../chutney/chutney" ]; then - echo "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney" - export CHUTNEY_PATH="$TOR_DIR/../chutney" + $ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney" + CHUTNEY_PATH="$TOR_DIR/../chutney" else - # TODO: work out how to package and install chutney, - # so users can find it in $PATH - echo "$myname: missing 'chutney' in \$CHUTNEY_PATH ($CHUTNEY_PATH)" - echo "$myname: Get chutney: git clone https://git.torproject.org/\ + $ECHO "$myname: missing 'chutney' in \$CHUTNEY_PATH ($CHUTNEY_PATH)" + $ECHO "$myname: Get chutney: git clone https://git.torproject.org/\ chutney.git" - echo "$myname: Set \$CHUTNEY_PATH to a non-standard location: export \ + $ECHO "$myname: Set \$CHUTNEY_PATH to a non-standard location: export \ CHUTNEY_PATH=\`pwd\`/chutney" unset CHUTNEY_PATH exit 1 fi fi -# When we switch to using test-network.sh in chutney/tools, this comment and -# everything below it can be removed - -# For picking up the right tor binaries. -# If these varibles aren't set, chutney looks for tor binaries in $PATH -if [ -d "$TOR_DIR" ]; then - tor_name=tor - tor_gencert_name=tor-gencert - if [ "$USE_COVERAGE_BINARY" = true ]; then - tor_name=tor-cov - fi - export CHUTNEY_TOR="${TOR_DIR}/src/or/${tor_name}" - export CHUTNEY_TOR_GENCERT="${TOR_DIR}/src/tools/${tor_gencert_name}" -fi - -# Set the variables for the chutney network flavour -export NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-"bridges+hs"} -export CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR - -# And finish up if we're doing a dry run -if [ "$NETWORK_DRY_RUN" = true ]; then - # we can't exit here, it breaks argument processing - return +TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh" +# Call the chutney version of this script, if it exists, and we can find it +if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then + $ECHO "$myname: Calling newer chutney script $TEST_NETWORK" + # this may fail if some arguments have spaces in them + # if so, set CHUTNEY_PATH before calling test-network.sh, and spaces + # will be handled correctly + exec "$TEST_NETWORK" $ORIGINAL_ARGS +else + $ECHO "$myname: Could not find tools/test-network.sh in CHUTNEY_PATH." + $ECHO "$myname: Please update your chutney using 'git pull'." + # We have failed to do what the user asked + exit 1 fi - -cd "$CHUTNEY_PATH" -./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 2 - -# Sleep some, waiting for the network to bootstrap. -# TODO: Add chutney command 'bootstrap-status' and use that instead. -BOOTSTRAP_TIME=${BOOTSTRAP_TIME:-35} -$ECHO_N "$myname: sleeping for $BOOTSTRAP_TIME seconds" -n=$BOOTSTRAP_TIME; while [ $n -gt 0 ]; do - sleep 1; n=$(expr $n - 1); $ECHO_N . -done; echo "" -./chutney verify $CHUTNEY_NETWORK -VERIFY_EXIT_STATUS=$? -# work around a bug/feature in make -j2 (or more) -# where make hangs if any child processes are still alive -./chutney stop $CHUTNEY_NETWORK -exit $VERIFY_EXIT_STATUS diff --git a/src/test/test-timers.c b/src/test/test-timers.c index b5fcade7f8..99715f4333 100644 --- a/src/test/test-timers.c +++ b/src/test/test-timers.c @@ -1,4 +1,4 @@ -/* Copyright 2016, The Tor Project, Inc. */ +/* Copyright 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test.c b/src/test/test.c index 866408e856..4d2cf1536b 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -44,13 +44,13 @@ double fabs(double x); #include "buffers.h" #include "circuitlist.h" #include "circuitstats.h" +#include "compress.h" #include "config.h" #include "connection_edge.h" #include "geoip.h" #include "rendcommon.h" #include "rendcache.h" #include "test.h" -#include "torgzip.h" #include "main.h" #include "memarea.h" #include "onion.h" @@ -1188,16 +1188,21 @@ struct testgroup_t testgroups[] = { { "channel/", channel_tests }, { "channeltls/", channeltls_tests }, { "checkdir/", checkdir_tests }, + { "circuitbuild/", circuitbuild_tests }, { "circuitlist/", circuitlist_tests }, { "circuitmux/", circuitmux_tests }, { "circuituse/", circuituse_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, { "connection/", connection_tests }, + { "conscache/", conscache_tests }, + { "consdiff/", consdiff_tests }, + { "consdiffmgr/", consdiffmgr_tests }, { "container/", container_tests }, { "control/", controller_tests }, { "control/event/", controller_event_tests }, { "crypto/", crypto_tests }, + { "crypto/openssl/", crypto_openssl_tests }, { "dir/", dir_tests }, { "dir_handle_get/", dir_handle_get_tests }, { "dir/md/", microdesc_tests }, @@ -1232,6 +1237,7 @@ struct testgroup_t testgroups[] = { { "socks/", socks_tests }, { "shared-random/", sr_tests }, { "status/" , status_tests }, + { "storagedir/", storagedir_tests }, { "tortls/", tortls_tests }, { "util/", util_tests }, { "util/format/", util_format_tests }, diff --git a/src/test/test.h b/src/test/test.h index 2bd58f51c8..3d7d05e771 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2003, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_TEST_H @@ -183,16 +183,21 @@ extern struct testcase_t cell_queue_tests[]; extern struct testcase_t channel_tests[]; extern struct testcase_t channeltls_tests[]; extern struct testcase_t checkdir_tests[]; +extern struct testcase_t circuitbuild_tests[]; extern struct testcase_t circuitlist_tests[]; extern struct testcase_t circuitmux_tests[]; extern struct testcase_t circuituse_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; extern struct testcase_t connection_tests[]; +extern struct testcase_t conscache_tests[]; +extern struct testcase_t consdiff_tests[]; +extern struct testcase_t consdiffmgr_tests[]; extern struct testcase_t container_tests[]; extern struct testcase_t controller_tests[]; extern struct testcase_t controller_event_tests[]; extern struct testcase_t crypto_tests[]; +extern struct testcase_t crypto_openssl_tests[]; extern struct testcase_t dir_tests[]; extern struct testcase_t dir_handle_get_tests[]; extern struct testcase_t entryconn_tests[]; @@ -227,6 +232,7 @@ 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 socks_tests[]; extern struct testcase_t status_tests[]; extern struct testcase_t thread_tests[]; diff --git a/src/test/test_addr.c b/src/test/test_addr.c index 49e248014f..b6d2445bad 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ADDRESSMAP_PRIVATE diff --git a/src/test/test_address.c b/src/test/test_address.c index 0d142ad483..50a0574522 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ADDRESS_PRIVATE diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c index 709d599f52..ed588ecc5b 100644 --- a/src/test/test_bt_cl.c +++ b/src/test/test_bt_cl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Tor Project, Inc. */ +/* Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index 9e7bdb8911..07114a8571 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define BUFFERS_PRIVATE @@ -578,120 +578,150 @@ test_buffer_time_tracking(void *arg) } static void -test_buffers_zlib_impl(int finalize_with_nil) +test_buffers_compress_fin_at_chunk_end_impl(compress_method_t method, + compression_level_t level) { char *msg = NULL; char *contents = NULL; char *expanded = NULL; buf_t *buf = NULL; - tor_zlib_state_t *zlib_state = NULL; + tor_compress_state_t *compress_state = NULL; size_t out_len, in_len; - int done; + size_t sz, headerjunk; buf = buf_new_with_capacity(128); /* will round up */ - zlib_state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); + sz = buf_get_default_chunk_size(buf); + msg = tor_malloc_zero(sz); - msg = tor_malloc(512); - crypto_rand(msg, 512); - tt_int_op(write_to_buf_zlib(buf, zlib_state, msg, 128, 0), OP_EQ, 0); - tt_int_op(write_to_buf_zlib(buf, zlib_state, msg+128, 128, 0), OP_EQ, 0); - tt_int_op(write_to_buf_zlib(buf, zlib_state, msg+256, 256, 0), OP_EQ, 0); - done = !finalize_with_nil; - tt_int_op(write_to_buf_zlib(buf, zlib_state, "all done", 9, done), OP_EQ, 0); - if (finalize_with_nil) { - tt_int_op(write_to_buf_zlib(buf, zlib_state, "", 0, 1), OP_EQ, 0); - } + write_to_buf(msg, 1, buf); + tt_assert(buf->head); + + /* Fill up the chunk so the compression stuff won't fit in one chunk. */ + tt_uint_op(buf->head->memlen, OP_LT, sz); + headerjunk = buf->head->memlen - 7; + write_to_buf(msg, headerjunk-1, buf); + tt_uint_op(buf->head->datalen, OP_EQ, headerjunk); + tt_uint_op(buf_datalen(buf), OP_EQ, headerjunk); + /* Write an empty string, with finalization on. */ + compress_state = tor_compress_new(1, method, level); + tt_int_op(write_to_buf_compress(buf, compress_state, "", 0, 1), OP_EQ, 0); in_len = buf_datalen(buf); contents = tor_malloc(in_len); tt_int_op(fetch_from_buf(contents, in_len, buf), OP_EQ, 0); - tt_int_op(0, OP_EQ, tor_gzip_uncompress(&expanded, &out_len, - contents, in_len, - ZLIB_METHOD, 1, - LOG_WARN)); + if (method == NO_METHOD) { + tt_uint_op(in_len, OP_EQ, headerjunk); + } else { + tt_uint_op(in_len, OP_GT, headerjunk); + } - tt_int_op(out_len, OP_GE, 128); - tt_mem_op(msg, OP_EQ, expanded, 128); - tt_int_op(out_len, OP_GE, 512); - tt_mem_op(msg, OP_EQ, expanded, 512); - tt_int_op(out_len, OP_EQ, 512+9); - tt_mem_op("all done", OP_EQ, expanded+512, 9); + tt_int_op(0, OP_EQ, tor_uncompress(&expanded, &out_len, + contents + headerjunk, + in_len - headerjunk, + method, 1, + LOG_WARN)); + + tt_int_op(out_len, OP_EQ, 0); + tt_assert(expanded); done: buf_free(buf); - tor_zlib_free(zlib_state); + tor_compress_free(compress_state); tor_free(contents); tor_free(expanded); tor_free(msg); } static void -test_buffers_zlib(void *arg) -{ - (void) arg; - test_buffers_zlib_impl(0); -} -static void -test_buffers_zlib_fin_with_nil(void *arg) -{ - (void) arg; - test_buffers_zlib_impl(1); -} - -static void -test_buffers_zlib_fin_at_chunk_end(void *arg) +test_buffers_compress_impl(compress_method_t method, + compression_level_t level, + int finalize_with_nil) { char *msg = NULL; char *contents = NULL; char *expanded = NULL; buf_t *buf = NULL; - tor_zlib_state_t *zlib_state = NULL; + tor_compress_state_t *compress_state = NULL; size_t out_len, in_len; - size_t sz, headerjunk; - (void) arg; + int done; buf = buf_new_with_capacity(128); /* will round up */ - sz = buf_get_default_chunk_size(buf); - msg = tor_malloc_zero(sz); - - write_to_buf(msg, 1, buf); - tt_assert(buf->head); + compress_state = tor_compress_new(1, method, level); - /* Fill up the chunk so the zlib stuff won't fit in one chunk. */ - tt_uint_op(buf->head->memlen, OP_LT, sz); - headerjunk = buf->head->memlen - 7; - write_to_buf(msg, headerjunk-1, buf); - tt_uint_op(buf->head->datalen, OP_EQ, headerjunk); - tt_uint_op(buf_datalen(buf), OP_EQ, headerjunk); - /* Write an empty string, with finalization on. */ - zlib_state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); - tt_int_op(write_to_buf_zlib(buf, zlib_state, "", 0, 1), OP_EQ, 0); + msg = tor_malloc(512); + crypto_rand(msg, 512); + tt_int_op(write_to_buf_compress(buf, compress_state, + msg, 128, 0), OP_EQ, 0); + tt_int_op(write_to_buf_compress(buf, compress_state, + msg+128, 128, 0), OP_EQ, 0); + tt_int_op(write_to_buf_compress(buf, compress_state, + msg+256, 256, 0), OP_EQ, 0); + done = !finalize_with_nil; + tt_int_op(write_to_buf_compress(buf, compress_state, + "all done", 9, done), OP_EQ, 0); + if (finalize_with_nil) { + tt_int_op(write_to_buf_compress(buf, compress_state, "", 0, 1), OP_EQ, 0); + } in_len = buf_datalen(buf); contents = tor_malloc(in_len); tt_int_op(fetch_from_buf(contents, in_len, buf), OP_EQ, 0); - tt_uint_op(in_len, OP_GT, headerjunk); - - tt_int_op(0, OP_EQ, tor_gzip_uncompress(&expanded, &out_len, - contents + headerjunk, in_len - headerjunk, - ZLIB_METHOD, 1, - LOG_WARN)); + tt_int_op(0, OP_EQ, tor_uncompress(&expanded, &out_len, + contents, in_len, + method, 1, + LOG_WARN)); - tt_int_op(out_len, OP_EQ, 0); - tt_assert(expanded); + tt_int_op(out_len, OP_GE, 128); + tt_mem_op(msg, OP_EQ, expanded, 128); + tt_int_op(out_len, OP_GE, 512); + tt_mem_op(msg, OP_EQ, expanded, 512); + tt_int_op(out_len, OP_EQ, 512+9); + tt_mem_op("all done", OP_EQ, expanded+512, 9); done: buf_free(buf); - tor_zlib_free(zlib_state); + tor_compress_free(compress_state); tor_free(contents); tor_free(expanded); tor_free(msg); } +static void +test_buffers_compress(void *arg) +{ + const char *methodname = arg; + tt_assert(methodname); + + compress_method_t method = compression_method_get_by_name(methodname); + tt_int_op(method, OP_NE, UNKNOWN_METHOD); + + if (! tor_compress_supports_method(method)) { + tt_skip(); + } + + compression_level_t levels[] = { + BEST_COMPRESSION, + HIGH_COMPRESSION, + MEDIUM_COMPRESSION, + LOW_COMPRESSION + }; + + for (unsigned l = 0; l < ARRAY_LENGTH(levels); ++l) { + compression_level_t level = levels[l]; + + test_buffers_compress_impl(method, level, 0); + test_buffers_compress_impl(method, level, 1); + test_buffers_compress_fin_at_chunk_end_impl(method, level); + } + + done: + ; +} + static const uint8_t *tls_read_ptr; static int n_remaining; static int next_reply_val[16]; @@ -816,14 +846,22 @@ struct testcase_t buffer_tests[] = { { "allocation_tracking", test_buffer_allocation_tracking, TT_FORK, NULL, NULL }, { "time_tracking", test_buffer_time_tracking, TT_FORK, NULL, NULL }, - { "zlib", test_buffers_zlib, TT_FORK, NULL, NULL }, - { "zlib_fin_with_nil", test_buffers_zlib_fin_with_nil, TT_FORK, NULL, NULL }, - { "zlib_fin_at_chunk_end", test_buffers_zlib_fin_at_chunk_end, TT_FORK, - NULL, NULL}, { "tls_read_mocked", test_buffers_tls_read_mocked, 0, NULL, NULL }, { "chunk_size", test_buffers_chunk_size, 0, NULL, NULL }, { "find_contentlen", test_buffers_find_contentlen, 0, NULL, NULL }, + + { "compress/zlib", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"deflate" }, + { "compress/gzip", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"gzip" }, + { "compress/zstd", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"x-zstd" }, + { "compress/lzma", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"x-tor-lzma" }, + { "compress/none", test_buffers_compress, TT_FORK, + &passthrough_setup, (char*)"identity" }, + END_OF_TESTCASES }; diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index 22c34b6d6c..007f7e3d3e 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_cell_queue.c b/src/test/test_cell_queue.c index 93ac9854d8..69e89b69b0 100644 --- a/src/test/test_cell_queue.c +++ b/src/test/test_cell_queue.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CIRCUITLIST_PRIVATE diff --git a/src/test/test_channel.c b/src/test/test_channel.c index 862bd6dfa6..f5999b8e67 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TOR_CHANNEL_INTERNAL_ diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c index fd98ee40fb..96c5eba9a5 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_checkdir.c b/src/test/test_checkdir.c index fbb33f87f6..38f3360b61 100644 --- a/src/test/test_checkdir.c +++ b/src/test/test_checkdir.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c new file mode 100644 index 0000000000..a5282df69d --- /dev/null +++ b/src/test/test_circuitbuild.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CIRCUITBUILD_PRIVATE + +#include "or.h" +#include "test.h" +#include "test_helpers.h" +#include "log_test_helpers.h" +#include "config.h" +#include "circuitbuild.h" + +/* Dummy nodes smartlist for testing */ +static smartlist_t dummy_nodes; +/* Dummy exit extend_info for testing */ +static extend_info_t dummy_ei; + +static int +mock_count_acceptable_nodes(smartlist_t *nodes) +{ + (void)nodes; + + return DEFAULT_ROUTE_LEN + 1; +} + +/* Test route lengths when the caller of new_route_len() doesn't + * specify exit_ei. */ +static void +test_new_route_len_noexit(void *arg) +{ + int r; + + (void)arg; + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); + + r = new_route_len(CIRCUIT_PURPOSE_C_GENERAL, NULL, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + r = new_route_len(CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT, NULL, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + r = new_route_len(CIRCUIT_PURPOSE_S_CONNECT_REND, NULL, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + done: + UNMOCK(count_acceptable_nodes); +} + +/* Test route lengths where someone else chose the "exit" node, which + * require an extra hop for safety. */ +static void +test_new_route_len_unsafe_exit(void *arg) +{ + int r; + + (void)arg; + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); + + /* connecting to hidden service directory */ + r = new_route_len(CIRCUIT_PURPOSE_C_GENERAL, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN + 1, OP_EQ, r); + + /* client connecting to introduction point */ + r = new_route_len(CIRCUIT_PURPOSE_C_INTRODUCING, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN + 1, OP_EQ, r); + + /* hidden service connecting to rendezvous point */ + r = new_route_len(CIRCUIT_PURPOSE_S_CONNECT_REND, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN + 1, OP_EQ, r); + + done: + UNMOCK(count_acceptable_nodes); +} + +/* Test route lengths where we chose the "exit" node, which don't + * require an extra hop for safety. */ +static void +test_new_route_len_safe_exit(void *arg) +{ + int r; + + (void)arg; + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); + + /* hidden service connecting to introduction point */ + r = new_route_len(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, &dummy_ei, + &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + /* router testing its own reachability */ + r = new_route_len(CIRCUIT_PURPOSE_TESTING, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN, OP_EQ, r); + + done: + UNMOCK(count_acceptable_nodes); +} + +/* Make sure a non-fatal assertion fails when new_route_len() gets an + * unexpected circuit purpose. */ +static void +test_new_route_len_unhandled_exit(void *arg) +{ + int r; + + (void)arg; + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); + + tor_capture_bugs_(1); + setup_full_capture_of_logs(LOG_WARN); + r = new_route_len(CIRCUIT_PURPOSE_CONTROLLER, &dummy_ei, &dummy_nodes); + tt_int_op(DEFAULT_ROUTE_LEN + 1, OP_EQ, r); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(exit_ei && !known_purpose)"); + expect_single_log_msg_containing("Unhandled purpose"); + expect_single_log_msg_containing("with a chosen exit; assuming routelen"); + teardown_capture_of_logs(); + tor_end_capture_bugs_(); + + done: + UNMOCK(count_acceptable_nodes); +} + +struct testcase_t circuitbuild_tests[] = { + { "noexit", test_new_route_len_noexit, 0, NULL, NULL }, + { "safe_exit", test_new_route_len_safe_exit, 0, NULL, NULL }, + { "unsafe_exit", test_new_route_len_unsafe_exit, 0, NULL, NULL }, + { "unhandled_exit", test_new_route_len_unhandled_exit, 0, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c index 7eed5fe225..344ab27921 100644 --- a/src/test/test_circuitlist.c +++ b/src/test/test_circuitlist.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TOR_CHANNEL_INTERNAL_ @@ -201,68 +201,68 @@ test_rend_token_maps(void *arg) tt_int_op(tok3[REND_TOKEN_LEN-1], OP_EQ, '.'); /* No maps; nothing there. */ - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok1)); - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok1)); - hs_circuitmap_register_rend_circ(c1, tok1); - hs_circuitmap_register_intro_circ_v2(c2, tok2); + hs_circuitmap_register_rend_circ_relay_side(c1, tok1); + hs_circuitmap_register_intro_circ_v2_relay_side(c2, tok2); - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok3)); - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok3)); - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok2)); - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok3)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok3)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok1)); /* Without purpose set, we don't get the circuits */ - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok1)); - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok2)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); c1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; c2->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; /* Okay, make sure they show up now. */ - tt_ptr_op(c1, OP_EQ, hs_circuitmap_get_rend_circ(tok1)); - tt_ptr_op(c2, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok2)); + tt_ptr_op(c1, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + tt_ptr_op(c2, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); /* Two items at the same place with the same token. */ c3->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; - hs_circuitmap_register_rend_circ(c3, tok2); - tt_ptr_op(c2, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok2)); - tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ(tok2)); + hs_circuitmap_register_rend_circ_relay_side(c3, tok2); + tt_ptr_op(c2, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); + tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); /* Marking a circuit makes it not get returned any more */ circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED); - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); circuit_free(TO_CIRCUIT(c1)); c1 = NULL; /* Freeing a circuit makes it not get returned any more. */ circuit_free(TO_CIRCUIT(c2)); c2 = NULL; - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok2)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); /* c3 -- are you still there? */ - tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ(tok2)); + tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); /* Change its cookie. This never happens in Tor per se, but hey. */ c3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; - hs_circuitmap_register_intro_circ_v2(c3, tok3); + hs_circuitmap_register_intro_circ_v2_relay_side(c3, tok3); - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok2)); - tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok3)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); + tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok3)); /* Now replace c3 with c4. */ c4->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; - hs_circuitmap_register_intro_circ_v2(c4, tok3); + hs_circuitmap_register_intro_circ_v2_relay_side(c4, tok3); - tt_ptr_op(c4, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok3)); + tt_ptr_op(c4, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok3)); - tt_ptr_op(c3->hs_token, OP_EQ, NULL); - tt_ptr_op(c4->hs_token, OP_NE, NULL); - tt_mem_op(c4->hs_token->token, OP_EQ, tok3, REND_TOKEN_LEN); + tt_ptr_op(TO_CIRCUIT(c3)->hs_token, OP_EQ, NULL); + tt_ptr_op(TO_CIRCUIT(c4)->hs_token, OP_NE, NULL); + tt_mem_op(TO_CIRCUIT(c4)->hs_token->token, OP_EQ, tok3, REND_TOKEN_LEN); /* Now clear c4's cookie. */ - hs_circuitmap_remove_circuit(c4); - tt_ptr_op(c4->hs_token, OP_EQ, NULL); - tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok3)); + hs_circuitmap_remove_circuit(TO_CIRCUIT(c4)); + tt_ptr_op(TO_CIRCUIT(c4)->hs_token, OP_EQ, NULL); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok3)); done: if (c1) @@ -370,10 +370,89 @@ test_pick_circid(void *arg) UNMOCK(channel_dump_statistics); } +/** Test that the circuit pools of our HS circuitmap are isolated based on + * their token type. */ +static void +test_hs_circuitmap_isolation(void *arg) +{ + or_circuit_t *circ1 = NULL; + origin_circuit_t *circ2 = NULL; + or_circuit_t *circ3 = NULL; + origin_circuit_t *circ4 = NULL; + + (void)arg; + + hs_circuitmap_init(); + + { + const uint8_t tok1[REND_TOKEN_LEN] = "bet i got some of th"; + + circ1 = or_circuit_new(0, NULL); + tt_assert(circ1); + circ1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; + + /* check that circuitmap is empty right? */ + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + + /* Register circ1 with tok1 as relay-side rend circ */ + hs_circuitmap_register_rend_circ_relay_side(circ1, tok1); + + /* check that service-side getters don't work */ + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_service_side(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_service_side(tok1)); + + /* Check that the right getter works. */ + tt_ptr_op(circ1, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); + } + + { + const uint8_t tok2[REND_TOKEN_LEN] = "you dont know anythi"; + + circ2 = origin_circuit_new(); + tt_assert(circ2); + circ2->base_.purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO; + circ3 = or_circuit_new(0, NULL); + tt_assert(circ3); + circ3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; + circ4 = origin_circuit_new(); + tt_assert(circ4); + circ4->base_.purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO; + + /* Register circ2 with tok2 as service-side intro v2 circ */ + hs_circuitmap_register_intro_circ_v2_service_side(circ2, tok2); + /* Register circ3 with tok2 again but for different purpose */ + hs_circuitmap_register_intro_circ_v2_relay_side(circ3, tok2); + + /* Check that the getters work */ + tt_ptr_op(circ2, OP_EQ, + hs_circuitmap_get_intro_circ_v2_service_side(tok2)); + tt_ptr_op(circ3, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); + + /* Register circ4 with tok2: it should override circ2 */ + hs_circuitmap_register_intro_circ_v2_service_side(circ4, tok2); + + /* check that relay-side getters don't work */ + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok2)); + + /* Check that the getter returns circ4; the last circuit registered with + * that token. */ + tt_ptr_op(circ4, OP_EQ, + hs_circuitmap_get_intro_circ_v2_service_side(tok2)); + } + + done: + circuit_free(TO_CIRCUIT(circ1)); + circuit_free(TO_CIRCUIT(circ2)); + circuit_free(TO_CIRCUIT(circ3)); + circuit_free(TO_CIRCUIT(circ4)); +} + struct testcase_t circuitlist_tests[] = { { "maps", test_clist_maps, TT_FORK, NULL, NULL }, { "rend_token_maps", test_rend_token_maps, TT_FORK, NULL, NULL }, { "pick_circid", test_pick_circid, TT_FORK, NULL, NULL }, + { "hs_circuitmap_isolation", test_hs_circuitmap_isolation, + TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c index 9e8fb54964..779783299d 100644 --- a/src/test/test_circuitmux.c +++ b/src/test/test_circuitmux.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TOR_CHANNEL_INTERNAL_ diff --git a/src/test/test_circuituse.c b/src/test/test_circuituse.c index 27a87660ff..5cc9fe571e 100644 --- a/src/test/test_circuituse.c +++ b/src/test/test_circuituse.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CIRCUITLIST_PRIVATE diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c index 0443cc0b1c..7dd8e65194 100644 --- a/src/test/test_compat_libevent.c +++ b/src/test/test_compat_libevent.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define COMPAT_LIBEVENT_PRIVATE diff --git a/src/test/test_config.c b/src/test/test_config.c index 3b19cad036..f0874e0e0b 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_connection.c b/src/test/test_connection.c index 5cda4f3175..7e5193b203 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -265,7 +265,7 @@ test_conn_get_rend_setup(const struct testcase_t *tc) rend_cache_init(); - /* TODO: use directory_initiate_command_rend() to do this - maybe? */ + /* TODO: use directory_initiate_request() to do this - maybe? */ tor_assert(strlen(TEST_CONN_REND_ADDR) == REND_SERVICE_ID_LEN_BASE32); conn->rend_data = rend_data_client_create(TEST_CONN_REND_ADDR, NULL, NULL, REND_NO_AUTH); diff --git a/src/test/test_conscache.c b/src/test/test_conscache.c new file mode 100644 index 0000000000..aee1ba8a06 --- /dev/null +++ b/src/test/test_conscache.c @@ -0,0 +1,340 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "config.h" +#include "conscache.h" +#include "test.h" + +#ifdef HAVE_UTIME_H +#include <utime.h> +#endif + +static void +test_conscache_open_failure(void *arg) +{ + (void) arg; + /* Try opening a directory that doesn't exist and which we shouldn't be + * able to create. */ + consensus_cache_t *cache = consensus_cache_open("a/b/c/d/e/f/g", 128); + tt_ptr_op(cache, OP_EQ, NULL); + + done: + ; +} + +static void +test_conscache_simple_usage(void *arg) +{ + (void)arg; + consensus_cache_entry_t *ent = NULL, *ent2 = NULL; + + /* Make a temporary datadir for these tests */ + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + check_private_dir(ddir_fname, CPD_CREATE, NULL); + consensus_cache_t *cache = consensus_cache_open("cons", 128); + + tt_assert(cache); + + /* Create object; make sure it exists. */ + config_line_t *labels = NULL; + config_line_append(&labels, "Hello", "world"); + config_line_append(&labels, "Adios", "planetas"); + ent = consensus_cache_add(cache, + labels, (const uint8_t *)"A\0B\0C", 5); + config_free_lines(labels); + labels = NULL; + tt_assert(ent); + + /* Make a second object */ + config_line_append(&labels, "Hello", "mundo"); + config_line_append(&labels, "Adios", "planets"); + ent2 = consensus_cache_add(cache, + labels, (const uint8_t *)"xyzzy", 5); + config_free_lines(labels); + labels = NULL; + tt_assert(ent2); + tt_assert(! consensus_cache_entry_is_mapped(ent2)); + consensus_cache_entry_decref(ent2); + ent2 = NULL; + + /* Check get_value */ + tt_ptr_op(NULL, OP_EQ, consensus_cache_entry_get_value(ent, "hebbo")); + tt_str_op("world", OP_EQ, consensus_cache_entry_get_value(ent, "Hello")); + + /* Check find_first */ + ent2 = consensus_cache_find_first(cache, "Hello", "world!"); + tt_ptr_op(ent2, OP_EQ, NULL); + ent2 = consensus_cache_find_first(cache, "Hello", "world"); + tt_ptr_op(ent2, OP_EQ, ent); + ent2 = consensus_cache_find_first(cache, "Hello", "mundo"); + tt_ptr_op(ent2, OP_NE, ent); + + tt_assert(! consensus_cache_entry_is_mapped(ent)); + + /* Check get_body */ + const uint8_t *bp = NULL; + size_t sz = 0; + int r = consensus_cache_entry_get_body(ent, &bp, &sz); + tt_int_op(r, OP_EQ, 0); + tt_u64_op(sz, OP_EQ, 5); + tt_mem_op(bp, OP_EQ, "A\0B\0C", 5); + tt_assert(consensus_cache_entry_is_mapped(ent)); + + /* Free and re-create the cache, to rescan the directory. */ + consensus_cache_free(cache); + consensus_cache_entry_decref(ent); + cache = consensus_cache_open("cons", 128); + + /* Make sure the entry is still there */ + ent = consensus_cache_find_first(cache, "Hello", "mundo"); + tt_assert(ent); + ent2 = consensus_cache_find_first(cache, "Adios", "planets"); + tt_ptr_op(ent, OP_EQ, ent2); + consensus_cache_entry_incref(ent); + tt_assert(! consensus_cache_entry_is_mapped(ent)); + r = consensus_cache_entry_get_body(ent, &bp, &sz); + tt_int_op(r, OP_EQ, 0); + tt_u64_op(sz, OP_EQ, 5); + tt_mem_op(bp, OP_EQ, "xyzzy", 5); + tt_assert(consensus_cache_entry_is_mapped(ent)); + + /* There should be two entries total. */ + smartlist_t *entries = smartlist_new(); + consensus_cache_find_all(entries, cache, NULL, NULL); + int n = smartlist_len(entries); + smartlist_free(entries); + tt_int_op(n, OP_EQ, 2); + + done: + consensus_cache_entry_decref(ent); + tor_free(ddir_fname); + consensus_cache_free(cache); +} + +static void +test_conscache_cleanup(void *arg) +{ + (void)arg; + const int N = 20; + consensus_cache_entry_t **ents = + tor_calloc(N, sizeof(consensus_cache_entry_t*)); + + /* Make a temporary datadir for these tests */ + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + check_private_dir(ddir_fname, CPD_CREATE, NULL); + consensus_cache_t *cache = consensus_cache_open("cons", 128); + + tt_assert(cache); + + /* Create a bunch of entries. */ + int i; + for (i = 0; i < N; ++i) { + config_line_t *labels = NULL; + char num[8]; + tor_snprintf(num, sizeof(num), "%d", i); + config_line_append(&labels, "test-id", "cleanup"); + config_line_append(&labels, "index", num); + size_t bodylen = i * 3; + uint8_t *body = tor_malloc(bodylen); + memset(body, i, bodylen); + ents[i] = consensus_cache_add(cache, labels, body, bodylen); + tor_free(body); + config_free_lines(labels); + tt_assert(ents[i]); + /* We're still holding a reference to each entry at this point. */ + } + + /* Page all of the entries into RAM */ + for (i = 0; i < N; ++i) { + const uint8_t *bp; + size_t sz; + tt_assert(! consensus_cache_entry_is_mapped(ents[i])); + consensus_cache_entry_get_body(ents[i], &bp, &sz); + tt_assert(consensus_cache_entry_is_mapped(ents[i])); + } + + /* Mark some of the entries as deletable. */ + for (i = 7; i < N; i += 7) { + consensus_cache_entry_mark_for_removal(ents[i]); + tt_assert(consensus_cache_entry_is_mapped(ents[i])); + } + + /* Mark some of the entries as aggressively unpaged. */ + for (i = 3; i < N; i += 3) { + consensus_cache_entry_mark_for_aggressive_release(ents[i]); + tt_assert(consensus_cache_entry_is_mapped(ents[i])); + } + + /* Incref some of the entries again */ + for (i = 0; i < N; i += 2) { + consensus_cache_entry_incref(ents[i]); + } + + /* Now we're going to decref everything. We do so at a specific time. I'm + * picking the moment when I was writing this test, at 2017-04-05 12:16:48 + * UTC. */ + const time_t example_time = 1491394608; + update_approx_time(example_time); + for (i = 0; i < N; ++i) { + consensus_cache_entry_decref(ents[i]); + if (i % 2) { + ents[i] = NULL; /* We're no longer holding any reference here. */ + } + } + + /* At this point, the aggressively-released items with refcount 1 should + * be unmapped. Nothing should be deleted. */ + consensus_cache_entry_t *e_tmp; + e_tmp = consensus_cache_find_first(cache, "index", "3"); + tt_assert(e_tmp); + tt_assert(! consensus_cache_entry_is_mapped(e_tmp)); + e_tmp = consensus_cache_find_first(cache, "index", "5"); + tt_assert(e_tmp); + tt_assert(consensus_cache_entry_is_mapped(e_tmp)); + e_tmp = consensus_cache_find_first(cache, "index", "6"); + tt_assert(e_tmp); + tt_assert(consensus_cache_entry_is_mapped(e_tmp)); + e_tmp = consensus_cache_find_first(cache, "index", "7"); + tt_assert(e_tmp == NULL); // not found because pending deletion. + + /* Delete the pending-deletion items. */ + consensus_cache_delete_pending(cache, 0); + { + smartlist_t *entries = smartlist_new(); + consensus_cache_find_all(entries, cache, NULL, NULL); + int n = smartlist_len(entries); + smartlist_free(entries); + tt_int_op(n, OP_EQ, 20 - 2); /* 1 entry was deleted; 1 is not-found. */ + } + e_tmp = consensus_cache_find_first(cache, "index", "7"); // refcnt == 1... + tt_assert(e_tmp == NULL); // so deleted. + e_tmp = consensus_cache_find_first(cache, "index", "14"); // refcnt == 2 + tt_assert(e_tmp == NULL); // not deleted; but not found. + + /* Now do lazy unmapping. */ + // should do nothing. + consensus_cache_unmap_lazy(cache, example_time - 10); + e_tmp = consensus_cache_find_first(cache, "index", "11"); + tt_assert(e_tmp); + tt_assert(consensus_cache_entry_is_mapped(e_tmp)); + // should actually unmap + consensus_cache_unmap_lazy(cache, example_time + 10); + e_tmp = consensus_cache_find_first(cache, "index", "11"); + tt_assert(e_tmp); + tt_assert(! consensus_cache_entry_is_mapped(e_tmp)); + // This one will still be mapped, since it has a reference. + e_tmp = consensus_cache_find_first(cache, "index", "16"); + tt_assert(e_tmp); + tt_assert(consensus_cache_entry_is_mapped(e_tmp)); + + for (i = 0; i < N; ++i) { + consensus_cache_entry_decref(ents[i]); + ents[i] = NULL; + } + + /* Free and re-create the cache, to rescan the directory. Make sure the + * deleted thing is still deleted, along with the other deleted thing. */ + consensus_cache_free(cache); + cache = consensus_cache_open("cons", 128); + { + smartlist_t *entries = smartlist_new(); + consensus_cache_find_all(entries, cache, NULL, NULL); + int n = smartlist_len(entries); + smartlist_free(entries); + tt_int_op(n, OP_EQ, 18); + } + + done: + for (i = 0; i < N; ++i) { + consensus_cache_entry_decref(ents[i]); + } + tor_free(ents); + tor_free(ddir_fname); + consensus_cache_free(cache); +} + +static void +test_conscache_filter(void *arg) +{ + (void)arg; + const int N = 30; + smartlist_t *lst = NULL; + + /* Make a temporary datadir for these tests */ + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + check_private_dir(ddir_fname, CPD_CREATE, NULL); + consensus_cache_t *cache = consensus_cache_open("cons", 128); + + tt_assert(cache); + + /* Create a bunch of entries with different labels */ + int i; + for (i = 0; i < N; ++i) { + config_line_t *labels = NULL; + char num[8]; + tor_snprintf(num, sizeof(num), "%d", i); + config_line_append(&labels, "test-id", "filter"); + config_line_append(&labels, "index", num); + tor_snprintf(num, sizeof(num), "%d", i % 3); + config_line_append(&labels, "mod3", num); + tor_snprintf(num, sizeof(num), "%d", i % 5); + config_line_append(&labels, "mod5", num); + + size_t bodylen = i * 3; + uint8_t *body = tor_malloc(bodylen); + memset(body, i, bodylen); + consensus_cache_entry_t *ent = + consensus_cache_add(cache, labels, body, bodylen); + tor_free(body); + config_free_lines(labels); + tt_assert(ent); + consensus_cache_entry_decref(ent); + } + + lst = smartlist_new(); + /* Find nothing. */ + consensus_cache_find_all(lst, cache, "mod5", "5"); + tt_int_op(smartlist_len(lst), OP_EQ, 0); + /* Find everything. */ + consensus_cache_find_all(lst, cache, "test-id", "filter"); + tt_int_op(smartlist_len(lst), OP_EQ, N); + + /* Now filter to find the entries that have i%3 == 1 */ + consensus_cache_filter_list(lst, "mod3", "1"); + tt_int_op(smartlist_len(lst), OP_EQ, 10); + /* Now filter to find the entries that also have i%5 == 3 */ + consensus_cache_filter_list(lst, "mod5", "3"); + tt_int_op(smartlist_len(lst), OP_EQ, 2); + /* So now we have those entries for which i%15 == 13. */ + + consensus_cache_entry_t *ent1 = smartlist_get(lst, 0); + consensus_cache_entry_t *ent2 = smartlist_get(lst, 1); + const char *idx1 = consensus_cache_entry_get_value(ent1, "index"); + const char *idx2 = consensus_cache_entry_get_value(ent2, "index"); + tt_assert( (!strcmp(idx1, "28") && !strcmp(idx2, "13")) || + (!strcmp(idx1, "13") && !strcmp(idx2, "28")) ); + + done: + tor_free(ddir_fname); + consensus_cache_free(cache); + smartlist_free(lst); +} + +#define ENT(name) \ + { #name, test_conscache_ ## name, TT_FORK, NULL, NULL } + +struct testcase_t conscache_tests[] = { + ENT(open_failure), + ENT(simple_usage), + ENT(cleanup), + ENT(filter), + END_OF_TESTCASES +}; + diff --git a/src/test/test_consdiff.c b/src/test/test_consdiff.c new file mode 100644 index 0000000000..2f826b5ef7 --- /dev/null +++ b/src/test/test_consdiff.c @@ -0,0 +1,1168 @@ +/* Copyright (c) 2014, Daniel Martà + * Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONSDIFF_PRIVATE + +#include "or.h" +#include "test.h" + +#include "consdiff.h" +#include "memarea.h" +#include "log_test_helpers.h" + +#define tt_str_eq_line(a,b) \ + tt_assert(line_str_eq((b),(a))) + +static void +test_consdiff_smartlist_slice(void *arg) +{ + smartlist_t *sl = smartlist_new(); + smartlist_slice_t *sls; + + /* Create a regular smartlist. */ + (void)arg; + smartlist_add(sl, (void*)1); + smartlist_add(sl, (void*)2); + smartlist_add(sl, (void*)3); + smartlist_add(sl, (void*)4); + smartlist_add(sl, (void*)5); + + /* See if the slice was done correctly. */ + sls = smartlist_slice(sl, 2, 5); + tt_ptr_op(sl, OP_EQ, sls->list); + tt_ptr_op((void*)3, OP_EQ, smartlist_get(sls->list, sls->offset)); + tt_ptr_op((void*)5, OP_EQ, + smartlist_get(sls->list, sls->offset + (sls->len-1))); + tor_free(sls); + + /* See that using -1 as the end does get to the last element. */ + sls = smartlist_slice(sl, 2, -1); + tt_ptr_op(sl, OP_EQ, sls->list); + tt_ptr_op((void*)3, OP_EQ, smartlist_get(sls->list, sls->offset)); + tt_ptr_op((void*)5, OP_EQ, + smartlist_get(sls->list, sls->offset + (sls->len-1))); + + done: + tor_free(sls); + smartlist_free(sl); +} + +static void +test_consdiff_smartlist_slice_string_pos(void *arg) +{ + smartlist_t *sl = smartlist_new(); + smartlist_slice_t *sls; + memarea_t *area = memarea_new(); + + /* Create a regular smartlist. */ + (void)arg; + consensus_split_lines(sl, "a\nd\nc\na\nb\n", area); + + /* See that smartlist_slice_string_pos respects the bounds of the slice. */ + sls = smartlist_slice(sl, 2, 5); + cdline_t a_line = { "a", 1 }; + tt_int_op(3, OP_EQ, smartlist_slice_string_pos(sls, &a_line)); + cdline_t d_line = { "d", 1 }; + tt_int_op(-1, OP_EQ, smartlist_slice_string_pos(sls, &d_line)); + + done: + tor_free(sls); + smartlist_free(sl); + memarea_drop_all(area); +} + +static void +test_consdiff_lcs_lengths(void *arg) +{ + smartlist_t *sl1 = smartlist_new(); + smartlist_t *sl2 = smartlist_new(); + smartlist_slice_t *sls1, *sls2; + int *lengths1, *lengths2; + memarea_t *area = memarea_new(); + + /* Expected lcs lengths in regular and reverse order. */ + int e_lengths1[] = { 0, 1, 2, 3, 3, 4 }; + int e_lengths2[] = { 0, 1, 1, 2, 3, 4 }; + + (void)arg; + consensus_split_lines(sl1, "a\nb\nc\nd\ne\n", area); + consensus_split_lines(sl2, "a\nc\nd\ni\ne\n", area); + + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + + lengths1 = lcs_lengths(sls1, sls2, 1); + lengths2 = lcs_lengths(sls1, sls2, -1); + tt_mem_op(e_lengths1, OP_EQ, lengths1, sizeof(int) * 6); + tt_mem_op(e_lengths2, OP_EQ, lengths2, sizeof(int) * 6); + + done: + tor_free(lengths1); + tor_free(lengths2); + tor_free(sls1); + tor_free(sls2); + smartlist_free(sl1); + smartlist_free(sl2); + memarea_drop_all(area); +} + +static void +test_consdiff_trim_slices(void *arg) +{ + smartlist_t *sl1 = smartlist_new(); + smartlist_t *sl2 = smartlist_new(); + smartlist_t *sl3 = smartlist_new(); + smartlist_t *sl4 = smartlist_new(); + smartlist_slice_t *sls1, *sls2, *sls3, *sls4; + memarea_t *area = memarea_new(); + + (void)arg; + consensus_split_lines(sl1, "a\nb\nb\nb\nd\n", area); + consensus_split_lines(sl2, "a\nc\nc\nc\nd\n", area); + consensus_split_lines(sl3, "a\nb\nb\nb\na\n", area); + consensus_split_lines(sl4, "c\nb\nb\nb\nc\n", area); + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + sls3 = smartlist_slice(sl3, 0, -1); + sls4 = smartlist_slice(sl4, 0, -1); + + /* They should be trimmed by one line at each end. */ + tt_int_op(5, OP_EQ, sls1->len); + tt_int_op(5, OP_EQ, sls2->len); + trim_slices(sls1, sls2); + tt_int_op(3, OP_EQ, sls1->len); + tt_int_op(3, OP_EQ, sls2->len); + + /* They should not be trimmed at all. */ + tt_int_op(5, OP_EQ, sls3->len); + tt_int_op(5, OP_EQ, sls4->len); + trim_slices(sls3, sls4); + tt_int_op(5, OP_EQ, sls3->len); + tt_int_op(5, OP_EQ, sls4->len); + + done: + tor_free(sls1); + tor_free(sls2); + tor_free(sls3); + tor_free(sls4); + smartlist_free(sl1); + smartlist_free(sl2); + smartlist_free(sl3); + smartlist_free(sl4); + memarea_drop_all(area); +} + +static void +test_consdiff_set_changed(void *arg) +{ + smartlist_t *sl1 = smartlist_new(); + smartlist_t *sl2 = smartlist_new(); + bitarray_t *changed1 = bitarray_init_zero(4); + bitarray_t *changed2 = bitarray_init_zero(4); + smartlist_slice_t *sls1, *sls2; + memarea_t *area = memarea_new(); + + (void)arg; + consensus_split_lines(sl1, "a\nb\na\na\n", area); + consensus_split_lines(sl2, "a\na\na\na\n", area); + + /* Length of sls1 is 0. */ + sls1 = smartlist_slice(sl1, 0, 0); + sls2 = smartlist_slice(sl2, 1, 3); + set_changed(changed1, changed2, sls1, sls2); + + /* The former is not changed, the latter changes all of its elements. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(!bitarray_is_set(changed1, 1)); + tt_assert(!bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(bitarray_is_set(changed2, 1)); + tt_assert(bitarray_is_set(changed2, 2)); + tt_assert(!bitarray_is_set(changed2, 3)); + bitarray_clear(changed2, 1); + bitarray_clear(changed2, 2); + + /* Length of sls1 is 1 and its element is in sls2. */ + tor_free(sls1); + sls1 = smartlist_slice(sl1, 0, 1); + set_changed(changed1, changed2, sls1, sls2); + + /* The latter changes all elements but the (first) common one. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(!bitarray_is_set(changed1, 1)); + tt_assert(!bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(!bitarray_is_set(changed2, 1)); + tt_assert(bitarray_is_set(changed2, 2)); + tt_assert(!bitarray_is_set(changed2, 3)); + bitarray_clear(changed2, 2); + + /* Length of sls1 is 1 and its element is not in sls2. */ + tor_free(sls1); + sls1 = smartlist_slice(sl1, 1, 2); + set_changed(changed1, changed2, sls1, sls2); + + /* The former changes its element, the latter changes all elements. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(bitarray_is_set(changed1, 1)); + tt_assert(!bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(bitarray_is_set(changed2, 1)); + tt_assert(bitarray_is_set(changed2, 2)); + tt_assert(!bitarray_is_set(changed2, 3)); + + done: + bitarray_free(changed1); + bitarray_free(changed2); + smartlist_free(sl1); + smartlist_free(sl2); + tor_free(sls1); + tor_free(sls2); + memarea_drop_all(area); +} + +static void +test_consdiff_calc_changes(void *arg) +{ + smartlist_t *sl1 = smartlist_new(); + smartlist_t *sl2 = smartlist_new(); + smartlist_slice_t *sls1, *sls2; + bitarray_t *changed1 = bitarray_init_zero(4); + bitarray_t *changed2 = bitarray_init_zero(4); + memarea_t *area = memarea_new(); + + (void)arg; + consensus_split_lines(sl1, "a\na\na\na\n", area); + consensus_split_lines(sl2, "a\na\na\na\n", area); + + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + calc_changes(sls1, sls2, changed1, changed2); + + /* Nothing should be set to changed. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(!bitarray_is_set(changed1, 1)); + tt_assert(!bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(!bitarray_is_set(changed2, 1)); + tt_assert(!bitarray_is_set(changed2, 2)); + tt_assert(!bitarray_is_set(changed2, 3)); + + smartlist_clear(sl2); + consensus_split_lines(sl2, "a\nb\na\nb\n", area); + tor_free(sls1); + tor_free(sls2); + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + calc_changes(sls1, sls2, changed1, changed2); + + /* Two elements are changed. */ + tt_assert(!bitarray_is_set(changed1, 0)); + tt_assert(bitarray_is_set(changed1, 1)); + tt_assert(bitarray_is_set(changed1, 2)); + tt_assert(!bitarray_is_set(changed1, 3)); + bitarray_clear(changed1, 1); + bitarray_clear(changed1, 2); + + tt_assert(!bitarray_is_set(changed2, 0)); + tt_assert(bitarray_is_set(changed2, 1)); + tt_assert(!bitarray_is_set(changed2, 2)); + tt_assert(bitarray_is_set(changed2, 3)); + bitarray_clear(changed1, 1); + bitarray_clear(changed1, 3); + + smartlist_clear(sl2); + consensus_split_lines(sl2, "b\nb\nb\nb\n", area); + tor_free(sls1); + tor_free(sls2); + sls1 = smartlist_slice(sl1, 0, -1); + sls2 = smartlist_slice(sl2, 0, -1); + calc_changes(sls1, sls2, changed1, changed2); + + /* All elements are changed. */ + tt_assert(bitarray_is_set(changed1, 0)); + tt_assert(bitarray_is_set(changed1, 1)); + tt_assert(bitarray_is_set(changed1, 2)); + tt_assert(bitarray_is_set(changed1, 3)); + + tt_assert(bitarray_is_set(changed2, 0)); + tt_assert(bitarray_is_set(changed2, 1)); + tt_assert(bitarray_is_set(changed2, 2)); + tt_assert(bitarray_is_set(changed2, 3)); + + done: + bitarray_free(changed1); + bitarray_free(changed2); + smartlist_free(sl1); + smartlist_free(sl2); + tor_free(sls1); + tor_free(sls2); + memarea_drop_all(area); +} + +static void +test_consdiff_get_id_hash(void *arg) +{ + (void)arg; + + cdline_t line1 = { "r name", 6 }; + cdline_t line2 = { "r name _hash_isnt_base64 etc", 28 }; + cdline_t line3 = { "r name hash+valid+base64 etc", 28 }; + cdline_t tmp; + + /* No hash. */ + tt_int_op(-1, OP_EQ, get_id_hash(&line1, &tmp)); + /* The hash contains characters that are not base64. */ + tt_int_op(-1, OP_EQ, get_id_hash(&line2, &tmp)); + + /* valid hash. */ + tt_int_op(0, OP_EQ, get_id_hash(&line3, &tmp)); + tt_ptr_op(tmp.s, OP_EQ, line3.s + 7); + tt_uint_op(tmp.len, OP_EQ, line3.len - 11); + + done: + ; +} + +static void +test_consdiff_is_valid_router_entry(void *arg) +{ + /* Doesn't start with "r ". */ + (void)arg; + cdline_t line0 = { "foo", 3 }; + tt_int_op(0, OP_EQ, is_valid_router_entry(&line0)); + + /* These are already tested with get_id_hash, but make sure it's run + * properly. */ + + cdline_t line1 = { "r name", 6 }; + cdline_t line2 = { "r name _hash_isnt_base64 etc", 28 }; + cdline_t line3 = { "r name hash+valid+base64 etc", 28 }; + tt_int_op(0, OP_EQ, is_valid_router_entry(&line1)); + tt_int_op(0, OP_EQ, is_valid_router_entry(&line2)); + tt_int_op(1, OP_EQ, is_valid_router_entry(&line3)); + + done: + ; +} + +static void +test_consdiff_next_router(void *arg) +{ + smartlist_t *sl = smartlist_new(); + memarea_t *area = memarea_new(); + (void)arg; + smartlist_add_linecpy(sl, area, "foo"); + smartlist_add_linecpy(sl, area, + "r name hash+longer+than+27+chars+and+valid+base64 etc"); + smartlist_add_linecpy(sl, area, "foo"); + smartlist_add_linecpy(sl, area, "foo"); + smartlist_add_linecpy(sl, area, + "r name hash+longer+than+27+chars+and+valid+base64 etc"); + smartlist_add_linecpy(sl, area, "foo"); + + /* Not currently on a router entry line, finding the next one. */ + tt_int_op(1, OP_EQ, next_router(sl, 0)); + tt_int_op(4, OP_EQ, next_router(sl, 2)); + + /* Already at the beginning of a router entry line, ignore it. */ + tt_int_op(4, OP_EQ, next_router(sl, 1)); + + /* There are no more router entries, so return the line after the last. */ + tt_int_op(6, OP_EQ, next_router(sl, 4)); + tt_int_op(6, OP_EQ, next_router(sl, 5)); + + done: + smartlist_free(sl); + memarea_drop_all(area); +} + +static int +base64cmp_wrapper(const char *a, const char *b) +{ + cdline_t aa = { a, a ? (uint32_t) strlen(a) : 0 }; + cdline_t bb = { b, b ? (uint32_t) strlen(b) : 0 }; + return base64cmp(&aa, &bb); +} + +static void +test_consdiff_base64cmp(void *arg) +{ + /* NULL arguments. */ + (void)arg; + tt_int_op(0, OP_EQ, base64cmp_wrapper(NULL, NULL)); + tt_int_op(-1, OP_EQ, base64cmp_wrapper(NULL, "foo")); + tt_int_op(1, OP_EQ, base64cmp_wrapper("bar", NULL)); + + /* Nil base64 values. */ + tt_int_op(0, OP_EQ, base64cmp_wrapper("", "")); + tt_int_op(0, OP_EQ, base64cmp_wrapper("_", "&")); + + /* Exact same valid strings. */ + tt_int_op(0, OP_EQ, base64cmp_wrapper("abcABC/+", "abcABC/+")); + /* Both end with an invalid base64 char other than '\0'. */ + tt_int_op(0, OP_EQ, base64cmp_wrapper("abcABC/+ ", "abcABC/+ ")); + /* Only one ends with an invalid base64 char other than '\0'. */ + tt_int_op(-1, OP_EQ, base64cmp_wrapper("abcABC/+ ", "abcABC/+a")); + + /* Comparisons that would return differently with strcmp(). */ + tt_int_op(-1, OP_EQ, strcmp("/foo", "Afoo")); + tt_int_op(1, OP_EQ, base64cmp_wrapper("/foo", "Afoo")); + tt_int_op(1, OP_EQ, strcmp("Afoo", "0foo")); + tt_int_op(-1, OP_EQ, base64cmp_wrapper("Afoo", "0foo")); + + /* Comparisons that would return the same as with strcmp(). */ + tt_int_op(1, OP_EQ, strcmp("afoo", "Afoo")); + tt_int_op(1, OP_EQ, base64cmp_wrapper("afoo", "Afoo")); + + /* Different lengths */ + tt_int_op(-1, OP_EQ, base64cmp_wrapper("afoo", "afooo")); + tt_int_op(1, OP_EQ, base64cmp_wrapper("afooo", "afoo")); + + done: + ; +} + +static void +test_consdiff_gen_ed_diff(void *arg) +{ + smartlist_t *cons1=NULL, *cons2=NULL, *diff=NULL; + int i; + memarea_t *area = memarea_new(); + setup_capture_of_logs(LOG_WARN); + + (void)arg; + cons1 = smartlist_new(); + cons2 = smartlist_new(); + + /* Identity hashes are not sorted properly, return NULL. */ + smartlist_add_linecpy(cons1, area, "r name bbbbbbbbbbbbbbbbbbbbbbbbbbb etc"); + smartlist_add_linecpy(cons1, area, "foo"); + smartlist_add_linecpy(cons1, area, "r name aaaaaaaaaaaaaaaaaaaaaaaaaaa etc"); + smartlist_add_linecpy(cons1, area, "bar"); + + smartlist_add_linecpy(cons2, area, "r name aaaaaaaaaaaaaaaaaaaaaaaaaaa etc"); + smartlist_add_linecpy(cons2, area, "foo"); + smartlist_add_linecpy(cons2, area, "r name ccccccccccccccccccccccccccc etc"); + smartlist_add_linecpy(cons2, area, "bar"); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the base consensus doesn't have its router entries sorted " + "properly."); + + /* Same, but now with the second consensus. */ + mock_clean_saved_logs(); + diff = gen_ed_diff(cons2, cons1, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the target consensus doesn't have its router entries sorted " + "properly."); + + /* Same as the two above, but with the reversed thing immediately after a + match. (The code handles this differently) */ + smartlist_del(cons1, 0); + smartlist_add_linecpy(cons1, area, "r name aaaaaaaaaaaaaaaaaaaaaaaaaaa etc"); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the base consensus doesn't have its router entries sorted " + "properly."); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons2, cons1, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the target consensus doesn't have its router entries sorted " + "properly."); + + /* Identity hashes are repeated, return NULL. */ + smartlist_clear(cons1); + + smartlist_add_linecpy(cons1, area, "r name bbbbbbbbbbbbbbbbbbbbbbbbbbb etc"); + smartlist_add_linecpy(cons1, area, "foo"); + smartlist_add_linecpy(cons1, area, "r name bbbbbbbbbbbbbbbbbbbbbbbbbbb etc"); + smartlist_add_linecpy(cons1, area, "bar"); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because the base consensus doesn't have its router entries sorted " + "properly."); + + /* We have to add a line that is just a dot, return NULL. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + + smartlist_add_linecpy(cons1, area, "foo1"); + smartlist_add_linecpy(cons1, area, "foo2"); + + smartlist_add_linecpy(cons2, area, "foo1"); + smartlist_add_linecpy(cons2, area, "."); + smartlist_add_linecpy(cons2, area, "foo2"); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Cannot generate consensus diff " + "because one of the lines to be added is \".\"."); + +#define MAX_LINE_COUNT (10000) + /* Too many lines to be fed to the quadratic-time function. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + + for (i=0; i < MAX_LINE_COUNT; ++i) smartlist_add_linecpy(cons1, area, "a"); + for (i=0; i < MAX_LINE_COUNT; ++i) smartlist_add_linecpy(cons1, area, "b"); + + mock_clean_saved_logs(); + diff = gen_ed_diff(cons1, cons2, area); + + tt_ptr_op(NULL, OP_EQ, diff); + expect_single_log_msg_containing("Refusing to generate consensus diff " + "because we found too few common router ids."); + + /* We have dot lines, but they don't interfere with the script format. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + + smartlist_add_linecpy(cons1, area, "foo1"); + smartlist_add_linecpy(cons1, area, "."); + smartlist_add_linecpy(cons1, area, "."); + smartlist_add_linecpy(cons1, area, "foo2"); + + smartlist_add_linecpy(cons2, area, "foo1"); + smartlist_add_linecpy(cons2, area, "."); + smartlist_add_linecpy(cons2, area, "foo2"); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + smartlist_free(diff); + + /* Empty diff tests. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(0, OP_EQ, smartlist_len(diff)); + smartlist_free(diff); + + smartlist_add_linecpy(cons1, area, "foo"); + smartlist_add_linecpy(cons1, area, "bar"); + + smartlist_add_linecpy(cons2, area, "foo"); + smartlist_add_linecpy(cons2, area, "bar"); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(0, OP_EQ, smartlist_len(diff)); + smartlist_free(diff); + + /* Everything is deleted. */ + smartlist_clear(cons2); + + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(1, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("1,2d", smartlist_get(diff, 0)); + + smartlist_free(diff); + + /* Everything is added. */ + diff = gen_ed_diff(cons2, cons1, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(4, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("0a", smartlist_get(diff, 0)); + tt_str_eq_line("foo", smartlist_get(diff, 1)); + tt_str_eq_line("bar", smartlist_get(diff, 2)); + tt_str_eq_line(".", smartlist_get(diff, 3)); + + smartlist_free(diff); + + /* Everything is changed. */ + smartlist_add_linecpy(cons2, area, "foo2"); + smartlist_add_linecpy(cons2, area, "bar2"); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(4, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("1,2c", smartlist_get(diff, 0)); + tt_str_eq_line("foo2", smartlist_get(diff, 1)); + tt_str_eq_line("bar2", smartlist_get(diff, 2)); + tt_str_eq_line(".", smartlist_get(diff, 3)); + + smartlist_free(diff); + + /* Test 'a', 'c' and 'd' together. See that it is done in reverse order. */ + smartlist_clear(cons1); + smartlist_clear(cons2); + consensus_split_lines(cons1, "A\nB\nC\nD\nE\n", area); + consensus_split_lines(cons2, "A\nC\nO\nE\nU\n", area); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(7, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("5a", smartlist_get(diff, 0)); + tt_str_eq_line("U", smartlist_get(diff, 1)); + tt_str_eq_line(".", smartlist_get(diff, 2)); + tt_str_eq_line("4c", smartlist_get(diff, 3)); + tt_str_eq_line("O", smartlist_get(diff, 4)); + tt_str_eq_line(".", smartlist_get(diff, 5)); + tt_str_eq_line("2d", smartlist_get(diff, 6)); + + smartlist_free(diff); + + smartlist_clear(cons1); + smartlist_clear(cons2); + consensus_split_lines(cons1, "B\n", area); + consensus_split_lines(cons2, "A\nB\n", area); + diff = gen_ed_diff(cons1, cons2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(3, OP_EQ, smartlist_len(diff)); + tt_str_eq_line("0a", smartlist_get(diff, 0)); + tt_str_eq_line("A", smartlist_get(diff, 1)); + tt_str_eq_line(".", smartlist_get(diff, 2)); + + /* TODO: small real use-cases, i.e. consensuses. */ + + done: + teardown_capture_of_logs(); + smartlist_free(cons1); + smartlist_free(cons2); + smartlist_free(diff); + memarea_drop_all(area); +} + +static void +test_consdiff_apply_ed_diff(void *arg) +{ + smartlist_t *cons1=NULL, *cons2=NULL, *diff=NULL; + memarea_t *area = memarea_new(); + (void)arg; + cons1 = smartlist_new(); + diff = smartlist_new(); + setup_capture_of_logs(LOG_WARN); + + consensus_split_lines(cons1, "A\nB\nC\nD\nE\n", area); + + /* Command without range. */ + smartlist_add_linecpy(diff, area, "a"); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + smartlist_clear(diff); + expect_single_log_msg_containing("an ed command was missing a line number"); + + /* Range without command. */ + smartlist_add_linecpy(diff, area, "1"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("a line with no ed command was found"); + + smartlist_clear(diff); + + /* Range without end. */ + smartlist_add_linecpy(diff, area, "1,"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command was missing a range " + "end line number."); + + smartlist_clear(diff); + + /* Incoherent ranges. */ + smartlist_add_linecpy(diff, area, "1,1"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an invalid range was found"); + + smartlist_clear(diff); + + smartlist_add_linecpy(diff, area, "3,2"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an invalid range was found"); + + smartlist_clear(diff); + + /* Unexpected range for add command. */ + smartlist_add_linecpy(diff, area, "1,2a"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("add lines after a range"); + + smartlist_clear(diff); + + /* Script is not in reverse order. */ + smartlist_add_linecpy(diff, area, "1d"); + smartlist_add_linecpy(diff, area, "3d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("its commands are not properly sorted"); + + smartlist_clear(diff); + + /* Script contains unrecognised commands longer than one char. */ + smartlist_add_linecpy(diff, area, "1foo"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command longer than one char was " + "found"); + + smartlist_clear(diff); + + /* Script contains unrecognised commands. */ + smartlist_add_linecpy(diff, area, "1e"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an unrecognised ed command was found"); + + smartlist_clear(diff); + + /* Command that should be followed by at least one line and a ".", but + * isn't. */ + smartlist_add_linecpy(diff, area, "0a"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("it has an ed command that tries to " + "insert zero lines."); + + /* Now it is followed by a ".", but it inserts zero lines. */ + smartlist_add_linecpy(diff, area, "."); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("it has an ed command that tries to " + "insert zero lines."); + + smartlist_clear(diff); + + /* Now it it inserts something, but has no terminator. */ + smartlist_add_linecpy(diff, area, "0a"); + smartlist_add_linecpy(diff, area, "hello"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("lines to be inserted that don't end with " + "a \".\"."); + + smartlist_clear(diff); + + /* Ranges must be numeric only and cannot contain spaces. */ + smartlist_add_linecpy(diff, area, "0, 4d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command was missing a range " + "end line number."); + + smartlist_clear(diff); + + /* '+' is not a number. */ + smartlist_add_linecpy(diff, area, "+0,4d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command was missing a line number"); + + smartlist_clear(diff); + + /* range duplication */ + smartlist_add_linecpy(diff, area, "0,4d,5d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command longer than one char was " + "found"); + + smartlist_clear(diff); + + /* space before command */ + smartlist_add_linecpy(diff, area, "0,4 d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command longer than one char was " + "found"); + + smartlist_clear(diff); + + /* space inside number */ + smartlist_add_linecpy(diff, area, "0,4 5d"); + mock_clean_saved_logs(); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("an ed command longer than one char was " + "found"); + + smartlist_clear(diff); + + /* Test appending text, 'a'. */ + consensus_split_lines(diff, "3a\nU\nO\n.\n0a\nV\n.\n", area); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_NE, cons2); + tt_int_op(8, OP_EQ, smartlist_len(cons2)); + tt_str_eq_line("V", smartlist_get(cons2, 0)); + tt_str_eq_line("A", smartlist_get(cons2, 1)); + tt_str_eq_line("B", smartlist_get(cons2, 2)); + tt_str_eq_line("C", smartlist_get(cons2, 3)); + tt_str_eq_line("U", smartlist_get(cons2, 4)); + tt_str_eq_line("O", smartlist_get(cons2, 5)); + tt_str_eq_line("D", smartlist_get(cons2, 6)); + tt_str_eq_line("E", smartlist_get(cons2, 7)); + + smartlist_clear(diff); + smartlist_free(cons2); + + /* Test deleting text, 'd'. */ + consensus_split_lines(diff, "4d\n1,2d\n", area); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_NE, cons2); + tt_int_op(2, OP_EQ, smartlist_len(cons2)); + tt_str_eq_line("C", smartlist_get(cons2, 0)); + tt_str_eq_line("E", smartlist_get(cons2, 1)); + + smartlist_clear(diff); + smartlist_free(cons2); + + /* Test changing text, 'c'. */ + consensus_split_lines(diff, "4c\nT\nX\n.\n1,2c\nM\n.\n", area); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_NE, cons2); + tt_int_op(5, OP_EQ, smartlist_len(cons2)); + tt_str_eq_line("M", smartlist_get(cons2, 0)); + tt_str_eq_line("C", smartlist_get(cons2, 1)); + tt_str_eq_line("T", smartlist_get(cons2, 2)); + tt_str_eq_line("X", smartlist_get(cons2, 3)); + tt_str_eq_line("E", smartlist_get(cons2, 4)); + + smartlist_clear(diff); + smartlist_free(cons2); + + /* Test 'a', 'd' and 'c' together. */ + consensus_split_lines(diff, "4c\nT\nX\n.\n2d\n0a\nM\n.\n", area); + cons2 = apply_ed_diff(cons1, diff, 0); + tt_ptr_op(NULL, OP_NE, cons2); + tt_int_op(6, OP_EQ, smartlist_len(cons2)); + tt_str_eq_line("M", smartlist_get(cons2, 0)); + tt_str_eq_line("A", smartlist_get(cons2, 1)); + tt_str_eq_line("C", smartlist_get(cons2, 2)); + tt_str_eq_line("T", smartlist_get(cons2, 3)); + tt_str_eq_line("X", smartlist_get(cons2, 4)); + tt_str_eq_line("E", smartlist_get(cons2, 5)); + + done: + teardown_capture_of_logs(); + smartlist_free(cons1); + smartlist_free(cons2); + smartlist_free(diff); + memarea_drop_all(area); +} + +static void +test_consdiff_gen_diff(void *arg) +{ + char *cons1_str=NULL, *cons2_str=NULL; + smartlist_t *cons1=NULL, *cons2=NULL, *diff=NULL; + consensus_digest_t digests1, digests2; + memarea_t *area = memarea_new(); + (void)arg; + cons1 = smartlist_new(); + cons2 = smartlist_new(); + + /* Identity hashes are not sorted properly, return NULL. + * Already tested in gen_ed_diff, but see that a NULL ed diff also makes + * gen_diff return NULL. */ + cons1_str = tor_strdup( + "network-status-version foo\n" + "r name bbbbbbbbbbbbbbbbb etc\nfoo\n" + "r name aaaaaaaaaaaaaaaaa etc\nbar\n" + "directory-signature foo bar\nbar\n" + ); + cons2_str = tor_strdup( + "network-status-version foo\n" + "r name aaaaaaaaaaaaaaaaa etc\nfoo\n" + "r name ccccccccccccccccc etc\nbar\n" + "directory-signature foo bar\nbar\n" + ); + + tt_int_op(0, OP_EQ, + consensus_compute_digest(cons1_str, &digests1)); + tt_int_op(0, OP_EQ, + consensus_compute_digest(cons2_str, &digests2)); + + consensus_split_lines(cons1, cons1_str, area); + consensus_split_lines(cons2, cons2_str, area); + + diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area); + tt_ptr_op(NULL, OP_EQ, diff); + + /* Check that the headers are done properly. */ + tor_free(cons1_str); + cons1_str = tor_strdup( + "network-status-version foo\n" + "r name ccccccccccccccccc etc\nfoo\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "directory-signature foo bar\nbar\n" + ); + tt_int_op(0, OP_EQ, + consensus_compute_digest(cons1_str, &digests1)); + smartlist_clear(cons1); + consensus_split_lines(cons1, cons1_str, area); + diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area); + tt_ptr_op(NULL, OP_NE, diff); + tt_int_op(7, OP_EQ, smartlist_len(diff)); + tt_assert(line_str_eq(smartlist_get(diff, 0), + "network-status-diff-version 1")); + tt_assert(line_str_eq(smartlist_get(diff, 1), "hash " + "06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4 " + "7AFECEFA4599BA33D603653E3D2368F648DF4AC4723929B0F7CF39281596B0C1")); + tt_assert(line_str_eq(smartlist_get(diff, 2), "3,4d")); + tt_assert(line_str_eq(smartlist_get(diff, 3), "1a")); + tt_assert(line_str_eq(smartlist_get(diff, 4), + "r name aaaaaaaaaaaaaaaaa etc")); + tt_assert(line_str_eq(smartlist_get(diff, 5), "foo")); + tt_assert(line_str_eq(smartlist_get(diff, 6), ".")); + + /* TODO: small real use-cases, i.e. consensuses. */ + + done: + tor_free(cons1_str); + tor_free(cons2_str); + smartlist_free(cons1); + smartlist_free(cons2); + smartlist_free(diff); + memarea_drop_all(area); +} + +static void +test_consdiff_apply_diff(void *arg) +{ + smartlist_t *cons1=NULL, *diff=NULL; + char *cons1_str=NULL, *cons2 = NULL; + consensus_digest_t digests1; + (void)arg; + memarea_t *area = memarea_new(); + cons1 = smartlist_new(); + diff = smartlist_new(); + setup_capture_of_logs(LOG_INFO); + + cons1_str = tor_strdup( + "network-status-version foo\n" + "r name ccccccccccccccccc etc\nfoo\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "directory-signature foo bar\nbar\n" + ); + tt_int_op(0, OP_EQ, + consensus_compute_digest(cons1_str, &digests1)); + consensus_split_lines(cons1, cons1_str, area); + + /* diff doesn't have enough lines. */ + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("too short") + + /* first line doesn't match format-version string. */ + smartlist_add_linecpy(diff, area, "foo-bar"); + smartlist_add_linecpy(diff, area, "header-line"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("format is not known") + + /* The first word of the second header line is not "hash". */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "word a b"); + smartlist_add_linecpy(diff, area, "x"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("does not include the necessary digests") + + /* Wrong number of words after "hash". */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash a b c"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("does not include the necessary digests") + + /* base16 digests do not have the expected length. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash aaa bbb"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("includes base16-encoded digests of " + "incorrect size") + + /* base16 digests contain non-base16 characters. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + " ????????????????????????????????????????????????????????????????" + " ----------------------------------------------------------------"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("includes malformed digests") + + /* Invalid ed diff. + * As tested in apply_ed_diff, but check that apply_diff does return NULL if + * the ed diff can't be applied. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4" + /* sha256 of cons2. */ + " 635D34593020C08E5ECD865F9986E29D50028EFA62843766A8197AD228A7F6AA"); + smartlist_add_linecpy(diff, area, "foobar"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_single_log_msg_containing("because an ed command was missing a line " + "number") + + /* Base consensus doesn't match its digest as found in the diff. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* bogus sha256. */ + " 3333333333333333333333333333333333333333333333333333333333333333" + /* sha256 of cons2. */ + " 635D34593020C08E5ECD865F9986E29D50028EFA62843766A8197AD228A7F6AA"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_log_msg_containing("base consensus doesn't match the digest " + "as found"); + + /* Resulting consensus doesn't match its digest as found in the diff. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4" + /* bogus sha3. */ + " 3333333333333333333333333333333333333333333333333333333333333333"); + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_log_msg_containing("resulting consensus doesn't match the " + "digest as found"); + +#if 0 + /* XXXX No longer possible, since we aren't using the other algorithm. */ + /* Resulting consensus digest cannot be computed */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4" + /* bogus sha3. */ + " 3333333333333333333333333333333333333333333333333333333333333333"); + smartlist_add_linecpy(diff, area, "1,2d"); // remove starting line + mock_clean_saved_logs(); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_EQ, cons2); + expect_log_msg_containing("Could not compute digests of the consensus " + "resulting from applying a consensus diff."); +#endif + + /* Very simple test, only to see that nothing errors. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4" + /* sha3 of cons2. */ + " 90A418881B2FCAB3D9E60EE02E4D666D56CFA38F8A3B7AA3E0ADBA530DDA9353"); + smartlist_add_linecpy(diff, area, "3c"); + smartlist_add_linecpy(diff, area, "sample"); + smartlist_add_linecpy(diff, area, "."); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_NE, cons2); + tt_str_op( + "network-status-version foo\n" + "r name ccccccccccccccccc etc\nsample\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "directory-signature foo bar\nbar\n", OP_EQ, + cons2); + tor_free(cons2); + + /* Check that lowercase letters in base16-encoded digests work too. */ + smartlist_clear(diff); + smartlist_add_linecpy(diff, area, "network-status-diff-version 1"); + smartlist_add_linecpy(diff, area, "hash" + /* sha3 of cons1. */ + " 06646d6cf563a41869d3b02e73254372ae3140046c5e7d83c9f71e54976af9b4" + /* sha3 of cons2. */ + " 90a418881b2fcab3d9e60ee02e4d666d56cfa38f8a3b7aa3e0adba530dda9353"); + smartlist_add_linecpy(diff, area, "3c"); + smartlist_add_linecpy(diff, area, "sample"); + smartlist_add_linecpy(diff, area, "."); + cons2 = consdiff_apply_diff(cons1, diff, &digests1); + tt_ptr_op(NULL, OP_NE, cons2); + tt_str_op( + "network-status-version foo\n" + "r name ccccccccccccccccc etc\nsample\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "directory-signature foo bar\nbar\n", OP_EQ, + cons2); + tor_free(cons2); + + smartlist_clear(diff); + + done: + teardown_capture_of_logs(); + tor_free(cons1_str); + smartlist_free(cons1); + smartlist_free(diff); + memarea_drop_all(area); +} + +#define CONSDIFF_LEGACY(name) \ + { #name, test_consdiff_ ## name , 0, NULL, NULL } + +struct testcase_t consdiff_tests[] = { + CONSDIFF_LEGACY(smartlist_slice), + CONSDIFF_LEGACY(smartlist_slice_string_pos), + CONSDIFF_LEGACY(lcs_lengths), + CONSDIFF_LEGACY(trim_slices), + CONSDIFF_LEGACY(set_changed), + CONSDIFF_LEGACY(calc_changes), + CONSDIFF_LEGACY(get_id_hash), + CONSDIFF_LEGACY(is_valid_router_entry), + CONSDIFF_LEGACY(next_router), + CONSDIFF_LEGACY(base64cmp), + CONSDIFF_LEGACY(gen_ed_diff), + CONSDIFF_LEGACY(apply_ed_diff), + CONSDIFF_LEGACY(gen_diff), + CONSDIFF_LEGACY(apply_diff), + END_OF_TESTCASES +}; + diff --git a/src/test/test_consdiffmgr.c b/src/test/test_consdiffmgr.c new file mode 100644 index 0000000000..31ce6ce901 --- /dev/null +++ b/src/test/test_consdiffmgr.c @@ -0,0 +1,890 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONSDIFFMGR_PRIVATE + +#include "or.h" +#include "config.h" +#include "conscache.h" +#include "consdiff.h" +#include "consdiffmgr.h" +#include "cpuworker.h" +#include "networkstatus.h" +#include "workqueue.h" + +#include "test.h" +#include "log_test_helpers.h" + +// ============================== Setup/teardown the consdiffmgr +// These functions get run before/after each test in this module + +static void * +consdiffmgr_test_setup(const struct testcase_t *arg) +{ + (void)arg; + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer. + check_private_dir(ddir_fname, CPD_CREATE, NULL); + + consdiff_cfg_t consdiff_cfg = { 7200, 300 }; + consdiffmgr_configure(&consdiff_cfg); + return (void *)1; // must return something non-null. +} +static int +consdiffmgr_test_teardown(const struct testcase_t *arg, void *ignore) +{ + (void)arg; + (void)ignore; + consdiffmgr_free_all(); + return 1; +} +static struct testcase_setup_t setup_diffmgr = { + consdiffmgr_test_setup, + consdiffmgr_test_teardown +}; + +// ============================== NS faking functions +// These functions are for making quick fake consensus objects and +// strings that are just good enough for consdiff and consdiffmgr. + +static networkstatus_t * +fake_ns_new(consensus_flavor_t flav, time_t valid_after) +{ + networkstatus_t *ns = tor_malloc_zero(sizeof(networkstatus_t)); + ns->type = NS_TYPE_CONSENSUS; + ns->flavor = flav; + ns->valid_after = valid_after; + return ns; +} + +static char * +fake_ns_body_new(consensus_flavor_t flav, time_t valid_after) +{ + const char *flavor_string = flav == FLAV_NS ? "" : " microdesc"; + char valid_after_string[ISO_TIME_LEN+1]; + + format_iso_time(valid_after_string, valid_after); + char *random_stuff = crypto_random_hostname(3, 25, "junk ", ""); + + char *consensus; + tor_asprintf(&consensus, + "network-status-version 3%s\n" + "vote-status consensus\n" + "valid-after %s\n" + "r name ccccccccccccccccc etc\nsample\n" + "r name eeeeeeeeeeeeeeeee etc\nbar\n" + "%s\n", + flavor_string, + valid_after_string, + random_stuff); + tor_free(random_stuff); + return consensus; +} + +// ============================== Cpuworker mocking code +// These mocking functions and types capture the cpuworker calls +// so we can inspect them and run them in the main thread. +static smartlist_t *fake_cpuworker_queue = NULL; +typedef struct fake_work_queue_ent_t { + enum workqueue_reply_t (*fn)(void *, void *); + void (*reply_fn)(void *); + void *arg; +} fake_work_queue_ent_t; +static struct workqueue_entry_s * +mock_cpuworker_queue_work(enum workqueue_reply_t (*fn)(void *, void *), + void (*reply_fn)(void *), + void *arg) +{ + if (! fake_cpuworker_queue) + fake_cpuworker_queue = smartlist_new(); + + fake_work_queue_ent_t *ent = tor_malloc_zero(sizeof(*ent)); + ent->fn = fn; + ent->reply_fn = reply_fn; + ent->arg = arg; + smartlist_add(fake_cpuworker_queue, ent); + return (struct workqueue_entry_s *)ent; +} +static int +mock_cpuworker_run_work(void) +{ + if (! fake_cpuworker_queue) + return 0; + SMARTLIST_FOREACH(fake_cpuworker_queue, fake_work_queue_ent_t *, ent, { + enum workqueue_reply_t r = ent->fn(NULL, ent->arg); + if (r != WQ_RPL_REPLY) + return -1; + }); + return 0; +} +static void +mock_cpuworker_handle_replies(void) +{ + if (! fake_cpuworker_queue) + return; + SMARTLIST_FOREACH(fake_cpuworker_queue, fake_work_queue_ent_t *, ent, { + ent->reply_fn(ent->arg); + tor_free(ent); + }); + smartlist_free(fake_cpuworker_queue); + fake_cpuworker_queue = NULL; +} + +// ============================== Other helpers + +static consdiff_status_t +lookup_diff_from(consensus_cache_entry_t **out, + consensus_flavor_t flav, + const char *str1) +{ + uint8_t digest[DIGEST256_LEN]; + crypto_digest256((char*)digest, str1, strlen(str1), DIGEST_SHA3_256); + return consdiffmgr_find_diff_from(out, flav, + DIGEST_SHA3_256, digest, sizeof(digest), + NO_METHOD); +} + +static int +lookup_apply_and_verify_diff(consensus_flavor_t flav, + const char *str1, + const char *str2) +{ + consensus_cache_entry_t *ent = NULL; + consdiff_status_t status = lookup_diff_from(&ent, flav, str1); + if (ent == NULL || status != CONSDIFF_AVAILABLE) + return -1; + + consensus_cache_entry_incref(ent); + size_t size; + char *diff_string = NULL; + int r = uncompress_or_copy(&diff_string, &size, ent); + consensus_cache_entry_decref(ent); + if (diff_string == NULL || r < 0) + return -1; + + char *applied = consensus_diff_apply(str1, diff_string); + tor_free(diff_string); + if (applied == NULL) + return -1; + + int match = !strcmp(applied, str2); + tor_free(applied); + return match ? 0 : -1; +} + +static void +cdm_reload(void) +{ + consdiffmgr_free_all(); + cdm_cache_get(); + consdiffmgr_rescan(); +} + +// ============================== Beginning of tests + +#if 0 +static int got_failure = 0; +static void +got_assertion_failure(void) +{ + ++got_failure; +} + +/* XXXX This test won't work, because there is currently no way to actually + * XXXX capture a real assertion failure. */ +static void +test_consdiffmgr_init_failure(void *arg) +{ + (void)arg; + // Capture assertions and bugs. + + /* As in ...test_setup, but do not create the datadir. The missing directory + * will cause a failure. */ + char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm")); + tor_free(get_options_mutable()->DataDirectory); + get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer. + + consdiff_cfg_t consdiff_cfg = { 7200, 300 }; + + tor_set_failed_assertion_callback(got_assertion_failure); + tor_capture_bugs_(1); + consdiffmgr_configure(&consdiff_cfg); // This should fail. + tt_int_op(got_failure, OP_EQ, 1); + const smartlist_t *bugs = tor_get_captured_bug_log_(); + tt_int_op(smartlist_len(bugs), OP_EQ, 1); + + done: + tor_end_capture_bugs_(); +} +#endif + +static void +test_consdiffmgr_sha3_helper(void *arg) +{ + (void) arg; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + config_line_t *lines = NULL; + char *mem_op_hex_tmp = NULL; + config_line_prepend(&lines, "good-sha", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00D"); + config_line_prepend(&lines, "short-sha", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF0"); + config_line_prepend(&lines, "long-sha", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00DF00D"); + config_line_prepend(&lines, "not-sha", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DXXXX"); + consensus_cache_entry_t *ent = + consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + + uint8_t buf[DIGEST256_LEN]; + tt_int_op(-1, OP_EQ, cdm_entry_get_sha3_value(buf, NULL, "good-sha")); + tt_int_op(0, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "good-sha")); + test_memeq_hex(buf, "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00D"); + + tt_int_op(-1, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "missing-sha")); + tt_int_op(-2, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "short-sha")); + tt_int_op(-2, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "long-sha")); + tt_int_op(-2, OP_EQ, cdm_entry_get_sha3_value(buf, ent, "not-sha")); + + done: + consensus_cache_entry_decref(ent); + config_free_lines(lines); + tor_free(mem_op_hex_tmp); +} + +static void +test_consdiffmgr_add(void *arg) +{ + (void) arg; + time_t now = approx_time(); + + char *body = NULL; + + consensus_cache_entry_t *ent = NULL; + networkstatus_t *ns_tmp = fake_ns_new(FLAV_NS, now); + const char *dummy = "foo"; + int r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, 0); + + /* If we add it again, it won't work */ + setup_capture_of_logs(LOG_INFO); + dummy = "bar"; + r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, -1); + expect_single_log_msg_containing("We already have a copy of that " + "consensus"); + mock_clean_saved_logs(); + + /* But it will work fine if the flavor is different */ + dummy = "baz"; + ns_tmp->flavor = FLAV_MICRODESC; + r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, 0); + + /* And it will work fine if the time is different */ + dummy = "quux"; + ns_tmp->flavor = FLAV_NS; + ns_tmp->valid_after = now - 60; + r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, 0); + + /* If we add one a long long time ago, it will fail. */ + dummy = "xyzzy"; + ns_tmp->valid_after = 86400 * 100; /* A few months into 1970 */ + r = consdiffmgr_add_consensus(dummy, ns_tmp); + tt_int_op(r, OP_EQ, -1); + expect_single_log_msg_containing("it's too old."); + + /* Try looking up a consensuses. */ + ent = cdm_cache_lookup_consensus(FLAV_NS, now-60); + tt_assert(ent); + consensus_cache_entry_incref(ent); + size_t s; + r = uncompress_or_copy(&body, &s, ent); + tt_int_op(r, OP_EQ, 0); + tt_int_op(s, OP_EQ, 4); + tt_mem_op(body, OP_EQ, "quux", 4); + + /* Try looking up another entry, but fail */ + tt_assert(NULL == cdm_cache_lookup_consensus(FLAV_MICRODESC, now-60)); + tt_assert(NULL == cdm_cache_lookup_consensus(FLAV_NS, now-61)); + + done: + networkstatus_vote_free(ns_tmp); + teardown_capture_of_logs(); + consensus_cache_entry_decref(ent); + tor_free(body); +} + +static void +test_consdiffmgr_make_diffs(void *arg) +{ + (void)arg; + networkstatus_t *ns = NULL; + char *ns_body = NULL, *md_ns_body = NULL, *md_ns_body_2 = NULL; + char *applied = NULL, *diff_text = NULL; + time_t now = approx_time(); + int r; + consensus_cache_entry_t *diff = NULL; + uint8_t md_ns_sha3[DIGEST256_LEN]; + consdiff_status_t diff_status; + + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + // Try rescan with no consensuses: shouldn't crash or queue work. + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + // Make two consensuses, 1 hour sec ago. + ns = fake_ns_new(FLAV_NS, now-3600); + ns_body = fake_ns_body_new(FLAV_NS, now-3600); + r = consdiffmgr_add_consensus(ns_body, ns); + networkstatus_vote_free(ns); + tor_free(ns_body); + tt_int_op(r, OP_EQ, 0); + + ns = fake_ns_new(FLAV_MICRODESC, now-3600); + md_ns_body = fake_ns_body_new(FLAV_MICRODESC, now-3600); + r = consdiffmgr_add_consensus(md_ns_body, ns); + crypto_digest256((char*)md_ns_sha3, md_ns_body, strlen(md_ns_body), + DIGEST_SHA3_256); + networkstatus_vote_free(ns); + tt_int_op(r, OP_EQ, 0); + + // No diffs will be generated. + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + // Add a MD consensus from 45 minutes ago. This should cause one diff + // worth of work to get queued. + ns = fake_ns_new(FLAV_MICRODESC, now-45*60); + md_ns_body_2 = fake_ns_body_new(FLAV_MICRODESC, now-45*60); + r = consdiffmgr_add_consensus(md_ns_body_2, ns); + networkstatus_vote_free(ns); + tt_int_op(r, OP_EQ, 0); + + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(1, OP_EQ, smartlist_len(fake_cpuworker_queue)); + diff_status = consdiffmgr_find_diff_from(&diff, FLAV_MICRODESC, + DIGEST_SHA3_256, + md_ns_sha3, DIGEST256_LEN, + NO_METHOD); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, diff_status); + + // Now run that process and get the diff. + r = mock_cpuworker_run_work(); + tt_int_op(r, OP_EQ, 0); + mock_cpuworker_handle_replies(); + + // At this point we should be able to get that diff. + diff_status = consdiffmgr_find_diff_from(&diff, FLAV_MICRODESC, + DIGEST_SHA3_256, + md_ns_sha3, DIGEST256_LEN, + NO_METHOD); + tt_int_op(CONSDIFF_AVAILABLE, OP_EQ, diff_status); + tt_assert(diff); + + /* Make sure applying the diff actually works */ + const uint8_t *diff_body; + size_t diff_size; + r = consensus_cache_entry_get_body(diff, &diff_body, &diff_size); + tt_int_op(r, OP_EQ, 0); + diff_text = tor_memdup_nulterm(diff_body, diff_size); + applied = consensus_diff_apply(md_ns_body, diff_text); + tt_assert(applied); + tt_str_op(applied, OP_EQ, md_ns_body_2); + + /* Rescan again: no more work to do. */ + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + done: + tor_free(md_ns_body); + tor_free(md_ns_body_2); + tor_free(diff_text); + tor_free(applied); +} + +static void +test_consdiffmgr_diff_rules(void *arg) +{ + (void)arg; +#define N 6 + char *md_body[N], *ns_body[N]; + networkstatus_t *md_ns[N], *ns_ns[N]; + uint8_t md_ns_sha3[N][DIGEST256_LEN], ns_ns_sha3[N][DIGEST256_LEN]; + int i; + + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + /* Create a bunch of consensus things at 15-second intervals. */ + time_t start = approx_time() - 120; + for (i = 0; i < N; ++i) { + time_t when = start + i * 15; + md_body[i] = fake_ns_body_new(FLAV_MICRODESC, when); + ns_body[i] = fake_ns_body_new(FLAV_NS, when); + md_ns[i] = fake_ns_new(FLAV_MICRODESC, when); + ns_ns[i] = fake_ns_new(FLAV_NS, when); + crypto_digest256((char *)md_ns_sha3[i], md_body[i], strlen(md_body[i]), + DIGEST_SHA3_256); + crypto_digest256((char *)ns_ns_sha3[i], ns_body[i], strlen(ns_body[i]), + DIGEST_SHA3_256); + } + + /* For the MD consensuses: add 4 of them, and make sure that + * diffs are created to one consensus (the most recent) only. */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[1], md_ns[1])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[2], md_ns[2])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[3], md_ns[3])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[4], md_ns[4])); + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(3, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + /* For the NS consensuses: add 3, generate, and add one older one and + * make sure that older one is the only one whose diff is generated */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(ns_body[0], ns_ns[0])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(ns_body[1], ns_ns[1])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(ns_body[5], ns_ns[5])); + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(2, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + + /* At this point, we should actually have working diffs! */ + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[0], ns_body[5])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[1], ns_body[5])); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[4])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[2], md_body[4])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[3], md_body[4])); + + /* Self-to-self diff won't be present */ + consensus_cache_entry_t *ent; + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_NS, ns_body[5])); + /* No diff from 2 has been added yet */ + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_NS, ns_body[2])); + /* No diff arriving at old things. */ + tt_int_op(-1, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[2])); + /* No backwards diff */ + tt_int_op(-1, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[4], md_body[3])); + + /* Now, an update: add number 2 and make sure it's the only one whose diff + * is regenerated. */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(ns_body[2], ns_ns[2])); + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(1, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[2], ns_body[5])); + + /* Finally: reload, and make sure that the information is still indexed */ + cdm_reload(); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[0], ns_body[5])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[2], ns_body[5])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_NS, ns_body[1], ns_body[5])); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[4])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[2], md_body[4])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[3], md_body[4])); + + done: + for (i = 0; i < N; ++i) { + tor_free(md_body[i]); + tor_free(ns_body[i]); + networkstatus_vote_free(md_ns[i]); + networkstatus_vote_free(ns_ns[i]); + } + UNMOCK(cpuworker_queue_work); +#undef N +} + +static void +test_consdiffmgr_diff_failure(void *arg) +{ + (void)arg; + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + /* We're going to make sure that if we have a bogus request where + * we can't actually compute a diff, the world must not end. */ + networkstatus_t *ns1 = NULL; + networkstatus_t *ns2 = NULL; + int r; + + ns1 = fake_ns_new(FLAV_NS, approx_time()-100); + ns2 = fake_ns_new(FLAV_NS, approx_time()-50); + r = consdiffmgr_add_consensus("foo bar baz\n", ns1); + tt_int_op(r, OP_EQ, 0); + // We refuse to compute a diff to or from a line holding only a single dot. + // We can add it here, though. + r = consdiffmgr_add_consensus("foo bar baz\n.\n.\n", ns2); + tt_int_op(r, OP_EQ, 0); + + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + setup_capture_of_logs(LOG_WARN); + tt_int_op(1, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + expect_single_log_msg_containing("one of the lines to be added is \".\"."); + mock_clean_saved_logs(); + mock_cpuworker_handle_replies(); + expect_single_log_msg_containing("Worker was unable to compute consensus " + "diff from "); + + /* Make sure the diff is not present */ + consensus_cache_entry_t *ent; + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_NS, "foo bar baz\n")); + + done: + teardown_capture_of_logs(); + UNMOCK(cpuworker_queue_work); + networkstatus_vote_free(ns1); + networkstatus_vote_free(ns2); +} + +static void +test_consdiffmgr_diff_pending(void *arg) +{ +#define N 3 + (void)arg; + char *md_body[N]; + networkstatus_t *md_ns[N]; + time_t start = approx_time() - 120; + int i; + for (i = 0; i < N; ++i) { + time_t when = start + i * 30; + md_body[i] = fake_ns_body_new(FLAV_MICRODESC, when); + md_ns[i] = fake_ns_new(FLAV_MICRODESC, when); + } + + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[1], md_ns[1])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[2], md_ns[2])); + /* Make a diff */ + consdiffmgr_rescan(); + tt_int_op(1, OP_EQ, smartlist_len(fake_cpuworker_queue)); + + /* Look it up. Is it pending? */ + consensus_cache_entry_t *ent = NULL; + consdiff_status_t diff_status; + diff_status = lookup_diff_from(&ent, FLAV_MICRODESC, md_body[1]); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, diff_status); + tt_ptr_op(ent, OP_EQ, NULL); + + /* Add another old consensus. only one new diff should launch! */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[0], md_ns[0])); + consdiffmgr_rescan(); + tt_int_op(2, OP_EQ, smartlist_len(fake_cpuworker_queue)); + + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[0], md_body[2])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[2])); + + done: + UNMOCK(cpuworker_queue_work); + for (i = 0; i < N; ++i) { + tor_free(md_body[i]); + networkstatus_vote_free(md_ns[i]); + } +#undef N +} + +static void +test_consdiffmgr_cleanup_old(void *arg) +{ + (void)arg; + config_line_t *labels = NULL; + consensus_cache_entry_t *ent = NULL; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + + /* This item will be will be cleanable because it has a valid-after + * time far in the past. */ + config_line_prepend(&labels, "document-type", "confribble-blarg"); + config_line_prepend(&labels, "consensus-valid-after", + "1980-10-10T10:10:10"); + ent = consensus_cache_add(cache, labels, (const uint8_t*)"Foo", 3); + tt_assert(ent); + consensus_cache_entry_decref(ent); + + setup_capture_of_logs(LOG_DEBUG); + tt_int_op(1, OP_EQ, consdiffmgr_cleanup()); + expect_log_msg_containing("Deleting entry because its consensus-valid-" + "after value (1980-10-10T10:10:10) was too old"); + + done: + teardown_capture_of_logs(); + config_free_lines(labels); +} + +static void +test_consdiffmgr_cleanup_bad_valid_after(void *arg) +{ + /* This will seem cleanable, but isn't, because its valid-after time is + * misformed. */ + + (void)arg; + config_line_t *labels = NULL; + consensus_cache_entry_t *ent = NULL; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + + config_line_prepend(&labels, "document-type", "consensus"); + config_line_prepend(&labels, "consensus-valid-after", + "whan that aprille with his shoures soote"); // (~1385?) + ent = consensus_cache_add(cache, labels, (const uint8_t*)"Foo", 3); + tt_assert(ent); + consensus_cache_entry_decref(ent); + + setup_capture_of_logs(LOG_DEBUG); + tt_int_op(0, OP_EQ, consdiffmgr_cleanup()); + expect_log_msg_containing("Ignoring entry because its consensus-valid-" + "after value (\"whan that aprille with his " + "shoures soote\") was unparseable"); + + done: + teardown_capture_of_logs(); + config_free_lines(labels); +} + +static void +test_consdiffmgr_cleanup_no_valid_after(void *arg) +{ + (void)arg; + config_line_t *labels = NULL; + consensus_cache_entry_t *ent = NULL; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + + /* This item will be will be uncleanable because it has no recognized + * valid-after. */ + config_line_prepend(&labels, "document-type", "consensus"); + config_line_prepend(&labels, "confrooble-voolid-oofter", + "2010-10-10T09:08:07"); + ent = consensus_cache_add(cache, labels, (const uint8_t*)"Foo", 3); + tt_assert(ent); + consensus_cache_entry_decref(ent); + + setup_capture_of_logs(LOG_DEBUG); + tt_int_op(0, OP_EQ, consdiffmgr_cleanup()); + expect_log_msg_containing("Ignoring entry because it had no consensus-" + "valid-after label"); + + done: + teardown_capture_of_logs(); + config_free_lines(labels); +} + +static void +test_consdiffmgr_cleanup_old_diffs(void *arg) +{ + (void)arg; +#define N 4 + char *md_body[N]; + networkstatus_t *md_ns[N]; + uint8_t md_ns_sha3[N][DIGEST256_LEN]; + int i; + consensus_cache_entry_t *hold_ent = NULL, *ent; + + /* Make sure that the cleanup function removes diffs to the not-most-recent + * consensus. */ + + MOCK(cpuworker_queue_work, mock_cpuworker_queue_work); + + /* Create a bunch of consensus things at 15-second intervals. */ + time_t start = approx_time() - 120; + for (i = 0; i < N; ++i) { + time_t when = start + i * 15; + md_body[i] = fake_ns_body_new(FLAV_MICRODESC, when); + md_ns[i] = fake_ns_new(FLAV_MICRODESC, when); + crypto_digest256((char *)md_ns_sha3[i], md_body[i], strlen(md_body[i]), + DIGEST_SHA3_256); + } + + /* add the first 3. */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[0], md_ns[0])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[1], md_ns[1])); + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[2], md_ns[2])); + /* Make diffs. */ + consdiffmgr_rescan(); + tt_ptr_op(NULL, OP_NE, fake_cpuworker_queue); + tt_int_op(2, OP_EQ, smartlist_len(fake_cpuworker_queue)); + tt_int_op(0, OP_EQ, mock_cpuworker_run_work()); + mock_cpuworker_handle_replies(); + tt_ptr_op(NULL, OP_EQ, fake_cpuworker_queue); + + /* Nothing is deletable now */ + tt_int_op(0, OP_EQ, consdiffmgr_cleanup()); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[0], md_body[2])); + tt_int_op(0, OP_EQ, + lookup_apply_and_verify_diff(FLAV_MICRODESC, md_body[1], md_body[2])); + + tt_int_op(CONSDIFF_AVAILABLE, OP_EQ, + lookup_diff_from(&hold_ent, FLAV_MICRODESC, md_body[1])); + consensus_cache_entry_incref(hold_ent); // incref, so it is preserved. + + /* Now add an even-more-recent consensus; this should make all previous + * diffs deletable */ + tt_int_op(0, OP_EQ, consdiffmgr_add_consensus(md_body[3], md_ns[3])); + tt_int_op(2 * n_diff_compression_methods(), OP_EQ, consdiffmgr_cleanup()); + + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[0])); + /* This one is marked deletable but still in the hashtable */ + tt_int_op(CONSDIFF_AVAILABLE, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[1])); + tt_int_op(CONSDIFF_NOT_FOUND, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[2])); + + /* Everything should be valid at this point */ + tt_int_op(0, OP_EQ, consdiffmgr_validate()); + + /* And if we recan NOW, we'll purge the hashtable of the entries, + * and launch attempts to generate new ones */ + consdiffmgr_rescan(); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[0])); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[1])); + tt_int_op(CONSDIFF_IN_PROGRESS, OP_EQ, + lookup_diff_from(&ent, FLAV_MICRODESC, md_body[2])); + + /* We're still holding on to this, though, so we can still map it! */ + const uint8_t *t1 = NULL; + size_t s; + int r = consensus_cache_entry_get_body(hold_ent, &t1, &s); + tt_int_op(r, OP_EQ, 0); + tt_assert(t1); + + done: + for (i = 0; i < N; ++i) { + tor_free(md_body[i]); + networkstatus_vote_free(md_ns[i]); + } + consensus_cache_entry_decref(hold_ent); + UNMOCK(cpuworker_queue_work); +#undef N +} + +static void +test_consdiffmgr_validate(void *arg) +{ + (void)arg; + config_line_t *lines = NULL; + consensus_cache_entry_t *ent = NULL; + consensus_cache_t *cache = cdm_cache_get(); // violate abstraction barrier + smartlist_t *vals = smartlist_new(); + + /* Put these: objects in the cache: one with a good sha3, one with bad sha3, + * one with a wrong sha3, and one with no sha3. */ + config_line_prepend(&lines, "id", "wrong sha3"); + config_line_prepend(&lines, "sha3-digest", + "F00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00D"); + ent = consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + consensus_cache_entry_decref(ent); + config_free_lines(lines); + lines = NULL; + + config_line_prepend(&lines, "id", "bad sha3"); + config_line_prepend(&lines, "sha3-digest", + "now is the winter of our dicotheque"); + ent = consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + consensus_cache_entry_decref(ent); + config_free_lines(lines); + lines = NULL; + + config_line_prepend(&lines, "id", "no sha3"); + ent = consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + consensus_cache_entry_decref(ent); + config_free_lines(lines); + lines = NULL; + + config_line_prepend(&lines, "id", "good sha3"); + config_line_prepend(&lines, "sha3-digest", + "8d8b1998616cd6b4c4055da8d38728dc" + "93c758d4131a53c7d81aa6337dee1c05"); + ent = consensus_cache_add(cache, lines, (const uint8_t *)"Hi there", 8); + consensus_cache_entry_decref(ent); + config_free_lines(lines); + lines = NULL; + + cdm_reload(); + cache = cdm_cache_get(); + tt_int_op(1, OP_EQ, consdiffmgr_validate()); + + consensus_cache_find_all(vals, cache, "id", "good sha3"); + tt_int_op(smartlist_len(vals), OP_EQ, 1); + smartlist_clear(vals); + + consensus_cache_find_all(vals, cache, "id", "no sha3"); + tt_int_op(smartlist_len(vals), OP_EQ, 1); + smartlist_clear(vals); + + consensus_cache_find_all(vals, cache, "id", "wrong sha3"); + tt_int_op(smartlist_len(vals), OP_EQ, 0); + consensus_cache_find_all(vals, cache, "id", "bad sha3"); + tt_int_op(smartlist_len(vals), OP_EQ, 0); + + done: + smartlist_free(vals); +} + +#define TEST(name) \ + { #name, test_consdiffmgr_ ## name , TT_FORK, &setup_diffmgr, NULL } + +struct testcase_t consdiffmgr_tests[] = { +#if 0 + { "init_failure", test_consdiffmgr_init_failure, TT_FORK, NULL, NULL }, +#endif + TEST(sha3_helper), + TEST(add), + TEST(make_diffs), + TEST(diff_rules), + TEST(diff_failure), + TEST(diff_pending), + TEST(cleanup_old), + TEST(cleanup_bad_valid_after), + TEST(cleanup_no_valid_after), + TEST(cleanup_old_diffs), + TEST(validate), + + // XXXX Test: non-cacheing cases of replyfn(). + + END_OF_TESTCASES +}; + diff --git a/src/test/test_containers.c b/src/test/test_containers.c index 41f3f873de..54484a2a91 100644 --- a/src/test/test_containers.c +++ b/src/test/test_containers.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_controller.c b/src/test/test_controller.c index 206315e289..592f91a988 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONTROL_PRIVATE diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index 11e1e3dc8f..901ad7ab3d 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONNECTION_PRIVATE diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index d66ddccd4f..ec9d4e2709 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -15,9 +15,6 @@ #include "crypto_ed25519.h" #include "ed25519_vectors.inc" -#include <openssl/evp.h> -#include <openssl/rand.h> - /** Run unit tests for Diffie-Hellman functionality. */ static void test_crypto_dh(void *arg) @@ -331,38 +328,6 @@ test_crypto_rng_strongest(void *arg) #undef N } -/* Test for rectifying openssl RAND engine. */ -static void -test_crypto_rng_engine(void *arg) -{ - (void)arg; - RAND_METHOD dummy_method; - memset(&dummy_method, 0, sizeof(dummy_method)); - - /* We should be a no-op if we're already on RAND_OpenSSL */ - tt_int_op(0, ==, crypto_force_rand_ssleay()); - tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); - - /* We should correct the method if it's a dummy. */ - RAND_set_rand_method(&dummy_method); -#ifdef LIBRESSL_VERSION_NUMBER - /* On libressl, you can't override the RNG. */ - tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); - tt_int_op(0, ==, crypto_force_rand_ssleay()); -#else - tt_assert(RAND_get_rand_method() == &dummy_method); - tt_int_op(1, ==, crypto_force_rand_ssleay()); -#endif - tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); - - /* Make sure we aren't calling dummy_method */ - crypto_rand((void *) &dummy_method, sizeof(dummy_method)); - crypto_rand((void *) &dummy_method, sizeof(dummy_method)); - - done: - ; -} - /** Run unit tests for our AES128 functionality */ static void test_crypto_aes128(void *arg) @@ -1477,28 +1442,6 @@ test_crypto_digest_names(void *arg) ; } -#ifndef OPENSSL_1_1_API -#define EVP_ENCODE_CTX_new() tor_malloc_zero(sizeof(EVP_ENCODE_CTX)) -#define EVP_ENCODE_CTX_free(ctx) tor_free(ctx) -#endif - -/** Encode src into dest with OpenSSL's EVP Encode interface, returning the - * length of the encoded data in bytes. - */ -static int -base64_encode_evp(char *dest, char *src, size_t srclen) -{ - const unsigned char *s = (unsigned char*)src; - EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new(); - int len, ret; - - EVP_EncodeInit(ctx); - EVP_EncodeUpdate(ctx, (unsigned char *)dest, &len, s, (int)srclen); - EVP_EncodeFinal(ctx, (unsigned char *)(dest + len), &ret); - EVP_ENCODE_CTX_free(ctx); - return ret+ len; -} - /** Run unit tests for misc crypto formatting functionality (base64, base32, * fingerprints, etc) */ static void @@ -1527,7 +1470,7 @@ test_crypto_formats(void *arg) tt_int_op(i, OP_GE, 0); tt_int_op(i, OP_EQ, strlen(data2)); tt_assert(! strchr(data2, '=')); - j = base64_decode_nopad((uint8_t*)data3, 1024, data2, i); + j = base64_decode(data3, 1024, data2, i); tt_int_op(j, OP_EQ, idx); tt_mem_op(data3,OP_EQ, data1, idx); } @@ -1554,20 +1497,6 @@ test_crypto_formats(void *arg) tt_assert(digest_from_base64(data3, "###") < 0); - for (i = 0; i < 256; i++) { - /* Test the multiline format Base64 encoder with 0 .. 256 bytes of - * output against OpenSSL. - */ - const size_t enclen = base64_encode_size(i, BASE64_ENCODE_MULTILINE); - data1[i] = i; - j = base64_encode(data2, 1024, data1, i, BASE64_ENCODE_MULTILINE); - tt_int_op(j, OP_EQ, enclen); - j = base64_encode_evp(data3, data1, i); - tt_int_op(j, OP_EQ, enclen); - tt_mem_op(data2, OP_EQ, data3, enclen); - tt_int_op(j, OP_EQ, strlen(data2)); - } - /* Encoding SHA256 */ crypto_rand(data2, DIGEST256_LEN); memset(data2, 100, 1024); @@ -2941,7 +2870,6 @@ struct testcase_t crypto_tests[] = { CRYPTO_LEGACY(formats), CRYPTO_LEGACY(rng), { "rng_range", test_crypto_rng_range, 0, NULL, NULL }, - { "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL }, { "rng_strongest", test_crypto_rng_strongest, TT_FORK, NULL, NULL }, { "rng_strongest_nosyscall", test_crypto_rng_strongest, TT_FORK, &passthrough_setup, (void*)"nosyscall" }, diff --git a/src/test/test_crypto_openssl.c b/src/test/test_crypto_openssl.c new file mode 100644 index 0000000000..3d7d2b4639 --- /dev/null +++ b/src/test/test_crypto_openssl.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define CRYPTO_PRIVATE + +#include "crypto.h" +#include "util.h" +#include "util_format.h" +#include "compat.h" +#include "test.h" + +#include <openssl/evp.h> +#include <openssl/rand.h> +#include "compat_openssl.h" + +/* Test for rectifying openssl RAND engine. */ +static void +test_crypto_rng_engine(void *arg) +{ + (void)arg; + RAND_METHOD dummy_method; + memset(&dummy_method, 0, sizeof(dummy_method)); + + /* We should be a no-op if we're already on RAND_OpenSSL */ + tt_int_op(0, ==, crypto_force_rand_ssleay()); + tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); + + /* We should correct the method if it's a dummy. */ + RAND_set_rand_method(&dummy_method); +#ifdef LIBRESSL_VERSION_NUMBER + /* On libressl, you can't override the RNG. */ + tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); + tt_int_op(0, ==, crypto_force_rand_ssleay()); +#else + tt_assert(RAND_get_rand_method() == &dummy_method); + tt_int_op(1, ==, crypto_force_rand_ssleay()); +#endif + tt_assert(RAND_get_rand_method() == RAND_OpenSSL()); + + /* Make sure we aren't calling dummy_method */ + crypto_rand((void *) &dummy_method, sizeof(dummy_method)); + crypto_rand((void *) &dummy_method, sizeof(dummy_method)); + + done: + ; +} + +#ifndef OPENSSL_1_1_API +#define EVP_ENCODE_CTX_new() tor_malloc_zero(sizeof(EVP_ENCODE_CTX)) +#define EVP_ENCODE_CTX_free(ctx) tor_free(ctx) +#endif + +/** Encode src into dest with OpenSSL's EVP Encode interface, returning the + * length of the encoded data in bytes. + */ +static int +base64_encode_evp(char *dest, char *src, size_t srclen) +{ + const unsigned char *s = (unsigned char*)src; + EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new(); + int len, ret; + + EVP_EncodeInit(ctx); + EVP_EncodeUpdate(ctx, (unsigned char *)dest, &len, s, (int)srclen); + EVP_EncodeFinal(ctx, (unsigned char *)(dest + len), &ret); + EVP_ENCODE_CTX_free(ctx); + return ret+ len; +} + +static void +test_crypto_base64_encode_matches(void *arg) +{ + (void)arg; + int i, j; + char data1[1024]; + char data2[1024]; + char data3[1024]; + + for (i = 0; i < 256; i++) { + /* Test the multiline format Base64 encoder with 0 .. 256 bytes of + * output against OpenSSL. + */ + const size_t enclen = base64_encode_size(i, BASE64_ENCODE_MULTILINE); + data1[i] = i; + j = base64_encode(data2, 1024, data1, i, BASE64_ENCODE_MULTILINE); + tt_int_op(j, OP_EQ, enclen); + j = base64_encode_evp(data3, data1, i); + tt_int_op(j, OP_EQ, enclen); + tt_mem_op(data2, OP_EQ, data3, enclen); + tt_int_op(j, OP_EQ, strlen(data2)); + } + + done: + ; +} + +struct testcase_t crypto_openssl_tests[] = { + { "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL }, + { "base64_encode_match", test_crypto_base64_encode_matches, + TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c index 0d7d65ac73..d6b0a43dd5 100644 --- a/src/test/test_crypto_slow.c +++ b/src/test/test_crypto_slow.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -12,11 +12,8 @@ #if defined(HAVE_LIBSCRYPT_H) && defined(HAVE_LIBSCRYPT_SCRYPT) #define HAVE_LIBSCRYPT -#include <libscrypt.h> #endif -#include <openssl/evp.h> - /** Run unit tests for our secret-to-key passphrase hashing functionality. */ static void test_crypto_s2k_rfc2440(void *arg) diff --git a/src/test/test_data.c b/src/test/test_data.c index 788489a097..ce6c3394f6 100644 --- a/src/test/test_data.c +++ b/src/test/test_data.c @@ -1,6 +1,6 @@ /* Copyright 2001-2004 Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "test.h" diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 4e5876fa3c..a9d9cba7df 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -1,12 +1,13 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" #include <math.h> #define CONFIG_PRIVATE +#define CONTROL_PRIVATE #define DIRSERV_PRIVATE #define DIRVOTE_PRIVATE #define ROUTER_PRIVATE @@ -19,6 +20,7 @@ #include "or.h" #include "confparse.h" #include "config.h" +#include "control.h" #include "crypto_ed25519.h" #include "directory.h" #include "dirserv.h" @@ -329,7 +331,7 @@ test_dir_formats(void *arg) ntor_cc = make_ntor_onion_key_crosscert(&r2_onion_keypair, &kp1.pubkey, r2->cache_info.published_on, - MIN_ONION_KEY_LIFETIME, + get_onion_key_lifetime(), &ntor_cc_sign); tt_assert(ntor_cc); base64_encode(cert_buf, sizeof(cert_buf), @@ -910,6 +912,23 @@ mock_get_by_ei_desc_digest(const char *d) } } +static signed_descriptor_t * +mock_ei_get_by_ei_digest(const char *d) +{ + char hex[HEX_DIGEST_LEN+1]; + base16_encode(hex, sizeof(hex), d, DIGEST_LEN); + signed_descriptor_t *sd = &sd_ei_minimal; + + if (!strcmp(hex, "11E0EDF526950739F7769810FCACAB8C882FAEEE")) { + sd->signed_descriptor_body = (char *)EX_EI_MINIMAL; + sd->signed_descriptor_len = sizeof(EX_EI_MINIMAL); + sd->annotations_len = 0; + sd->saved_location = SAVED_NOWHERE; + return sd; + } + return NULL; +} + static smartlist_t *mock_ei_insert_list = NULL; static was_router_added_t mock_ei_insert(routerlist_t *rl, extrainfo_t *ei, int warn_if_incompatible) @@ -999,6 +1018,37 @@ test_dir_load_extrainfo(void *arg) } static void +test_dir_getinfo_extra(void *arg) +{ + int r; + char *answer = NULL; + const char *errmsg = NULL; + + (void)arg; + MOCK(extrainfo_get_by_descriptor_digest, mock_ei_get_by_ei_digest); + r = getinfo_helper_dir(NULL, "extra-info/digest/" + "11E0EDF526950739F7769810FCACAB8C882FAEEE", &answer, + &errmsg); + tt_int_op(0, OP_EQ, r); + tt_ptr_op(NULL, OP_EQ, errmsg); + tt_str_op(answer, OP_EQ, EX_EI_MINIMAL); + tor_free(answer); + + answer = NULL; + r = getinfo_helper_dir(NULL, "extra-info/digest/" + "NOTAVALIDHEXSTRINGNOTAVALIDHEXSTRINGNOTA", &answer, + &errmsg); + tt_int_op(0, OP_EQ, r); + /* getinfo_helper_dir() should maybe return an error here but doesn't */ + tt_ptr_op(NULL, OP_EQ, errmsg); + /* In any case, there should be no answer for an invalid hex string. */ + tt_ptr_op(NULL, OP_EQ, answer); + + done: + UNMOCK(extrainfo_get_by_descriptor_digest); +} + +static void test_dir_versions(void *arg) { tor_version_t ver1; @@ -1065,6 +1115,7 @@ test_dir_versions(void *arg) tt_int_op(0, OP_EQ, ver1.patchlevel); tt_int_op(VER_RELEASE, OP_EQ, ver1.status); tt_str_op("alpha", OP_EQ, ver1.status_tag); + /* Go through the full set of status tags */ tt_int_op(0, OP_EQ, tor_version_parse("2.1.700-alpha", &ver1)); tt_int_op(2, OP_EQ, ver1.major); tt_int_op(1, OP_EQ, ver1.minor); @@ -1079,6 +1130,60 @@ test_dir_versions(void *arg) tt_int_op(0, OP_EQ, ver1.patchlevel); tt_int_op(VER_RELEASE, OP_EQ, ver1.status); tt_str_op("alpha-dev", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.5-rc", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(5, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("rc", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.6-rc-dev", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(6, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("rc-dev", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.8", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(8, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.9-dev", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(9, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("dev", OP_EQ, ver1.status_tag); + /* In #21450, we fixed an inconsistency in parsing versions > INT32_MAX + * between i386 and x86_64, as we used tor_parse_long, and then cast to int + */ + tt_int_op(0, OP_EQ, tor_version_parse("0.2147483647.0", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2147483647, OP_EQ, ver1.minor); + tt_int_op(0, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(-1, OP_EQ, tor_version_parse("0.2147483648.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.4294967295.0", &ver1)); + /* In #21278, we reject negative version components */ + tt_int_op(-1, OP_EQ, tor_version_parse("0.-1.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.-2147483648.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.-4294967295.0", &ver1)); + /* In #21507, we reject version components with non-numeric prefixes */ + tt_int_op(-1, OP_EQ, tor_version_parse("0.-0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("+1.0.0", &ver1)); + /* use the list in isspace() */ + tt_int_op(-1, OP_EQ, tor_version_parse("0.\t0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\n0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\v0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\f0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\r0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0. 0.0", &ver1)); #define tt_versionstatus_op(vs1, op, vs2) \ tt_assert_test_type(vs1,vs2,#vs1" "#op" "#vs2,version_status_t, \ @@ -1098,6 +1203,7 @@ test_dir_versions(void *arg) test_v_i_o(VS_RECOMMENDED, "0.0.7rc2", "0.0.7,Tor 0.0.7rc2,Tor 0.0.8"); test_v_i_o(VS_OLD, "0.0.5.0", "0.0.5.1-cvs"); test_v_i_o(VS_NEW_IN_SERIES, "0.0.5.1-cvs", "0.0.5, 0.0.6"); + test_v_i_o(VS_NEW, "0.2.9.9-dev", "0.2.9.9"); /* Not on list, but newer than any in same series. */ test_v_i_o(VS_NEW_IN_SERIES, "0.1.0.3", "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0"); @@ -1136,6 +1242,70 @@ test_dir_versions(void *arg) "Tor 0.2.1.0-dev (r99)")); tt_int_op(1,OP_EQ, tor_version_as_new_as("Tor 0.2.1.1", "Tor 0.2.1.0-dev (r99)")); + /* And git revisions */ + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-56788a2489127072)", + "Tor 0.2.9.9 (git-56788a2489127072)")); + /* a git revision is newer than no git revision */ + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-56788a2489127072)", + "Tor 0.2.9.9")); + /* a longer git revision is newer than a shorter git revision + * this should be true if they prefix-match, but if they don't, they are + * incomparable, because hashes aren't ordered (but we compare their bytes + * anyway) */ + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-56788a2489127072d513cf4baf35a8ff475f3c7b)", + "Tor 0.2.9.9 (git-56788a2489127072)")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-0102)", + "Tor 0.2.9.9 (git-03)")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-0102)", + "Tor 0.2.9.9 (git-00)")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-01)", + "Tor 0.2.9.9 (git-00)")); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-00)", + "Tor 0.2.9.9 (git-01)")); + /* In #21278, we comapre without integer overflows. + * But since #21450 limits version components to [0, INT32_MAX], it is no + * longer possible to cause an integer overflow in tor_version_compare() */ + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.0.0.0", + "Tor 2147483647.0.0.0")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 2147483647.0.0.0", + "Tor 0.0.0.0")); + /* These versions used to cause an overflow, now they don't parse + * (and authorities reject their descriptors), and log a BUG message */ + setup_full_capture_of_logs(LOG_WARN); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.0.0.0", + "Tor 0.-2147483648.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.2147483647.0.0", + "Tor 0.-1.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.2147483647.0.0", + "Tor 0.-2147483648.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 4294967295.0.0.0", + "Tor 0.0.0.0")); + expect_no_log_entry(); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.4294967295.0.0", + "Tor 0.-4294967295.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + teardown_capture_of_logs(); /* Now try git revisions */ tt_int_op(0,OP_EQ, tor_version_parse("0.5.6.7 (git-ff00ff)", &ver1)); @@ -1145,11 +1315,24 @@ test_dir_versions(void *arg) tt_int_op(7,OP_EQ, ver1.patchlevel); tt_int_op(3,OP_EQ, ver1.git_tag_len); tt_mem_op(ver1.git_tag,OP_EQ, "\xff\x00\xff", 3); + /* reject bad hex digits */ tt_int_op(-1,OP_EQ, tor_version_parse("0.5.6.7 (git-ff00xx)", &ver1)); + /* reject odd hex digit count */ tt_int_op(-1,OP_EQ, tor_version_parse("0.5.6.7 (git-ff00fff)", &ver1)); + /* ignore "git " */ tt_int_op(0,OP_EQ, tor_version_parse("0.5.6.7 (git ff00fff)", &ver1)); + /* standard length is 16 hex digits */ + tt_int_op(0,OP_EQ, tor_version_parse("0.5.6.7 (git-0010203040506070)", + &ver1)); + /* length limit is 40 hex digits */ + tt_int_op(0,OP_EQ, tor_version_parse( + "0.5.6.7 (git-000102030405060708090a0b0c0d0e0f10111213)", + &ver1)); + tt_int_op(-1,OP_EQ, tor_version_parse( + "0.5.6.7 (git-000102030405060708090a0b0c0d0e0f1011121314)", + &ver1)); done: - ; + teardown_capture_of_logs(); } /** Run unit tests for directory fp_pair functions. */ @@ -4399,15 +4582,7 @@ test_dir_should_use_directory_guards(void *data) } NS_DECL(void, -directory_initiate_command_routerstatus, (const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since, - circuit_guard_state_t *guardstate)); +directory_initiate_request, (directory_request_t *req)); static void test_dir_should_not_init_request_to_ourselves(void *data) @@ -4417,7 +4592,7 @@ test_dir_should_not_init_request_to_ourselves(void *data) crypto_pk_t *key = pk_generate(2); (void) data; - NS_MOCK(directory_initiate_command_routerstatus); + NS_MOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); @@ -4432,15 +4607,15 @@ test_dir_should_not_init_request_to_ourselves(void *data) dir_server_add(ourself); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 0); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 0); done: - NS_UNMOCK(directory_initiate_command_routerstatus); + NS_UNMOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); crypto_pk_free(key); @@ -4454,7 +4629,7 @@ test_dir_should_not_init_request_to_dir_auths_without_v3_info(void *data) | MICRODESC_DIRINFO; (void) data; - NS_MOCK(directory_initiate_command_routerstatus); + NS_MOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); @@ -4465,14 +4640,14 @@ test_dir_should_not_init_request_to_dir_auths_without_v3_info(void *data) dir_server_add(ds); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 0); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 0); done: - NS_UNMOCK(directory_initiate_command_routerstatus); + NS_UNMOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); } @@ -4483,7 +4658,7 @@ test_dir_should_init_request_to_dir_auths(void *data) dir_server_t *ds = NULL; (void) data; - NS_MOCK(directory_initiate_command_routerstatus); + NS_MOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); @@ -4494,39 +4669,23 @@ test_dir_should_init_request_to_dir_auths(void *data) dir_server_add(ds); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 1); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 1); directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0, NULL); - tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 2); + tt_int_op(CALLED(directory_initiate_request), OP_EQ, 2); done: - NS_UNMOCK(directory_initiate_command_routerstatus); + NS_UNMOCK(directory_initiate_request); clear_dir_servers(); routerlist_free_all(); } void -NS(directory_initiate_command_routerstatus)(const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since, - circuit_guard_state_t *guardstate) +NS(directory_initiate_request)(directory_request_t *req) { - (void)status; - (void)dir_purpose; - (void)router_purpose; - (void)indirection; - (void)resource; - (void)payload; - (void)payload_len; - (void)if_modified_since; - (void)guardstate; - CALLED(directory_initiate_command_routerstatus)++; + (void)req; + CALLED(directory_initiate_request)++; } static void @@ -5837,6 +5996,7 @@ struct testcase_t dir_tests[] = { DIR(parse_router_list, TT_FORK), DIR(load_routers, TT_FORK), DIR(load_extrainfo, TT_FORK), + DIR(getinfo_extra, 0), DIR_LEGACY(versions), DIR_LEGACY(fp_pairs), DIR(split_fps, 0), diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c index ca43dd4c04..fca70249bd 100644 --- a/src/test/test_dir_common.c +++ b/src/test/test_dir_common.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_dir_common.h b/src/test/test_dir_common.h index 9682b0db49..65b9cf6436 100644 --- a/src/test/test_dir_common.h +++ b/src/test/test_dir_common.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index a0868f9253..6e963913d1 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define RENDCOMMON_PRIVATE @@ -14,6 +14,7 @@ #include "connection.h" #include "directory.h" #include "test.h" +#include "compress.h" #include "connection.h" #include "rendcommon.h" #include "rendcache.h" @@ -28,7 +29,6 @@ #include "networkstatus.h" #include "geoip.h" #include "dirserv.h" -#include "torgzip.h" #include "dirvote.h" #include "log_test_helpers.h" @@ -743,7 +743,7 @@ test_dir_handle_get_server_descriptors_not_found(void* data) NULL, NULL, 1, 0); tt_str_op(NOT_FOUND, OP_EQ, header); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_SERVER_BY_FP); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: UNMOCK(connection_write_to_buf_impl_); @@ -773,6 +773,7 @@ test_dir_handle_get_server_descriptors_all(void* data) tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1); mock_routerinfo = smartlist_get(our_routerlist->routers, 0); set_server_identity_key(mock_routerinfo->identity_pkey); + mock_routerinfo->cache_info.published_on = time(NULL); /* Treat "all" requests as if they were unencrypted */ mock_routerinfo->cache_info.send_unencrypted = 1; @@ -787,7 +788,7 @@ test_dir_handle_get_server_descriptors_all(void* data) //which is smaller than that by annotation_len bytes fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, &body, &body_used, - mock_routerinfo->cache_info.signed_descriptor_len+1, 0); + 1024*1024, 0); tt_assert(header); tt_assert(body); @@ -803,7 +804,7 @@ test_dir_handle_get_server_descriptors_all(void* data) tt_str_op(body, OP_EQ, mock_routerinfo->cache_info.signed_descriptor_body + mock_routerinfo->cache_info.annotations_len); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: NS_UNMOCK(router_get_my_routerinfo); @@ -882,6 +883,7 @@ test_dir_handle_get_server_descriptors_authority(void* data) mock_routerinfo->cache_info.signed_descriptor_len = strlen(TEST_DESCRIPTOR) - annotation_len;; mock_routerinfo->cache_info.annotations_len = annotation_len; + mock_routerinfo->cache_info.published_on = time(NULL); conn = new_dir_conn(); @@ -904,7 +906,7 @@ test_dir_handle_get_server_descriptors_authority(void* data) tt_int_op(body_used, OP_EQ, strlen(body)); tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: NS_UNMOCK(router_get_my_routerinfo); @@ -946,6 +948,7 @@ test_dir_handle_get_server_descriptors_fp(void* data) mock_routerinfo->cache_info.signed_descriptor_len = strlen(TEST_DESCRIPTOR) - annotation_len; mock_routerinfo->cache_info.annotations_len = annotation_len; + mock_routerinfo->cache_info.published_on = time(NULL); conn = new_dir_conn(); @@ -975,7 +978,7 @@ test_dir_handle_get_server_descriptors_fp(void* data) tt_int_op(body_used, OP_EQ, strlen(body)); tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: NS_UNMOCK(router_get_my_routerinfo); @@ -1041,7 +1044,7 @@ test_dir_handle_get_server_descriptors_d(void* data) tt_str_op(body, OP_EQ, router->cache_info.signed_descriptor_body + router->cache_info.annotations_len); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: UNMOCK(connection_write_to_buf_impl_); @@ -1096,7 +1099,7 @@ test_dir_handle_get_server_descriptors_busy(void* data) tt_assert(header); tt_str_op(SERVER_BUSY, OP_EQ, header); - tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE); + tt_ptr_op(conn->spool, OP_EQ, NULL); done: UNMOCK(get_options); @@ -1829,8 +1832,8 @@ test_dir_handle_get_status_vote_current_consensus_ns(void* data) comp_body_used); tt_int_op(ZLIB_METHOD, OP_EQ, compression); - tor_gzip_uncompress(&body, &body_used, comp_body, comp_body_used, - compression, 0, LOG_PROTOCOL_WARN); + tor_uncompress(&body, &body_used, comp_body, comp_body_used, + compression, 0, LOG_PROTOCOL_WARN); tt_str_op(NETWORK_STATUS, OP_EQ, body); tt_int_op(strlen(NETWORK_STATUS), OP_EQ, body_used); @@ -2494,6 +2497,53 @@ test_dir_handle_get_status_vote_current_authority(void* data) dirvote_free_all(); } +static void +test_dir_handle_get_parse_accept_encoding(void *arg) +{ + (void)arg; + const unsigned B_NONE = 1u << NO_METHOD; + const unsigned B_ZLIB = 1u << ZLIB_METHOD; + const unsigned B_GZIP = 1u << GZIP_METHOD; + const unsigned B_LZMA = 1u << LZMA_METHOD; + const unsigned B_ZSTD = 1u << ZSTD_METHOD; + + unsigned encodings; + + encodings = parse_accept_encoding_header(""); + tt_uint_op(B_NONE, OP_EQ, encodings); + + encodings = parse_accept_encoding_header(" "); + tt_uint_op(B_NONE, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("dewey, cheatham, and howe "); + tt_uint_op(B_NONE, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("dewey, cheatham, and gzip"); + tt_uint_op(B_NONE, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("dewey, cheatham, and, gzip"); + tt_uint_op(B_NONE|B_GZIP, OP_EQ, encodings); + + encodings = parse_accept_encoding_header(" gzip"); + tt_uint_op(B_NONE|B_GZIP, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("gzip"); + tt_uint_op(B_NONE|B_GZIP, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("x-zstd, deflate, x-tor-lzma"); + tt_uint_op(B_NONE|B_ZLIB|B_ZSTD|B_LZMA, OP_EQ, encodings); + + encodings = parse_accept_encoding_header( + "x-zstd, deflate, x-tor-lzma, gzip"); + tt_uint_op(B_NONE|B_ZLIB|B_ZSTD|B_LZMA|B_GZIP, OP_EQ, encodings); + + encodings = parse_accept_encoding_header("x-zstd,deflate,x-tor-lzma,gzip"); + tt_uint_op(B_NONE|B_ZLIB|B_ZSTD|B_LZMA|B_GZIP, OP_EQ, encodings); + + done: + ; +} + #define DIR_HANDLE_CMD(name,flags) \ { #name, test_dir_handle_get_##name, (flags), NULL, NULL } @@ -2552,6 +2602,7 @@ struct testcase_t dir_handle_get_tests[] = { DIR_HANDLE_CMD(status_vote_next_consensus_signatures_not_found, 0), DIR_HANDLE_CMD(status_vote_next_consensus_signatures_busy, 0), DIR_HANDLE_CMD(status_vote_next_consensus_signatures, 0), + DIR_HANDLE_CMD(parse_accept_encoding, 0), END_OF_TESTCASES }; diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c index 50848cfec2..12a631630b 100644 --- a/src/test/test_entryconn.c +++ b/src/test/test_entryconn.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index 249fd1f973..3db7e63ee3 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -584,6 +584,9 @@ test_entry_guard_parse_from_state_full(void *arg) smartlist_t *text = smartlist_new(); char *joined = NULL; + // So nodes aren't expired. This is Tue, 13 Dec 2016 09:37:14 GMT + update_approx_time(1481621834); + MOCK(entry_guard_is_listed, mock_entry_guard_is_listed); dummy_state = state; diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index 1f92780177..fc9f27a5ac 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONNECTION_PRIVATE @@ -72,9 +72,9 @@ test_ext_or_id_map(void *arg) * writes to outbuf. */ static void connection_write_to_buf_impl_replacement(const char *string, size_t len, - connection_t *conn, int zlib) + connection_t *conn, int compressed) { - (void) zlib; + (void) compressed; tor_assert(string); tor_assert(conn); diff --git a/src/test/test_guardfraction.c b/src/test/test_guardfraction.c index 8173e44d47..56006f3cc3 100644 --- a/src/test/test_guardfraction.c +++ b/src/test/test_guardfraction.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define DIRSERV_PRIVATE diff --git a/src/test/test_handles.c b/src/test/test_handles.c index 536a478689..7ddee6e376 100644 --- a/src/test/test_handles.c +++ b/src/test/test_handles.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index 5b84366e6d..9fada5a675 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -98,9 +98,9 @@ helper_setup_fake_routerlist(void) void connection_write_to_buf_mock(const char *string, size_t len, - connection_t *conn, int zlib) + connection_t *conn, int compressed) { - (void) zlib; + (void) compressed; tor_assert(string); tor_assert(conn); diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index c6d4d9c41f..4621631cc1 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_TEST_HELPERS_H @@ -15,7 +15,7 @@ void helper_setup_fake_routerlist(void); #define GET(path) "GET " path " HTTP/1.0\r\n\r\n" void connection_write_to_buf_mock(const char *string, size_t len, - connection_t *conn, int zlib); + connection_t *conn, int compressed); int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, uint16_t family, tor_addr_t *out); diff --git a/src/test/test_hs.c b/src/test/test_hs.c index fbaabe91d8..c3457c43da 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -821,7 +821,9 @@ test_prune_services_on_reload(void *arg) smartlist_add(old, e1); /* Only put the non ephemeral in the new list. */ smartlist_add(new, s1); - prune_services_on_reload(old, new); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); /* We expect that the ephemeral one is in the new list but removed from * the old one. */ tt_int_op(smartlist_len(old), OP_EQ, 1); @@ -840,7 +842,9 @@ test_prune_services_on_reload(void *arg) * one. */ smartlist_add(old, s1); smartlist_add(old, e1); - prune_services_on_reload(old, new); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); tt_int_op(smartlist_len(old), OP_EQ, 1); tt_assert(smartlist_get(old, 0) == s1); tt_int_op(smartlist_len(new), OP_EQ, 1); @@ -855,7 +859,9 @@ test_prune_services_on_reload(void *arg) * list being completely different. */ smartlist_add(new, s1); smartlist_add(new, e1); - prune_services_on_reload(old, new); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); tt_int_op(smartlist_len(old), OP_EQ, 0); tt_int_op(smartlist_len(new), OP_EQ, 2); tt_assert(smartlist_get(new, 0) == s1); @@ -871,7 +877,9 @@ test_prune_services_on_reload(void *arg) /* Setup our list. */ smartlist_add(old, s1); smartlist_add(new, s2); - prune_services_on_reload(old, new); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); tt_int_op(smartlist_len(old), OP_EQ, 1); /* Intro nodes have been moved to the s2 in theory so it must be empty. */ tt_int_op(smartlist_len(s1->intro_nodes), OP_EQ, 0); @@ -892,7 +900,9 @@ test_prune_services_on_reload(void *arg) /* Test two ephemeral services. */ smartlist_add(old, e1); smartlist_add(old, e2); - prune_services_on_reload(old, new); + set_rend_service_list(old); + set_rend_rend_service_staging_list(new); + rend_service_prune_list_impl_(); /* Check if they've all been transfered. */ tt_int_op(smartlist_len(old), OP_EQ, 0); tt_int_op(smartlist_len(new), OP_EQ, 2); diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c index 1943d0ffac..bc4bfabde9 100644 --- a/src/test/test_hs_cache.c +++ b/src/test/test_hs_cache.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -93,8 +93,8 @@ helper_build_hs_desc(uint64_t revision_counter, uint32_t lifetime, /* Setup encrypted data section. */ desc->encrypted_data.create2_ntor = 1; - desc->encrypted_data.auth_types = smartlist_new(); - smartlist_add(desc->encrypted_data.auth_types, tor_strdup("ed25519")); + desc->encrypted_data.intro_auth_types = smartlist_new(); + smartlist_add(desc->encrypted_data.intro_auth_types, tor_strdup("ed25519")); desc->encrypted_data.intro_points = smartlist_new(); /* Add an intro point. */ smartlist_add(desc->encrypted_data.intro_points, @@ -333,7 +333,7 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) size_t body_used = 0; fetch_from_buf_http(TO_CONN(conn)->outbuf, &headers, MAX_HEADERS_SIZE, - &received_desc, &body_used, 10000, 0); + &received_desc, &body_used, HS_DESC_MAX_LEN, 0); tor_free(headers); } diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c index 02a71aa473..a1a1f14fb8 100644 --- a/src/test/test_hs_descriptor.c +++ b/src/test/test_hs_descriptor.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -15,6 +15,9 @@ #include "test.h" #include "torcert.h" +#include "test_helpers.h" +#include "log_test_helpers.h" + static hs_desc_intro_point_t * helper_build_intro_point(const ed25519_keypair_t *blinded_kp, time_t now, const char *addr, int legacy) @@ -105,9 +108,9 @@ helper_build_hs_desc(unsigned int no_ip, ed25519_public_key_t *signing_pubkey) /* Setup encrypted data section. */ desc->encrypted_data.create2_ntor = 1; - desc->encrypted_data.auth_types = smartlist_new(); + desc->encrypted_data.intro_auth_types = smartlist_new(); desc->encrypted_data.single_onion_service = 1; - smartlist_add(desc->encrypted_data.auth_types, tor_strdup("ed25519")); + smartlist_add(desc->encrypted_data.intro_auth_types, tor_strdup("ed25519")); desc->encrypted_data.intro_points = smartlist_new(); if (!no_ip) { /* Add four intro points. */ @@ -157,14 +160,17 @@ helper_compare_hs_desc(const hs_descriptor_t *desc1, desc2->encrypted_data.create2_ntor); /* Authentication type. */ - tt_int_op(!!desc1->encrypted_data.auth_types, ==, - !!desc2->encrypted_data.auth_types); - if (desc1->encrypted_data.auth_types && desc2->encrypted_data.auth_types) { - tt_int_op(smartlist_len(desc1->encrypted_data.auth_types), ==, - smartlist_len(desc2->encrypted_data.auth_types)); - for (int i = 0; i < smartlist_len(desc1->encrypted_data.auth_types); i++) { - tt_str_op(smartlist_get(desc1->encrypted_data.auth_types, i), OP_EQ, - smartlist_get(desc2->encrypted_data.auth_types, i)); + tt_int_op(!!desc1->encrypted_data.intro_auth_types, ==, + !!desc2->encrypted_data.intro_auth_types); + if (desc1->encrypted_data.intro_auth_types && + desc2->encrypted_data.intro_auth_types) { + tt_int_op(smartlist_len(desc1->encrypted_data.intro_auth_types), ==, + smartlist_len(desc2->encrypted_data.intro_auth_types)); + for (int i = 0; + i < smartlist_len(desc1->encrypted_data.intro_auth_types); + i++) { + tt_str_op(smartlist_get(desc1->encrypted_data.intro_auth_types, i),OP_EQ, + smartlist_get(desc2->encrypted_data.intro_auth_types, i)); } } @@ -311,13 +317,13 @@ test_descriptor_padding(void *arg) /* Example: if l = 129, the ceiled division gives 2 and then multiplied by 128 * to give 256. With l = 127, ceiled division gives 1 then times 128. */ #define PADDING_EXPECTED_LEN(l) \ - CEIL_DIV(l, HS_DESC_PLAINTEXT_PADDING_MULTIPLE) * \ - HS_DESC_PLAINTEXT_PADDING_MULTIPLE + CEIL_DIV(l, HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE) * \ + HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE (void) arg; { /* test #1: no padding */ - plaintext_len = HS_DESC_PLAINTEXT_PADDING_MULTIPLE; + plaintext_len = HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE; plaintext = tor_malloc(plaintext_len); padded_len = build_plaintext_padding(plaintext, plaintext_len, &padded_plaintext); @@ -333,7 +339,7 @@ test_descriptor_padding(void *arg) } { /* test #2: one byte padding? */ - plaintext_len = HS_DESC_PLAINTEXT_PADDING_MULTIPLE - 1; + plaintext_len = HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE - 1; plaintext = tor_malloc(plaintext_len); padded_plaintext = NULL; padded_len = build_plaintext_padding(plaintext, plaintext_len, @@ -350,7 +356,7 @@ test_descriptor_padding(void *arg) } { /* test #3: Lots more bytes of padding? */ - plaintext_len = HS_DESC_PLAINTEXT_PADDING_MULTIPLE + 1; + plaintext_len = HS_DESC_SUPERENC_PLAINTEXT_PAD_MULTIPLE + 1; plaintext = tor_malloc(plaintext_len); padded_plaintext = NULL; padded_len = build_plaintext_padding(plaintext, plaintext_len, @@ -587,19 +593,11 @@ test_encrypted_data_len(void *arg) /* No length, error. */ ret = encrypted_data_length_is_valid(0); tt_int_op(ret, OP_EQ, 0); - /* Not a multiple of our encryption algorithm (thus no padding). It's - * suppose to be aligned on HS_DESC_PLAINTEXT_PADDING_MULTIPLE. */ - value = HS_DESC_PLAINTEXT_PADDING_MULTIPLE * 10 - 1; - ret = encrypted_data_length_is_valid(value); - tt_int_op(ret, OP_EQ, 0); /* Valid value. */ - value = HS_DESC_PADDED_PLAINTEXT_MAX_LEN + HS_DESC_ENCRYPTED_SALT_LEN + - DIGEST256_LEN; + value = HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN + 1; ret = encrypted_data_length_is_valid(value); tt_int_op(ret, OP_EQ, 1); - /* XXX: Test maximum possible size. */ - done: ; } @@ -1006,6 +1004,103 @@ test_desc_signature(void *arg) tor_free(data); } +/* bad desc auth type */ +static const char bad_superencrypted_text1[] = "desc-auth-type scoobysnack\n" + "desc-auth-ephemeral-key A/O8DVtnUheb3r1JqoB8uJB7wxXL1XJX3eny4yB+eFA=\n" + "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n" + "encrypted\n" + "-----BEGIN MESSAGE-----\n" + "YmVpbmcgb24gbW91bnRhaW5zLCB0aGlua2luZyBhYm91dCBjb21wdXRlcnMsIGlzIG5vdC" + "BiYWQgYXQgYWxs\n" + "-----END MESSAGE-----\n"; + +/* bad ephemeral key */ +static const char bad_superencrypted_text2[] = "desc-auth-type x25519\n" + "desc-auth-ephemeral-key differentalphabet\n" + "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n" + "encrypted\n" + "-----BEGIN MESSAGE-----\n" + "YmVpbmcgb24gbW91bnRhaW5zLCB0aGlua2luZyBhYm91dCBjb21wdXRlcnMsIGlzIG5vdC" + "BiYWQgYXQgYWxs\n" + "-----END MESSAGE-----\n"; + +/* bad encrypted msg */ +static const char bad_superencrypted_text3[] = "desc-auth-type x25519\n" + "desc-auth-ephemeral-key A/O8DVtnUheb3r1JqoB8uJB7wxXL1XJX3eny4yB+eFA=\n" + "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n" + "encrypted\n" + "-----BEGIN MESSAGE-----\n" + "SO SMALL NOT GOOD\n" + "-----END MESSAGE-----\n"; + +static const char correct_superencrypted_text[] = "desc-auth-type x25519\n" + "desc-auth-ephemeral-key A/O8DVtnUheb3r1JqoB8uJB7wxXL1XJX3eny4yB+eFA=\n" + "auth-client oiNrQB8WwKo S5D02W7vKgiWIMygrBl8RQ FB//SfOBmLEx1kViEWWL1g\n" + "auth-client Od09Qu636Qo /PKLzqewAdS/+0+vZC+MvQ dpw4NFo13zDnuPz45rxrOg\n" + "auth-client JRr840iGYN0 8s8cxYqF7Lx23+NducC4Qg zAafl4wPLURkuEjJreZq1g\n" + "encrypted\n" + "-----BEGIN MESSAGE-----\n" + "YmVpbmcgb24gbW91bnRhaW5zLCB0aGlua2luZyBhYm91dCBjb21wdXRlcnMsIGlzIG5vdC" + "BiYWQgYXQgYWxs\n" + "-----END MESSAGE-----\n"; + +static const char correct_encrypted_plaintext[] = "being on mountains, " + "thinking about computers, is not bad at all"; + +static void +test_parse_hs_desc_superencrypted(void *arg) +{ + (void) arg; + size_t retval; + uint8_t *encrypted_out = NULL; + + { + setup_full_capture_of_logs(LOG_WARN); + retval = decode_superencrypted(bad_superencrypted_text1, + strlen(bad_superencrypted_text1), + &encrypted_out); + tt_u64_op(retval, ==, 0); + tt_assert(!encrypted_out); + expect_log_msg_containing("Unrecognized desc auth type"); + teardown_capture_of_logs(); + } + + { + setup_full_capture_of_logs(LOG_WARN); + retval = decode_superencrypted(bad_superencrypted_text2, + strlen(bad_superencrypted_text2), + &encrypted_out); + tt_u64_op(retval, ==, 0); + tt_assert(!encrypted_out); + expect_log_msg_containing("Bogus desc auth key in HS desc"); + teardown_capture_of_logs(); + } + + { + setup_full_capture_of_logs(LOG_WARN); + retval = decode_superencrypted(bad_superencrypted_text3, + strlen(bad_superencrypted_text3), + &encrypted_out); + tt_u64_op(retval, ==, 0); + tt_assert(!encrypted_out); + expect_log_msg_containing("Length of descriptor\'s encrypted data " + "is too small."); + teardown_capture_of_logs(); + } + + /* Now finally the good one */ + retval = decode_superencrypted(correct_superencrypted_text, + strlen(correct_superencrypted_text), + &encrypted_out); + + tt_u64_op(retval, ==, strlen(correct_encrypted_plaintext)); + tt_mem_op(encrypted_out, OP_EQ, correct_encrypted_plaintext, + strlen(correct_encrypted_plaintext)); + + done: + tor_free(encrypted_out); +} + struct testcase_t hs_descriptor[] = { /* Encoding tests. */ { "cert_encoding", test_cert_encoding, TT_FORK, @@ -1035,6 +1130,9 @@ struct testcase_t hs_descriptor[] = { { "desc_signature", test_desc_signature, TT_FORK, NULL, NULL }, + { "parse_hs_desc_superencrypted", test_parse_hs_desc_superencrypted, + TT_FORK, NULL, NULL }, + END_OF_TESTCASES }; diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index ea12aeb2da..ef5cb46567 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -69,10 +69,10 @@ helper_create_intro_circuit(void) return circ; } -static hs_cell_introduce1_t * +static trn_cell_introduce1_t * helper_create_introduce1_cell(void) { - hs_cell_introduce1_t *cell = NULL; + trn_cell_introduce1_t *cell = NULL; ed25519_keypair_t auth_key_kp; /* Generate the auth_key of the cell. */ @@ -80,39 +80,39 @@ helper_create_introduce1_cell(void) goto err; } - cell = hs_cell_introduce1_new(); + cell = trn_cell_introduce1_new(); tt_assert(cell); /* Set the auth key. */ { size_t auth_key_len = sizeof(auth_key_kp.pubkey); - hs_cell_introduce1_set_auth_key_type(cell, + trn_cell_introduce1_set_auth_key_type(cell, HS_INTRO_AUTH_KEY_TYPE_ED25519); - hs_cell_introduce1_set_auth_key_len(cell, auth_key_len); - hs_cell_introduce1_setlen_auth_key(cell, auth_key_len); - uint8_t *auth_key_ptr = hs_cell_introduce1_getarray_auth_key(cell); + trn_cell_introduce1_set_auth_key_len(cell, auth_key_len); + trn_cell_introduce1_setlen_auth_key(cell, auth_key_len); + uint8_t *auth_key_ptr = trn_cell_introduce1_getarray_auth_key(cell); memcpy(auth_key_ptr, auth_key_kp.pubkey.pubkey, auth_key_len); } /* Set the cell extentions to none. */ { - cell_extension_t *ext = cell_extension_new(); - cell_extension_set_num(ext, 0); - hs_cell_introduce1_set_extensions(cell, ext); + trn_cell_extension_t *ext = trn_cell_extension_new(); + trn_cell_extension_set_num(ext, 0); + trn_cell_introduce1_set_extensions(cell, ext); } /* Set the encrypted section to some data. */ { size_t enc_len = 128; - hs_cell_introduce1_setlen_encrypted(cell, enc_len); - uint8_t *enc_ptr = hs_cell_introduce1_getarray_encrypted(cell); + trn_cell_introduce1_setlen_encrypted(cell, enc_len); + uint8_t *enc_ptr = trn_cell_introduce1_getarray_encrypted(cell); memset(enc_ptr, 'a', enc_len); } return cell; err: done: - hs_cell_introduce1_free(cell); + trn_cell_introduce1_free(cell); return NULL; } @@ -122,7 +122,7 @@ static void test_establish_intro_wrong_purpose(void *arg) { int retval; - hs_cell_establish_intro_t *establish_intro_cell = NULL; + trn_cell_establish_intro_t *establish_intro_cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL);; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; @@ -154,7 +154,7 @@ test_establish_intro_wrong_purpose(void *arg) tt_int_op(retval, ==, -1); done: - hs_cell_establish_intro_free(establish_intro_cell); + trn_cell_establish_intro_free(establish_intro_cell); circuit_free(TO_CIRCUIT(intro_circ)); } @@ -198,7 +198,7 @@ static void test_establish_intro_wrong_keytype2(void *arg) { int retval; - hs_cell_establish_intro_t *establish_intro_cell = NULL; + trn_cell_establish_intro_t *establish_intro_cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL);; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; @@ -230,7 +230,7 @@ test_establish_intro_wrong_keytype2(void *arg) tt_int_op(retval, ==, -1); done: - hs_cell_establish_intro_free(establish_intro_cell); + trn_cell_establish_intro_free(establish_intro_cell); circuit_free(TO_CIRCUIT(intro_circ)); } @@ -239,7 +239,7 @@ static void test_establish_intro_wrong_mac(void *arg) { int retval; - hs_cell_establish_intro_t *establish_intro_cell = NULL; + trn_cell_establish_intro_t *establish_intro_cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL);; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; @@ -258,7 +258,7 @@ test_establish_intro_wrong_mac(void *arg) tt_assert(establish_intro_cell); /* Mangle one byte of the MAC. */ uint8_t *handshake_ptr = - hs_cell_establish_intro_getarray_handshake_mac(establish_intro_cell); + trn_cell_establish_intro_getarray_handshake_mac(establish_intro_cell); handshake_ptr[TRUNNEL_SHA3_256_LEN - 1]++; /* We need to resign the payload with that change. */ { @@ -269,7 +269,7 @@ test_establish_intro_wrong_mac(void *arg) retval = ed25519_keypair_generate(&key_struct, 0); tt_int_op(retval, OP_EQ, 0); uint8_t *auth_key_ptr = - hs_cell_establish_intro_getarray_auth_key(establish_intro_cell); + trn_cell_establish_intro_getarray_auth_key(establish_intro_cell); memcpy(auth_key_ptr, key_struct.pubkey.pubkey, ED25519_PUBKEY_LEN); /* Encode payload so we can sign it. */ cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), @@ -284,7 +284,7 @@ test_establish_intro_wrong_mac(void *arg) tt_int_op(retval, OP_EQ, 0); /* And write the signature to the cell */ uint8_t *sig_ptr = - hs_cell_establish_intro_getarray_sig(establish_intro_cell); + trn_cell_establish_intro_getarray_sig(establish_intro_cell); memcpy(sig_ptr, sig.sig, establish_intro_cell->sig_len); /* Re-encode with the new signature. */ cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), @@ -299,7 +299,7 @@ test_establish_intro_wrong_mac(void *arg) tt_int_op(retval, ==, -1); done: - hs_cell_establish_intro_free(establish_intro_cell); + trn_cell_establish_intro_free(establish_intro_cell); circuit_free(TO_CIRCUIT(intro_circ)); } @@ -309,7 +309,7 @@ static void test_establish_intro_wrong_auth_key_len(void *arg) { int retval; - hs_cell_establish_intro_t *establish_intro_cell = NULL; + trn_cell_establish_intro_t *establish_intro_cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL);; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; @@ -328,9 +328,9 @@ test_establish_intro_wrong_auth_key_len(void *arg) sizeof(circuit_key_material)); tt_assert(establish_intro_cell); /* Mangle the auth key length. */ - hs_cell_establish_intro_set_auth_key_len(establish_intro_cell, + trn_cell_establish_intro_set_auth_key_len(establish_intro_cell, bad_auth_key_len); - hs_cell_establish_intro_setlen_auth_key(establish_intro_cell, + trn_cell_establish_intro_setlen_auth_key(establish_intro_cell, bad_auth_key_len); cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), establish_intro_cell); @@ -344,7 +344,7 @@ test_establish_intro_wrong_auth_key_len(void *arg) tt_int_op(retval, ==, -1); done: - hs_cell_establish_intro_free(establish_intro_cell); + trn_cell_establish_intro_free(establish_intro_cell); circuit_free(TO_CIRCUIT(intro_circ)); } @@ -354,7 +354,7 @@ static void test_establish_intro_wrong_sig_len(void *arg) { int retval; - hs_cell_establish_intro_t *establish_intro_cell = NULL; + trn_cell_establish_intro_t *establish_intro_cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL);; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; @@ -373,8 +373,8 @@ test_establish_intro_wrong_sig_len(void *arg) sizeof(circuit_key_material)); tt_assert(establish_intro_cell); /* Mangle the signature length. */ - hs_cell_establish_intro_set_sig_len(establish_intro_cell, bad_sig_len); - hs_cell_establish_intro_setlen_sig(establish_intro_cell, bad_sig_len); + trn_cell_establish_intro_set_sig_len(establish_intro_cell, bad_sig_len); + trn_cell_establish_intro_setlen_sig(establish_intro_cell, bad_sig_len); cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), establish_intro_cell); tt_int_op(cell_len, >, 0); @@ -387,7 +387,7 @@ test_establish_intro_wrong_sig_len(void *arg) tt_int_op(retval, ==, -1); done: - hs_cell_establish_intro_free(establish_intro_cell); + trn_cell_establish_intro_free(establish_intro_cell); circuit_free(TO_CIRCUIT(intro_circ)); } @@ -397,7 +397,7 @@ static void test_establish_intro_wrong_sig(void *arg) { int retval; - hs_cell_establish_intro_t *establish_intro_cell = NULL; + trn_cell_establish_intro_t *establish_intro_cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL);; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; @@ -429,17 +429,17 @@ test_establish_intro_wrong_sig(void *arg) tt_int_op(retval, ==, -1); done: - hs_cell_establish_intro_free(establish_intro_cell); + trn_cell_establish_intro_free(establish_intro_cell); circuit_free(TO_CIRCUIT(intro_circ)); } /* Helper function: Send a well-formed v3 ESTABLISH_INTRO cell to * <b>intro_circ</b>. Return the cell. */ -static hs_cell_establish_intro_t * +static trn_cell_establish_intro_t * helper_establish_intro_v3(or_circuit_t *intro_circ) { int retval; - hs_cell_establish_intro_t *establish_intro_cell = NULL; + trn_cell_establish_intro_t *establish_intro_cell = NULL; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; uint8_t circuit_key_material[DIGEST_LEN] = {0}; @@ -489,6 +489,7 @@ helper_establish_intro_v2(or_circuit_t *intro_circ) /* Use old circuit_key_material why not */ cell_len = encode_establish_intro_cell_legacy((char*)cell_body, + sizeof(cell_body), key1, (char *) circuit_key_material); tt_int_op(cell_len, >, 0); @@ -511,7 +512,7 @@ test_intro_point_registration(void *arg) hs_circuitmap_ht *the_hs_circuitmap = NULL; or_circuit_t *intro_circ = NULL; - hs_cell_establish_intro_t *establish_intro_cell = NULL; + trn_cell_establish_intro_t *establish_intro_cell = NULL; ed25519_public_key_t auth_key; crypto_pk_t *legacy_auth_key = NULL; @@ -531,7 +532,7 @@ test_intro_point_registration(void *arg) tt_assert(the_hs_circuitmap); tt_int_op(0, ==, HT_SIZE(the_hs_circuitmap)); /* Do a circuitmap query in any case */ - returned_intro_circ = hs_circuitmap_get_intro_circ_v3(&auth_key); + returned_intro_circ =hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); tt_ptr_op(returned_intro_circ, ==, NULL); } @@ -547,7 +548,8 @@ test_intro_point_registration(void *arg) tt_int_op(1, ==, HT_SIZE(the_hs_circuitmap)); get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO, establish_intro_cell); - returned_intro_circ = hs_circuitmap_get_intro_circ_v3(&auth_key); + returned_intro_circ = + hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); tt_ptr_op(intro_circ, ==, returned_intro_circ); } @@ -568,7 +570,8 @@ test_intro_point_registration(void *arg) /* Check that the new element is our legacy intro circuit. */ retval = crypto_pk_get_digest(legacy_auth_key, key_digest); tt_int_op(retval, ==, 0); - returned_intro_circ= hs_circuitmap_get_intro_circ_v2((uint8_t*)key_digest); + returned_intro_circ = + hs_circuitmap_get_intro_circ_v2_relay_side((uint8_t*)key_digest); tt_ptr_op(legacy_intro_circ, ==, returned_intro_circ); } @@ -579,7 +582,7 @@ test_intro_point_registration(void *arg) crypto_pk_free(legacy_auth_key); circuit_free(TO_CIRCUIT(intro_circ)); circuit_free(TO_CIRCUIT(legacy_intro_circ)); - hs_cell_establish_intro_free(establish_intro_cell); + trn_cell_establish_intro_free(establish_intro_cell); { /* Test circuitmap free_all function. */ the_hs_circuitmap = get_hs_circuitmap(); @@ -673,7 +676,7 @@ static void test_introduce1_validation(void *arg) { int ret; - hs_cell_introduce1_t *cell = NULL; + trn_cell_introduce1_t *cell = NULL; (void) arg; @@ -713,25 +716,25 @@ test_introduce1_validation(void *arg) ret = validate_introduce1_parsed_cell(cell); tt_int_op(ret, OP_EQ, 0); /* Set an invalid size of the auth key buffer. */ - hs_cell_introduce1_setlen_auth_key(cell, 3); + trn_cell_introduce1_setlen_auth_key(cell, 3); ret = validate_introduce1_parsed_cell(cell); tt_int_op(ret, OP_EQ, -1); /* Reset auth key buffer and make sure it works. */ - hs_cell_introduce1_setlen_auth_key(cell, sizeof(ed25519_public_key_t)); + trn_cell_introduce1_setlen_auth_key(cell, sizeof(ed25519_public_key_t)); ret = validate_introduce1_parsed_cell(cell); tt_int_op(ret, OP_EQ, 0); /* Empty encrypted section. */ - hs_cell_introduce1_setlen_encrypted(cell, 0); + trn_cell_introduce1_setlen_encrypted(cell, 0); ret = validate_introduce1_parsed_cell(cell); tt_int_op(ret, OP_EQ, -1); /* Reset it to some non zero bytes and validate. */ - hs_cell_introduce1_setlen_encrypted(cell, 1); + trn_cell_introduce1_setlen_encrypted(cell, 1); ret = validate_introduce1_parsed_cell(cell); tt_int_op(ret, OP_EQ, 0); done: - hs_cell_introduce1_free(cell); + trn_cell_introduce1_free(cell); } static void @@ -739,7 +742,7 @@ test_received_introduce1_handling(void *arg) { int ret; uint8_t *request = NULL, buf[128]; - hs_cell_introduce1_t *cell = NULL; + trn_cell_introduce1_t *cell = NULL; or_circuit_t *circ = NULL; (void) arg; @@ -773,12 +776,12 @@ test_received_introduce1_handling(void *arg) /* Valid case. */ { cell = helper_create_introduce1_cell(); - ssize_t request_len = hs_cell_introduce1_encoded_len(cell); - tt_size_op(request_len, OP_GT, 0); + ssize_t request_len = trn_cell_introduce1_encoded_len(cell); + tt_int_op((int)request_len, OP_GT, 0); request = tor_malloc_zero(request_len); ssize_t encoded_len = - hs_cell_introduce1_encode(request, request_len, cell); - tt_size_op(encoded_len, OP_GT, 0); + trn_cell_introduce1_encode(request, request_len, cell); + tt_int_op((int)encoded_len, OP_GT, 0); circ = helper_create_intro_circuit(); or_circuit_t *service_circ = helper_create_intro_circuit(); @@ -787,9 +790,9 @@ test_received_introduce1_handling(void *arg) /* Register the circuit in the map for the auth key of the cell. */ ed25519_public_key_t auth_key; const uint8_t *cell_auth_key = - hs_cell_introduce1_getconstarray_auth_key(cell); + trn_cell_introduce1_getconstarray_auth_key(cell); memcpy(auth_key.pubkey, cell_auth_key, ED25519_PUBKEY_LEN); - hs_circuitmap_register_intro_circ_v3(service_circ, &auth_key); + hs_circuitmap_register_intro_circ_v3_relay_side(service_circ, &auth_key); ret = hs_intro_received_introduce1(circ, request, request_len); circuit_free(TO_CIRCUIT(circ)); circuit_free(TO_CIRCUIT(service_circ)); @@ -799,17 +802,17 @@ test_received_introduce1_handling(void *arg) /* Valid legacy cell. */ { tor_free(request); - hs_cell_introduce1_free(cell); + trn_cell_introduce1_free(cell); cell = helper_create_introduce1_cell(); - uint8_t *legacy_key_id = hs_cell_introduce1_getarray_legacy_key_id(cell); + uint8_t *legacy_key_id = trn_cell_introduce1_getarray_legacy_key_id(cell); memset(legacy_key_id, 'a', DIGEST_LEN); /* Add an arbitrary amount of data for the payload of a v2 cell. */ - size_t request_len = hs_cell_introduce1_encoded_len(cell) + 256; + size_t request_len = trn_cell_introduce1_encoded_len(cell) + 256; tt_size_op(request_len, OP_GT, 0); request = tor_malloc_zero(request_len + 256); ssize_t encoded_len = - hs_cell_introduce1_encode(request, request_len, cell); - tt_size_op(encoded_len, OP_GT, 0); + trn_cell_introduce1_encode(request, request_len, cell); + tt_int_op((int)encoded_len, OP_GT, 0); circ = helper_create_intro_circuit(); or_circuit_t *service_circ = helper_create_intro_circuit(); @@ -818,7 +821,7 @@ test_received_introduce1_handling(void *arg) /* Register the circuit in the map for the auth key of the cell. */ uint8_t token[REND_TOKEN_LEN]; memcpy(token, legacy_key_id, sizeof(token)); - hs_circuitmap_register_intro_circ_v2(service_circ, token); + hs_circuitmap_register_intro_circ_v2_relay_side(service_circ, token); ret = hs_intro_received_introduce1(circ, request, request_len); circuit_free(TO_CIRCUIT(circ)); circuit_free(TO_CIRCUIT(service_circ)); @@ -826,7 +829,7 @@ test_received_introduce1_handling(void *arg) } done: - hs_cell_introduce1_free(cell); + trn_cell_introduce1_free(cell); tor_free(request); hs_circuitmap_free_all(); UNMOCK(relay_send_command_from_edge_); diff --git a/src/test/test_hs_ntor.sh b/src/test/test_hs_ntor.sh new file mode 100755 index 0000000000..8a0003d44a --- /dev/null +++ b/src/test/test_hs_ntor.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Validate Tor's ntor implementation. + +exitcode=0 + +# Run the python integration test sand return the exitcode of the python +# script. The python script might ask the testsuite to skip it if not all +# python dependencies are covered. +"${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/hs_ntor_ref.py" || exitcode=$? + +exit ${exitcode} diff --git a/src/test/test_hs_ntor_cl.c b/src/test/test_hs_ntor_cl.c new file mode 100644 index 0000000000..ed1eda58ea --- /dev/null +++ b/src/test/test_hs_ntor_cl.c @@ -0,0 +1,255 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** This is a wrapper over the little-t-tor HS ntor functions. The wrapper is + * used by src/test/hs_ntor_ref.py to conduct the HS ntor integration + * tests. + * + * The logic of this wrapper is basically copied from src/test/test_ntor_cl.c + */ + +#include "orconfig.h" +#include <stdio.h> +#include <stdlib.h> + +#define ONION_NTOR_PRIVATE +#include "or.h" +#include "util.h" +#include "compat.h" +#include "crypto.h" +#include "crypto_curve25519.h" +#include "hs_ntor.h" +#include "onion_ntor.h" + +#define N_ARGS(n) STMT_BEGIN { \ + if (argc < (n)) { \ + fprintf(stderr, "%s needs %d arguments.\n",argv[1],n); \ + return 1; \ + } \ + } STMT_END +#define BASE16(idx, var, n) STMT_BEGIN { \ + const char *s = argv[(idx)]; \ + if (base16_decode((char*)var, n, s, strlen(s)) < (int)n ) { \ + fprintf(stderr, "couldn't decode argument %d (%s)\n",idx,s); \ + return 1; \ + } \ + } STMT_END +#define INT(idx, var) STMT_BEGIN { \ + var = atoi(argv[(idx)]); \ + if (var <= 0) { \ + fprintf(stderr, "bad integer argument %d (%s)\n",idx,argv[(idx)]); \ + } \ + } STMT_END + +/** The first part of the HS ntor protocol. The client-side computes all + necessary key material and sends the appropriate message to the service. */ +static int +client1(int argc, char **argv) +{ + int retval; + + /* Inputs */ + curve25519_public_key_t intro_enc_pubkey; + ed25519_public_key_t intro_auth_pubkey; + curve25519_keypair_t client_ephemeral_enc_keypair; + uint8_t subcredential[DIGEST256_LEN]; + + /* Output */ + hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys; + + char buf[256]; + + N_ARGS(6); + BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN); + BASE16(3, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN); + BASE16(4, client_ephemeral_enc_keypair.seckey.secret_key, + CURVE25519_SECKEY_LEN); + BASE16(5, subcredential, DIGEST256_LEN); + + /* Generate keypair */ + curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey, + &client_ephemeral_enc_keypair.seckey); + + retval = hs_ntor_client_get_introduce1_keys(&intro_auth_pubkey, + &intro_enc_pubkey, + &client_ephemeral_enc_keypair, + subcredential, + &hs_ntor_intro_cell_keys); + if (retval < 0) { + goto done; + } + + /* Send ENC_KEY */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_intro_cell_keys.enc_key, + sizeof(hs_ntor_intro_cell_keys.enc_key)); + printf("%s\n", buf); + /* Send MAC_KEY */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_intro_cell_keys.mac_key, + sizeof(hs_ntor_intro_cell_keys.mac_key)); + printf("%s\n", buf); + + done: + return retval; +} + +/** The second part of the HS ntor protocol. The service-side computes all + necessary key material and sends the appropriate message to the client */ +static int +server1(int argc, char **argv) +{ + int retval; + + /* Inputs */ + curve25519_keypair_t intro_enc_keypair; + ed25519_public_key_t intro_auth_pubkey; + curve25519_public_key_t client_ephemeral_enc_pubkey; + uint8_t subcredential[DIGEST256_LEN]; + + /* Output */ + hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys; + hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys; + curve25519_keypair_t service_ephemeral_rend_keypair; + + char buf[256]; + + N_ARGS(6); + BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN); + BASE16(3, intro_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN); + BASE16(4, client_ephemeral_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN); + BASE16(5, subcredential, DIGEST256_LEN); + + /* Generate keypair */ + curve25519_public_key_generate(&intro_enc_keypair.pubkey, + &intro_enc_keypair.seckey); + curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0); + + /* Get INTRODUCE1 keys */ + retval = hs_ntor_service_get_introduce1_keys(&intro_auth_pubkey, + &intro_enc_keypair, + &client_ephemeral_enc_pubkey, + subcredential, + &hs_ntor_intro_cell_keys); + if (retval < 0) { + goto done; + } + + /* Get RENDEZVOUS1 keys */ + retval = hs_ntor_service_get_rendezvous1_keys(&intro_auth_pubkey, + &intro_enc_keypair, + &service_ephemeral_rend_keypair, + &client_ephemeral_enc_pubkey, + &hs_ntor_rend_cell_keys); + if (retval < 0) { + goto done; + } + + /* Send ENC_KEY */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_intro_cell_keys.enc_key, + sizeof(hs_ntor_intro_cell_keys.enc_key)); + printf("%s\n", buf); + /* Send MAC_KEY */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_intro_cell_keys.mac_key, + sizeof(hs_ntor_intro_cell_keys.mac_key)); + printf("%s\n", buf); + /* Send AUTH_MAC */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac, + sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac)); + printf("%s\n", buf); + /* Send NTOR_KEY_SEED */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_rend_cell_keys.ntor_key_seed, + sizeof(hs_ntor_rend_cell_keys.ntor_key_seed)); + printf("%s\n", buf); + /* Send service ephemeral pubkey (Y) */ + base16_encode(buf, sizeof(buf), + (const char*)service_ephemeral_rend_keypair.pubkey.public_key, + sizeof(service_ephemeral_rend_keypair.pubkey.public_key)); + printf("%s\n", buf); + + done: + return retval; +} + +/** The final step of the ntor protocol, the client computes and returns the + * rendezvous key material. */ +static int +client2(int argc, char **argv) +{ + int retval; + + /* Inputs */ + curve25519_public_key_t intro_enc_pubkey; + ed25519_public_key_t intro_auth_pubkey; + curve25519_keypair_t client_ephemeral_enc_keypair; + curve25519_public_key_t service_ephemeral_rend_pubkey; + uint8_t subcredential[DIGEST256_LEN]; + + /* Output */ + hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys; + + char buf[256]; + + N_ARGS(7); + BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN); + BASE16(3, client_ephemeral_enc_keypair.seckey.secret_key, + CURVE25519_SECKEY_LEN); + BASE16(4, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN); + BASE16(5, service_ephemeral_rend_pubkey.public_key, CURVE25519_PUBKEY_LEN); + BASE16(6, subcredential, DIGEST256_LEN); + + /* Generate keypair */ + curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey, + &client_ephemeral_enc_keypair.seckey); + + /* Get RENDEZVOUS1 keys */ + retval = hs_ntor_client_get_rendezvous1_keys(&intro_auth_pubkey, + &client_ephemeral_enc_keypair, + &intro_enc_pubkey, + &service_ephemeral_rend_pubkey, + &hs_ntor_rend_cell_keys); + if (retval < 0) { + goto done; + } + + /* Send AUTH_MAC */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac, + sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac)); + printf("%s\n", buf); + /* Send NTOR_KEY_SEED */ + base16_encode(buf, sizeof(buf), + (const char*)hs_ntor_rend_cell_keys.ntor_key_seed, + sizeof(hs_ntor_rend_cell_keys.ntor_key_seed)); + printf("%s\n", buf); + + done: + return 1; +} + +/** Perform a different part of the protocol depdning on the argv used. */ +int +main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "I need arguments. Read source for more info.\n"); + return 1; + } + + curve25519_init(); + if (!strcmp(argv[1], "client1")) { + return client1(argc, argv); + } else if (!strcmp(argv[1], "server1")) { + return server1(argc, argv); + } else if (!strcmp(argv[1], "client2")) { + return client2(argc, argv); + } else { + fprintf(stderr, "What's a %s?\n", argv[1]); + return 1; + } +} + diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 039d727cea..fcfb3b992d 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -6,6 +6,7 @@ * \brief Test hidden service functionality. */ +#define HS_COMMON_PRIVATE #define HS_SERVICE_PRIVATE #define HS_INTROPOINT_PRIVATE @@ -14,9 +15,12 @@ #include "crypto.h" #include "hs/cell_establish_intro.h" +#include "hs_common.h" #include "hs_service.h" #include "hs_intropoint.h" +#include "hs_ntor.h" + /** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we * parse it from the receiver side. */ static void @@ -26,8 +30,8 @@ test_gen_establish_intro_cell(void *arg) ssize_t retval; uint8_t circuit_key_material[DIGEST_LEN] = {0}; uint8_t buf[RELAY_PAYLOAD_SIZE]; - hs_cell_establish_intro_t *cell_out = NULL; - hs_cell_establish_intro_t *cell_in = NULL; + trn_cell_establish_intro_t *cell_out = NULL; + trn_cell_establish_intro_t *cell_in = NULL; crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); @@ -44,7 +48,7 @@ test_gen_establish_intro_cell(void *arg) /* Parse it as the receiver */ { - ssize_t parse_result = hs_cell_establish_intro_parse(&cell_in, + ssize_t parse_result = trn_cell_establish_intro_parse(&cell_in, buf, sizeof(buf)); tt_int_op(parse_result, >=, 0); @@ -55,8 +59,8 @@ test_gen_establish_intro_cell(void *arg) } done: - hs_cell_establish_intro_free(cell_out); - hs_cell_establish_intro_free(cell_in); + trn_cell_establish_intro_free(cell_out); + trn_cell_establish_intro_free(cell_in); } /* Mocked ed25519_sign_prefixed() function that always fails :) */ @@ -78,7 +82,7 @@ static void test_gen_establish_intro_cell_bad(void *arg) { (void) arg; - hs_cell_establish_intro_t *cell = NULL; + trn_cell_establish_intro_t *cell = NULL; uint8_t circuit_key_material[DIGEST_LEN] = {0}; MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed); @@ -96,15 +100,150 @@ test_gen_establish_intro_cell_bad(void *arg) tt_assert(!cell); done: - hs_cell_establish_intro_free(cell); + trn_cell_establish_intro_free(cell); UNMOCK(ed25519_sign_prefixed); } +/** Test the HS ntor handshake. Simulate the sending of an encrypted INTRODUCE1 + * cell, and verify the proper derivation of decryption keys on the other end. + * Then simulate the sending of an authenticated RENDEZVOUS1 cell and verify + * the proper verification on the other end. */ +static void +test_hs_ntor(void *arg) +{ + int retval; + + uint8_t subcredential[DIGEST256_LEN]; + + ed25519_keypair_t service_intro_auth_keypair; + curve25519_keypair_t service_intro_enc_keypair; + curve25519_keypair_t service_ephemeral_rend_keypair; + + curve25519_keypair_t client_ephemeral_enc_keypair; + + hs_ntor_intro_cell_keys_t client_hs_ntor_intro_cell_keys; + hs_ntor_intro_cell_keys_t service_hs_ntor_intro_cell_keys; + + hs_ntor_rend_cell_keys_t service_hs_ntor_rend_cell_keys; + hs_ntor_rend_cell_keys_t client_hs_ntor_rend_cell_keys; + + (void) arg; + + /* Generate fake data for this unittest */ + { + /* Generate fake subcredential */ + memset(subcredential, 'Z', DIGEST256_LEN); + + /* service */ + curve25519_keypair_generate(&service_intro_enc_keypair, 0); + ed25519_keypair_generate(&service_intro_auth_keypair, 0); + curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0); + /* client */ + curve25519_keypair_generate(&client_ephemeral_enc_keypair, 0); + } + + /* Client: Simulate the sending of an encrypted INTRODUCE1 cell */ + retval = + hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey, + &service_intro_enc_keypair.pubkey, + &client_ephemeral_enc_keypair, + subcredential, + &client_hs_ntor_intro_cell_keys); + tt_int_op(retval, ==, 0); + + /* Service: Simulate the decryption of the received INTRODUCE1 */ + retval = + hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey, + &service_intro_enc_keypair, + &client_ephemeral_enc_keypair.pubkey, + subcredential, + &service_hs_ntor_intro_cell_keys); + tt_int_op(retval, ==, 0); + + /* Test that the INTRODUCE1 encryption/mac keys match! */ + tt_mem_op(client_hs_ntor_intro_cell_keys.enc_key, OP_EQ, + service_hs_ntor_intro_cell_keys.enc_key, + CIPHER256_KEY_LEN); + tt_mem_op(client_hs_ntor_intro_cell_keys.mac_key, OP_EQ, + service_hs_ntor_intro_cell_keys.mac_key, + DIGEST256_LEN); + + /* Service: Simulate creation of RENDEZVOUS1 key material. */ + retval = + hs_ntor_service_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey, + &service_intro_enc_keypair, + &service_ephemeral_rend_keypair, + &client_ephemeral_enc_keypair.pubkey, + &service_hs_ntor_rend_cell_keys); + tt_int_op(retval, ==, 0); + + /* Client: Simulate the verification of a received RENDEZVOUS1 cell */ + retval = + hs_ntor_client_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey, + &client_ephemeral_enc_keypair, + &service_intro_enc_keypair.pubkey, + &service_ephemeral_rend_keypair.pubkey, + &client_hs_ntor_rend_cell_keys); + tt_int_op(retval, ==, 0); + + /* Test that the RENDEZVOUS1 key material match! */ + tt_mem_op(client_hs_ntor_rend_cell_keys.rend_cell_auth_mac, OP_EQ, + service_hs_ntor_rend_cell_keys.rend_cell_auth_mac, + DIGEST256_LEN); + tt_mem_op(client_hs_ntor_rend_cell_keys.ntor_key_seed, OP_EQ, + service_hs_ntor_rend_cell_keys.ntor_key_seed, + DIGEST256_LEN); + + done: + ; +} + +/** Test that our HS time period calculation functions work properly */ +static void +test_time_period(void *arg) +{ + (void) arg; + uint64_t tn; + int retval; + time_t fake_time; + + /* Let's do the example in prop224 section [TIME-PERIODS] */ + retval = parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC", + &fake_time); + tt_int_op(retval, ==, 0); + + /* Check that the time period number is right */ + tn = get_time_period_num(fake_time); + tt_u64_op(tn, ==, 16903); + + /* Increase current time to 11:59:59 UTC and check that the time period + number is still the same */ + fake_time += 3599; + tn = get_time_period_num(fake_time); + tt_u64_op(tn, ==, 16903); + + /* Now take time to 12:00:00 UTC and check that the time period rotated */ + fake_time += 1; + tn = get_time_period_num(fake_time); + tt_u64_op(tn, ==, 16904); + + /* Now also check our hs_get_next_time_period_num() function */ + tn = hs_get_next_time_period_num(fake_time); + tt_u64_op(tn, ==, 16905); + + done: + ; +} + struct testcase_t hs_service_tests[] = { { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK, NULL, NULL }, { "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK, NULL, NULL }, + { "hs_ntor", test_hs_ntor, TT_FORK, + NULL, NULL }, + { "time_period", test_time_period, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c index 810b03c93d..cfb8d83b1d 100644 --- a/src/test/test_introduce.c +++ b/src/test/test_introduce.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Tor Project, Inc. */ +/* Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_keypin.c b/src/test/test_keypin.c index 95657349c6..d2ec8e9ca7 100644 --- a/src/test/test_keypin.c +++ b/src/test/test_keypin.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index 421f3aaedf..217088ee37 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -10,13 +10,6 @@ #include "compat.h" -/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in - * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ -DISABLE_GCC_WARNING(redundant-decls) -#include <openssl/x509.h> -#include <openssl/ssl.h> -ENABLE_GCC_WARNING(redundant-decls) - #include "or.h" #include "config.h" #include "connection.h" @@ -785,19 +778,14 @@ CERTS_FAIL(expired_rsa_id, /* both */ certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 1); const tor_x509_cert_t *idc; tor_tls_get_my_certs(1, NULL, &idc); - X509 *newc = X509_dup(idc->cert); + tor_x509_cert_t *newc; time_t new_end = time(NULL) - 86400 * 10; - X509_time_adj(X509_get_notAfter(newc), 0, &new_end); - EVP_PKEY *pk = crypto_pk_get_evp_pkey_(d->key2, 1); - tt_assert(X509_sign(newc, pk, EVP_sha1())); - int len = i2d_X509(newc, NULL); - certs_cell_cert_setlen_body(cert, len); - uint8_t *body = certs_cell_cert_getarray_body(cert); - int len2 = i2d_X509(newc, &body); - tt_int_op(len, ==, len2); + newc = tor_x509_cert_replace_expiration(idc, new_end, d->key2); + certs_cell_cert_setlen_body(cert, newc->encoded_len); + memcpy(certs_cell_cert_getarray_body(cert), + newc->encoded, newc->encoded_len); REENCODE(); - X509_free(newc); - EVP_PKEY_free(pk); + tor_x509_cert_free(newc); }) CERTS_FAIL(expired_ed_id, /* ed25519 */ { diff --git a/src/test/test_logging.c b/src/test/test_logging.c index 15471e46d0..94b3e4ea68 100644 --- a/src/test/test_logging.c +++ b/src/test/test_logging.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index 2ae605b8db..c78fda3b69 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -14,12 +14,6 @@ #include "test.h" -DISABLE_GCC_WARNING(redundant-decls) -#include <openssl/rsa.h> -#include <openssl/bn.h> -#include <openssl/pem.h> -ENABLE_GCC_WARNING(redundant-decls) - #ifdef _WIN32 /* For mkdir() */ #include <direct.h> diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c index d58f8a7fca..256354415c 100644 --- a/src/test/test_nodelist.c +++ b/src/test/test_nodelist.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c index a560e5fc5e..d0eea85d6f 100644 --- a/src/test/test_ntor_cl.c +++ b/src/test/test_ntor_cl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Tor Project, Inc. */ +/* Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_oom.c b/src/test/test_oom.c index 0f97972032..f03a504d1d 100644 --- a/src/test/test_oom.c +++ b/src/test/test_oom.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Unit tests for OOM handling logic */ diff --git a/src/test/test_oos.c b/src/test/test_oos.c index db06625116..9fd6bce5ae 100644 --- a/src/test/test_oos.c +++ b/src/test/test_oos.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Unit tests for OOS handler */ diff --git a/src/test/test_options.c b/src/test/test_options.c index d5782e9ec0..29f6665f23 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONFIG_PRIVATE @@ -105,27 +105,13 @@ clear_log_messages(void) "EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212:80 7EA6 EAD6 FD83" \ " 083C 538F 4403 8BBF A077 587D D755\n" -static void -test_options_validate_impl(const char *configuration, - const char *expect_errmsg, - int expect_log_severity, - const char *expect_log) +static int +test_options_checkmsgs(const char *configuration, + const char *expect_errmsg, + int expect_log_severity, + const char *expect_log, + char *msg) { - or_options_t *opt=NULL; - or_options_t *dflt; - config_line_t *cl=NULL; - char *msg=NULL; - int r; - - setup_options(opt, dflt); - - r = config_get_lines(configuration, &cl, 1); - tt_int_op(r, OP_EQ, 0); - - r = config_assign(&options_format, opt, cl, 0, &msg); - tt_int_op(r, OP_EQ, 0); - - r = options_validate(NULL, opt, dflt, 0, &msg); if (expect_errmsg && !msg) { TT_DIE(("Expected error message <%s> from <%s>, but got none.", expect_errmsg, configuration)); @@ -136,8 +122,6 @@ test_options_validate_impl(const char *configuration, TT_DIE(("Expected no error message from <%s> but got <%s>.", configuration, msg)); } - tt_int_op((r == 0), OP_EQ, (msg == NULL)); - if (expect_log) { int found = 0; if (messages) { @@ -157,6 +141,56 @@ test_options_validate_impl(const char *configuration, configuration, msg)); } } + return 0; + + done: + return -1; +} + +/* Which phases of config parsing/validation to check for messages/logs */ +enum { PH_GETLINES, PH_ASSIGN, PH_VALIDATE }; + +static void +test_options_validate_impl(const char *configuration, + const char *expect_errmsg, + int expect_log_severity, + const char *expect_log, + int phase) +{ + or_options_t *opt=NULL; + or_options_t *dflt; + config_line_t *cl=NULL; + char *msg=NULL; + int r; + + setup_options(opt, dflt); + + r = config_get_lines(configuration, &cl, 1); + if (phase == PH_GETLINES) { + if (test_options_checkmsgs(configuration, expect_errmsg, + expect_log_severity, + expect_log, msg)) + goto done; + } + tt_int_op((r == 0), OP_EQ, (msg == NULL)); + + r = config_assign(&options_format, opt, cl, 0, &msg); + if (phase == PH_ASSIGN) { + if (test_options_checkmsgs(configuration, expect_errmsg, + expect_log_severity, + expect_log, msg)) + goto done; + } + tt_int_op((r == 0), OP_EQ, (msg == NULL)); + + r = options_validate(NULL, opt, dflt, 0, &msg); + if (phase == PH_VALIDATE) { + if (test_options_checkmsgs(configuration, expect_errmsg, + expect_log_severity, + expect_log, msg)) + goto done; + } + tt_int_op((r == 0), OP_EQ, (msg == NULL)); done: escaped(NULL); @@ -168,14 +202,14 @@ test_options_validate_impl(const char *configuration, clear_log_messages(); } -#define WANT_ERR(config, msg) \ - test_options_validate_impl((config), (msg), 0, NULL) -#define WANT_LOG(config, severity, msg) \ - test_options_validate_impl((config), NULL, (severity), (msg)) -#define WANT_ERR_LOG(config, msg, severity, logmsg) \ - test_options_validate_impl((config), (msg), (severity), (logmsg)) -#define OK(config) \ - test_options_validate_impl((config), NULL, 0, NULL) +#define WANT_ERR(config, msg, ph) \ + test_options_validate_impl((config), (msg), 0, NULL, (ph)) +#define WANT_LOG(config, severity, msg, ph) \ + test_options_validate_impl((config), NULL, (severity), (msg), (ph)) +#define WANT_ERR_LOG(config, msg, severity, logmsg, ph) \ + test_options_validate_impl((config), (msg), (severity), (logmsg), (ph)) +#define OK(config, ph) \ + test_options_validate_impl((config), NULL, 0, NULL, (ph)) static void test_options_validate(void *arg) @@ -184,21 +218,32 @@ test_options_validate(void *arg) setup_log_callback(); sandbox_disable_getaddrinfo_cache(); - WANT_ERR("ExtORPort 500000", "Invalid ExtORPort"); + WANT_ERR("ExtORPort 500000", "Invalid ExtORPort", PH_VALIDATE); WANT_ERR_LOG("ServerTransportOptions trebuchet", "ServerTransportOptions did not parse", - LOG_WARN, "Too few arguments"); - OK("ServerTransportOptions trebuchet sling=snappy"); - OK("ServerTransportOptions trebuchet sling="); + LOG_WARN, "Too few arguments", PH_VALIDATE); + OK("ServerTransportOptions trebuchet sling=snappy", PH_VALIDATE); + OK("ServerTransportOptions trebuchet sling=", PH_VALIDATE); WANT_ERR_LOG("ServerTransportOptions trebuchet slingsnappy", "ServerTransportOptions did not parse", - LOG_WARN, "\"slingsnappy\" is not a k=v"); + LOG_WARN, "\"slingsnappy\" is not a k=v", PH_VALIDATE); WANT_ERR("DirPort 8080\nDirCache 0", - "DirPort configured but DirCache disabled."); + "DirPort configured but DirCache disabled.", PH_VALIDATE); WANT_ERR("BridgeRelay 1\nDirCache 0", - "We're a bridge but DirCache is disabled."); + "We're a bridge but DirCache is disabled.", PH_VALIDATE); + + WANT_ERR_LOG("HeartbeatPeriod 21 snarks", + "Interval 'HeartbeatPeriod 21 snarks' is malformed or" + " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + PH_ASSIGN); + WANT_ERR_LOG("LogTimeGranularity 21 snarks", + "Msec interval 'LogTimeGranularity 21 snarks' is malformed or" + " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + PH_ASSIGN); + OK("HeartbeatPeriod 1 hour", PH_VALIDATE); + OK("LogTimeGranularity 100 milliseconds", PH_VALIDATE); close_temp_logs(); clear_log_messages(); diff --git a/src/test/test_policy.c b/src/test/test_policy.c index e86d0f0274..1b2fac4325 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Tor Project, Inc. */ +/* Copyright (c) 2013-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_procmon.c b/src/test/test_procmon.c index 9e63fc006d..5c52af8693 100644 --- a/src/test/test_procmon.c +++ b/src/test/test_procmon.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define PROCMON_PRIVATE diff --git a/src/test/test_protover.c b/src/test/test_protover.c index f00955d1b4..5626816024 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define PROTOVER_PRIVATE diff --git a/src/test/test_pt.c b/src/test/test_pt.c index f93019f1c4..79b03171bc 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -284,13 +284,13 @@ test_pt_get_extrainfo_string(void *arg) } #ifdef _WIN32 -#define STDIN_HANDLE HANDLE +#define STDIN_HANDLE HANDLE* #else -#define STDIN_HANDLE FILE +#define STDIN_HANDLE int #endif static smartlist_t * -tor_get_lines_from_handle_replacement(STDIN_HANDLE *handle, +tor_get_lines_from_handle_replacement(STDIN_HANDLE handle, enum stream_status *stream_status_out) { static int times_called = 0; diff --git a/src/test/test_pubsub.c b/src/test/test_pubsub.c index 547d6c6b32..2f047d9f2c 100644 --- a/src/test/test_pubsub.c +++ b/src/test/test_pubsub.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/test/test_relay.c b/src/test/test_relay.c index 4713c79ea5..238d4c5baf 100644 --- a/src/test/test_relay.c +++ b/src/test/test_relay.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c index fb6748965a..eea1f5dc80 100644 --- a/src/test/test_relaycell.c +++ b/src/test/test_relaycell.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Unit tests for handling different kinds of relay cell */ diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c index 0d53c78817..feba8f664e 100644 --- a/src/test/test_rendcache.c +++ b/src/test/test_rendcache.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -11,7 +11,6 @@ #include "routerlist.h" #include "config.h" #include "hs_common.h" -#include <openssl/rsa.h> #include "rend_test_helpers.h" #include "log_test_helpers.h" diff --git a/src/test/test_replay.c b/src/test/test_replay.c index e882bc6164..80e7203716 100644 --- a/src/test/test_replay.c +++ b/src/test/test_replay.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Tor Project, Inc. */ +/* Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define REPLAYCACHE_PRIVATE diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 64692d28a0..263923f50f 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index 78f1cf16b7..0b4b6c5c44 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c index 05ea8e86e8..4c536b0905 100644 --- a/src/test/test_scheduler.c +++ b/src/test/test_scheduler.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_slow.c b/src/test/test_slow.c index 7c9f0b1cc2..e640702499 100644 --- a/src/test/test_slow.c +++ b/src/test/test_slow.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/test/test_socks.c b/src/test/test_socks.c index 62ff12fe15..bb1be11f2b 100644 --- a/src/test/test_socks.c +++ b/src/test/test_socks.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_storagedir.c b/src/test/test_storagedir.c new file mode 100644 index 0000000000..19e5de4ea3 --- /dev/null +++ b/src/test/test_storagedir.c @@ -0,0 +1,375 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "storagedir.h" +#include "test.h" + +#ifdef HAVE_UTIME_H +#include <utime.h> +#endif + +static void +test_storagedir_empty(void *arg) +{ + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + (void)arg; + + tt_int_op(FN_NOENT, OP_EQ, file_status(dirname)); + + d = storage_dir_new(dirname, 10); + tt_assert(d); + + tt_int_op(FN_DIR, OP_EQ, file_status(dirname)); + + tt_int_op(0, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + storage_dir_free(d); + d = storage_dir_new(dirname, 10); + tt_assert(d); + + tt_int_op(FN_DIR, OP_EQ, file_status(dirname)); + + tt_int_op(0, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + done: + storage_dir_free(d); + tor_free(dirname); +} + +static void +test_storagedir_basic(void *arg) +{ + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + uint8_t *junk = NULL, *bytes = NULL; + const size_t junklen = 1024; + char *fname1 = NULL, *fname2 = NULL; + const char hello_str[] = "then what are we but cold, alone ... ?"; + tor_mmap_t *mapping = NULL; + (void)arg; + + junk = tor_malloc(junklen); + crypto_rand((void*)junk, junklen); + + d = storage_dir_new(dirname, 10); + tt_assert(d); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + int r; + r = storage_dir_save_string_to_file(d, hello_str, 1, &fname1); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(fname1, OP_NE, NULL); + tt_u64_op(strlen(hello_str), OP_EQ, storage_dir_get_usage(d)); + + r = storage_dir_save_bytes_to_file(d, junk, junklen, 1, &fname2); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(fname2, OP_NE, NULL); + + tt_str_op(fname1, OP_NE, fname2); + + tt_int_op(2, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(junklen + strlen(hello_str), OP_EQ, storage_dir_get_usage(d)); + tt_assert(smartlist_contains_string(storage_dir_list(d), fname1)); + tt_assert(smartlist_contains_string(storage_dir_list(d), fname2)); + + storage_dir_free(d); + d = storage_dir_new(dirname, 10); + tt_assert(d); + tt_int_op(2, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(junklen + strlen(hello_str), OP_EQ, storage_dir_get_usage(d)); + tt_assert(smartlist_contains_string(storage_dir_list(d), fname1)); + tt_assert(smartlist_contains_string(storage_dir_list(d), fname2)); + + size_t n; + bytes = storage_dir_read(d, fname2, 1, &n); + tt_assert(bytes); + tt_u64_op(n, OP_EQ, junklen); + tt_mem_op(bytes, OP_EQ, junk, junklen); + + mapping = storage_dir_map(d, fname1); + tt_assert(mapping); + tt_u64_op(mapping->size, OP_EQ, strlen(hello_str)); + tt_mem_op(mapping->data, OP_EQ, hello_str, strlen(hello_str)); + + done: + tor_free(dirname); + tor_free(junk); + tor_free(bytes); + tor_munmap_file(mapping); + storage_dir_free(d); + tor_free(fname1); + tor_free(fname2); +} + +static void +test_storagedir_deletion(void *arg) +{ + (void)arg; + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + char *fn1 = NULL, *fn2 = NULL; + char *bytes = NULL; + int r; + const char str1[] = "There are nine and sixty ways to disguise communiques"; + const char str2[] = "And rather more than one of them is right"; + + // Make sure the directory is there. */ + d = storage_dir_new(dirname, 10); + storage_dir_free(d); + d = NULL; + + tor_asprintf(&fn1, "%s/1007", dirname); + r = write_str_to_file(fn1, str1, 0); + tt_int_op(r, OP_EQ, 0); + + tor_asprintf(&fn2, "%s/1003.tmp", dirname); + r = write_str_to_file(fn2, str2, 0); + tt_int_op(r, OP_EQ, 0); + + // The tempfile should be deleted the next time we list the directory. + d = storage_dir_new(dirname, 10); + tt_int_op(1, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(strlen(str1), OP_EQ, storage_dir_get_usage(d)); + tt_int_op(FN_FILE, OP_EQ, file_status(fn1)); + tt_int_op(FN_NOENT, OP_EQ, file_status(fn2)); + + bytes = (char*) storage_dir_read(d, "1007", 1, NULL); + tt_str_op(bytes, OP_EQ, str1); + + // Should have no effect; file already gone. + storage_dir_remove_file(d, "1003.tmp"); + tt_int_op(1, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(strlen(str1), OP_EQ, storage_dir_get_usage(d)); + + // Actually remove a file. + storage_dir_remove_file(d, "1007"); + tt_int_op(FN_NOENT, OP_EQ, file_status(fn1)); + tt_int_op(0, OP_EQ, smartlist_len(storage_dir_list(d))); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + done: + tor_free(dirname); + tor_free(fn1); + tor_free(fn2); + storage_dir_free(d); + tor_free(bytes); +} + +static void +test_storagedir_full(void *arg) +{ + (void)arg; + + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + const char str[] = "enemies of the peephole"; + int r; + + d = storage_dir_new(dirname, 3); + tt_assert(d); + + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, 0); + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, 0); + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, 0); + + // These should fail! + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, -1); + r = storage_dir_save_string_to_file(d, str, 1, NULL); + tt_int_op(r, OP_EQ, -1); + + tt_u64_op(strlen(str) * 3, OP_EQ, storage_dir_get_usage(d)); + + done: + tor_free(dirname); + storage_dir_free(d); +} + +static void +test_storagedir_cleaning(void *arg) +{ + (void)arg; + + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + const char str[] = + "On a mountain halfway between Reno and Rome / " + "We have a machine in a plexiglass dome / " + "Which listens and looks into everyone's home." + " -- Dr. Seuss"; + char *fns[8]; + int r, i; + + memset(fns, 0, sizeof(fns)); + d = storage_dir_new(dirname, 10); + tt_assert(d); + + for (i = 0; i < 8; ++i) { + r = storage_dir_save_string_to_file(d, str+i*2, 1, &fns[i]); + tt_int_op(r, OP_EQ, 0); + } + + /* Now we're going to make sure all the files have distinct mtimes. */ + time_t now = time(NULL); + struct utimbuf ub; + ub.actime = now; + ub.modtime = now - 1000; + for (i = 0; i < 8; ++i) { + char *f = NULL; + tor_asprintf(&f, "%s/%s", dirname, fns[i]); + r = utime(f, &ub); + tor_free(f); + tt_int_op(r, OP_EQ, 0); + ub.modtime += 5; + } + + const uint64_t usage_orig = storage_dir_get_usage(d); + /* No changes needed if we are already under target. */ + storage_dir_shrink(d, 1024*1024, 0); + tt_u64_op(usage_orig, OP_EQ, storage_dir_get_usage(d)); + + /* Get rid of at least one byte. This will delete fns[0]. */ + storage_dir_shrink(d, usage_orig - 1, 0); + tt_u64_op(usage_orig, OP_GT, storage_dir_get_usage(d)); + tt_u64_op(usage_orig - strlen(str), OP_EQ, storage_dir_get_usage(d)); + + /* Get rid of at least two files. This will delete fns[1] and fns[2]. */ + storage_dir_shrink(d, 1024*1024, 2); + tt_u64_op(usage_orig - strlen(str)*3 + 6, OP_EQ, storage_dir_get_usage(d)); + + /* Get rid of everything. */ + storage_dir_remove_all(d); + tt_u64_op(0, OP_EQ, storage_dir_get_usage(d)); + + done: + tor_free(dirname); + storage_dir_free(d); + for (i = 0; i < 8; ++i) { + tor_free(fns[i]); + } +} + +static void +test_storagedir_save_labeled(void *arg) +{ + (void)arg; + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + uint8_t *inp = tor_malloc_zero(8192); + config_line_t *labels = NULL; + char *fname = NULL; + uint8_t *saved = NULL; + + d = storage_dir_new(dirname, 10); + tt_assert(d); + + crypto_rand((char *)inp, 8192); + + config_line_append(&labels, "Foo", "bar baz"); + config_line_append(&labels, "quux", "quuzXxz"); + const char expected[] = + "Foo bar baz\n" + "quux quuzXxz\n"; + + int r = storage_dir_save_labeled_to_file(d, labels, inp, 8192, &fname); + tt_int_op(r, OP_EQ, 0); + + size_t n; + saved = storage_dir_read(d, fname, 1, &n); + tt_assert(memchr(saved, '\0', n)); + tt_str_op((char*)saved, OP_EQ, expected); /* NUL guarantees strcmp works */ + tt_mem_op(saved+strlen(expected)+1, OP_EQ, inp, 8192); + + done: + storage_dir_free(d); + tor_free(dirname); + tor_free(inp); + tor_free(fname); + config_free_lines(labels); + tor_free(saved); +} + +static void +test_storagedir_read_labeled(void *arg) +{ + (void)arg; + char *dirname = tor_strdup(get_fname_rnd("store_dir")); + storage_dir_t *d = NULL; + uint8_t *inp = tor_malloc_zero(8192); + config_line_t *labels = NULL, *labels2 = NULL; + char *fname = NULL; + tor_mmap_t *map = NULL; + uint8_t *as_read = NULL; + + d = storage_dir_new(dirname, 10); + tt_assert(d); + + tor_snprintf((char*)inp, 8192, + "Hello world\n" + "This is a test\n" + "Yadda yadda.\n"); + size_t bodylen = 8192 - strlen((char*)inp) - 1; + crypto_rand((char *)inp+strlen((char*)inp)+1, bodylen); + + int r = storage_dir_save_bytes_to_file(d, inp, 8192, 1, &fname); + tt_int_op(r, OP_EQ, 0); + + /* Try mapping */ + const uint8_t *datap = NULL; + size_t sz = 0; + map = storage_dir_map_labeled(d, fname, &labels, &datap, &sz); + tt_assert(map); + tt_assert(datap); + tt_u64_op(sz, OP_EQ, bodylen); + tt_mem_op(datap, OP_EQ, inp+strlen((char*)inp)+1, bodylen); + tt_assert(labels); + tt_str_op(labels->key, OP_EQ, "Hello"); + tt_str_op(labels->value, OP_EQ, "world"); + tt_assert(labels->next); + tt_str_op(labels->next->key, OP_EQ, "This"); + tt_str_op(labels->next->value, OP_EQ, "is a test"); + tt_assert(labels->next->next); + tt_str_op(labels->next->next->key, OP_EQ, "Yadda"); + tt_str_op(labels->next->next->value, OP_EQ, "yadda."); + tt_assert(labels->next->next->next == NULL); + + /* Try reading this time. */ + sz = 0; + as_read = storage_dir_read_labeled(d, fname, &labels2, &sz); + tt_assert(as_read); + tt_u64_op(sz, OP_EQ, bodylen); + tt_mem_op(as_read, OP_EQ, inp+strlen((char*)inp)+1, bodylen); + tt_assert(config_lines_eq(labels, labels2)); + + done: + storage_dir_free(d); + tor_free(dirname); + tor_free(inp); + tor_free(fname); + config_free_lines(labels); + config_free_lines(labels2); + tor_munmap_file(map); + tor_free(as_read); +} + +#define ENT(name) \ + { #name, test_storagedir_ ## name, TT_FORK, NULL, NULL } + +struct testcase_t storagedir_tests[] = { + ENT(empty), + ENT(basic), + ENT(deletion), + ENT(full), + ENT(cleaning), + ENT(save_labeled), + ENT(read_labeled), + END_OF_TESTCASES +}; + diff --git a/src/test/test_switch_id.c b/src/test/test_switch_id.c index 322f5bdc7a..44e02e87b3 100644 --- a/src/test/test_switch_id.c +++ b/src/test/test_switch_id.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_threads.c b/src/test/test_threads.c index ebbc95c7ca..18a9407ff7 100644 --- a/src/test/test_threads.c +++ b/src/test/test_threads.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 4bfcea211d..7aa3051464 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -1,7 +1,8 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TORTLS_PRIVATE +#define TORTLS_OPENSSL_PRIVATE #define LOG_PRIVATE #include "orconfig.h" diff --git a/src/test/test_util.c b/src/test/test_util.c index e80201737a..b3f8ecdf51 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -2242,114 +2242,160 @@ test_util_pow2(void *arg) ; } -/** Run unit tests for compression functions */ static void -test_util_gzip(void *arg) +test_util_compress_impl(compress_method_t method) { - char *buf1=NULL, *buf2=NULL, *buf3=NULL, *cp1, *cp2; - const char *ccp2; + char *buf1=NULL, *buf2=NULL, *buf3=NULL; size_t len1, len2; - tor_zlib_state_t *state = NULL; - (void)arg; + tt_assert(tor_compress_supports_method(method)); + buf1 = tor_strdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ"); tt_assert(detect_compression_method(buf1, strlen(buf1)) == UNKNOWN_METHOD); - tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1, - GZIP_METHOD)); - tt_assert(buf2); - tt_assert(len1 < strlen(buf1)); - tt_assert(detect_compression_method(buf2, len1) == GZIP_METHOD); - - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1, - GZIP_METHOD, 1, LOG_INFO)); - tt_assert(buf3); - tt_int_op(strlen(buf1) + 1,OP_EQ, len2); - tt_str_op(buf1,OP_EQ, buf3); - - tor_free(buf2); - tor_free(buf3); - - tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1, - ZLIB_METHOD)); - tt_assert(buf2); - tt_assert(detect_compression_method(buf2, len1) == ZLIB_METHOD); + tt_assert(!tor_compress(&buf2, &len1, buf1, strlen(buf1)+1, method)); + tt_assert(buf2 != NULL); + if (method == NO_METHOD) { + // The identity transform doesn't actually compress, and it isn't + // detectable as "the identity transform." + tt_int_op(len1, OP_EQ, strlen(buf1)+1); + tt_int_op(detect_compression_method(buf2, len1), OP_EQ, UNKNOWN_METHOD); + } else { + tt_int_op(len1, OP_LT, strlen(buf1)); + tt_int_op(detect_compression_method(buf2, len1), OP_EQ, method); + } - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1, - ZLIB_METHOD, 1, LOG_INFO)); - tt_assert(buf3); - tt_int_op(strlen(buf1) + 1,OP_EQ, len2); - tt_str_op(buf1,OP_EQ, buf3); + tt_assert(!tor_uncompress(&buf3, &len2, buf2, len1, method, 1, LOG_INFO)); + tt_assert(buf3 != NULL); + tt_int_op(strlen(buf1) + 1, OP_EQ, len2); + tt_str_op(buf1, OP_EQ, buf3); + tt_int_op(buf3[len2], OP_EQ, 0); /* Check whether we can uncompress concatenated, compressed strings. */ tor_free(buf3); buf2 = tor_reallocarray(buf2, len1, 2); memcpy(buf2+len1, buf2, len1); - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1*2, - ZLIB_METHOD, 1, LOG_INFO)); - tt_int_op((strlen(buf1)+1)*2,OP_EQ, len2); - tt_mem_op(buf3,OP_EQ, + tt_assert(!tor_uncompress(&buf3, &len2, buf2, len1*2, method, 1, LOG_INFO)); + tt_int_op((strlen(buf1)+1)*2, OP_EQ, len2); + tt_mem_op(buf3, OP_EQ, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ\0" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ\0", (strlen(buf1)+1)*2); + tt_int_op(buf3[len2], OP_EQ, 0); + + /* Check whether we can uncompress partial strings */ tor_free(buf1); tor_free(buf2); tor_free(buf3); - /* Check whether we can uncompress partial strings. */ - buf1 = - tor_strdup("String with low redundancy that won't be compressed much."); - tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1, - ZLIB_METHOD)); - tt_assert(len1>16); - /* when we allow an incomplete string, we should succeed.*/ - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1-16, - ZLIB_METHOD, 0, LOG_INFO)); - tt_assert(len2 > 5); - buf3[len2]='\0'; - tt_assert(!strcmpstart(buf1, buf3)); - - /* when we demand a complete string, this must fail. */ + size_t b1len = 1<<10; + if (method == ZSTD_METHOD) { + // zstd needs a big input before it starts generating output that it + // can partially decompress. + b1len = 1<<18; + } + buf1 = tor_malloc(b1len); + crypto_rand(buf1, b1len); + tt_assert(!tor_compress(&buf2, &len1, buf1, b1len, method)); + tt_int_op(len1, OP_GT, 16); + /* when we allow an incomplete output we should succeed.*/ + tt_assert(!tor_uncompress(&buf3, &len2, buf2, len1-16, + method, 0, LOG_INFO)); + tt_int_op(len2, OP_GT, 5); + tt_int_op(len2, OP_LE, len1); + tt_assert(fast_memeq(buf1, buf3, len2)); + tt_int_op(buf3[len2], OP_EQ, 0); + + /* when we demand a complete output from a real compression method, this + * must fail. */ tor_free(buf3); - tt_assert(tor_gzip_uncompress(&buf3, &len2, buf2, len1-16, - ZLIB_METHOD, 1, LOG_INFO)); - tt_assert(!buf3); + if (method != NO_METHOD) { + tt_assert(tor_uncompress(&buf3, &len2, buf2, len1-16, + method, 1, LOG_INFO)); + tt_assert(buf3 == NULL); + } - /* Now, try streaming compression. */ + done: tor_free(buf1); tor_free(buf2); tor_free(buf3); - state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); +} + +static void +test_util_compress_stream_impl(compress_method_t method, + compression_level_t level) +{ + char *buf1=NULL, *buf2=NULL, *buf3=NULL, *cp1, *cp2; + const char *ccp2; + size_t len1, len2; + + tor_compress_state_t *state = NULL; + state = tor_compress_new(1, method, level); tt_assert(state); cp1 = buf1 = tor_malloc(1024); len1 = 1024; ccp2 = "ABCDEFGHIJABCDEFGHIJ"; len2 = 21; - tt_assert(tor_zlib_process(state, &cp1, &len1, &ccp2, &len2, 0) - == TOR_ZLIB_OK); - tt_int_op(0,OP_EQ, len2); /* Make sure we compressed it all. */ + tt_int_op(tor_compress_process(state, &cp1, &len1, &ccp2, &len2, 0), + OP_EQ, TOR_COMPRESS_OK); + tt_int_op(0, OP_EQ, len2); /* Make sure we compressed it all. */ tt_assert(cp1 > buf1); len2 = 0; cp2 = cp1; - tt_assert(tor_zlib_process(state, &cp1, &len1, &ccp2, &len2, 1) - == TOR_ZLIB_DONE); - tt_int_op(0,OP_EQ, len2); - tt_assert(cp1 > cp2); /* Make sure we really added something. */ + tt_int_op(tor_compress_process(state, &cp1, &len1, &ccp2, &len2, 1), + OP_EQ, TOR_COMPRESS_DONE); + tt_int_op(0, OP_EQ, len2); + if (method == NO_METHOD) { + tt_ptr_op(cp1, OP_EQ, cp2); + } else { + tt_assert(cp1 > cp2); /* Make sure we really added something. */ + } - tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf1, 1024-len1, - ZLIB_METHOD, 1, LOG_WARN)); + tt_assert(!tor_uncompress(&buf3, &len2, buf1, 1024-len1, + method, 1, LOG_WARN)); /* Make sure it compressed right. */ tt_str_op(buf3, OP_EQ, "ABCDEFGHIJABCDEFGHIJ"); - tt_int_op(21,OP_EQ, len2); + tt_int_op(21, OP_EQ, len2); done: if (state) - tor_zlib_free(state); + tor_compress_free(state); + tor_free(buf1); tor_free(buf2); tor_free(buf3); - tor_free(buf1); +} + +/** Run unit tests for compression functions */ +static void +test_util_compress(void *arg) +{ + const char *methodname = arg; + tt_assert(methodname); + + compress_method_t method = compression_method_get_by_name(methodname); + tt_int_op(method, OP_NE, UNKNOWN_METHOD); + + if (! tor_compress_supports_method(method)) { + tt_skip(); + } + + compression_level_t levels[] = { + BEST_COMPRESSION, + HIGH_COMPRESSION, + MEDIUM_COMPRESSION, + LOW_COMPRESSION + }; + + test_util_compress_impl(method); + + for (unsigned l = 0; l < ARRAY_LENGTH(levels); ++l) { + compression_level_t level = levels[l]; + test_util_compress_stream_impl(method, level); + } + done: + ; } static void @@ -2364,44 +2410,44 @@ test_util_gzip_compression_bomb(void *arg) char *one_mb = tor_malloc_zero(one_million); char *result = NULL; size_t result_len = 0; - tor_zlib_state_t *state = NULL; + tor_compress_state_t *state = NULL; /* Make sure we can't produce a compression bomb */ setup_full_capture_of_logs(LOG_WARN); - tt_int_op(-1, OP_EQ, tor_gzip_compress(&result, &result_len, - one_mb, one_million, - ZLIB_METHOD)); + tt_int_op(-1, OP_EQ, tor_compress(&result, &result_len, + one_mb, one_million, + ZLIB_METHOD)); expect_single_log_msg_containing( "We compressed something and got an insanely high " "compression factor; other Tors would think this " - "was a zlib bomb."); + "was a compression bomb."); teardown_capture_of_logs(); /* Here's a compression bomb that we made manually. */ const char compression_bomb[1039] = { 0x78, 0xDA, 0xED, 0xC1, 0x31, 0x01, 0x00, 0x00, 0x00, 0xC2, 0xA0, 0xF5, 0x4F, 0x6D, 0x08, 0x5F, 0xA0 /* .... */ }; - tt_int_op(-1, OP_EQ, tor_gzip_uncompress(&result, &result_len, - compression_bomb, 1039, - ZLIB_METHOD, 0, LOG_WARN)); + tt_int_op(-1, OP_EQ, tor_uncompress(&result, &result_len, + compression_bomb, 1039, + ZLIB_METHOD, 0, LOG_WARN)); /* Now try streaming that. */ - state = tor_zlib_new(0, ZLIB_METHOD, HIGH_COMPRESSION); - tor_zlib_output_t r; + state = tor_compress_new(0, ZLIB_METHOD, HIGH_COMPRESSION); + tor_compress_output_t r; const char *inp = compression_bomb; size_t inlen = 1039; do { char *outp = one_mb; size_t outleft = 4096; /* small on purpose */ - r = tor_zlib_process(state, &outp, &outleft, &inp, &inlen, 0); + r = tor_compress_process(state, &outp, &outleft, &inp, &inlen, 0); tt_int_op(inlen, OP_NE, 0); - } while (r == TOR_ZLIB_BUF_FULL); + } while (r == TOR_COMPRESS_BUFFER_FULL); - tt_int_op(r, OP_EQ, TOR_ZLIB_ERR); + tt_int_op(r, OP_EQ, TOR_COMPRESS_ERROR); done: tor_free(one_mb); - tor_zlib_free(state); + tor_compress_free(state); } /** Run unit tests for mmap() wrapper functionality. */ @@ -3340,6 +3386,13 @@ test_util_memarea(void *arg) void *malloced_ptr = NULL; int i; +#ifdef DISABLE_MEMORY_SENTINELS + /* If memory sentinels are disabled, this whole module is just an alias for + malloc(), which is free to lay out memory most any way it wants. */ + if (1) + tt_skip(); +#endif + (void)arg; tt_assert(area); @@ -3933,17 +3986,13 @@ test_util_exit_status(void *ptr) #endif #ifndef _WIN32 -/* Check that fgets with a non-blocking pipe returns partial lines and sets - * EAGAIN, returns full lines and sets no error, and returns NULL on EOF and - * sets no error */ static void -test_util_fgets_eagain(void *ptr) +test_util_string_from_pipe(void *ptr) { int test_pipe[2] = {-1, -1}; - int retval; + int retval = 0; + enum stream_status status = IO_STREAM_TERM; ssize_t retlen; - char *retptr; - FILE *test_stream = NULL; char buf[4] = { 0 }; (void)ptr; @@ -3954,91 +4003,115 @@ test_util_fgets_eagain(void *ptr) retval = pipe(test_pipe); tt_int_op(retval, OP_EQ, 0); - /* Set up the read-end to be non-blocking */ - retval = fcntl(test_pipe[0], F_SETFL, O_NONBLOCK); - tt_int_op(retval, OP_EQ, 0); + /* Send in a string. */ + retlen = write(test_pipe[1], "ABC", 3); + tt_int_op(retlen, OP_EQ, 3); - /* Open it as a stdio stream */ - test_stream = fdopen(test_pipe[0], "r"); - tt_ptr_op(test_stream, OP_NE, NULL); + 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 partial line */ - retlen = write(test_pipe[1], "A", 1); + /* 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); - retptr = fgets(buf, sizeof(buf), test_stream); - tt_int_op(errno, OP_EQ, EAGAIN); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "A"); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); + tt_int_op(errno, OP_EQ, 0); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, ""); errno = 0; - /* Send in the rest */ - retlen = write(test_pipe[1], "B\n", 2); - tt_int_op(retlen, OP_EQ, 2); - retptr = fgets(buf, sizeof(buf), test_stream); + /* Send in a string that contains a trailing newline. */ + retlen = write(test_pipe[1], "AB\n", 3); + tt_int_op(retlen, OP_EQ, 3); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); + tt_int_op(errno, OP_EQ, 0); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "AB"); + errno = 0; + + /* Send in a string that contains a newline only. */ + retlen = write(test_pipe[1], "\n", 1); + tt_int_op(retlen, OP_EQ, 1); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "B\n"); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, ""); errno = 0; - /* Send in a full line */ - retlen = write(test_pipe[1], "CD\n", 3); + /* Send in a string and check that we nul terminate return values. */ + retlen = write(test_pipe[1], "AAA", 3); tt_int_op(retlen, OP_EQ, 3); - retptr = fgets(buf, sizeof(buf), test_stream); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "CD\n"); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "AAA"); + tt_mem_op(buf, OP_EQ, "AAA\0", sizeof(buf)); errno = 0; - /* Send in a partial line */ - retlen = write(test_pipe[1], "E", 1); + retlen = write(test_pipe[1], "B", 1); tt_int_op(retlen, OP_EQ, 1); - retptr = fgets(buf, sizeof(buf), test_stream); - tt_int_op(errno, OP_EQ, EAGAIN); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "E"); + + memset(buf, '\xff', sizeof(buf)); + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); + tt_int_op(errno, OP_EQ, 0); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "B"); + tt_mem_op(buf, OP_EQ, "B\0\xff\xff", sizeof(buf)); errno = 0; - /* Send in the rest */ - retlen = write(test_pipe[1], "F\n", 2); - tt_int_op(retlen, OP_EQ, 2); - retptr = fgets(buf, sizeof(buf), test_stream); + /* Send in multiple lines. */ + retlen = write(test_pipe[1], "A\nB", 3); + tt_int_op(retlen, OP_EQ, 3); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "F\n"); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "A\nB"); errno = 0; - /* Send in a full line and close */ - retlen = write(test_pipe[1], "GH", 2); + /* Send in a line and close */ + retlen = write(test_pipe[1], "AB", 2); tt_int_op(retlen, OP_EQ, 2); retval = close(test_pipe[1]); tt_int_op(retval, OP_EQ, 0); test_pipe[1] = -1; - retptr = fgets(buf, sizeof(buf), test_stream); + + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, buf); - tt_str_op(buf, OP_EQ, "GH"); + tt_int_op(status, OP_EQ, IO_STREAM_OKAY); + tt_str_op(buf, OP_EQ, "AB"); errno = 0; /* Check for EOF */ - retptr = fgets(buf, sizeof(buf), test_stream); + status = get_string_from_pipe(test_pipe[0], buf, sizeof(buf)-1); tt_int_op(errno, OP_EQ, 0); - tt_ptr_op(retptr, OP_EQ, NULL); - retval = feof(test_stream); - tt_int_op(retval, OP_NE, 0); + tt_int_op(status, OP_EQ, IO_STREAM_CLOSED); errno = 0; - /* Check that buf is unchanged according to C99 and C11 */ - tt_str_op(buf, OP_EQ, "GH"); - done: - if (test_stream != NULL) - fclose(test_stream); if (test_pipe[0] != -1) close(test_pipe[0]); if (test_pipe[1] != -1) close(test_pipe[1]); } -#endif + +#endif // _WIN32 /** * Test for format_hex_number_sigsafe() @@ -5664,6 +5737,10 @@ test_util_htonll(void *arg) #define UTIL_TEST(name, flags) \ { #name, test_util_ ## name, flags, NULL, NULL } +#define COMPRESS(name, identifier) \ + { "compress/" #name, test_util_compress, 0, &passthrough_setup, \ + (char*)(identifier) } + #ifdef _WIN32 #define UTIL_TEST_NO_WIN(n, f) { #n, NULL, TT_SKIP, NULL, NULL } #define UTIL_TEST_WIN_ONLY(n, f) UTIL_TEST(n, (f)) @@ -5688,7 +5765,11 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(strmisc), UTIL_TEST(parse_integer, 0), UTIL_LEGACY(pow2), - UTIL_LEGACY(gzip), + COMPRESS(zlib, "deflate"), + COMPRESS(gzip, "gzip"), + COMPRESS(lzma, "x-tor-lzma"), + COMPRESS(zstd, "x-zstd"), + COMPRESS(none, "identity"), UTIL_TEST(gzip_compression_bomb, TT_FORK), UTIL_LEGACY(datadir), UTIL_LEGACY(memarea), @@ -5712,7 +5793,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(num_cpus, 0), UTIL_TEST_WIN_ONLY(load_win_lib, 0), UTIL_TEST_NO_WIN(exit_status, 0), - UTIL_TEST_NO_WIN(fgets_eagain, 0), + UTIL_TEST_NO_WIN(string_from_pipe, 0), UTIL_TEST(format_hex_number, 0), UTIL_TEST(format_dec_number, 0), UTIL_TEST(join_win_cmdline, 0), diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c index 21a6923c6d..ea0a86499f 100644 --- a/src/test/test_util_format.c +++ b/src/test/test_util_format.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -133,48 +133,54 @@ test_util_format_base64_encode(void *ignored) } static void -test_util_format_base64_decode_nopad(void *ignored) +test_util_format_base64_decode_oddsize(void *ignored) { (void)ignored; int res; int i; char *src; - uint8_t *dst, *real_dst; - uint8_t expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65}; + char *dst, real_dst[7]; + char expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65}; char real_src[] = "ZXhhbXBsZQ"; + char expected40[] = "testing40characteroddsizebase64encoding!"; + char src40[] = "dGVzdGluZzQwY2hhcmFjdGVyb2Rkc2l6ZWJhc2U2NGVuY29kaW5nIQ"; + char pad40[] = "dGVzdGluZzQwY2hhcmFjdGVyb2Rkc2l6ZWJhc2U2NGVuY29kaW5nIQ=="; src = tor_malloc_zero(256); dst = tor_malloc_zero(1000); - real_dst = tor_malloc_zero(10); for (i=0;i<256;i++) { src[i] = (char)i; } - res = base64_decode_nopad(dst, 1, src, SIZE_T_CEILING); - tt_int_op(res, OP_EQ, -1); - - res = base64_decode_nopad(dst, 1, src, 5); + res = base64_decode(dst, 1, src, 5); tt_int_op(res, OP_EQ, -1); const char *s = "SGVsbG8gd29ybGQ"; - res = base64_decode_nopad(dst, 1000, s, strlen(s)); + res = base64_decode(dst, 1000, s, strlen(s)); tt_int_op(res, OP_EQ, 11); tt_mem_op(dst, OP_EQ, "Hello world", 11); s = "T3BhIG11bmRv"; - res = base64_decode_nopad(dst, 9, s, strlen(s)); + res = base64_decode(dst, 9, s, strlen(s)); tt_int_op(res, OP_EQ, 9); tt_mem_op(dst, OP_EQ, "Opa mundo", 9); - res = base64_decode_nopad(real_dst, 10, real_src, 10); + res = base64_decode(real_dst, sizeof(real_dst), real_src, 10); tt_int_op(res, OP_EQ, 7); tt_mem_op(real_dst, OP_EQ, expected, 7); + res = base64_decode(dst, 40, src40, strlen(src40)); + tt_int_op(res, OP_EQ, 40); + tt_mem_op(dst, OP_EQ, expected40, 40); + + res = base64_decode(dst, 40, pad40, strlen(pad40)); + tt_int_op(res, OP_EQ, 40); + tt_mem_op(dst, OP_EQ, expected40, 40); + done: tor_free(src); tor_free(dst); - tor_free(real_dst); } static void @@ -196,13 +202,10 @@ test_util_format_base64_decode(void *ignored) src[i] = (char)i; } - res = base64_decode(dst, 1, src, SIZE_T_CEILING); + res = base64_decode(dst, 1, src, 100); tt_int_op(res, OP_EQ, -1); - res = base64_decode(dst, SIZE_T_CEILING+1, src, 10); - tt_int_op(res, OP_EQ, -1); - - res = base64_decode(dst, 1, real_src, SIZE_MAX/3+1); + res = base64_decode(dst, 1, real_src, 10); tt_int_op(res, OP_EQ, -1); const char *s = "T3BhIG11bmRv"; @@ -370,11 +373,39 @@ test_util_format_base32_decode(void *arg) tor_free(dst); } +static void +test_util_format_encoded_size(void *arg) +{ + (void)arg; + uint8_t inbuf[256]; + char outbuf[1024]; + unsigned i; + + crypto_rand((char *)inbuf, sizeof(inbuf)); + for (i = 0; i <= sizeof(inbuf); ++i) { + /* XXXX (Once the return values are consistent, check them too.) */ + + base32_encode(outbuf, sizeof(outbuf), (char *)inbuf, i); + /* The "+ 1" below is an API inconsistency. */ + tt_int_op(strlen(outbuf) + 1, OP_EQ, base32_encoded_size(i)); + + base64_encode(outbuf, sizeof(outbuf), (char *)inbuf, i, 0); + tt_int_op(strlen(outbuf), OP_EQ, base64_encode_size(i, 0)); + base64_encode(outbuf, sizeof(outbuf), (char *)inbuf, i, + BASE64_ENCODE_MULTILINE); + tt_int_op(strlen(outbuf), OP_EQ, + base64_encode_size(i, BASE64_ENCODE_MULTILINE)); + } + + done: + ; +} + struct testcase_t util_format_tests[] = { { "unaligned_accessors", test_util_format_unaligned_accessors, 0, NULL, NULL }, { "base64_encode", test_util_format_base64_encode, 0, NULL, NULL }, - { "base64_decode_nopad", test_util_format_base64_decode_nopad, 0, + { "base64_decode_oddsize", test_util_format_base64_decode_oddsize, 0, NULL, NULL }, { "base64_decode", test_util_format_base64_decode, 0, NULL, NULL }, { "base16_decode", test_util_format_base16_decode, 0, NULL, NULL }, @@ -382,6 +413,7 @@ struct testcase_t util_format_tests[] = { NULL, NULL }, { "base32_decode", test_util_format_base32_decode, 0, NULL, NULL }, + { "encoded_size", test_util_format_encoded_size, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_util_process.c b/src/test/test_util_process.c index 4e75b97f3d..70292f2287 100644 --- a/src/test/test_util_process.c +++ b/src/test/test_util_process.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Tor Project, Inc. */ +/* Copyright (c) 2010-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define UTIL_PROCESS_PRIVATE diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c index 1e7160598c..3e5d78948d 100644 --- a/src/test/test_util_slow.c +++ b/src/test/test_util_slow.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -242,7 +242,7 @@ test_util_spawn_background_partial_read_impl(int exit_early) #else /* Check that we didn't read the end of file last time */ tt_assert(!eof); - pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, NULL, &eof); #endif log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos); @@ -273,7 +273,7 @@ test_util_spawn_background_partial_read_impl(int exit_early) #else if (!eof) { /* We should have got all the data, but maybe not the EOF flag */ - pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, process_handle, &eof); tt_int_op(0,OP_EQ, pos); diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c index ccb8d0c8ca..10714457f7 100644 --- a/src/test/test_workqueue.c +++ b/src/test/test_workqueue.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/testing_common.c b/src/test/testing_common.c index caeae13a38..d3dc761c77 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ extern const char tor_git_revision[]; @@ -38,7 +38,6 @@ const char tor_git_revision[] = ""; #ifdef USE_DMALLOC #include <dmalloc.h> -#include <openssl/crypto.h> #include "main.h" #endif @@ -238,14 +237,15 @@ main(int c, const char **v) #ifdef USE_DMALLOC { - int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_); - tor_assert(r); + int r = crypto_use_tor_alloc_functions(); + tor_assert(r == 0); } #endif update_approx_time(time(NULL)); options = options_new(); tor_threads_init(); + tor_compress_init(); network_init(); diff --git a/src/test/testing_rsakeys.c b/src/test/testing_rsakeys.c index 134770bb0d..5dff233a69 100644 --- a/src/test/testing_rsakeys.c +++ b/src/test/testing_rsakeys.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" |