summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/Makefile.nmake3
-rw-r--r--src/test/bench.c45
-rwxr-xr-xsrc/test/bt_test.py9
-rw-r--r--src/test/include.am51
-rw-r--r--src/test/log_test_helpers.c113
-rw-r--r--src/test/log_test_helpers.h56
-rw-r--r--src/test/rend_test_helpers.c73
-rw-r--r--src/test/rend_test_helpers.h15
-rw-r--r--src/test/test-memwipe.c2
-rw-r--r--src/test/test.c30
-rw-r--r--src/test/test_accounting.c26
-rw-r--r--src/test/test_addr.c1
-rw-r--r--src/test/test_address.c211
-rw-r--r--src/test/test_bt_cl.c1
-rw-r--r--src/test/test_channeltls.c2
-rw-r--r--src/test/test_circuitmux.c4
-rw-r--r--src/test/test_compat_libevent.c224
-rw-r--r--src/test/test_config.c1409
-rw-r--r--src/test/test_connection.c761
-rw-r--r--src/test/test_crypto.c644
-rw-r--r--src/test/test_dir.c1211
-rw-r--r--src/test/test_dir_common.c425
-rw-r--r--src/test/test_dir_common.h52
-rw-r--r--src/test/test_dir_handle_get.c2552
-rw-r--r--src/test/test_dns.c506
-rw-r--r--src/test/test_entrynodes.c226
-rw-r--r--src/test/test_extorport.c5
-rw-r--r--src/test/test_nodelist.c41
-rw-r--r--src/test/test_ntor_cl.c1
-rw-r--r--src/test/test_options.c4260
-rw-r--r--src/test/test_policy.c1288
-rw-r--r--src/test/test_procmon.c58
-rw-r--r--src/test/test_rendcache.c1340
-rw-r--r--src/test/test_replay.c34
-rw-r--r--src/test/test_routerlist.c354
-rw-r--r--src/test/test_routerset.c102
-rw-r--r--src/test/test_status.c15
-rw-r--r--src/test/test_switch_id.c191
-rwxr-xr-xsrc/test/test_switch_id.sh25
-rw-r--r--src/test/test_threads.c2
-rw-r--r--src/test/test_tortls.c2834
-rw-r--r--src/test/test_util.c320
-rw-r--r--src/test/test_util_format.c302
-rw-r--r--src/test/test_util_process.c82
-rw-r--r--src/test/test_workqueue.c10
-rw-r--r--src/test/testing_common.c15
-rw-r--r--src/test/vote_descriptors.inc94
47 files changed, 19506 insertions, 519 deletions
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake
index 0435617683..0ba56d7036 100644
--- a/src/test/Makefile.nmake
+++ b/src/test/Makefile.nmake
@@ -14,7 +14,8 @@ LIBS = ..\..\..\build-alpha\lib\libevent.lib \
TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.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_checkdir.obj test_microdesc.obj test_pt.obj test_util.obj \
+ test_config.obj test_connection.obj \
test_cell_formats.obj test_relay.obj test_replay.obj \
test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj
diff --git a/src/test/bench.c b/src/test/bench.c
index 2a27377c80..1ef54042b2 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -443,6 +443,45 @@ bench_siphash(void)
}
static void
+bench_digest(void)
+{
+ char buf[8192];
+ char out[DIGEST512_LEN];
+ const int lens[] = { 1, 16, 32, 64, 128, 512, 1024, 2048, -1 };
+ const int N = 300000;
+ uint64_t start, end;
+ crypto_rand(buf, sizeof(buf));
+
+ for (int alg = 0; alg < N_DIGEST_ALGORITHMS; alg++) {
+ for (int i = 0; lens[i] > 0; ++i) {
+ reset_perftime();
+ start = perftime();
+ for (int j = 0; j < N; ++j) {
+ switch (alg) {
+ case DIGEST_SHA1:
+ crypto_digest(out, buf, lens[i]);
+ break;
+ case DIGEST_SHA256:
+ case DIGEST_SHA3_256:
+ crypto_digest256(out, buf, lens[i], alg);
+ break;
+ case DIGEST_SHA512:
+ case DIGEST_SHA3_512:
+ crypto_digest512(out, buf, lens[i], alg);
+ break;
+ default:
+ tor_assert(0);
+ }
+ }
+ end = perftime();
+ printf("%s(%d): %.2f ns per call\n",
+ crypto_digest_algorithm_get_name(alg),
+ lens[i], NANOCOUNT(start,end,N));
+ }
+ }
+}
+
+static void
bench_cell_ops(void)
{
const int iters = 1<<16;
@@ -589,6 +628,7 @@ typedef struct benchmark_t {
static struct benchmark_t benchmarks[] = {
ENT(dmap),
ENT(siphash),
+ ENT(digest),
ENT(aes),
ENT(onion_TAP),
ENT(onion_ntor),
@@ -643,7 +683,10 @@ main(int argc, const char **argv)
reset_perftime();
- crypto_seed_rng();
+ if (crypto_seed_rng() < 0) {
+ printf("Couldn't seed RNG; exiting.\n");
+ return 1;
+ }
crypto_init_siphash_key();
options = options_new();
init_logging(1);
diff --git a/src/test/bt_test.py b/src/test/bt_test.py
index e694361703..dab02d7699 100755
--- a/src/test/bt_test.py
+++ b/src/test/bt_test.py
@@ -15,6 +15,7 @@ OK
"""
+from __future__ import print_function
import sys
@@ -37,6 +38,8 @@ for I in range(len(LINES)):
if matches(LINES[I:], FUNCNAMES):
print("OK")
sys.exit(0)
-else:
- print("BAD")
- sys.exit(1)
+
+for l in LINES:
+ print("{}".format(l), end="")
+
+sys.exit(1)
diff --git a/src/test/include.am b/src/test/include.am
index 7a02a4e2f2..786fb77dd0 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -8,14 +8,16 @@ TESTS_ENVIRONMENT = \
export builddir="$(builddir)"; \
export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)";
-TESTSCRIPTS = src/test/test_zero_length_keys.sh
+TESTSCRIPTS = src/test/test_zero_length_keys.sh \
+ src/test/test_switch_id.sh
if USEPYTHON
TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh
endif
TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
- src/test/test_workqueue src/test/test_keygen.sh $(TESTSCRIPTS)
+ src/test/test_workqueue src/test/test_keygen.sh \
+ $(TESTSCRIPTS)
# These flavors are run using automake's test-driver and test-network.sh
TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min bridges+hs
@@ -37,7 +39,8 @@ noinst_PROGRAMS+= \
src/test/test-slow \
src/test/test-memwipe \
src/test/test-child \
- src/test/test_workqueue
+ src/test/test_workqueue \
+ src/test/test-switch-id
endif
src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
@@ -53,6 +56,8 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
# matters a lot there, and is quite hard to debug if you forget to do it.
src_test_test_SOURCES = \
+ src/test/log_test_helpers.c \
+ src/test/rend_test_helpers.c \
src/test/test.c \
src/test/test_accounting.c \
src/test/test_addr.c \
@@ -65,13 +70,17 @@ src_test_test_SOURCES = \
src/test/test_checkdir.c \
src/test/test_circuitlist.c \
src/test/test_circuitmux.c \
+ src/test/test_compat_libevent.c \
src/test/test_config.c \
+ src/test/test_connection.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_data.c \
src/test/test_dir.c \
+ src/test/test_dir_common.c \
+ src/test/test_dir_handle_get.c \
src/test/test_entryconn.c \
src/test/test_entrynodes.c \
src/test/test_guardfraction.c \
@@ -86,9 +95,11 @@ src_test_test_SOURCES = \
src/test/test_oom.c \
src/test/test_options.c \
src/test/test_policy.c \
+ src/test/test_procmon.c \
src/test/test_pt.c \
src/test/test_relay.c \
src/test/test_relaycell.c \
+ src/test/test_rendcache.c \
src/test/test_replay.c \
src/test/test_routerkeys.c \
src/test/test_routerlist.c \
@@ -97,9 +108,12 @@ src_test_test_SOURCES = \
src/test/test_socks.c \
src/test/test_status.c \
src/test/test_threads.c \
+ src/test/test_tortls.c \
src/test/test_util.c \
+ src/test/test_util_format.c \
+ src/test/test_util_process.c \
src/test/test_helpers.c \
- src/test/test_dns.c \
+ src/test/test_dns.c \
src/test/testing_common.c \
src/ext/tinytest.c
@@ -126,11 +140,21 @@ src_test_test_workqueue_SOURCES = \
src_test_test_workqueue_CPPFLAGS= $(src_test_AM_CPPFLAGS)
src_test_test_workqueue_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_test_test_switch_id_SOURCES = \
+ src/test/test_switch_id.c
+src_test_test_switch_id_CPPFLAGS= $(src_test_AM_CPPFLAGS)
+src_test_test_switch_id_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_test_test_switch_id_LDFLAGS = @TOR_LDFLAGS_zlib@
+src_test_test_switch_id_LDADD = \
+ src/common/libor-testing.a \
+ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@
+
src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
- src/common/libor-crypto-testing.a $(LIBDONNA) src/common/libor.a \
- src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
+ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
+ src/common/libor.a src/common/libor-event-testing.a \
+ src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
@TOR_SYSTEMD_LIBS@
@@ -148,7 +172,7 @@ src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS)
src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \
- src/common/libor-crypto.a $(LIBDONNA) \
+ src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event.a src/trunnel/libor-trunnel.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
@@ -158,26 +182,30 @@ src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_test_workqueue_LDADD = src/or/libtor-testing.a \
src/common/libor-testing.a \
- src/common/libor-crypto-testing.a $(LIBDONNA) \
+ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
noinst_HEADERS+= \
src/test/fakechans.h \
+ src/test/log_test_helpers.h \
+ src/test/rend_test_helpers.h \
src/test/test.h \
src/test/test_helpers.h \
+ src/test/test_dir_common.h \
src/test/test_descriptors.inc \
src/test/example_extrainfo.inc \
src/test/failing_routerdescs.inc \
src/test/ed25519_vectors.inc \
- src/test/test_descriptors.inc
+ src/test/test_descriptors.inc \
+ src/test/vote_descriptors.inc
noinst_PROGRAMS+= src/test/test-ntor-cl
src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c
src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
- src/common/libor-crypto.a $(LIBDONNA) \
+ 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_ntor_cl_AM_CPPFLAGS = \
@@ -199,4 +227,5 @@ EXTRA_DIST += \
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-network.sh
+ src/test/test-network.sh \
+ src/test/test_switch_id.sh
diff --git a/src/test/log_test_helpers.c b/src/test/log_test_helpers.c
new file mode 100644
index 0000000000..bcf73a8437
--- /dev/null
+++ b/src/test/log_test_helpers.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+#define LOG_PRIVATE
+#include "torlog.h"
+#include "log_test_helpers.h"
+
+static smartlist_t *saved_logs = NULL;
+
+int
+setup_capture_of_logs(int new_level)
+{
+ int previous_log = log_global_min_severity_;
+ log_global_min_severity_ = new_level;
+ mock_clean_saved_logs();
+ MOCK(logv, mock_saving_logv);
+ return previous_log;
+}
+
+void
+teardown_capture_of_logs(int prev)
+{
+ UNMOCK(logv);
+ log_global_min_severity_ = prev;
+ mock_clean_saved_logs();
+}
+
+void
+mock_clean_saved_logs(void)
+{
+ if (!saved_logs)
+ return;
+ SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
+ { tor_free(m->generated_msg); tor_free(m); });
+ smartlist_free(saved_logs);
+ saved_logs = NULL;
+}
+
+const smartlist_t *
+mock_saved_logs(void)
+{
+ return saved_logs;
+}
+
+int
+mock_saved_log_has_message(const char *msg)
+{
+ int has_msg = 0;
+ if (saved_logs) {
+ SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
+ {
+ if (msg && m->generated_msg &&
+ !strcmp(msg, m->generated_msg)) {
+ has_msg = 1;
+ }
+ });
+ }
+
+ return has_msg;
+}
+
+/* Do the saved logs have any messages with severity? */
+int
+mock_saved_log_has_severity(int severity)
+{
+ int has_sev = 0;
+ if (saved_logs) {
+ SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
+ {
+ if (m->severity == severity) {
+ has_sev = 1;
+ }
+ });
+ }
+
+ return has_sev;
+}
+
+/* Do the saved logs have any messages? */
+int
+mock_saved_log_has_entry(void)
+{
+ if (saved_logs) {
+ return smartlist_len(saved_logs) > 0;
+ }
+ return 0;
+}
+
+void
+mock_saving_logv(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap)
+{
+ (void)domain;
+ char *buf = tor_malloc_zero(10240);
+ int n;
+ n = tor_vsnprintf(buf,10240,format,ap);
+ tor_assert(n < 10240-1);
+ buf[n]='\n';
+ buf[n+1]='\0';
+
+ mock_saved_log_entry_t *e = tor_malloc_zero(sizeof(mock_saved_log_entry_t));
+ e->severity = severity;
+ e->funcname = funcname;
+ e->suffix = suffix;
+ e->format = format;
+ e->generated_msg = tor_strdup(buf);
+ tor_free(buf);
+
+ if (!saved_logs)
+ saved_logs = smartlist_new();
+ smartlist_add(saved_logs, e);
+}
+
diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h
new file mode 100644
index 0000000000..02f31a5220
--- /dev/null
+++ b/src/test/log_test_helpers.h
@@ -0,0 +1,56 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifndef TOR_LOG_TEST_HELPERS_H
+#define TOR_LOG_TEST_HELPERS_H
+
+typedef struct mock_saved_log_entry_t {
+ int severity;
+ const char *funcname;
+ const char *suffix;
+ const char *format;
+ char *generated_msg;
+ struct mock_saved_log_entry_t *next;
+} mock_saved_log_entry_t;
+
+void mock_saving_logv(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap)
+ CHECK_PRINTF(5, 0);
+void mock_clean_saved_logs(void);
+const smartlist_t *mock_saved_logs(void);
+int setup_capture_of_logs(int new_level);
+void teardown_capture_of_logs(int prev);
+
+int mock_saved_log_has_message(const char *msg);
+int mock_saved_log_has_severity(int severity);
+int mock_saved_log_has_entry(void);
+
+#define expect_log_msg(str) \
+ tt_assert_msg(mock_saved_log_has_message(str), \
+ "expected log to contain " # str);
+
+#define expect_no_log_msg(str) \
+ tt_assert_msg(!mock_saved_log_has_message(str), \
+ "expected log to not contain " # str);
+
+#define expect_log_severity(severity) \
+ tt_assert_msg(mock_saved_log_has_severity(severity), \
+ "expected log to contain severity " # severity);
+
+#define expect_no_log_severity(severity) \
+ tt_assert_msg(!mock_saved_log_has_severity(severity), \
+ "expected log to not contain severity " # severity);
+
+#define expect_log_entry() \
+ tt_assert_msg(mock_saved_log_has_entry(), \
+ "expected log to contain entries");
+
+#define expect_no_log_entry() \
+ tt_assert_msg(!mock_saved_log_has_entry(), \
+ "expected log to not contain entries");
+
+#endif
+
diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c
new file mode 100644
index 0000000000..f16d67fa1a
--- /dev/null
+++ b/src/test/rend_test_helpers.c
@@ -0,0 +1,73 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "test.h"
+#include "rendcommon.h"
+#include "rend_test_helpers.h"
+
+void
+generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
+ char **service_id, int intro_points)
+{
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t now;
+
+ now = time(NULL) + time_diff;
+ create_descriptor(&generated, service_id, intro_points);
+ generated->timestamp = now;
+
+ rend_encode_v2_descriptors(descs, generated, now, 0, REND_NO_AUTH, NULL,
+ NULL);
+ tor_assert(smartlist_len(descs) > 1);
+ *desc = smartlist_get(descs, 0);
+ smartlist_set(descs, 0, NULL);
+
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_service_descriptor_free(generated);
+}
+
+void
+create_descriptor(rend_service_descriptor_t **generated, char **service_id,
+ int intro_points)
+{
+ crypto_pk_t *pk1 = NULL;
+ crypto_pk_t *pk2 = NULL;
+ int i;
+
+ *service_id = tor_malloc(REND_SERVICE_ID_LEN_BASE32+1);
+ pk1 = pk_generate(0);
+ pk2 = pk_generate(1);
+
+ *generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ (*generated)->pk = crypto_pk_dup_key(pk1);
+ rend_get_service_id((*generated)->pk, *service_id);
+
+ (*generated)->version = 2;
+ (*generated)->protocols = 42;
+ (*generated)->intro_nodes = smartlist_new();
+
+ for (i = 0; i < intro_points; i++) {
+ rend_intro_point_t *intro = tor_malloc_zero(sizeof(rend_intro_point_t));
+ crypto_pk_t *okey = pk_generate(2 + i);
+ intro->extend_info = tor_malloc_zero(sizeof(extend_info_t));
+ intro->extend_info->onion_key = okey;
+ crypto_pk_get_digest(intro->extend_info->onion_key,
+ intro->extend_info->identity_digest);
+ intro->extend_info->nickname[0] = '$';
+ base16_encode(intro->extend_info->nickname + 1,
+ sizeof(intro->extend_info->nickname) - 1,
+ intro->extend_info->identity_digest, DIGEST_LEN);
+ tor_addr_from_ipv4h(&intro->extend_info->addr, crypto_rand_int(65536));
+ intro->extend_info->port = 1 + crypto_rand_int(65535);
+ intro->intro_key = crypto_pk_dup_key(pk2);
+ smartlist_add((*generated)->intro_nodes, intro);
+ }
+
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk2);
+}
+
diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h
new file mode 100644
index 0000000000..1ef03747d7
--- /dev/null
+++ b/src/test/rend_test_helpers.h
@@ -0,0 +1,15 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifndef TOR_REND_TEST_HELPERS_H
+#define TOR_REND_TEST_HELPERS_H
+
+void generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
+ char **service_id, int intro_points);
+void create_descriptor(rend_service_descriptor_t **generated,
+ char **service_id, int intro_points);
+
+#endif
+
diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c
index a39bad1540..5d4fcec664 100644
--- a/src/test/test-memwipe.c
+++ b/src/test/test-memwipe.c
@@ -62,7 +62,7 @@ fill_a_buffer_nothing(void)
return sum;
}
-static INLINE int
+static inline int
vmemeq(volatile char *a, const char *b, size_t n)
{
while (n--) {
diff --git a/src/test/test.c b/src/test/test.c
index e10e260266..d671ac896e 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -28,6 +28,7 @@
#define ROUTER_PRIVATE
#define CIRCUITSTATS_PRIVATE
#define CIRCUITLIST_PRIVATE
+#define MAIN_PRIVATE
#define STATEFILE_PRIVATE
/*
@@ -47,8 +48,10 @@ double fabs(double x);
#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"
#include "onion_ntor.h"
@@ -316,6 +319,9 @@ test_circuit_timeout(void *arg)
int i, runs;
double close_ms;
(void)arg;
+
+ initialize_periodic_events();
+
circuit_build_times_init(&initial);
circuit_build_times_init(&estimate);
circuit_build_times_init(&final);
@@ -455,6 +461,7 @@ test_circuit_timeout(void *arg)
circuit_build_times_free_timeouts(&estimate);
circuit_build_times_free_timeouts(&final);
or_state_free(state);
+ teardown_periodic_events();
}
/** Test encoding and parsing of rendezvous service descriptors. */
@@ -494,6 +501,9 @@ test_rend_fns(void *arg)
tt_str_op(address6,OP_EQ, "abcdefghijklmnop");
tt_assert(BAD_HOSTNAME == parse_extended_hostname(address7));
+ /* Initialize the service cache. */
+ rend_cache_init();
+
pk1 = pk_generate(0);
pk2 = pk_generate(1);
generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
@@ -1105,8 +1115,8 @@ static struct testcase_t test_array[] = {
{ "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL },
ENT(onion_queues),
{ "ntor_handshake", test_ntor_handshake, 0, NULL, NULL },
- ENT(circuit_timeout),
- ENT(rend_fns),
+ FORK(circuit_timeout),
+ FORK(rend_fns),
ENT(geoip),
FORK(geoip_with_pt),
FORK(stats),
@@ -1125,12 +1135,15 @@ extern struct testcase_t channeltls_tests[];
extern struct testcase_t checkdir_tests[];
extern struct testcase_t circuitlist_tests[];
extern struct testcase_t circuitmux_tests[];
+extern struct testcase_t compat_libevent_tests[];
extern struct testcase_t config_tests[];
+extern struct testcase_t connection_tests[];
extern struct testcase_t container_tests[];
extern struct testcase_t controller_tests[];
extern struct testcase_t controller_event_tests[];
extern struct testcase_t crypto_tests[];
extern struct testcase_t dir_tests[];
+extern struct testcase_t dir_handle_get_tests[];
extern struct testcase_t entryconn_tests[];
extern struct testcase_t entrynodes_tests[];
extern struct testcase_t guardfraction_tests[];
@@ -1145,9 +1158,11 @@ extern struct testcase_t nodelist_tests[];
extern struct testcase_t oom_tests[];
extern struct testcase_t options_tests[];
extern struct testcase_t policy_tests[];
+extern struct testcase_t procmon_tests[];
extern struct testcase_t pt_tests[];
extern struct testcase_t relay_tests[];
extern struct testcase_t relaycell_tests[];
+extern struct testcase_t rend_cache_tests[];
extern struct testcase_t replaycache_tests[];
extern struct testcase_t router_tests[];
extern struct testcase_t routerkeys_tests[];
@@ -1157,7 +1172,10 @@ extern struct testcase_t scheduler_tests[];
extern struct testcase_t socks_tests[];
extern struct testcase_t status_tests[];
extern struct testcase_t thread_tests[];
+extern struct testcase_t tortls_tests[];
extern struct testcase_t util_tests[];
+extern struct testcase_t util_format_tests[];
+extern struct testcase_t util_process_tests[];
extern struct testcase_t dns_tests[];
struct testgroup_t testgroups[] = {
@@ -1173,12 +1191,15 @@ struct testgroup_t testgroups[] = {
{ "checkdir/", checkdir_tests },
{ "circuitlist/", circuitlist_tests },
{ "circuitmux/", circuitmux_tests },
+ { "compat/libevent/", compat_libevent_tests },
{ "config/", config_tests },
+ { "connection/", connection_tests },
{ "container/", container_tests },
{ "control/", controller_tests },
{ "control/event/", controller_event_tests },
{ "crypto/", crypto_tests },
{ "dir/", dir_tests },
+ { "dir_handle_get/", dir_handle_get_tests },
{ "dir/md/", microdesc_tests },
{ "entryconn/", entryconn_tests },
{ "entrynodes/", entrynodes_tests },
@@ -1192,9 +1213,11 @@ struct testgroup_t testgroups[] = {
{ "oom/", oom_tests },
{ "options/", options_tests },
{ "policy/" , policy_tests },
+ { "procmon/", procmon_tests },
{ "pt/", pt_tests },
{ "relay/" , relay_tests },
{ "relaycell/", relaycell_tests },
+ { "rend_cache/", rend_cache_tests },
{ "replaycache/", replaycache_tests },
{ "routerkeys/", routerkeys_tests },
{ "routerlist/", routerlist_tests },
@@ -1202,8 +1225,11 @@ struct testgroup_t testgroups[] = {
{ "scheduler/", scheduler_tests },
{ "socks/", socks_tests },
{ "status/" , status_tests },
+ { "tortls/", tortls_tests },
{ "util/", util_tests },
+ { "util/format/", util_format_tests },
{ "util/logging/", logging_tests },
+ { "util/process/", util_process_tests },
{ "util/thread/", thread_tests },
{ "dns/", dns_tests },
END_OF_GROUPS
diff --git a/src/test/test_accounting.c b/src/test/test_accounting.c
index 25908e942c..7edba988a6 100644
--- a/src/test/test_accounting.c
+++ b/src/test/test_accounting.c
@@ -61,6 +61,32 @@ test_accounting_limits(void *arg)
fake_time += 1;
consider_hibernation(fake_time);
tor_assert(we_are_hibernating() == 1);
+
+ options->AccountingRule = ACCT_OUT;
+
+ accounting_add_bytes(100, 10, 1);
+ fake_time += 1;
+ consider_hibernation(fake_time);
+ tor_assert(we_are_hibernating() == 0);
+
+ accounting_add_bytes(0, 90, 1);
+ fake_time += 1;
+ consider_hibernation(fake_time);
+ tor_assert(we_are_hibernating() == 1);
+
+ options->AccountingMax = 300;
+ options->AccountingRule = ACCT_IN;
+
+ accounting_add_bytes(10, 100, 1);
+ fake_time += 1;
+ consider_hibernation(fake_time);
+ tor_assert(we_are_hibernating() == 0);
+
+ accounting_add_bytes(90, 0, 1);
+ fake_time += 1;
+ consider_hibernation(fake_time);
+ tor_assert(we_are_hibernating() == 1);
+
goto done;
done:
NS_UNMOCK(get_or_state);
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index 2c25c1ef7d..85130db4fe 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -302,6 +302,7 @@ test_addr_ip6_helpers(void *arg)
//test_ntop6_reduces("0:0:0:0:0:0:c0a8:0101", "::192.168.1.1");
test_ntop6_reduces("0:0:0:0:0:ffff:c0a8:0101", "::ffff:192.168.1.1");
+ test_ntop6_reduces("0:0:0:0:0:0:c0a8:0101", "::192.168.1.1");
test_ntop6_reduces("002:0:0000:0:3::4", "2::3:0:0:4");
test_ntop6_reduces("0:0::1:0:3", "::1:0:3");
test_ntop6_reduces("008:0::0", "8::");
diff --git a/src/test/test_address.c b/src/test/test_address.c
index 4cf3a5a3a6..748dfc5a68 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -119,6 +119,21 @@ smartlist_contains_internal_tor_addr(smartlist_t *smartlist)
}
/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is NULL or the null tor_addr_t. Otherwise, return 0.
+ */
+static int
+smartlist_contains_null_tor_addr(smartlist_t *smartlist)
+{
+ SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+ if (tor_addr == NULL || tor_addr_is_null(tor_addr)) {
+ return 1;
+ }
+ } SMARTLIST_FOREACH_END(tor_addr);
+
+ return 0;
+}
+
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
* that is an IPv4 address. Otherwise, return 0.
*/
static int
@@ -205,7 +220,7 @@ test_address_ifaddrs_to_smartlist(void *arg)
ifa_ipv6->ifa_dstaddr = NULL;
ifa_ipv6->ifa_data = NULL;
- smartlist = ifaddrs_to_smartlist(ifa);
+ smartlist = ifaddrs_to_smartlist(ifa, AF_UNSPEC);
tt_assert(smartlist);
tt_assert(smartlist_len(smartlist) == 3);
@@ -266,15 +281,25 @@ test_address_get_if_addrs_ifaddrs(void *arg)
(void)arg;
- results = get_interface_addresses_ifaddrs(LOG_ERR);
+ results = get_interface_addresses_ifaddrs(LOG_ERR, AF_UNSPEC);
+
+ tt_assert(results);
+ /* Some FreeBSD jails don't have localhost IP address. Instead, they only
+ * have the address assigned to the jail (whatever that may be).
+ * And a jail without a network connection might not have any addresses at
+ * all. */
+ tt_assert(!smartlist_contains_null_tor_addr(results));
+
+ /* If there are addresses, they must be IPv4 or IPv6 */
+ if (smartlist_len(results) > 0) {
+ tt_assert(smartlist_contains_ipv4_tor_addr(results)
+ || smartlist_contains_ipv6_tor_addr(results));
+ }
- tt_int_op(smartlist_len(results),>=,1);
-#ifndef __FreeBSD__
- /* FreeBSD doesn't have a localhost in jails sometimes. */
- tt_assert(smartlist_contains_localhost_tor_addr(results));
-#endif
done:
- SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
+ if (results) {
+ SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
+ }
smartlist_free(results);
return;
}
@@ -291,10 +316,17 @@ test_address_get_if_addrs_win32(void *arg)
(void)arg;
- results = get_interface_addresses_win32(LOG_ERR);
+ results = get_interface_addresses_win32(LOG_ERR, AF_UNSPEC);
tt_int_op(smartlist_len(results),>=,1);
tt_assert(smartlist_contains_localhost_tor_addr(results));
+ tt_assert(!smartlist_contains_null_tor_addr(results));
+
+ /* If there are addresses, they must be IPv4 or IPv6 */
+ if (smartlist_len(results) > 0) {
+ tt_assert(smartlist_contains_ipv4_tor_addr(results)
+ || smartlist_contains_ipv6_tor_addr(results));
+ }
done:
SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
@@ -481,16 +513,28 @@ test_address_get_if_addrs_ioctl(void *arg)
(void)arg;
- result = get_interface_addresses_ioctl(LOG_ERR);
+ result = get_interface_addresses_ioctl(LOG_ERR, AF_INET);
+ /* On an IPv6-only system, this will fail and return NULL
tt_assert(result);
- tt_int_op(smartlist_len(result),>=,1);
+ */
-#ifndef __FreeBSD__
- /* FreeBSD doesn't have a localhost in jails sometimes. */
- tt_assert(smartlist_contains_localhost_tor_addr(result));
-#endif
- done:
+ /* Some FreeBSD jails don't have localhost IP address. Instead, they only
+ * have the address assigned to the jail (whatever that may be).
+ * And a jail without a network connection might not have any addresses at
+ * all. */
+ if (result) {
+ tt_assert(!smartlist_contains_null_tor_addr(result));
+
+ /* If there are addresses, they must be IPv4 or IPv6.
+ * (AIX supports IPv6 from SIOCGIFCONF.) */
+ if (smartlist_len(result) > 0) {
+ tt_assert(smartlist_contains_ipv4_tor_addr(result)
+ || smartlist_contains_ipv6_tor_addr(result));
+ }
+ }
+
+ done:
if (result) {
SMARTLIST_FOREACH(result, tor_addr_t *, t, tor_free(t));
smartlist_free(result);
@@ -700,12 +744,13 @@ test_address_get_if_addrs_list_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
/* The list may or may not contain internal addresses */
+ tt_assert(!smartlist_contains_null_tor_addr(results));
- /* Allow unit tests to pass on IPv6-only machines */
+ /* if there are any addresses, they must be IPv4 */
if (smartlist_len(results) > 0) {
- tt_assert(smartlist_contains_ipv4_tor_addr(results)
- || smartlist_contains_ipv6_tor_addr(results));
+ tt_assert(smartlist_contains_ipv4_tor_addr(results));
}
+ tt_assert(!smartlist_contains_ipv6_tor_addr(results));
done:
free_interface_address_list(results);
@@ -728,6 +773,7 @@ test_address_get_if_addrs_list_no_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
tt_assert(!smartlist_contains_internal_tor_addr(results));
+ tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv4 */
if (smartlist_len(results) > 0) {
@@ -756,6 +802,7 @@ test_address_get_if_addrs6_list_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
/* The list may or may not contain internal addresses */
+ tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv6 */
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
@@ -784,7 +831,9 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
tt_assert(!smartlist_contains_internal_tor_addr(results));
+ tt_assert(!smartlist_contains_null_tor_addr(results));
+ /* if there are any addresses, they must be IPv6 */
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
if (smartlist_len(results) > 0) {
tt_assert(smartlist_contains_ipv6_tor_addr(results));
@@ -798,9 +847,10 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
static int called_get_interface_addresses_raw = 0;
static smartlist_t *
-mock_get_interface_addresses_raw_fail(int severity)
+mock_get_interface_addresses_raw_fail(int severity, sa_family_t family)
{
(void)severity;
+ (void)family;
called_get_interface_addresses_raw++;
return smartlist_new();
@@ -852,7 +902,7 @@ test_address_get_if_addrs_internal_fail(void *arg)
rv = get_interface_address(LOG_ERR, &ipv4h_addr);
tt_assert(rv == -1);
-done:
+ done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
free_interface_address6_list(results1);
@@ -880,7 +930,7 @@ test_address_get_if_addrs_no_internal_fail(void *arg)
tt_assert(results2 != NULL);
tt_int_op(smartlist_len(results2),==,0);
-done:
+ done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
free_interface_address6_list(results1);
@@ -939,6 +989,118 @@ test_address_get_if_addrs6(void *arg)
return;
}
+static void
+test_address_tor_addr_to_in6(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ const struct in6_addr *res;
+ uint8_t expected[16] = {42, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15};
+
+ a->family = AF_INET;
+ res = tor_addr_to_in6(a);
+ tt_assert(!res);
+
+ a->family = AF_INET6;
+ memcpy(a->addr.in6_addr.s6_addr, expected, 16);
+ res = tor_addr_to_in6(a);
+ tt_assert(res);
+ tt_mem_op(res->s6_addr, OP_EQ, expected, 16);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_to_in(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ const struct in_addr *res;
+
+ a->family = AF_INET6;
+ res = tor_addr_to_in(a);
+ tt_assert(!res);
+
+ a->family = AF_INET;
+ a->addr.in_addr.s_addr = 44;
+ res = tor_addr_to_in(a);
+ tt_assert(res);
+ tt_int_op(res->s_addr, OP_EQ, 44);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_to_ipv4n(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ uint32_t res;
+
+ a->family = AF_INET6;
+ res = tor_addr_to_ipv4n(a);
+ tt_assert(!res);
+
+ a->family = AF_INET;
+ a->addr.in_addr.s_addr = 43;
+ res = tor_addr_to_ipv4n(a);
+ tt_assert(res);
+ tt_int_op(res, OP_EQ, 43);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_to_mapped_ipv4h(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ uint32_t res;
+ uint8_t toset[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 42};
+
+ a->family = AF_INET;
+ res = tor_addr_to_mapped_ipv4h(a);
+ tt_assert(!res);
+
+ a->family = AF_INET6;
+
+ memcpy(a->addr.in6_addr.s6_addr, toset, 16);
+ res = tor_addr_to_mapped_ipv4h(a);
+ tt_assert(res);
+ tt_int_op(res, OP_EQ, 42);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_eq_ipv4h(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ int res;
+
+ a->family = AF_INET6;
+ res = tor_addr_eq_ipv4h(a, 42);
+ tt_assert(!res);
+
+ a->family = AF_INET;
+ a->addr.in_addr.s_addr = 52;
+ res = tor_addr_eq_ipv4h(a, 42);
+ tt_assert(!res);
+
+ a->addr.in_addr.s_addr = 52;
+ res = tor_addr_eq_ipv4h(a, ntohl(52));
+ tt_assert(res);
+
+ done:
+ tor_free(a);
+}
+
#define ADDRESS_TEST(name, flags) \
{ #name, test_address_ ## name, flags, NULL, NULL }
@@ -965,6 +1127,11 @@ struct testcase_t address_tests[] = {
ADDRESS_TEST(get_if_addrs_ioctl, TT_FORK),
ADDRESS_TEST(ifreq_to_smartlist, 0),
#endif
+ ADDRESS_TEST(tor_addr_to_in6, 0),
+ ADDRESS_TEST(tor_addr_to_in, 0),
+ ADDRESS_TEST(tor_addr_to_ipv4n, 0),
+ ADDRESS_TEST(tor_addr_to_mapped_ipv4h, 0),
+ ADDRESS_TEST(tor_addr_eq_ipv4h, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c
index dabaee6e0a..c43143ffe9 100644
--- a/src/test/test_bt_cl.c
+++ b/src/test/test_bt_cl.c
@@ -119,6 +119,7 @@ main(int argc, char **argv)
printf("%d\n", we_weave(2));
clean_up_backtrace_handler();
+ logs_free_all();
return 0;
}
diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c
index 016e504ab3..dff1dde87e 100644
--- a/src/test/test_channeltls.c
+++ b/src/test/test_channeltls.c
@@ -123,7 +123,7 @@ test_channeltls_num_bytes_queued(void *arg)
/*
* Next, we have to test ch->num_bytes_queued, which is
* channel_tls_num_bytes_queued_method. We can't mock
- * connection_get_outbuf_len() directly because it's static INLINE
+ * connection_get_outbuf_len() directly because it's static inline
* in connection.h, but we can mock buf_datalen(). Note that
* if bufferevents ever work, this will break with them enabled.
*/
diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c
index 6d93731eea..5c72fc656d 100644
--- a/src/test/test_circuitmux.c
+++ b/src/test/test_circuitmux.c
@@ -36,11 +36,7 @@ test_cmux_destroy_cell_queue(void *arg)
circuit_t *circ = NULL;
cell_queue_t *cq = NULL;
packed_cell_t *pc = NULL;
- tor_libevent_cfg cfg;
- memset(&cfg, 0, sizeof(cfg));
-
- tor_libevent_initialize(&cfg);
scheduler_init();
(void) arg;
diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c
new file mode 100644
index 0000000000..f3fe113012
--- /dev/null
+++ b/src/test/test_compat_libevent.c
@@ -0,0 +1,224 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define COMPAT_LIBEVENT_PRIVATE
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+
+#include "compat_libevent.h"
+
+#ifdef HAVE_EVENT2_EVENT_H
+#include <event2/event.h>
+#include <event2/thread.h>
+#ifdef USE_BUFFEREVENTS
+#include <event2/bufferevent.h>
+#endif
+#else
+#include <event.h>
+#endif
+
+#include "log_test_helpers.h"
+
+#define NS_MODULE compat_libevent
+
+static void
+test_compat_libevent_logging_callback(void *ignored)
+{
+ (void)ignored;
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+
+ libevent_logging_callback(_EVENT_LOG_DEBUG, "hello world");
+ expect_log_msg("Message from libevent: hello world\n");
+ expect_log_severity(LOG_DEBUG);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_MSG, "hello world another time");
+ expect_log_msg("Message from libevent: hello world another time\n");
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_WARN, "hello world a third time");
+ expect_log_msg("Warning from libevent: hello world a third time\n");
+ expect_log_severity(LOG_WARN);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_ERR, "hello world a fourth time");
+ expect_log_msg("Error from libevent: hello world a fourth time\n");
+ expect_log_severity(LOG_ERR);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(42, "hello world a fifth time");
+ expect_log_msg("Message [42] from libevent: hello world a fifth time\n");
+ expect_log_severity(LOG_WARN);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_DEBUG,
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ );
+ expect_log_msg("Message from libevent: "
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789\n");
+ expect_log_severity(LOG_DEBUG);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(42, "xxx\n");
+ expect_log_msg("Message [42] from libevent: xxx\n");
+ expect_log_severity(LOG_WARN);
+
+ suppress_libevent_log_msg("something");
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_MSG, "hello there");
+ expect_log_msg("Message from libevent: hello there\n");
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_MSG, "hello there something else");
+ expect_no_log_msg("hello there something else");
+
+ // No way of verifying the result of this, it seems =/
+ configure_libevent_logging();
+
+ done:
+ suppress_libevent_log_msg(NULL);
+ teardown_capture_of_logs(previous_log);
+}
+
+static void
+test_compat_libevent_le_versions_compatibility(void *ignored)
+{
+ (void)ignored;
+ int res;
+
+ res = le_versions_compatibility(LE_OTHER);
+ tt_int_op(res, OP_EQ, 0);
+
+ res = le_versions_compatibility(V_OLD(0,9,'c'));
+ tt_int_op(res, OP_EQ, 1);
+
+ res = le_versions_compatibility(V(1,3,98));
+ tt_int_op(res, OP_EQ, 2);
+
+ res = le_versions_compatibility(V(1,4,98));
+ tt_int_op(res, OP_EQ, 3);
+
+ res = le_versions_compatibility(V(1,5,0));
+ tt_int_op(res, OP_EQ, 4);
+
+ res = le_versions_compatibility(V(2,0,0));
+ tt_int_op(res, OP_EQ, 4);
+
+ res = le_versions_compatibility(V(2,0,2));
+ tt_int_op(res, OP_EQ, 5);
+
+ done:
+ (void)0;
+}
+
+static void
+test_compat_libevent_tor_decode_libevent_version(void *ignored)
+{
+ (void)ignored;
+ le_version_t res;
+
+ res = tor_decode_libevent_version("SOMETHING WRONG");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.4.11");
+ tt_int_op(res, OP_EQ, V(1,4,11));
+
+ res = tor_decode_libevent_version("1.4.12b-stable");
+ tt_int_op(res, OP_EQ, V(1,4,12));
+
+ res = tor_decode_libevent_version("1.4.17b_stable");
+ tt_int_op(res, OP_EQ, V(1,4,17));
+
+ res = tor_decode_libevent_version("1.4.12!stable");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.4.12b!stable");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.4.13-");
+ tt_int_op(res, OP_EQ, V(1,4,13));
+
+ res = tor_decode_libevent_version("1.4.14_");
+ tt_int_op(res, OP_EQ, V(1,4,14));
+
+ res = tor_decode_libevent_version("1.4.15c-");
+ tt_int_op(res, OP_EQ, V(1,4,15));
+
+ res = tor_decode_libevent_version("1.4.16c_");
+ tt_int_op(res, OP_EQ, V(1,4,16));
+
+ res = tor_decode_libevent_version("1.4.17-s");
+ tt_int_op(res, OP_EQ, V(1,4,17));
+
+ res = tor_decode_libevent_version("1.5");
+ tt_int_op(res, OP_EQ, V(1,5,0));
+
+ res = tor_decode_libevent_version("1.2");
+ tt_int_op(res, OP_EQ, V(1,2,0));
+
+ res = tor_decode_libevent_version("1.2-");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.6e");
+ tt_int_op(res, OP_EQ, V_OLD(1,6,'e'));
+
+ done:
+ (void)0;
+}
+
+#if defined(LIBEVENT_VERSION)
+#define HEADER_VERSION LIBEVENT_VERSION
+#elif defined(_EVENT_VERSION)
+#define HEADER_VERSION _EVENT_VERSION
+#endif
+
+static void
+test_compat_libevent_header_version(void *ignored)
+{
+ (void)ignored;
+ const char *res;
+
+ res = tor_libevent_get_header_version_str();
+ tt_str_op(res, OP_EQ, HEADER_VERSION);
+
+ done:
+ (void)0;
+}
+
+struct testcase_t compat_libevent_tests[] = {
+ { "logging_callback", test_compat_libevent_logging_callback,
+ TT_FORK, NULL, NULL },
+ { "le_versions_compatibility",
+ test_compat_libevent_le_versions_compatibility, 0, NULL, NULL },
+ { "tor_decode_libevent_version",
+ test_compat_libevent_tor_decode_libevent_version, 0, NULL, NULL },
+ { "header_version", test_compat_libevent_header_version, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 28e9fa0f32..6be503abc5 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -7,17 +7,43 @@
#define CONFIG_PRIVATE
#define PT_PRIVATE
+#define ROUTERSET_PRIVATE
#include "or.h"
+#include "address.h"
#include "addressmap.h"
+#include "circuitmux_ewma.h"
+#include "circuitbuild.h"
#include "config.h"
#include "confparse.h"
+#include "connection.h"
#include "connection_edge.h"
#include "test.h"
#include "util.h"
#include "address.h"
+#include "connection_or.h"
+#include "control.h"
+#include "cpuworker.h"
+#include "dirserv.h"
+#include "dirvote.h"
+#include "dns.h"
#include "entrynodes.h"
#include "transports.h"
+#include "ext_orport.h"
+#include "geoip.h"
+#include "hibernate.h"
+#include "main.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "policies.h"
+#include "rendclient.h"
+#include "rendservice.h"
+#include "router.h"
#include "routerlist.h"
+#include "routerset.h"
+#include "statefile.h"
+#include "test.h"
+#include "transports.h"
+#include "util.h"
static void
test_config_addressmap(void *arg)
@@ -1444,6 +1470,176 @@ test_config_resolve_my_address(void *arg)
UNMOCK(tor_gethostname);
}
+static void
+test_config_adding_trusted_dir_server(void *arg)
+{
+ (void)arg;
+
+ const char digest[DIGEST_LEN] = "";
+ dir_server_t *ds = NULL;
+ tor_addr_port_t ipv6;
+ int rv = -1;
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds without an IPv6 address and port */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+ tt_assert(get_n_authorities(V3_DIRINFO) == 1);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 1);
+
+ /* create a trusted ds with an IPv6 address and port */
+ rv = tor_addr_port_parse(LOG_WARN, "[::1]:9061", &ipv6.addr, &ipv6.port, -1);
+ tt_assert(rv == 0);
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, &ipv6, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+ tt_assert(get_n_authorities(V3_DIRINFO) == 2);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 2);
+
+ done:
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_config_adding_fallback_dir_server(void *arg)
+{
+ (void)arg;
+
+ const char digest[DIGEST_LEN] = "";
+ dir_server_t *ds = NULL;
+ tor_addr_t ipv4;
+ tor_addr_port_t ipv6;
+ int rv = -1;
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ rv = tor_addr_parse(&ipv4, "127.0.0.1");
+ tt_assert(rv == AF_INET);
+
+ /* create a trusted ds without an IPv6 address and port */
+ ds = fallback_dir_server_new(&ipv4, 9059, 9060, NULL, digest, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 1);
+
+ /* create a trusted ds with an IPv6 address and port */
+ rv = tor_addr_port_parse(LOG_WARN, "[::1]:9061", &ipv6.addr, &ipv6.port, -1);
+ tt_assert(rv == 0);
+ ds = fallback_dir_server_new(&ipv4, 9059, 9060, &ipv6, digest, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 2);
+
+ done:
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+/* No secrets here:
+ * v3ident is `echo "onion" | shasum | cut -d" " -f1 | tr "a-f" "A-F"`
+ * fingerprint is `echo "unionem" | shasum | cut -d" " -f1 | tr "a-f" "A-F"`
+ * with added spaces
+ */
+#define TEST_DIR_AUTH_LINE_START \
+ "foobar orport=12345 " \
+ "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
+#define TEST_DIR_AUTH_LINE_END \
+ "1.2.3.4:54321 " \
+ "FDB2 FBD2 AAA5 25FA 2999 E617 5091 5A32 C777 3B17"
+#define TEST_DIR_AUTH_IPV6_FLAG \
+ "ipv6=[feed::beef]:9 "
+
+static void
+test_config_parsing_trusted_dir_server(void *arg)
+{
+ (void)arg;
+ int rv = -1;
+
+ /* parse a trusted dir server without an IPv6 address and port */
+ rv = parse_dir_authority_line(TEST_DIR_AUTH_LINE_START
+ TEST_DIR_AUTH_LINE_END,
+ V3_DIRINFO, 1);
+ tt_assert(rv == 0);
+
+ /* parse a trusted dir server with an IPv6 address and port */
+ rv = parse_dir_authority_line(TEST_DIR_AUTH_LINE_START
+ TEST_DIR_AUTH_IPV6_FLAG
+ TEST_DIR_AUTH_LINE_END,
+ V3_DIRINFO, 1);
+ tt_assert(rv == 0);
+
+ /* Since we are only validating, there is no cleanup. */
+ done:
+ ;
+}
+
+#undef TEST_DIR_AUTH_LINE_START
+#undef TEST_DIR_AUTH_LINE_END
+#undef TEST_DIR_AUTH_IPV6_FLAG
+
+/* No secrets here:
+ * id is `echo "syn-propanethial-S-oxide" | shasum | cut -d" " -f1`
+ */
+#define TEST_DIR_FALLBACK_LINE \
+ "1.2.3.4:54321 orport=12345 " \
+ "id=50e643986f31ea1235bcc1af17a1c5c5cfc0ee54 "
+#define TEST_DIR_FALLBACK_IPV6_FLAG \
+ "ipv6=[2015:c0de::deed]:9"
+
+static void
+test_config_parsing_fallback_dir_server(void *arg)
+{
+ (void)arg;
+ int rv = -1;
+
+ /* parse a trusted dir server without an IPv6 address and port */
+ rv = parse_dir_fallback_line(TEST_DIR_FALLBACK_LINE, 1);
+ tt_assert(rv == 0);
+
+ /* parse a trusted dir server with an IPv6 address and port */
+ rv = parse_dir_fallback_line(TEST_DIR_FALLBACK_LINE
+ TEST_DIR_FALLBACK_IPV6_FLAG,
+ 1);
+ tt_assert(rv == 0);
+
+ /* Since we are only validating, there is no cleanup. */
+ done:
+ ;
+}
+
+#undef TEST_DIR_FALLBACK_LINE
+#undef TEST_DIR_FALLBACK_IPV6_FLAG
+
+static void
+test_config_adding_default_trusted_dir_servers(void *arg)
+{
+ (void)arg;
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* Assume we only have one bridge authority */
+ add_default_trusted_dir_authorities(BRIDGE_DIRINFO);
+ tt_assert(get_n_authorities(BRIDGE_DIRINFO) == 1);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 1);
+
+ /* Assume we have nine V3 authorities */
+ add_default_trusted_dir_authorities(V3_DIRINFO);
+ tt_assert(get_n_authorities(V3_DIRINFO) == 9);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 10);
+
+ done:
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
static int n_add_default_fallback_dir_servers_known_default = 0;
/**
@@ -1471,13 +1667,14 @@ add_default_fallback_dir_servers_known_default(void)
n_add_default_fallback_dir_servers_known_default++;
}
+/* Test all the different combinations of adding dir servers */
static void
test_config_adding_dir_servers(void *arg)
{
(void)arg;
/* allocate options */
- or_options_t *options = tor_malloc(sizeof(or_options_t));
+ or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
/* Allocate and populate configuration lines:
*
@@ -1486,8 +1683,7 @@ test_config_adding_dir_servers(void *arg)
* Zeroing the structure has the same effect as initialising to:
* { NULL, NULL, NULL, CONFIG_LINE_NORMAL, 0};
*/
- config_line_t *test_dir_authority = tor_malloc(sizeof(config_line_t));
- memset(test_dir_authority, 0, sizeof(config_line_t));
+ config_line_t *test_dir_authority = tor_malloc_zero(sizeof(config_line_t));
test_dir_authority->key = tor_strdup("DirAuthority");
test_dir_authority->value = tor_strdup(
"D0 orport=9000 "
@@ -1495,16 +1691,16 @@ test_config_adding_dir_servers(void *arg)
"127.0.0.1:60090 0123 4567 8901 2345 6789 0123 4567 8901 2345 6789"
);
- config_line_t *test_alt_bridge_authority = tor_malloc(sizeof(config_line_t));
- memset(test_alt_bridge_authority, 0, sizeof(config_line_t));
+ config_line_t *test_alt_bridge_authority = tor_malloc_zero(
+ sizeof(config_line_t));
test_alt_bridge_authority->key = tor_strdup("AlternateBridgeAuthority");
test_alt_bridge_authority->value = tor_strdup(
"B1 orport=9001 bridge "
"127.0.0.1:60091 1123 4567 8901 2345 6789 0123 4567 8901 2345 6789"
);
- config_line_t *test_alt_dir_authority = tor_malloc(sizeof(config_line_t));
- memset(test_alt_dir_authority, 0, sizeof(config_line_t));
+ config_line_t *test_alt_dir_authority = tor_malloc_zero(
+ sizeof(config_line_t));
test_alt_dir_authority->key = tor_strdup("AlternateDirAuthority");
test_alt_dir_authority->value = tor_strdup(
"A2 orport=9002 "
@@ -1513,23 +1709,23 @@ test_config_adding_dir_servers(void *arg)
);
/* Use the format specified in the manual page */
- config_line_t *test_fallback_directory = tor_malloc(sizeof(config_line_t));
- memset(test_fallback_directory, 0, sizeof(config_line_t));
+ config_line_t *test_fallback_directory = tor_malloc_zero(
+ sizeof(config_line_t));
test_fallback_directory->key = tor_strdup("FallbackDir");
test_fallback_directory->value = tor_strdup(
"127.0.0.1:60093 orport=9003 id=0323456789012345678901234567890123456789"
);
/* We need to know if add_default_fallback_dir_servers is called,
+ * whatever the size of the list in fallback_dirs.inc,
* so we use a version of add_default_fallback_dir_servers that adds
- * one known default fallback directory.
- * There doesn't appear to be any need to test it unmocked. */
+ * one known default fallback directory. */
MOCK(add_default_fallback_dir_servers,
add_default_fallback_dir_servers_known_default);
/* There are 16 different cases, covering each combination of set/NULL for:
* DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority &
- * FallbackDir.
+ * FallbackDir. (We always set UseDefaultFallbackDirs to 1.)
* But validate_dir_servers() ensures that:
* "You cannot set both DirAuthority and Alternate*Authority."
* This reduces the number of cases to 10.
@@ -1543,8 +1739,6 @@ test_config_adding_dir_servers(void *arg)
* The valid cases are cases 0-9 counting using this method, as every case
* greater than or equal to 10 = 1010 is invalid.
*
- * After #15642 - Disable default fallback dirs when any custom dirs set
- *
* 1. Outcome: Use Set Directory Authorities
* - No Default Authorities
* - Use AlternateBridgeAuthority, AlternateDirAuthority, and FallbackDir
@@ -1581,20 +1775,6 @@ test_config_adding_dir_servers(void *arg)
* Cases expected to yield this outcome:
* 0 (DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority
* and FallbackDir are all NULL)
- *
- * Before #15642 but after #13163 - Stop using default authorities when both
- * Alternate Dir and Bridge Authority are set
- * (#13163 was committed in 0.2.6 as c1dd43d823c7)
- *
- * The behaviour is different in the following cases
- * where FallbackDir is NULL:
- * 2, 6, 8
- *
- * In these cases, the Default Fallback Directories are applied, even when
- * DirAuthorities or AlternateDirAuthority are set.
- *
- * However, as the list of default fallback directories is currently empty,
- * this change doesn't modify any user-visible behaviour.
*/
/*
@@ -1628,6 +1808,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -1637,6 +1818,9 @@ test_config_adding_dir_servers(void *arg)
/* we must have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* fallback_dir_servers */
const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
@@ -1669,7 +1853,10 @@ test_config_adding_dir_servers(void *arg)
n_default_fallback_dir = (smartlist_len(fallback_servers) -
n_default_alt_bridge_authority -
n_default_alt_dir_authority);
- /* If we have a negative count, something has gone really wrong */
+ /* If we have a negative count, something has gone really wrong,
+ * or some authorities aren't being added as fallback directories.
+ * (networkstatus_consensus_can_use_extra_fallbacks depends on all
+ * authorities being fallback directories.) */
tt_assert(n_default_fallback_dir >= 0);
}
}
@@ -1703,6 +1890,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -1712,6 +1900,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -1840,6 +2031,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -1849,6 +2041,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we just have the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 0);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -1977,6 +2172,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = test_alt_bridge_authority;
options->AlternateDirAuthority = test_alt_dir_authority;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -1986,6 +2182,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2115,6 +2314,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = test_alt_bridge_authority;
options->AlternateDirAuthority = test_alt_dir_authority;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2124,6 +2324,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 0);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2263,6 +2466,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = test_alt_bridge_authority;
options->AlternateDirAuthority = NULL;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2272,6 +2476,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2413,6 +2620,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = test_alt_bridge_authority;
options->AlternateDirAuthority = NULL;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2422,6 +2630,9 @@ test_config_adding_dir_servers(void *arg)
/* we must have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2572,6 +2783,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = test_alt_dir_authority;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2581,6 +2793,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2725,6 +2940,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = test_alt_dir_authority;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2734,6 +2950,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we just have the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 0);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2887,6 +3106,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2896,6 +3116,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -3046,6 +3269,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -3055,6 +3279,9 @@ test_config_adding_dir_servers(void *arg)
/* we must have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -3209,11 +3436,1130 @@ test_config_adding_dir_servers(void *arg)
UNMOCK(add_default_fallback_dir_servers);
}
+static void
+test_config_default_dir_servers(void *arg)
+{
+ or_options_t *opts = NULL;
+ (void)arg;
+ int trusted_count = 0;
+ int fallback_count = 0;
+
+ /* new set of options should stop fallback parsing */
+ opts = tor_malloc_zero(sizeof(or_options_t));
+ opts->UseDefaultFallbackDirs = 0;
+ /* set old_options to NULL to force dir update */
+ consider_adding_dir_servers(opts, NULL);
+ trusted_count = smartlist_len(router_get_trusted_dir_servers());
+ fallback_count = smartlist_len(router_get_fallback_dir_servers());
+ or_options_free(opts);
+ opts = NULL;
+
+ /* assume a release will never go out with less than 7 authorities */
+ tt_assert(trusted_count >= 7);
+ /* if we disable the default fallbacks, there must not be any extra */
+ tt_assert(fallback_count == trusted_count);
+
+ opts = tor_malloc_zero(sizeof(or_options_t));
+ opts->UseDefaultFallbackDirs = 1;
+ consider_adding_dir_servers(opts, opts);
+ trusted_count = smartlist_len(router_get_trusted_dir_servers());
+ fallback_count = smartlist_len(router_get_fallback_dir_servers());
+ or_options_free(opts);
+ opts = NULL;
+
+ /* assume a release will never go out with less than 7 authorities */
+ tt_assert(trusted_count >= 7);
+ /* XX/teor - allow for default fallbacks to be added without breaking
+ * the unit tests. Set a minimum fallback count once the list is stable. */
+ tt_assert(fallback_count >= trusted_count);
+
+ done:
+ or_options_free(opts);
+}
+
+static int mock_router_pick_published_address_result = 0;
+
+static int
+mock_router_pick_published_address(const or_options_t *options, uint32_t *addr)
+{
+ (void)options;
+ (void)addr;
+ return mock_router_pick_published_address_result;
+}
+
+static int mock_router_my_exit_policy_is_reject_star_result = 0;
+
+static int
+mock_router_my_exit_policy_is_reject_star(void)
+{
+ return mock_router_my_exit_policy_is_reject_star_result;
+}
+
+static int mock_advertised_server_mode_result = 0;
+
+static int
+mock_advertised_server_mode(void)
+{
+ return mock_advertised_server_mode_result;
+}
+
+static routerinfo_t *mock_router_get_my_routerinfo_result = NULL;
+
+static const routerinfo_t *
+mock_router_get_my_routerinfo(void)
+{
+ return mock_router_get_my_routerinfo_result;
+}
+
+static void
+test_config_directory_fetch(void *arg)
+{
+ (void)arg;
+
+ /* Test Setup */
+ or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
+ routerinfo_t routerinfo;
+ memset(&routerinfo, 0, sizeof(routerinfo));
+ mock_router_pick_published_address_result = -1;
+ mock_router_my_exit_policy_is_reject_star_result = 1;
+ mock_advertised_server_mode_result = 0;
+ mock_router_get_my_routerinfo_result = NULL;
+ MOCK(router_pick_published_address, mock_router_pick_published_address);
+ MOCK(router_my_exit_policy_is_reject_star,
+ mock_router_my_exit_policy_is_reject_star);
+ MOCK(advertised_server_mode, mock_advertised_server_mode);
+ MOCK(router_get_my_routerinfo, mock_router_get_my_routerinfo);
+
+ /* Clients can use multiple directory mirrors for bootstrap */
+ memset(options, 0, sizeof(or_options_t));
+ options->ClientOnly = 1;
+ tt_assert(server_mode(options) == 0);
+ tt_assert(public_server_mode(options) == 0);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 1);
+
+ /* Bridge Clients can use multiple directory mirrors for bootstrap */
+ memset(options, 0, sizeof(or_options_t));
+ options->UseBridges = 1;
+ tt_assert(server_mode(options) == 0);
+ tt_assert(public_server_mode(options) == 0);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 1);
+
+ /* Bridge Relays (Bridges) must act like clients, and use multiple
+ * directory mirrors for bootstrap */
+ memset(options, 0, sizeof(or_options_t));
+ options->BridgeRelay = 1;
+ options->ORPort_set = 1;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 0);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 1);
+
+ /* Clients set to FetchDirInfoEarly must fetch it from the authorities,
+ * but can use multiple authorities for bootstrap */
+ memset(options, 0, sizeof(or_options_t));
+ options->FetchDirInfoEarly = 1;
+ tt_assert(server_mode(options) == 0);
+ tt_assert(public_server_mode(options) == 0);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 1);
+
+ /* OR servers only fetch the consensus from the authorities when they don't
+ * know their own address, but never use multiple directories for bootstrap
+ */
+ memset(options, 0, sizeof(or_options_t));
+ options->ORPort_set = 1;
+
+ mock_router_pick_published_address_result = -1;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_router_pick_published_address_result = 0;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ /* Exit OR servers only fetch the consensus from the authorities when they
+ * refuse unknown exits, but never use multiple directories for bootstrap
+ */
+ memset(options, 0, sizeof(or_options_t));
+ options->ORPort_set = 1;
+ options->ExitRelay = 1;
+ mock_router_pick_published_address_result = 0;
+ mock_router_my_exit_policy_is_reject_star_result = 0;
+ mock_advertised_server_mode_result = 1;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+
+ routerinfo.supports_tunnelled_dir_requests = 1;
+
+ options->RefuseUnknownExits = 1;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ options->RefuseUnknownExits = 0;
+ mock_router_pick_published_address_result = 0;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ /* Dir servers fetch the consensus from the authorities, unless they are not
+ * advertising themselves (hibernating) or have no routerinfo or are not
+ * advertising their dirport, and never use multiple directories for
+ * bootstrap. This only applies if they are also OR servers.
+ * (We don't care much about the behaviour of non-OR directory servers.) */
+ memset(options, 0, sizeof(or_options_t));
+ options->DirPort_set = 1;
+ options->ORPort_set = 1;
+ options->DirCache = 1;
+ mock_router_pick_published_address_result = 0;
+ mock_router_my_exit_policy_is_reject_star_result = 1;
+
+ mock_advertised_server_mode_result = 1;
+ routerinfo.dir_port = 1;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_advertised_server_mode_result = 0;
+ routerinfo.dir_port = 1;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_advertised_server_mode_result = 1;
+ mock_router_get_my_routerinfo_result = NULL;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_advertised_server_mode_result = 1;
+ routerinfo.dir_port = 0;
+ routerinfo.supports_tunnelled_dir_requests = 0;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_advertised_server_mode_result = 1;
+ routerinfo.dir_port = 1;
+ routerinfo.supports_tunnelled_dir_requests = 1;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ done:
+ tor_free(options);
+ UNMOCK(router_pick_published_address);
+ UNMOCK(router_get_my_routerinfo);
+ UNMOCK(advertised_server_mode);
+ UNMOCK(router_my_exit_policy_is_reject_star);
+}
+
+static void
+test_config_default_fallback_dirs(void *arg)
+{
+ const char *fallback[] = {
+#include "../or/fallback_dirs.inc"
+ NULL
+ };
+
+ int n_included_fallback_dirs = 0;
+ int n_added_fallback_dirs = 0;
+
+ (void)arg;
+ clear_dir_servers();
+
+ while (fallback[n_included_fallback_dirs])
+ n_included_fallback_dirs++;
+
+ add_default_fallback_dir_servers();
+
+ n_added_fallback_dirs = smartlist_len(router_get_fallback_dir_servers());
+
+ tt_assert(n_included_fallback_dirs == n_added_fallback_dirs);
+
+ done:
+ clear_dir_servers();
+}
+
+static config_line_t *
+mock_config_line(const char *key, const char *val)
+{
+ config_line_t *config_line = tor_malloc(sizeof(config_line_t));
+ memset(config_line, 0, sizeof(config_line_t));
+ config_line->key = tor_strdup(key);
+ config_line->value = tor_strdup(val);
+ return config_line;
+}
+
+static void
+test_config_parse_port_config__listenaddress(void *data)
+{
+ (void)data;
+ int ret;
+ config_line_t *config_listen_address = NULL, *config_listen_address2 = NULL,
+ *config_listen_address3 = NULL;
+ config_line_t *config_port1 = NULL, *config_port2 = NULL,
+ *config_port3 = NULL, *config_port4 = NULL, *config_port5 = NULL;
+ smartlist_t *slout = NULL;
+ port_cfg_t *port_cfg = NULL;
+
+ // Test basic invocation with no arguments
+ ret = parse_port_config(NULL, NULL, NULL, NULL, 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Setup some test data
+ config_listen_address = mock_config_line("DNSListenAddress", "127.0.0.1");
+ config_listen_address2 = mock_config_line("DNSListenAddress", "x$$$:::345");
+ config_listen_address3 = mock_config_line("DNSListenAddress",
+ "127.0.0.1:1442");
+ config_port1 = mock_config_line("DNSPort", "42");
+ config_port2 = mock_config_line("DNSPort", "43");
+ config_port1->next = config_port2;
+ config_port3 = mock_config_line("DNSPort", "auto");
+ config_port4 = mock_config_line("DNSPort", "55542");
+ config_port5 = mock_config_line("DNSPort", "666777");
+
+ // Test failure when we have a ListenAddress line and several
+ // Port lines for the same portname
+ ret = parse_port_config(NULL, config_port1, config_listen_address, "DNS", 0,
+ NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test case when we have a listen address, no default port and allow
+ // spurious listen address lines
+ ret = parse_port_config(NULL, NULL, config_listen_address, "DNS", 0, NULL,
+ 0, CL_PORT_ALLOW_EXTRA_LISTENADDR);
+ tt_int_op(ret, OP_EQ, 1);
+
+ // Test case when we have a listen address, no default port but doesn't
+ // allow spurious listen address lines
+ ret = parse_port_config(NULL, NULL, config_listen_address, "DNS", 0, NULL,
+ 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test case when we have a listen address, and a port that points to auto,
+ // should use the AUTO port
+ slout = smartlist_new();
+ ret = parse_port_config(slout, config_port3, config_listen_address, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, CFG_AUTO_PORT);
+
+ // Test when we have a listen address and a custom port
+ ret = parse_port_config(slout, config_port4, config_listen_address, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 2);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 1);
+ tt_int_op(port_cfg->port, OP_EQ, 55542);
+
+ // Test when we have a listen address and an invalid custom port
+ ret = parse_port_config(slout, config_port5, config_listen_address, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test we get a server port configuration when asked for it
+ ret = parse_port_config(slout, NULL, config_listen_address, "DNS", 0, NULL,
+ 123, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 4);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 2);
+ tt_int_op(port_cfg->port, OP_EQ, 123);
+ tt_int_op(port_cfg->server_cfg.no_listen, OP_EQ, 1);
+ tt_int_op(port_cfg->server_cfg.bind_ipv4_only, OP_EQ, 1);
+
+ // Test an invalid ListenAddress configuration
+ ret = parse_port_config(NULL, NULL, config_listen_address2, "DNS", 0, NULL,
+ 222, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test default to the port in the listen address if available
+ ret = parse_port_config(slout, config_port2, config_listen_address3, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 5);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 4);
+ tt_int_op(port_cfg->port, OP_EQ, 1442);
+
+ // Test we work correctly without an out, but with a listen address
+ // and a port
+ ret = parse_port_config(NULL, config_port2, config_listen_address, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test warning nonlocal control
+ ret = parse_port_config(slout, config_port2, config_listen_address, "DNS",
+ CONN_TYPE_CONTROL_LISTENER, NULL, 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test warning nonlocal ext or listener
+ ret = parse_port_config(slout, config_port2, config_listen_address, "DNS",
+ CONN_TYPE_EXT_OR_LISTENER, NULL, 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test warning nonlocal other
+ ret = parse_port_config(slout, config_port2, config_listen_address, "DNS",
+ 0, NULL, 0, CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test warning nonlocal control without an out
+ ret = parse_port_config(NULL, config_port2, config_listen_address, "DNS",
+ CONN_TYPE_CONTROL_LISTENER, NULL, 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ tor_free(config_listen_address);
+ tor_free(config_listen_address2);
+ tor_free(config_listen_address3);
+ tor_free(config_port1);
+ tor_free(config_port2);
+ tor_free(config_port3);
+ tor_free(config_port4);
+ tor_free(config_port5);
+ smartlist_free(slout);
+}
+
+static void
+test_config_parse_port_config__ports__no_ports_given(void *data)
+{
+ (void)data;
+ int ret;
+ smartlist_t *slout = NULL;
+ port_cfg_t *port_cfg = NULL;
+
+ slout = smartlist_new();
+
+ // Test no defaultport, no defaultaddress and no out
+ ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test with defaultport, no defaultaddress and no out
+ ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, NULL, 42, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test no defaultport, with defaultaddress and no out
+ ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, "127.0.0.2", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test with defaultport, with defaultaddress and no out
+ ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, "127.0.0.2", 42, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test no defaultport, no defaultaddress and with out
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 0);
+
+ // Test with defaultport, no defaultaddress and with out
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, NULL, 42, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 0);
+
+ // Test no defaultport, with defaultaddress and with out
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, "127.0.0.2", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 0);
+
+ // Test with defaultport, with defaultaddress and out, adds a new port cfg
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, "127.0.0.2", 42, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, 42);
+ tt_int_op(port_cfg->is_unix_addr, OP_EQ, 0);
+
+ // Test with defaultport, with defaultaddress and out, adds a new port cfg
+ // for a unix address
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, "/foo/bar/unixdomain",
+ 42, CL_PORT_IS_UNIXSOCKET);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, 0);
+ tt_int_op(port_cfg->is_unix_addr, OP_EQ, 1);
+ tt_str_op(port_cfg->unix_addr, OP_EQ, "/foo/bar/unixdomain");
+
+ done:
+ smartlist_free(slout);
+}
+
+static void
+test_config_parse_port_config__ports__ports_given(void *data)
+{
+ (void)data;
+ int ret;
+ smartlist_t *slout = NULL;
+ port_cfg_t *port_cfg = NULL;
+ config_line_t *config_port_invalid = NULL, *config_port_valid = NULL;
+ tor_addr_t addr;
+
+ slout = smartlist_new();
+
+ // Test error when encounters an invalid Port specification
+ config_port_invalid = mock_config_line("DNSPort", "");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test error when encounters an empty unix domain specification
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort", "unix:");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test error when encounters a unix domain specification but the listener
+ // doesnt support domain sockets
+ config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar");
+ ret = parse_port_config(NULL, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_DNS_LISTENER, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test valid unix domain
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, NULL, 0, 0);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, 0);
+ tt_int_op(port_cfg->is_unix_addr, OP_EQ, 1);
+ tt_str_op(port_cfg->unix_addr, OP_EQ, "/tmp/foo/bar");
+#endif
+
+ // Test failure if we have no ipv4 and no ipv6 (for unix domain sockets,
+ // this makes no sense - it should be fixed)
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort",
+ "unix:/tmp/foo/bar NoIPv4Traffic");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with no ipv4 but take ipv6 (for unix domain sockets, this
+ // makes no sense - it should be fixed)
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar "
+ "NoIPv4Traffic IPv6Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1);
+#endif
+
+ // Test success with both ipv4 and ipv6 (for unix domain sockets,
+ // this makes no sense - it should be fixed)
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar "
+ "IPv4Traffic IPv6Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1);
+#endif
+
+ // Test failure if we specify world writable for an IP Port
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort", "42 WorldWritable");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure if we specify group writable for an IP Port
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort", "42 GroupWritable");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with only a port (this will fail without a default address)
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ config_port_valid = mock_config_line("DNSPort", "42");
+ ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with only a port and isolate destination port
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateDestPort");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_DESTPORT);
+
+ // Test success with a negative isolate destination port, and plural
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 NoIsolateDestPorts");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT & ~ISO_DESTPORT);
+
+ // Test success with isolate destination address
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateDestAddr");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_DESTADDR);
+
+ // Test success with isolate socks AUTH
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateSOCKSAuth");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_SOCKSAUTH);
+
+ // Test success with isolate client protocol
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateClientProtocol");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_CLIENTPROTO);
+
+ // Test success with isolate client address
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateClientAddr");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_CLIENTADDR);
+
+ // Test success with ignored unknown options
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ config_port_valid = mock_config_line("DNSPort", "42 ThisOptionDoesntExist");
+ ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with no isolate socks AUTH
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 NoIsolateSOCKSAuth");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.socks_prefer_no_auth, OP_EQ, 1);
+
+ // Test success with prefer ipv6
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IPv6Traffic PreferIPv6");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, "127.0.0.42", 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.prefer_ipv6, OP_EQ, 1);
+
+ // Test success with cache ipv4 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 CacheIPv4DNS");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 0);
+
+ // Test success with cache ipv6 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 CacheIPv6DNS");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 1);
+
+ // Test success with no cache ipv4 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 NoCacheIPv4DNS");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 0);
+
+ // Test success with cache DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 CacheDNS");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 1);
+
+ // Test success with use cached ipv4 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 UseIPv4Cache");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv6_answers, OP_EQ, 0);
+
+ // Test success with use cached ipv6 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 UseIPv6Cache");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv4_answers, OP_EQ, 0);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv6_answers, OP_EQ, 1);
+
+ // Test success with use cached DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 UseDNSCache");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv6_answers, OP_EQ, 1);
+
+ // Test success with not preferring ipv6 automap
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 NoPreferIPv6Automap");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.prefer_ipv6_virtaddr, OP_EQ, 0);
+
+ // Test success with prefer SOCKS no auth
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 PreferSOCKSNoAuth");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.socks_prefer_no_auth, OP_EQ, 1);
+
+ // Test failure with both a zero port and a non-zero port
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "0");
+ config_port_valid = mock_config_line("DNSPort", "42");
+ config_port_invalid->next = config_port_valid;
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with warn non-local control
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_CONTROL_LISTENER, "127.0.0.42", 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with warn non-local listener
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_EXT_OR_LISTENER, "127.0.0.42", 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with warn non-local other
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with warn non-local other without out
+ ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with both ipv4 and ipv6 but without stream options
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IPv4Traffic "
+ "IPv6Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.44", 0,
+ CL_PORT_TAKES_HOSTNAMES |
+ CL_PORT_NO_STREAM_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0);
+
+ // Test failure for a SessionGroup argument with invalid value
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "42 SessionGroup=invalid");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.44", 0, CL_PORT_NO_STREAM_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // TODO: this seems wrong. Shouldn't it be the other way around?
+ // Potential bug.
+ // Test failure for a SessionGroup argument with valid value but with stream
+ // options allowed
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "42 SessionGroup=123");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.44", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure for more than one SessionGroup argument
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "42 SessionGroup=123 "
+ "SessionGroup=321");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.44", 0, CL_PORT_NO_STREAM_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with a sessiongroup options
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 SessionGroup=1111122");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.44", 0, CL_PORT_NO_STREAM_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.session_group, OP_EQ, 1111122);
+
+ // Test success with a zero unix domain socket, and doesnt add it to out
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "0");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.45", 0, CL_PORT_IS_UNIXSOCKET);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 0);
+
+ // Test success with a one unix domain socket, and doesnt add it to out
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "something");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.45", 0, CL_PORT_IS_UNIXSOCKET);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->is_unix_addr, OP_EQ, 1);
+ tt_str_op(port_cfg->unix_addr, OP_EQ, "something");
+
+ // Test success with a port of auto - it uses the default address
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "auto");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, CFG_AUTO_PORT);
+ tor_addr_parse(&addr, "127.0.0.46");
+ tt_assert(tor_addr_eq(&port_cfg->addr, &addr))
+
+ // Test success with parsing both an address and an auto port
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.122:auto");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, CFG_AUTO_PORT);
+ tor_addr_parse(&addr, "127.0.0.122");
+ tt_assert(tor_addr_eq(&port_cfg->addr, &addr))
+
+ // Test failure when asked to parse an invalid address followed by auto
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort", "invalidstuff!!:auto");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with parsing both an address and a real port
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.123:656");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, 656);
+ tor_addr_parse(&addr, "127.0.0.123");
+ tt_assert(tor_addr_eq(&port_cfg->addr, &addr))
+
+ // Test failure if we can't parse anything at all
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "something wrong");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure if we find both an address, a port and an auto
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "127.0.1.0:123:auto");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test that default to group writeable default sets group writeable for
+ // domain socket
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "unix:/tmp/somewhere");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, "127.0.0.46", 0,
+ CL_PORT_DFLT_GROUP_WRITABLE);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->is_group_writable, OP_EQ, 1);
+#endif
+
+ done:
+ smartlist_free(slout);
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+}
+
+static void
+test_config_parse_port_config__ports__server_options(void *data)
+{
+ (void)data;
+ int ret;
+ smartlist_t *slout = NULL;
+ port_cfg_t *port_cfg = NULL;
+ config_line_t *config_port_invalid = NULL, *config_port_valid = NULL;
+
+ slout = smartlist_new();
+
+ // Test success with NoAdvertise option
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ config_port_valid = mock_config_line("DNSPort",
+ "127.0.0.124:656 NoAdvertise");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->server_cfg.no_advertise, OP_EQ, 1);
+ tt_int_op(port_cfg->server_cfg.no_listen, OP_EQ, 0);
+
+ // Test success with NoListen option
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 NoListen");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->server_cfg.no_advertise, OP_EQ, 0);
+ tt_int_op(port_cfg->server_cfg.no_listen, OP_EQ, 1);
+
+ // Test failure with both NoAdvertise and NoListen option
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "127.0.0.124:656 NoListen "
+ "NoAdvertise");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with IPv4Only
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 IPv4Only");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->server_cfg.bind_ipv4_only, OP_EQ, 1);
+ tt_int_op(port_cfg->server_cfg.bind_ipv6_only, OP_EQ, 0);
+
+ // Test success with IPv6Only
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "[::1]:656 IPv6Only");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->server_cfg.bind_ipv4_only, OP_EQ, 0);
+ tt_int_op(port_cfg->server_cfg.bind_ipv6_only, OP_EQ, 1);
+
+ // Test failure with both IPv4Only and IPv6Only
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "127.0.0.124:656 IPv6Only "
+ "IPv4Only");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with invalid parameter
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 unknown");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+
+ // Test failure when asked to bind only to ipv6 but gets an ipv4 address
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort",
+ "127.0.0.124:656 IPv6Only");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure when asked to bind only to ipv4 but gets an ipv6 address
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "[::1]:656 IPv4Only");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ smartlist_free(slout);
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+}
+
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
struct testcase_t config_tests[] = {
+ CONFIG_TEST(adding_trusted_dir_server, TT_FORK),
+ CONFIG_TEST(adding_fallback_dir_server, TT_FORK),
+ CONFIG_TEST(parsing_trusted_dir_server, 0),
+ CONFIG_TEST(parsing_fallback_dir_server, 0),
+ CONFIG_TEST(adding_default_trusted_dir_servers, TT_FORK),
CONFIG_TEST(adding_dir_servers, TT_FORK),
+ CONFIG_TEST(default_dir_servers, TT_FORK),
+ CONFIG_TEST(default_fallback_dirs, 0),
CONFIG_TEST(resolve_my_address, TT_FORK),
CONFIG_TEST(addressmap, 0),
CONFIG_TEST(parse_bridge_line, 0),
@@ -3222,6 +4568,11 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(check_or_create_data_subdir, TT_FORK),
CONFIG_TEST(write_to_data_subdir, TT_FORK),
CONFIG_TEST(fix_my_family, 0),
+ CONFIG_TEST(directory_fetch, 0),
+ CONFIG_TEST(parse_port_config__listenaddress, 0),
+ CONFIG_TEST(parse_port_config__ports__no_ports_given, 0),
+ CONFIG_TEST(parse_port_config__ports__server_options, 0),
+ CONFIG_TEST(parse_port_config__ports__ports_given, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_connection.c b/src/test/test_connection.c
new file mode 100644
index 0000000000..c5ef92931d
--- /dev/null
+++ b/src/test/test_connection.c
@@ -0,0 +1,761 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#define CONNECTION_PRIVATE
+#define MAIN_PRIVATE
+
+#include "or.h"
+#include "test.h"
+
+#include "connection.h"
+#include "main.h"
+#include "networkstatus.h"
+#include "rendcache.h"
+#include "directory.h"
+
+static void test_conn_lookup_addr_helper(const char *address,
+ int family,
+ tor_addr_t *addr);
+
+static void * test_conn_get_basic_setup(const struct testcase_t *tc);
+static int test_conn_get_basic_teardown(const struct testcase_t *tc,
+ void *arg);
+
+static void * test_conn_get_rend_setup(const struct testcase_t *tc);
+static int test_conn_get_rend_teardown(const struct testcase_t *tc,
+ void *arg);
+
+static void * test_conn_get_rsrc_setup(const struct testcase_t *tc);
+static int test_conn_get_rsrc_teardown(const struct testcase_t *tc,
+ void *arg);
+
+/* Arbitrary choice - IPv4 Directory Connection to localhost */
+#define TEST_CONN_TYPE (CONN_TYPE_DIR)
+/* We assume every machine has IPv4 localhost, is that ok? */
+#define TEST_CONN_ADDRESS "127.0.0.1"
+#define TEST_CONN_PORT (12345)
+#define TEST_CONN_ADDRESS_PORT "127.0.0.1:12345"
+#define TEST_CONN_FAMILY (AF_INET)
+#define TEST_CONN_STATE (DIR_CONN_STATE_MIN_)
+#define TEST_CONN_ADDRESS_2 "127.0.0.2"
+
+#define TEST_CONN_BASIC_PURPOSE (DIR_PURPOSE_MIN_)
+
+#define TEST_CONN_REND_ADDR "cfs3rltphxxvabci"
+#define TEST_CONN_REND_PURPOSE (DIR_PURPOSE_FETCH_RENDDESC_V2)
+#define TEST_CONN_REND_PURPOSE_SUCCESSFUL (DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2)
+#define TEST_CONN_REND_TYPE_2 (CONN_TYPE_AP)
+#define TEST_CONN_REND_ADDR_2 "icbavxxhptlr3sfc"
+
+#define TEST_CONN_RSRC (networkstatus_get_flavor_name(FLAV_MICRODESC))
+#define TEST_CONN_RSRC_PURPOSE (DIR_PURPOSE_FETCH_CONSENSUS)
+#define TEST_CONN_RSRC_STATE_SUCCESSFUL (DIR_CONN_STATE_CLIENT_FINISHED)
+#define TEST_CONN_RSRC_2 (networkstatus_get_flavor_name(FLAV_NS))
+
+#define TEST_CONN_DL_STATE (DIR_CONN_STATE_CLIENT_SENDING)
+
+#define TEST_CONN_FD_INIT 50
+static int mock_connection_connect_sockaddr_called = 0;
+static int fake_socket_number = TEST_CONN_FD_INIT;
+
+static int
+mock_connection_connect_sockaddr(connection_t *conn,
+ const struct sockaddr *sa,
+ socklen_t sa_len,
+ const struct sockaddr *bindaddr,
+ socklen_t bindaddr_len,
+ int *socket_error)
+{
+ (void)sa_len;
+ (void)bindaddr;
+ (void)bindaddr_len;
+
+ tor_assert(conn);
+ tor_assert(sa);
+ tor_assert(socket_error);
+
+ mock_connection_connect_sockaddr_called++;
+
+ conn->s = fake_socket_number++;
+ tt_assert(SOCKET_OK(conn->s));
+ /* We really should call tor_libevent_initialize() here. Because we don't,
+ * we are relying on other parts of the code not checking if the_event_base
+ * (and therefore event->ev_base) is NULL. */
+ tt_assert(connection_add_connecting(conn) == 0);
+
+ done:
+ /* Fake "connected" status */
+ return 1;
+}
+
+static void
+test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr)
+{
+ int rv = 0;
+
+ tt_assert(addr);
+
+ rv = tor_addr_lookup(address, family, addr);
+ /* XXXX - should we retry on transient failure? */
+ tt_assert(rv == 0);
+ tt_assert(tor_addr_is_loopback(addr));
+ tt_assert(tor_addr_is_v4(addr));
+
+ return;
+
+ done:
+ tor_addr_make_null(addr, TEST_CONN_FAMILY);
+}
+
+static void *
+test_conn_get_basic_setup(const struct testcase_t *tc)
+{
+ connection_t *conn = NULL;
+ tor_addr_t addr;
+ int socket_err = 0;
+ int in_progress = 0;
+ (void)tc;
+
+ MOCK(connection_connect_sockaddr,
+ mock_connection_connect_sockaddr);
+
+ init_connection_lists();
+
+ conn = connection_new(TEST_CONN_TYPE, TEST_CONN_FAMILY);
+ tt_assert(conn);
+
+ test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
+ tt_assert(!tor_addr_is_null(&addr));
+
+ /* XXXX - connection_connect doesn't set these, should it? */
+ tor_addr_copy_tight(&conn->addr, &addr);
+ conn->port = TEST_CONN_PORT;
+ mock_connection_connect_sockaddr_called = 0;
+ in_progress = connection_connect(conn, TEST_CONN_ADDRESS_PORT, &addr,
+ TEST_CONN_PORT, &socket_err);
+ tt_assert(mock_connection_connect_sockaddr_called == 1);
+ tt_assert(!socket_err);
+ tt_assert(in_progress == 0 || in_progress == 1);
+
+ /* fake some of the attributes so the connection looks OK */
+ conn->state = TEST_CONN_STATE;
+ conn->purpose = TEST_CONN_BASIC_PURPOSE;
+ assert_connection_ok(conn, time(NULL));
+
+ UNMOCK(connection_connect_sockaddr);
+
+ return conn;
+
+ /* On failure */
+ done:
+ UNMOCK(connection_connect_sockaddr);
+ test_conn_get_basic_teardown(tc, conn);
+
+ /* Returning NULL causes the unit test to fail */
+ return NULL;
+}
+
+static int
+test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg)
+{
+ (void)tc;
+ connection_t *conn = arg;
+
+ tt_assert(conn);
+ assert_connection_ok(conn, time(NULL));
+
+ /* teardown the connection as fast as possible */
+ if (conn->linked_conn) {
+ assert_connection_ok(conn->linked_conn, time(NULL));
+
+ /* We didn't call tor_libevent_initialize(), so event_base was NULL,
+ * so we can't rely on connection_unregister_events() use of event_del().
+ */
+ if (conn->linked_conn->read_event) {
+ tor_free(conn->linked_conn->read_event);
+ conn->linked_conn->read_event = NULL;
+ }
+ if (conn->linked_conn->write_event) {
+ tor_free(conn->linked_conn->write_event);
+ conn->linked_conn->write_event = NULL;
+ }
+
+ if (!conn->linked_conn->marked_for_close) {
+ connection_close_immediate(conn->linked_conn);
+ connection_mark_for_close(conn->linked_conn);
+ }
+ conn->linked_conn->linked_conn = NULL;
+ connection_free(conn->linked_conn);
+ conn->linked_conn = NULL;
+ }
+
+ /* We didn't set the events up properly, so we can't use event_del() in
+ * close_closeable_connections() > connection_free()
+ * > connection_unregister_events() */
+ if (conn->read_event) {
+ tor_free(conn->read_event);
+ conn->read_event = NULL;
+ }
+ if (conn->write_event) {
+ tor_free(conn->write_event);
+ conn->write_event = NULL;
+ }
+
+ if (!conn->marked_for_close) {
+ connection_close_immediate(conn);
+ connection_mark_for_close(conn);
+ }
+
+ close_closeable_connections();
+
+ /* The unit test will fail if we return 0 */
+ return 1;
+
+ /* When conn == NULL, we can't cleanup anything */
+ done:
+ return 0;
+}
+
+static void *
+test_conn_get_rend_setup(const struct testcase_t *tc)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t,
+ test_conn_get_basic_setup(tc));
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ rend_cache_init();
+
+ /* TODO: use directory_initiate_command_rend() to do this - maybe? */
+ conn->rend_data = tor_malloc_zero(sizeof(rend_data_t));
+ tor_assert(strlen(TEST_CONN_REND_ADDR) == REND_SERVICE_ID_LEN_BASE32);
+ memcpy(conn->rend_data->onion_address,
+ TEST_CONN_REND_ADDR,
+ REND_SERVICE_ID_LEN_BASE32+1);
+ conn->rend_data->hsdirs_fp = smartlist_new();
+ conn->base_.purpose = TEST_CONN_REND_PURPOSE;
+
+ assert_connection_ok(&conn->base_, time(NULL));
+ return conn;
+
+ /* On failure */
+ done:
+ test_conn_get_rend_teardown(tc, conn);
+ /* Returning NULL causes the unit test to fail */
+ return NULL;
+}
+
+static int
+test_conn_get_rend_teardown(const struct testcase_t *tc, void *arg)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
+ int rv = 0;
+
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ /* avoid a last-ditch attempt to refetch the descriptor */
+ conn->base_.purpose = TEST_CONN_REND_PURPOSE_SUCCESSFUL;
+
+ /* connection_free_() cleans up rend_data */
+ rv = test_conn_get_basic_teardown(tc, arg);
+ done:
+ rend_cache_free_all();
+ return rv;
+}
+
+static void *
+test_conn_get_rsrc_setup(const struct testcase_t *tc)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t,
+ test_conn_get_basic_setup(tc));
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ /* TODO: use the canonical function to do this - maybe? */
+ conn->requested_resource = tor_strdup(TEST_CONN_RSRC);
+ conn->base_.purpose = TEST_CONN_RSRC_PURPOSE;
+
+ assert_connection_ok(&conn->base_, time(NULL));
+ return conn;
+
+ /* On failure */
+ done:
+ test_conn_get_rend_teardown(tc, conn);
+ /* Returning NULL causes the unit test to fail */
+ return NULL;
+}
+
+static int
+test_conn_get_rsrc_teardown(const struct testcase_t *tc, void *arg)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
+ int rv = 0;
+
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ /* avoid a last-ditch attempt to refetch the consensus */
+ conn->base_.state = TEST_CONN_RSRC_STATE_SUCCESSFUL;
+
+ /* connection_free_() cleans up requested_resource */
+ rv = test_conn_get_basic_teardown(tc, arg);
+ done:
+ return rv;
+}
+
+static void *
+test_conn_download_status_setup(const struct testcase_t *tc)
+{
+ (void)tc;
+
+ /* Don't return NULL, that causes the test to fail */
+ return (void*)"ok";
+}
+
+static int
+test_conn_download_status_teardown(const struct testcase_t *tc, void *arg)
+{
+ (void)arg;
+ int rv = 0;
+
+ /* Ignore arg, and just loop through the connection array */
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ if (conn) {
+ assert_connection_ok(conn, time(NULL));
+
+ /* connection_free_() cleans up requested_resource */
+ rv = test_conn_get_rsrc_teardown(tc, conn);
+ tt_assert(rv == 1);
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ done:
+ return rv;
+}
+
+static dir_connection_t *
+test_conn_download_status_add_a_connection(void)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t,
+ test_conn_get_rsrc_setup(NULL));
+
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ return conn;
+
+ done:
+ test_conn_download_status_teardown(NULL, NULL);
+ return NULL;
+}
+
+static struct testcase_setup_t test_conn_get_basic_st = {
+ test_conn_get_basic_setup, test_conn_get_basic_teardown
+};
+
+static struct testcase_setup_t test_conn_get_rend_st = {
+ test_conn_get_rend_setup, test_conn_get_rend_teardown
+};
+
+static struct testcase_setup_t test_conn_get_rsrc_st = {
+ test_conn_get_rsrc_setup, test_conn_get_rsrc_teardown
+};
+
+static struct testcase_setup_t test_conn_download_status_st = {
+ test_conn_download_status_setup, test_conn_download_status_teardown
+};
+
+static void
+test_conn_get_basic(void *arg)
+{
+ connection_t *conn = (connection_t*)arg;
+ tor_addr_t addr, addr2;
+
+ tt_assert(conn);
+ assert_connection_ok(conn, time(NULL));
+
+ test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
+ tt_assert(!tor_addr_is_null(&addr));
+ test_conn_lookup_addr_helper(TEST_CONN_ADDRESS_2, TEST_CONN_FAMILY, &addr2);
+ tt_assert(!tor_addr_is_null(&addr2));
+
+ /* Check that we get this connection back when we search for it by
+ * its attributes, but get NULL when we supply a different value. */
+
+ tt_assert(connection_get_by_global_id(conn->global_identifier) == conn);
+ tt_assert(connection_get_by_global_id(!conn->global_identifier) == NULL);
+
+ tt_assert(connection_get_by_type(conn->type) == conn);
+ tt_assert(connection_get_by_type(TEST_CONN_TYPE) == conn);
+ tt_assert(connection_get_by_type(!conn->type) == NULL);
+ tt_assert(connection_get_by_type(!TEST_CONN_TYPE) == NULL);
+
+ tt_assert(connection_get_by_type_state(conn->type, conn->state)
+ == conn);
+ tt_assert(connection_get_by_type_state(TEST_CONN_TYPE, TEST_CONN_STATE)
+ == conn);
+ tt_assert(connection_get_by_type_state(!conn->type, !conn->state)
+ == NULL);
+ tt_assert(connection_get_by_type_state(!TEST_CONN_TYPE, !TEST_CONN_STATE)
+ == NULL);
+
+ /* Match on the connection fields themselves */
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ conn->port,
+ conn->purpose)
+ == conn);
+ /* Match on the original inputs to the connection */
+ tt_assert(connection_get_by_type_addr_port_purpose(TEST_CONN_TYPE,
+ &conn->addr,
+ conn->port,
+ conn->purpose)
+ == conn);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &addr,
+ conn->port,
+ conn->purpose)
+ == conn);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ TEST_CONN_PORT,
+ conn->purpose)
+ == conn);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ conn->port,
+ TEST_CONN_BASIC_PURPOSE)
+ == conn);
+ tt_assert(connection_get_by_type_addr_port_purpose(TEST_CONN_TYPE,
+ &addr,
+ TEST_CONN_PORT,
+ TEST_CONN_BASIC_PURPOSE)
+ == conn);
+ /* Then try each of the not-matching combinations */
+ tt_assert(connection_get_by_type_addr_port_purpose(!conn->type,
+ &conn->addr,
+ conn->port,
+ conn->purpose)
+ == NULL);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &addr2,
+ conn->port,
+ conn->purpose)
+ == NULL);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ !conn->port,
+ conn->purpose)
+ == NULL);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ conn->port,
+ !conn->purpose)
+ == NULL);
+ /* Then try everything not-matching */
+ tt_assert(connection_get_by_type_addr_port_purpose(!conn->type,
+ &addr2,
+ !conn->port,
+ !conn->purpose)
+ == NULL);
+ tt_assert(connection_get_by_type_addr_port_purpose(!TEST_CONN_TYPE,
+ &addr2,
+ !TEST_CONN_PORT,
+ !TEST_CONN_BASIC_PURPOSE)
+ == NULL);
+
+ done:
+ ;
+}
+
+static void
+test_conn_get_rend(void *arg)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ tt_assert(connection_get_by_type_state_rendquery(
+ conn->base_.type,
+ conn->base_.state,
+ conn->rend_data->onion_address)
+ == TO_CONN(conn));
+ tt_assert(connection_get_by_type_state_rendquery(
+ TEST_CONN_TYPE,
+ TEST_CONN_STATE,
+ TEST_CONN_REND_ADDR)
+ == TO_CONN(conn));
+ tt_assert(connection_get_by_type_state_rendquery(TEST_CONN_REND_TYPE_2,
+ !conn->base_.state,
+ "")
+ == NULL);
+ tt_assert(connection_get_by_type_state_rendquery(TEST_CONN_REND_TYPE_2,
+ !TEST_CONN_STATE,
+ TEST_CONN_REND_ADDR_2)
+ == NULL);
+
+ done:
+ ;
+}
+
+#define sl_is_conn_assert(sl_input, conn) \
+ do { \
+ the_sl = (sl_input); \
+ tt_assert(smartlist_len((the_sl)) == 1); \
+ tt_assert(smartlist_get((the_sl), 0) == (conn)); \
+ smartlist_free(the_sl); the_sl = NULL; \
+ } while (0)
+
+#define sl_no_conn_assert(sl_input) \
+ do { \
+ the_sl = (sl_input); \
+ tt_assert(smartlist_len((the_sl)) == 0); \
+ smartlist_free(the_sl); the_sl = NULL; \
+ } while (0)
+
+static void
+test_conn_get_rsrc(void *arg)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
+ smartlist_t *the_sl = NULL;
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ tt_assert(connection_dir_get_by_purpose_and_resource(
+ conn->base_.purpose,
+ conn->requested_resource)
+ == conn);
+ tt_assert(connection_dir_get_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC)
+ == conn);
+ tt_assert(connection_dir_get_by_purpose_and_resource(
+ !conn->base_.purpose,
+ "")
+ == NULL);
+ tt_assert(connection_dir_get_by_purpose_and_resource(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2)
+ == NULL);
+
+ tt_assert(connection_dir_get_by_purpose_resource_and_state(
+ conn->base_.purpose,
+ conn->requested_resource,
+ conn->base_.state)
+ == conn);
+ tt_assert(connection_dir_get_by_purpose_resource_and_state(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC,
+ TEST_CONN_STATE)
+ == conn);
+ tt_assert(connection_dir_get_by_purpose_resource_and_state(
+ !conn->base_.purpose,
+ "",
+ !conn->base_.state)
+ == NULL);
+ tt_assert(connection_dir_get_by_purpose_resource_and_state(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2,
+ !TEST_CONN_STATE)
+ == NULL);
+
+ sl_is_conn_assert(connection_dir_list_by_purpose_and_resource(
+ conn->base_.purpose,
+ conn->requested_resource),
+ conn);
+ sl_is_conn_assert(connection_dir_list_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC),
+ conn);
+ sl_no_conn_assert(connection_dir_list_by_purpose_and_resource(
+ !conn->base_.purpose,
+ ""));
+ sl_no_conn_assert(connection_dir_list_by_purpose_and_resource(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2));
+
+ sl_is_conn_assert(connection_dir_list_by_purpose_resource_and_state(
+ conn->base_.purpose,
+ conn->requested_resource,
+ conn->base_.state),
+ conn);
+ sl_is_conn_assert(connection_dir_list_by_purpose_resource_and_state(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC,
+ TEST_CONN_STATE),
+ conn);
+ sl_no_conn_assert(connection_dir_list_by_purpose_resource_and_state(
+ !conn->base_.purpose,
+ "",
+ !conn->base_.state));
+ sl_no_conn_assert(connection_dir_list_by_purpose_resource_and_state(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2,
+ !TEST_CONN_STATE));
+
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ conn->base_.purpose,
+ conn->requested_resource)
+ == 1);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC)
+ == 1);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ !conn->base_.purpose,
+ "")
+ == 0);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2)
+ == 0);
+
+ tt_assert(connection_dir_count_by_purpose_resource_and_state(
+ conn->base_.purpose,
+ conn->requested_resource,
+ conn->base_.state)
+ == 1);
+ tt_assert(connection_dir_count_by_purpose_resource_and_state(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC,
+ TEST_CONN_STATE)
+ == 1);
+ tt_assert(connection_dir_count_by_purpose_resource_and_state(
+ !conn->base_.purpose,
+ "",
+ !conn->base_.state)
+ == 0);
+ tt_assert(connection_dir_count_by_purpose_resource_and_state(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2,
+ !TEST_CONN_STATE)
+ == 0);
+
+ done:
+ smartlist_free(the_sl);
+}
+
+static void
+test_conn_download_status(void *arg)
+{
+ (void)arg;
+ dir_connection_t *conn = NULL;
+ dir_connection_t *conn2 = NULL;
+ dir_connection_t *conn3 = NULL;
+
+ /* no connections, no excess, not downloading */
+ tt_assert(networkstatus_consensus_has_excess_connections() == 0);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 0);
+
+ /* one connection, no excess, not downloading */
+ conn = test_conn_download_status_add_a_connection();
+ tt_assert(networkstatus_consensus_has_excess_connections() == 0);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 0);
+
+ /* one connection, no excess, but downloading */
+ conn->base_.state = TEST_CONN_DL_STATE;
+ tt_assert(networkstatus_consensus_has_excess_connections() == 0);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+ conn->base_.state = TEST_CONN_STATE;
+
+ /* two connections, excess, but not downloading */
+ conn2 = test_conn_download_status_add_a_connection();
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 0);
+
+ /* two connections, excess, downloading */
+ conn2->base_.state = TEST_CONN_DL_STATE;
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+ conn2->base_.state = TEST_CONN_STATE;
+
+ /* more connections, excess, but not downloading */
+ conn3 = test_conn_download_status_add_a_connection();
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 0);
+
+ /* more connections, excess, downloading */
+ conn3->base_.state = TEST_CONN_DL_STATE;
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* more connections, more downloading */
+ conn2->base_.state = TEST_CONN_DL_STATE;
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* now try closing the one that isn't downloading:
+ * these tests won't work unless tor thinks it is bootstrapping */
+ tt_assert(networkstatus_consensus_is_boostrapping(time(NULL)));
+
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 3);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+ tt_assert(connection_dir_close_consensus_conn_if_extra(conn) == -1);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 2);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* now try closing one that is already closed - nothing happens */
+ tt_assert(connection_dir_close_consensus_conn_if_extra(conn) == 0);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 2);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* now try closing one that is downloading - it stays open */
+ tt_assert(connection_dir_close_consensus_conn_if_extra(conn2) == 0);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 2);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* now try closing all excess connections */
+ connection_dir_close_extra_consensus_conns();
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ done:
+ /* the teardown function removes all the connections */;
+}
+
+#define CONNECTION_TESTCASE(name, fork, setup) \
+ { #name, test_conn_##name, fork, &setup, NULL }
+
+struct testcase_t connection_tests[] = {
+ CONNECTION_TESTCASE(get_basic, TT_FORK, test_conn_get_basic_st),
+ CONNECTION_TESTCASE(get_rend, TT_FORK, test_conn_get_rend_st),
+ CONNECTION_TESTCASE(get_rsrc, TT_FORK, test_conn_get_rsrc_st),
+ CONNECTION_TESTCASE(download_status, TT_FORK, test_conn_download_status_st),
+//CONNECTION_TESTCASE(func_suffix, TT_FORK, setup_func_pair),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index dbaec61ee9..c6684e285e 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -5,6 +5,7 @@
#include "orconfig.h"
#define CRYPTO_CURVE25519_PRIVATE
+#define CRYPTO_PRIVATE
#include "or.h"
#include "test.h"
#include "aes.h"
@@ -15,6 +16,7 @@
#include "ed25519_vectors.inc"
#include <openssl/evp.h>
+#include <openssl/rand.h>
extern const char AUTHORITY_SIGNKEY_3[];
extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
@@ -131,6 +133,38 @@ test_crypto_rng_range(void *arg)
;
}
+/* 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 AES functionality */
static void
test_crypto_aes(void *arg)
@@ -284,10 +318,11 @@ test_crypto_sha(void *arg)
{
crypto_digest_t *d1 = NULL, *d2 = NULL;
int i;
- char key[160];
- char digest[32];
- char data[50];
- char d_out1[DIGEST_LEN], d_out2[DIGEST256_LEN];
+#define RFC_4231_MAX_KEY_SIZE 131
+ char key[RFC_4231_MAX_KEY_SIZE];
+ char digest[DIGEST256_LEN];
+ char data[DIGEST512_LEN];
+ char d_out1[DIGEST512_LEN], d_out2[DIGEST512_LEN];
char *mem_op_hex_tmp=NULL;
/* Test SHA-1 with a test vector from the specification. */
@@ -302,6 +337,13 @@ test_crypto_sha(void *arg)
"96177A9CB410FF61F20015AD");
tt_int_op(i, OP_EQ, 0);
+ /* Test SHA-512 with a test vector from the specification. */
+ i = crypto_digest512(data, "abc", 3, DIGEST_SHA512);
+ test_memeq_hex(data, "ddaf35a193617abacc417349ae20413112e6fa4e89a97"
+ "ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3"
+ "feebbd454d4423643ce80e2a9ac94fa54ca49f");
+ tt_int_op(i, OP_EQ, 0);
+
/* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */
/* Case empty (wikipedia) */
@@ -378,15 +420,15 @@ test_crypto_sha(void *arg)
d2 = crypto_digest_dup(d1);
tt_assert(d2);
crypto_digest_add_bytes(d2, "ghijkl", 6);
- crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d2, d_out1, DIGEST_LEN);
crypto_digest(d_out2, "abcdefghijkl", 12);
tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
crypto_digest_assign(d2, d1);
crypto_digest_add_bytes(d2, "mno", 3);
- crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d2, d_out1, DIGEST_LEN);
crypto_digest(d_out2, "abcdefmno", 9);
tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
- crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d1, d_out1, DIGEST_LEN);
crypto_digest(d_out2, "abcdef", 6);
tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
crypto_digest_free(d1);
@@ -399,17 +441,38 @@ test_crypto_sha(void *arg)
d2 = crypto_digest_dup(d1);
tt_assert(d2);
crypto_digest_add_bytes(d2, "ghijkl", 6);
- crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d2, d_out1, DIGEST256_LEN);
crypto_digest256(d_out2, "abcdefghijkl", 12, DIGEST_SHA256);
- tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
crypto_digest_assign(d2, d1);
crypto_digest_add_bytes(d2, "mno", 3);
- crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d2, d_out1, DIGEST256_LEN);
crypto_digest256(d_out2, "abcdefmno", 9, DIGEST_SHA256);
- tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
- crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_get_digest(d1, d_out1, DIGEST256_LEN);
crypto_digest256(d_out2, "abcdef", 6, DIGEST_SHA256);
- tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_free(d1);
+ crypto_digest_free(d2);
+
+ /* Incremental digest code with sha512 */
+ d1 = crypto_digest512_new(DIGEST_SHA512);
+ tt_assert(d1);
+ crypto_digest_add_bytes(d1, "abcdef", 6);
+ d2 = crypto_digest_dup(d1);
+ tt_assert(d2);
+ crypto_digest_add_bytes(d2, "ghijkl", 6);
+ crypto_digest_get_digest(d2, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdefghijkl", 12, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_assign(d2, d1);
+ crypto_digest_add_bytes(d2, "mno", 3);
+ crypto_digest_get_digest(d2, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdefmno", 9, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_get_digest(d1, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdef", 6, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
done:
if (d1)
@@ -419,6 +482,394 @@ test_crypto_sha(void *arg)
tor_free(mem_op_hex_tmp);
}
+static void
+test_crypto_sha3(void *arg)
+{
+ crypto_digest_t *d1 = NULL, *d2 = NULL;
+ int i;
+ char data[DIGEST512_LEN];
+ char d_out1[DIGEST512_LEN], d_out2[DIGEST512_LEN];
+ char *mem_op_hex_tmp=NULL;
+ char *large = NULL;
+
+ (void)arg;
+
+ /* Test SHA3-[256,512] with a test vectors from the Keccak Code Package.
+ *
+ * NB: The code package's test vectors have length expressed in bits.
+ */
+
+ /* Len = 8, Msg = CC */
+ const uint8_t keccak_kat_msg8[] = { 0xcc };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg8, 1, DIGEST_SHA3_256);
+ test_memeq_hex(data, "677035391CD3701293D385F037BA3279"
+ "6252BB7CE180B00B582DD9B20AAAD7F0");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg8, 1, DIGEST_SHA3_512);
+ test_memeq_hex(data, "3939FCC8B57B63612542DA31A834E5DC"
+ "C36E2EE0F652AC72E02624FA2E5ADEEC"
+ "C7DD6BB3580224B4D6138706FC6E8059"
+ "7B528051230B00621CC2B22999EAA205");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 24, Msg = 1F877C */
+ const uint8_t keccak_kat_msg24[] = { 0x1f, 0x87, 0x7c };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg24, 3,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "BC22345E4BD3F792A341CF18AC0789F1"
+ "C9C966712A501B19D1B6632CCD408EC5");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg24, 3,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "CB20DCF54955F8091111688BECCEF48C"
+ "1A2F0D0608C3A575163751F002DB30F4"
+ "0F2F671834B22D208591CFAF1F5ECFE4"
+ "3C49863A53B3225BDFD7C6591BA7658B");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1080, Msg = B771D5CEF... ...C35AC81B5 (SHA3-256 rate - 1) */
+ const uint8_t keccak_kat_msg1080[] = {
+ 0xB7, 0x71, 0xD5, 0xCE, 0xF5, 0xD1, 0xA4, 0x1A, 0x93, 0xD1,
+ 0x56, 0x43, 0xD7, 0x18, 0x1D, 0x2A, 0x2E, 0xF0, 0xA8, 0xE8,
+ 0x4D, 0x91, 0x81, 0x2F, 0x20, 0xED, 0x21, 0xF1, 0x47, 0xBE,
+ 0xF7, 0x32, 0xBF, 0x3A, 0x60, 0xEF, 0x40, 0x67, 0xC3, 0x73,
+ 0x4B, 0x85, 0xBC, 0x8C, 0xD4, 0x71, 0x78, 0x0F, 0x10, 0xDC,
+ 0x9E, 0x82, 0x91, 0xB5, 0x83, 0x39, 0xA6, 0x77, 0xB9, 0x60,
+ 0x21, 0x8F, 0x71, 0xE7, 0x93, 0xF2, 0x79, 0x7A, 0xEA, 0x34,
+ 0x94, 0x06, 0x51, 0x28, 0x29, 0x06, 0x5D, 0x37, 0xBB, 0x55,
+ 0xEA, 0x79, 0x6F, 0xA4, 0xF5, 0x6F, 0xD8, 0x89, 0x6B, 0x49,
+ 0xB2, 0xCD, 0x19, 0xB4, 0x32, 0x15, 0xAD, 0x96, 0x7C, 0x71,
+ 0x2B, 0x24, 0xE5, 0x03, 0x2D, 0x06, 0x52, 0x32, 0xE0, 0x2C,
+ 0x12, 0x74, 0x09, 0xD2, 0xED, 0x41, 0x46, 0xB9, 0xD7, 0x5D,
+ 0x76, 0x3D, 0x52, 0xDB, 0x98, 0xD9, 0x49, 0xD3, 0xB0, 0xFE,
+ 0xD6, 0xA8, 0x05, 0x2F, 0xBB,
+ };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1080, 135,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "A19EEE92BB2097B64E823D597798AA18"
+ "BE9B7C736B8059ABFD6779AC35AC81B5");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1080, 135,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "7575A1FB4FC9A8F9C0466BD5FCA496D1"
+ "CB78696773A212A5F62D02D14E3259D1"
+ "92A87EBA4407DD83893527331407B6DA"
+ "DAAD920DBC46489B677493CE5F20B595");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1088, Msg = B32D95B0... ...8E380C04 (SHA3-256 rate) */
+ const uint8_t keccak_kat_msg1088[] = {
+ 0xB3, 0x2D, 0x95, 0xB0, 0xB9, 0xAA, 0xD2, 0xA8, 0x81, 0x6D,
+ 0xE6, 0xD0, 0x6D, 0x1F, 0x86, 0x00, 0x85, 0x05, 0xBD, 0x8C,
+ 0x14, 0x12, 0x4F, 0x6E, 0x9A, 0x16, 0x3B, 0x5A, 0x2A, 0xDE,
+ 0x55, 0xF8, 0x35, 0xD0, 0xEC, 0x38, 0x80, 0xEF, 0x50, 0x70,
+ 0x0D, 0x3B, 0x25, 0xE4, 0x2C, 0xC0, 0xAF, 0x05, 0x0C, 0xCD,
+ 0x1B, 0xE5, 0xE5, 0x55, 0xB2, 0x30, 0x87, 0xE0, 0x4D, 0x7B,
+ 0xF9, 0x81, 0x36, 0x22, 0x78, 0x0C, 0x73, 0x13, 0xA1, 0x95,
+ 0x4F, 0x87, 0x40, 0xB6, 0xEE, 0x2D, 0x3F, 0x71, 0xF7, 0x68,
+ 0xDD, 0x41, 0x7F, 0x52, 0x04, 0x82, 0xBD, 0x3A, 0x08, 0xD4,
+ 0xF2, 0x22, 0xB4, 0xEE, 0x9D, 0xBD, 0x01, 0x54, 0x47, 0xB3,
+ 0x35, 0x07, 0xDD, 0x50, 0xF3, 0xAB, 0x42, 0x47, 0xC5, 0xDE,
+ 0x9A, 0x8A, 0xBD, 0x62, 0xA8, 0xDE, 0xCE, 0xA0, 0x1E, 0x3B,
+ 0x87, 0xC8, 0xB9, 0x27, 0xF5, 0xB0, 0x8B, 0xEB, 0x37, 0x67,
+ 0x4C, 0x6F, 0x8E, 0x38, 0x0C, 0x04,
+ };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1088, 136,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "DF673F4105379FF6B755EEAB20CEB0DC"
+ "77B5286364FE16C59CC8A907AFF07732");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1088, 136,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "2E293765022D48996CE8EFF0BE54E87E"
+ "FB94A14C72DE5ACD10D0EB5ECE029CAD"
+ "FA3BA17A40B2FFA2163991B17786E51C"
+ "ABA79E5E0FFD34CF085E2A098BE8BACB");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1096, Msg = 04410E310... ...601016A0D (SHA3-256 rate + 1) */
+ const uint8_t keccak_kat_msg1096[] = {
+ 0x04, 0x41, 0x0E, 0x31, 0x08, 0x2A, 0x47, 0x58, 0x4B, 0x40,
+ 0x6F, 0x05, 0x13, 0x98, 0xA6, 0xAB, 0xE7, 0x4E, 0x4D, 0xA5,
+ 0x9B, 0xB6, 0xF8, 0x5E, 0x6B, 0x49, 0xE8, 0xA1, 0xF7, 0xF2,
+ 0xCA, 0x00, 0xDF, 0xBA, 0x54, 0x62, 0xC2, 0xCD, 0x2B, 0xFD,
+ 0xE8, 0xB6, 0x4F, 0xB2, 0x1D, 0x70, 0xC0, 0x83, 0xF1, 0x13,
+ 0x18, 0xB5, 0x6A, 0x52, 0xD0, 0x3B, 0x81, 0xCA, 0xC5, 0xEE,
+ 0xC2, 0x9E, 0xB3, 0x1B, 0xD0, 0x07, 0x8B, 0x61, 0x56, 0x78,
+ 0x6D, 0xA3, 0xD6, 0xD8, 0xC3, 0x30, 0x98, 0xC5, 0xC4, 0x7B,
+ 0xB6, 0x7A, 0xC6, 0x4D, 0xB1, 0x41, 0x65, 0xAF, 0x65, 0xB4,
+ 0x45, 0x44, 0xD8, 0x06, 0xDD, 0xE5, 0xF4, 0x87, 0xD5, 0x37,
+ 0x3C, 0x7F, 0x97, 0x92, 0xC2, 0x99, 0xE9, 0x68, 0x6B, 0x7E,
+ 0x58, 0x21, 0xE7, 0xC8, 0xE2, 0x45, 0x83, 0x15, 0xB9, 0x96,
+ 0xB5, 0x67, 0x7D, 0x92, 0x6D, 0xAC, 0x57, 0xB3, 0xF2, 0x2D,
+ 0xA8, 0x73, 0xC6, 0x01, 0x01, 0x6A, 0x0D,
+ };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1096, 137,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "D52432CF3B6B4B949AA848E058DCD62D"
+ "735E0177279222E7AC0AF8504762FAA0");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1096, 137,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "BE8E14B6757FFE53C9B75F6DDE9A7B6C"
+ "40474041DE83D4A60645A826D7AF1ABE"
+ "1EEFCB7B74B62CA6A514E5F2697D585B"
+ "FECECE12931BBE1D4ED7EBF7B0BE660E");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1144, Msg = EA40E83C... ...66DFAFEC (SHA3-512 rate *2 - 1) */
+ const uint8_t keccak_kat_msg1144[] = {
+ 0xEA, 0x40, 0xE8, 0x3C, 0xB1, 0x8B, 0x3A, 0x24, 0x2C, 0x1E,
+ 0xCC, 0x6C, 0xCD, 0x0B, 0x78, 0x53, 0xA4, 0x39, 0xDA, 0xB2,
+ 0xC5, 0x69, 0xCF, 0xC6, 0xDC, 0x38, 0xA1, 0x9F, 0x5C, 0x90,
+ 0xAC, 0xBF, 0x76, 0xAE, 0xF9, 0xEA, 0x37, 0x42, 0xFF, 0x3B,
+ 0x54, 0xEF, 0x7D, 0x36, 0xEB, 0x7C, 0xE4, 0xFF, 0x1C, 0x9A,
+ 0xB3, 0xBC, 0x11, 0x9C, 0xFF, 0x6B, 0xE9, 0x3C, 0x03, 0xE2,
+ 0x08, 0x78, 0x33, 0x35, 0xC0, 0xAB, 0x81, 0x37, 0xBE, 0x5B,
+ 0x10, 0xCD, 0xC6, 0x6F, 0xF3, 0xF8, 0x9A, 0x1B, 0xDD, 0xC6,
+ 0xA1, 0xEE, 0xD7, 0x4F, 0x50, 0x4C, 0xBE, 0x72, 0x90, 0x69,
+ 0x0B, 0xB2, 0x95, 0xA8, 0x72, 0xB9, 0xE3, 0xFE, 0x2C, 0xEE,
+ 0x9E, 0x6C, 0x67, 0xC4, 0x1D, 0xB8, 0xEF, 0xD7, 0xD8, 0x63,
+ 0xCF, 0x10, 0xF8, 0x40, 0xFE, 0x61, 0x8E, 0x79, 0x36, 0xDA,
+ 0x3D, 0xCA, 0x5C, 0xA6, 0xDF, 0x93, 0x3F, 0x24, 0xF6, 0x95,
+ 0x4B, 0xA0, 0x80, 0x1A, 0x12, 0x94, 0xCD, 0x8D, 0x7E, 0x66,
+ 0xDF, 0xAF, 0xEC,
+ };
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1144, 143,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "3A8E938C45F3F177991296B24565D9A6"
+ "605516615D96A062C8BE53A0D6C5A648"
+ "7BE35D2A8F3CF6620D0C2DBA2C560D68"
+ "295F284BE7F82F3B92919033C9CE5D80");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1144, 143,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "E58A947E98D6DD7E932D2FE02D9992E6"
+ "118C0C2C606BDCDA06E7943D2C95E0E5");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1152, Msg = 157D5B7E... ...79EE00C63 (SHA3-512 rate * 2) */
+ const uint8_t keccak_kat_msg1152[] = {
+ 0x15, 0x7D, 0x5B, 0x7E, 0x45, 0x07, 0xF6, 0x6D, 0x9A, 0x26,
+ 0x74, 0x76, 0xD3, 0x38, 0x31, 0xE7, 0xBB, 0x76, 0x8D, 0x4D,
+ 0x04, 0xCC, 0x34, 0x38, 0xDA, 0x12, 0xF9, 0x01, 0x02, 0x63,
+ 0xEA, 0x5F, 0xCA, 0xFB, 0xDE, 0x25, 0x79, 0xDB, 0x2F, 0x6B,
+ 0x58, 0xF9, 0x11, 0xD5, 0x93, 0xD5, 0xF7, 0x9F, 0xB0, 0x5F,
+ 0xE3, 0x59, 0x6E, 0x3F, 0xA8, 0x0F, 0xF2, 0xF7, 0x61, 0xD1,
+ 0xB0, 0xE5, 0x70, 0x80, 0x05, 0x5C, 0x11, 0x8C, 0x53, 0xE5,
+ 0x3C, 0xDB, 0x63, 0x05, 0x52, 0x61, 0xD7, 0xC9, 0xB2, 0xB3,
+ 0x9B, 0xD9, 0x0A, 0xCC, 0x32, 0x52, 0x0C, 0xBB, 0xDB, 0xDA,
+ 0x2C, 0x4F, 0xD8, 0x85, 0x6D, 0xBC, 0xEE, 0x17, 0x31, 0x32,
+ 0xA2, 0x67, 0x91, 0x98, 0xDA, 0xF8, 0x30, 0x07, 0xA9, 0xB5,
+ 0xC5, 0x15, 0x11, 0xAE, 0x49, 0x76, 0x6C, 0x79, 0x2A, 0x29,
+ 0x52, 0x03, 0x88, 0x44, 0x4E, 0xBE, 0xFE, 0x28, 0x25, 0x6F,
+ 0xB3, 0x3D, 0x42, 0x60, 0x43, 0x9C, 0xBA, 0x73, 0xA9, 0x47,
+ 0x9E, 0xE0, 0x0C, 0x63,
+ };
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1152, 144,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "FE45289874879720CE2A844AE34BB735"
+ "22775DCB6019DCD22B8885994672A088"
+ "9C69E8115C641DC8B83E39F7311815A1"
+ "64DC46E0BA2FCA344D86D4BC2EF2532C");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1152, 144,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "A936FB9AF87FB67857B3EAD5C76226AD"
+ "84DA47678F3C2FFE5A39FDB5F7E63FFB");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1160, Msg = 836B34B5... ...11044C53 (SHA3-512 rate * 2 + 1) */
+ const uint8_t keccak_kat_msg1160[] = {
+ 0x83, 0x6B, 0x34, 0xB5, 0x15, 0x47, 0x6F, 0x61, 0x3F, 0xE4,
+ 0x47, 0xA4, 0xE0, 0xC3, 0xF3, 0xB8, 0xF2, 0x09, 0x10, 0xAC,
+ 0x89, 0xA3, 0x97, 0x70, 0x55, 0xC9, 0x60, 0xD2, 0xD5, 0xD2,
+ 0xB7, 0x2B, 0xD8, 0xAC, 0xC7, 0x15, 0xA9, 0x03, 0x53, 0x21,
+ 0xB8, 0x67, 0x03, 0xA4, 0x11, 0xDD, 0xE0, 0x46, 0x6D, 0x58,
+ 0xA5, 0x97, 0x69, 0x67, 0x2A, 0xA6, 0x0A, 0xD5, 0x87, 0xB8,
+ 0x48, 0x1D, 0xE4, 0xBB, 0xA5, 0x52, 0xA1, 0x64, 0x57, 0x79,
+ 0x78, 0x95, 0x01, 0xEC, 0x53, 0xD5, 0x40, 0xB9, 0x04, 0x82,
+ 0x1F, 0x32, 0xB0, 0xBD, 0x18, 0x55, 0xB0, 0x4E, 0x48, 0x48,
+ 0xF9, 0xF8, 0xCF, 0xE9, 0xEB, 0xD8, 0x91, 0x1B, 0xE9, 0x57,
+ 0x81, 0xA7, 0x59, 0xD7, 0xAD, 0x97, 0x24, 0xA7, 0x10, 0x2D,
+ 0xBE, 0x57, 0x67, 0x76, 0xB7, 0xC6, 0x32, 0xBC, 0x39, 0xB9,
+ 0xB5, 0xE1, 0x90, 0x57, 0xE2, 0x26, 0x55, 0x2A, 0x59, 0x94,
+ 0xC1, 0xDB, 0xB3, 0xB5, 0xC7, 0x87, 0x1A, 0x11, 0xF5, 0x53,
+ 0x70, 0x11, 0x04, 0x4C, 0x53,
+ };
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1160, 145,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "AFF61C6E11B98E55AC213B1A0BC7DE04"
+ "05221AC5EFB1229842E4614F4A029C9B"
+ "D14A0ED7FD99AF3681429F3F309FDB53"
+ "166AA9A3CD9F1F1223D04B4A9015E94A");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1160, 145,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "3A654B88F88086C2751EDAE6D3924814"
+ "3CF6235C6B0B7969342C45A35194B67E");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* SHA3-[256,512] Empty case (wikipedia) */
+ i = crypto_digest256(data, "", 0, DIGEST_SHA3_256);
+ test_memeq_hex(data, "a7ffc6f8bf1ed76651c14756a061d662"
+ "f580ff4de43b49fa82d80a4b80f8434a");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, "", 0, DIGEST_SHA3_512);
+ test_memeq_hex(data, "a69f73cca23a9ac5c8b567dc185a756e"
+ "97c982164fe25859e0d1dcc1475c80a6"
+ "15b2123af1f5f94c11e3e9402c3ac558"
+ "f500199d95b6d3e301758586281dcd26");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Incremental digest code with SHA3-256 */
+ d1 = crypto_digest256_new(DIGEST_SHA3_256);
+ tt_assert(d1);
+ crypto_digest_add_bytes(d1, "abcdef", 6);
+ d2 = crypto_digest_dup(d1);
+ tt_assert(d2);
+ crypto_digest_add_bytes(d2, "ghijkl", 6);
+ crypto_digest_get_digest(d2, d_out1, DIGEST256_LEN);
+ crypto_digest256(d_out2, "abcdefghijkl", 12, DIGEST_SHA3_256);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_assign(d2, d1);
+ crypto_digest_add_bytes(d2, "mno", 3);
+ crypto_digest_get_digest(d2, d_out1, DIGEST256_LEN);
+ crypto_digest256(d_out2, "abcdefmno", 9, DIGEST_SHA3_256);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_get_digest(d1, d_out1, DIGEST256_LEN);
+ crypto_digest256(d_out2, "abcdef", 6, DIGEST_SHA3_256);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_free(d1);
+ crypto_digest_free(d2);
+
+ /* Incremental digest code with SHA3-512 */
+ d1 = crypto_digest512_new(DIGEST_SHA3_512);
+ tt_assert(d1);
+ crypto_digest_add_bytes(d1, "abcdef", 6);
+ d2 = crypto_digest_dup(d1);
+ tt_assert(d2);
+ crypto_digest_add_bytes(d2, "ghijkl", 6);
+ crypto_digest_get_digest(d2, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdefghijkl", 12, DIGEST_SHA3_512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_assign(d2, d1);
+ crypto_digest_add_bytes(d2, "mno", 3);
+ crypto_digest_get_digest(d2, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdefmno", 9, DIGEST_SHA3_512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_get_digest(d1, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdef", 6, DIGEST_SHA3_512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_free(d1);
+
+ /* Attempt to exercise the incremental hashing code by creating a randomized
+ * 100 KiB buffer, and hashing rand[1, 5 * Rate] bytes at a time. SHA3-512
+ * is used because it has a lowest rate of the family (the code is common,
+ * but the slower rate exercises more of it).
+ */
+ const size_t bufsz = 100 * 1024;
+ size_t j = 0;
+ large = tor_malloc(bufsz);
+ crypto_rand(large, bufsz);
+ d1 = crypto_digest512_new(DIGEST_SHA3_512); /* Running digest. */
+ while (j < bufsz) {
+ /* Pick how much data to add to the running digest. */
+ size_t incr = (size_t)crypto_rand_int_range(1, 72 * 5);
+ incr = MIN(bufsz - j, incr);
+
+ /* Add the data, and calculate the hash. */
+ crypto_digest_add_bytes(d1, large + j, incr);
+ crypto_digest_get_digest(d1, d_out1, DIGEST512_LEN);
+
+ /* One-shot hash the buffer up to the data that was just added,
+ * and ensure that the values match up.
+ *
+ * XXX/yawning: If this actually fails, it'll be rather difficult to
+ * reproduce. Improvements welcome.
+ */
+ i = crypto_digest512(d_out2, large, j + incr, DIGEST_SHA3_512);
+ tt_int_op(i, OP_EQ, 0);
+ tt_mem_op(d_out1, OP_EQ, d_out2, DIGEST512_LEN);
+
+ j += incr;
+ }
+
+ done:
+ if (d1)
+ crypto_digest_free(d1);
+ if (d2)
+ crypto_digest_free(d2);
+ tor_free(large);
+ tor_free(mem_op_hex_tmp);
+}
+
+/** Run unit tests for our XOF. */
+static void
+test_crypto_sha3_xof(void *arg)
+{
+ uint8_t msg[255];
+ uint8_t out[512];
+ crypto_xof_t *xof;
+ char *mem_op_hex_tmp=NULL;
+
+ (void)arg;
+
+ /* SHAKE256 test vector (Len = 2040) from the Keccak Code Package. */
+ base16_decode((char *)msg, 255,
+ "3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431"
+ "E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1"
+ "A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F"
+ "0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD457"
+ "4EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F114186"
+ "90A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED3"
+ "06FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565"
+ "CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D7"
+ "9DC266F6A5BE6BB0E4A92ECEEBAEB1", 510);
+ const char *squeezed_hex =
+ "8A5199B4A7E133E264A86202720655894D48CFF344A928CF8347F48379CE"
+ "F347DFC5BCFFAB99B27B1F89AA2735E23D30088FFA03B9EDB02B9635470A"
+ "B9F1038985D55F9CA774572DD006470EA65145469609F9FA0831BF1FFD84"
+ "2DC24ACADE27BD9816E3B5BF2876CB112232A0EB4475F1DFF9F5C713D9FF"
+ "D4CCB89AE5607FE35731DF06317949EEF646E9591CF3BE53ADD6B7DD2B60"
+ "96E2B3FB06E662EC8B2D77422DAAD9463CD155204ACDBD38E319613F39F9"
+ "9B6DFB35CA9365160066DB19835888C2241FF9A731A4ACBB5663727AAC34"
+ "A401247FBAA7499E7D5EE5B69D31025E63D04C35C798BCA1262D5673A9CF"
+ "0930B5AD89BD485599DC184528DA4790F088EBD170B635D9581632D2FF90"
+ "DB79665CED430089AF13C9F21F6D443A818064F17AEC9E9C5457001FA8DC"
+ "6AFBADBE3138F388D89D0E6F22F66671255B210754ED63D81DCE75CE8F18"
+ "9B534E6D6B3539AA51E837C42DF9DF59C71E6171CD4902FE1BDC73FB1775"
+ "B5C754A1ED4EA7F3105FC543EE0418DAD256F3F6118EA77114A16C15355B"
+ "42877A1DB2A7DF0E155AE1D8670ABCEC3450F4E2EEC9838F895423EF63D2"
+ "61138BAAF5D9F104CB5A957AEA06C0B9B8C78B0D441796DC0350DDEABB78"
+ "A33B6F1F9E68EDE3D1805C7B7E2CFD54E0FAD62F0D8CA67A775DC4546AF9"
+ "096F2EDB221DB42843D65327861282DC946A0BA01A11863AB2D1DFD16E39"
+ "73D4";
+
+ /* Test oneshot absorb/squeeze. */
+ xof = crypto_xof_new();
+ tt_assert(xof);
+ crypto_xof_add_bytes(xof, msg, sizeof(msg));
+ crypto_xof_squeeze_bytes(xof, out, sizeof(out));
+ test_memeq_hex(out, squeezed_hex);
+ crypto_xof_free(xof);
+ memset(out, 0, sizeof(out));
+
+ /* Test incremental absorb/squeeze. */
+ xof = crypto_xof_new();
+ tt_assert(xof);
+ for (size_t i = 0; i < sizeof(msg); i++)
+ crypto_xof_add_bytes(xof, msg + i, 1);
+ for (size_t i = 0; i < sizeof(out); i++)
+ crypto_xof_squeeze_bytes(xof, out + i, 1);
+ test_memeq_hex(out, squeezed_hex);
+
+ done:
+ if (xof)
+ crypto_xof_free(xof);
+ tor_free(mem_op_hex_tmp);
+}
+
/** Run unit tests for our public key crypto functions */
static void
test_crypto_pk(void *arg)
@@ -639,7 +1090,7 @@ test_crypto_digests(void *arg)
{
crypto_pk_t *k = NULL;
ssize_t r;
- digests_t pkey_digests;
+ common_digests_t pkey_digests;
char digest[DIGEST_LEN];
(void)arg;
@@ -653,7 +1104,7 @@ test_crypto_digests(void *arg)
tt_mem_op(hex_str(digest, DIGEST_LEN),OP_EQ,
AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN);
- r = crypto_pk_get_all_digests(k, &pkey_digests);
+ r = crypto_pk_get_common_digests(k, &pkey_digests);
tt_mem_op(hex_str(pkey_digests.d[DIGEST_SHA1], DIGEST_LEN),OP_EQ,
AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN);
@@ -663,6 +1114,11 @@ test_crypto_digests(void *arg)
crypto_pk_free(k);
}
+#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.
*/
@@ -670,12 +1126,13 @@ 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 *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_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;
}
@@ -1271,6 +1728,24 @@ test_crypto_curve25519_persist(void *arg)
tor_free(tag);
}
+static void *
+ed25519_testcase_setup(const struct testcase_t *testcase)
+{
+ crypto_ed25519_testing_force_impl(testcase->setup_data);
+ return testcase->setup_data;
+}
+static int
+ed25519_testcase_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+ (void)testcase;
+ (void)ptr;
+ crypto_ed25519_testing_restore_impl();
+ return 1;
+}
+static const struct testcase_setup_t ed25519_test_setup = {
+ ed25519_testcase_setup, ed25519_testcase_cleanup
+};
+
static void
test_crypto_ed25519_simple(void *arg)
{
@@ -1803,13 +2278,126 @@ test_crypto_siphash(void *arg)
;
}
+/* We want the likelihood that the random buffer exhibits any regular pattern
+ * to be far less than the memory bit error rate in the int return value.
+ * Using 2048 bits provides a failure rate of 1/(3 * 10^616), and we call
+ * 3 functions, leading to an overall error rate of 1/10^616.
+ * This is comparable with the 1/10^603 failure rate of test_crypto_rng_range.
+ */
+#define FAILURE_MODE_BUFFER_SIZE (2048/8)
+
+/** Check crypto_rand for a failure mode where it does nothing to the buffer,
+ * or it sets the buffer to all zeroes. Return 0 when the check passes,
+ * or -1 when it fails. */
+static int
+crypto_rand_check_failure_mode_zero(void)
+{
+ char buf[FAILURE_MODE_BUFFER_SIZE];
+
+ memset(buf, 0, FAILURE_MODE_BUFFER_SIZE);
+ crypto_rand(buf, FAILURE_MODE_BUFFER_SIZE);
+
+ for (size_t i = 0; i < FAILURE_MODE_BUFFER_SIZE; i++) {
+ if (buf[i] != 0) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/** Check crypto_rand for a failure mode where every int64_t in the buffer is
+ * the same. Return 0 when the check passes, or -1 when it fails. */
+static int
+crypto_rand_check_failure_mode_identical(void)
+{
+ /* just in case the buffer size isn't a multiple of sizeof(int64_t) */
+#define FAILURE_MODE_BUFFER_SIZE_I64 \
+ (FAILURE_MODE_BUFFER_SIZE/SIZEOF_INT64_T)
+#define FAILURE_MODE_BUFFER_SIZE_I64_BYTES \
+ (FAILURE_MODE_BUFFER_SIZE_I64*SIZEOF_INT64_T)
+
+#if FAILURE_MODE_BUFFER_SIZE_I64 < 2
+#error FAILURE_MODE_BUFFER_SIZE needs to be at least 2*SIZEOF_INT64_T
+#endif
+
+ int64_t buf[FAILURE_MODE_BUFFER_SIZE_I64];
+
+ memset(buf, 0, FAILURE_MODE_BUFFER_SIZE_I64_BYTES);
+ crypto_rand((char *)buf, FAILURE_MODE_BUFFER_SIZE_I64_BYTES);
+
+ for (size_t i = 1; i < FAILURE_MODE_BUFFER_SIZE_I64; i++) {
+ if (buf[i] != buf[i-1]) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/** Check crypto_rand for a failure mode where it increments the "random"
+ * value by 1 for every byte in the buffer. (This is OpenSSL's PREDICT mode.)
+ * Return 0 when the check passes, or -1 when it fails. */
+static int
+crypto_rand_check_failure_mode_predict(void)
+{
+ unsigned char buf[FAILURE_MODE_BUFFER_SIZE];
+
+ memset(buf, 0, FAILURE_MODE_BUFFER_SIZE);
+ crypto_rand((char *)buf, FAILURE_MODE_BUFFER_SIZE);
+
+ for (size_t i = 1; i < FAILURE_MODE_BUFFER_SIZE; i++) {
+ /* check if the last byte was incremented by 1, including integer
+ * wrapping */
+ if (buf[i] - buf[i-1] != 1 && buf[i-1] - buf[i] != 255) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+#undef FAILURE_MODE_BUFFER_SIZE
+
+static void
+test_crypto_failure_modes(void *arg)
+{
+ int rv = 0;
+ (void)arg;
+
+ rv = crypto_early_init();
+ tt_assert(rv == 0);
+
+ /* Check random works */
+ rv = crypto_rand_check_failure_mode_zero();
+ tt_assert(rv == 0);
+
+ rv = crypto_rand_check_failure_mode_identical();
+ tt_assert(rv == 0);
+
+ rv = crypto_rand_check_failure_mode_predict();
+ tt_assert(rv == 0);
+
+ done:
+ ;
+}
+
#define CRYPTO_LEGACY(name) \
{ #name, test_crypto_ ## name , 0, NULL, NULL }
+#define ED25519_TEST_ONE(name, fl, which) \
+ { #name "/ed25519_" which, test_crypto_ed25519_ ## name, (fl), \
+ &ed25519_test_setup, (void*)which }
+
+#define ED25519_TEST(name, fl) \
+ ED25519_TEST_ONE(name, (fl), "donna"), \
+ ED25519_TEST_ONE(name, (fl), "ref10")
+
struct testcase_t 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 },
{ "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" },
{ "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" },
CRYPTO_LEGACY(sha),
@@ -1817,6 +2405,8 @@ struct testcase_t crypto_tests[] = {
{ "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
{ "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL },
CRYPTO_LEGACY(digests),
+ { "sha3", test_crypto_sha3, TT_FORK, NULL, NULL},
+ { "sha3_xof", test_crypto_sha3_xof, TT_FORK, NULL, NULL},
CRYPTO_LEGACY(dh),
{ "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
(void*)"aes" },
@@ -1832,15 +2422,15 @@ struct testcase_t crypto_tests[] = {
{ "curve25519_wrappers", test_crypto_curve25519_wrappers, 0, NULL, NULL },
{ "curve25519_encode", test_crypto_curve25519_encode, 0, NULL, NULL },
{ "curve25519_persist", test_crypto_curve25519_persist, 0, NULL, NULL },
- { "ed25519_simple", test_crypto_ed25519_simple, 0, NULL, NULL },
- { "ed25519_test_vectors", test_crypto_ed25519_test_vectors, 0, NULL, NULL },
- { "ed25519_encode", test_crypto_ed25519_encode, 0, NULL, NULL },
- { "ed25519_convert", test_crypto_ed25519_convert, 0, NULL, NULL },
- { "ed25519_blinding", test_crypto_ed25519_blinding, 0, NULL, NULL },
- { "ed25519_testvectors", test_crypto_ed25519_testvectors, 0, NULL, NULL },
- { "ed25519_fuzz_donna", test_crypto_ed25519_fuzz_donna, TT_FORK, NULL,
- NULL },
+ ED25519_TEST(simple, 0),
+ ED25519_TEST(test_vectors, 0),
+ ED25519_TEST(encode, 0),
+ ED25519_TEST(convert, 0),
+ ED25519_TEST(blinding, 0),
+ ED25519_TEST(testvectors, 0),
+ ED25519_TEST(fuzz_donna, TT_FORK),
{ "siphash", test_crypto_siphash, 0, NULL, NULL },
+ { "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 855746e749..72e619f9cd 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -6,19 +6,24 @@
#include "orconfig.h"
#include <math.h>
+#define CONFIG_PRIVATE
#define DIRSERV_PRIVATE
#define DIRVOTE_PRIVATE
#define ROUTER_PRIVATE
#define ROUTERLIST_PRIVATE
#define HIBERNATE_PRIVATE
#define NETWORKSTATUS_PRIVATE
+#define RELAY_PRIVATE
+
#include "or.h"
+#include "confparse.h"
#include "config.h"
#include "crypto_ed25519.h"
#include "directory.h"
#include "dirserv.h"
#include "dirvote.h"
#include "hibernate.h"
+#include "memarea.h"
#include "networkstatus.h"
#include "router.h"
#include "routerkeys.h"
@@ -26,7 +31,11 @@
#include "routerparse.h"
#include "routerset.h"
#include "test.h"
+#include "test_dir_common.h"
#include "torcert.h"
+#include "relay.h"
+
+#define NS_MODULE dir
static void
test_dir_nicknames(void *arg)
@@ -110,6 +119,7 @@ test_dir_formats(void *arg)
r1->cache_info.published_on = 0;
r1->or_port = 9000;
r1->dir_port = 9003;
+ r1->supports_tunnelled_dir_requests = 1;
tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
r1->ipv6_orport = 9999;
r1->onion_pkey = crypto_pk_dup_key(pk1);
@@ -154,6 +164,7 @@ test_dir_formats(void *arg)
r2->cache_info.published_on = 5;
r2->or_port = 9005;
r2->dir_port = 0;
+ r2->supports_tunnelled_dir_requests = 1;
r2->onion_pkey = crypto_pk_dup_key(pk2);
curve25519_keypair_t r2_onion_keypair;
curve25519_keypair_generate(&r2_onion_keypair, 0);
@@ -174,7 +185,9 @@ test_dir_formats(void *arg)
/* XXXX025 router_dump_to_string should really take this from ri.*/
options->ContactInfo = tor_strdup("Magri White "
"<magri@elsewhere.example.com>");
+
buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL);
+
tor_free(options->ContactInfo);
tt_assert(buf);
@@ -200,7 +213,8 @@ test_dir_formats(void *arg)
strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
strlcat(buf2, "contact Magri White <magri@elsewhere.example.com>\n",
sizeof(buf2));
- strlcat(buf2, "reject *:*\nrouter-signature\n", sizeof(buf2));
+ strlcat(buf2, "reject *:*\n", sizeof(buf2));
+ strlcat(buf2, "tunnelled-dir-server\nrouter-signature\n", sizeof(buf2));
buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
* twice */
@@ -214,12 +228,13 @@ test_dir_formats(void *arg)
tt_assert(rp1);
tt_int_op(rp1->addr,OP_EQ, r1->addr);
tt_int_op(rp1->or_port,OP_EQ, r1->or_port);
- //test_eq(rp1->dir_port, r1->dir_port);
+ tt_int_op(rp1->dir_port,OP_EQ, r1->dir_port);
tt_int_op(rp1->bandwidthrate,OP_EQ, r1->bandwidthrate);
tt_int_op(rp1->bandwidthburst,OP_EQ, r1->bandwidthburst);
tt_int_op(rp1->bandwidthcapacity,OP_EQ, r1->bandwidthcapacity);
tt_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0);
tt_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0);
+ tt_assert(rp1->supports_tunnelled_dir_requests);
//tt_assert(rp1->exit_policy == NULL);
tor_free(buf);
@@ -290,6 +305,7 @@ test_dir_formats(void *arg)
BASE64_ENCODE_MULTILINE);
strlcat(buf2, cert_buf, sizeof(buf2));
strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2));
+ strlcat(buf2, "tunnelled-dir-server\n", sizeof(buf2));
strlcat(buf2, "router-sig-ed25519 ", sizeof(buf2));
buf = router_dump_router_to_string(r2, pk1, pk2, &r2_onion_keypair, &kp2);
@@ -301,6 +317,8 @@ test_dir_formats(void *arg)
tor_free(buf);
buf = router_dump_router_to_string(r2, pk1, NULL, NULL, NULL);
+
+ /* Reset for later */
cp = buf;
rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
tt_assert(rp2);
@@ -315,6 +333,7 @@ test_dir_formats(void *arg)
CURVE25519_PUBKEY_LEN);
tt_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0);
tt_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0);
+ tt_assert(rp2->supports_tunnelled_dir_requests);
tt_int_op(smartlist_len(rp2->exit_policy),OP_EQ, 2);
@@ -478,6 +497,7 @@ test_dir_routerinfo_parsing(void *arg)
#undef CHECK_FAIL
#undef CHECK_OK
done:
+ memarea_clear_freelist();
routerinfo_free(ri);
}
@@ -580,6 +600,8 @@ test_dir_extrainfo_parsing(void *arg)
#undef CHECK_FAIL
done:
+ escaped(NULL);
+ memarea_clear_freelist();
extrainfo_free(ei);
routerinfo_free(ri);
digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_);
@@ -1477,13 +1499,6 @@ test_dir_param_voting(void *arg)
return;
}
-extern const char AUTHORITY_CERT_1[];
-extern const char AUTHORITY_SIGNKEY_1[];
-extern const char AUTHORITY_CERT_2[];
-extern const char AUTHORITY_SIGNKEY_2[];
-extern const char AUTHORITY_CERT_3[];
-extern const char AUTHORITY_SIGNKEY_3[];
-
/** Helper: Test that two networkstatus_voter_info_t do in fact represent the
* same voting authority, and that they do in fact have all the same
* information. */
@@ -1503,42 +1518,6 @@ test_same_voter(networkstatus_voter_info_t *v1,
;
}
-/** Helper: Make a new routerinfo containing the right information for a
- * given vote_routerstatus_t. */
-static routerinfo_t *
-generate_ri_from_rs(const vote_routerstatus_t *vrs)
-{
- routerinfo_t *r;
- const routerstatus_t *rs = &vrs->status;
- static time_t published = 0;
-
- r = tor_malloc_zero(sizeof(routerinfo_t));
- r->cert_expiration_time = TIME_MAX;
- memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN);
- memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest,
- DIGEST_LEN);
- r->cache_info.do_not_cache = 1;
- r->cache_info.routerlist_index = -1;
- r->cache_info.signed_descriptor_body =
- tor_strdup("123456789012345678901234567890123");
- r->cache_info.signed_descriptor_len =
- strlen(r->cache_info.signed_descriptor_body);
- r->exit_policy = smartlist_new();
- r->cache_info.published_on = ++published + time(NULL);
- if (rs->has_bandwidth) {
- /*
- * Multiply by 1000 because the routerinfo_t and the routerstatus_t
- * seem to use different units (*sigh*) and because we seem stuck on
- * icky and perverse decimal kilobytes (*double sigh*) - see
- * router_get_advertised_bandwidth_capped() of routerlist.c and
- * routerstatus_format_entry() of dirserv.c.
- */
- r->bandwidthrate = rs->bandwidth_kb * 1000;
- r->bandwidthcapacity = rs->bandwidth_kb * 1000;
- }
- return r;
-}
-
/** Helper: get a detached signatures document for one or two
* consensuses. */
static char *
@@ -1556,100 +1535,6 @@ get_detached_sigs(networkstatus_t *ns, networkstatus_t *ns2)
return r;
}
-/**
- * Generate a routerstatus for v3_networkstatus test
- */
-static vote_routerstatus_t *
-gen_routerstatus_for_v3ns(int idx, time_t now)
-{
- vote_routerstatus_t *vrs=NULL;
- routerstatus_t *rs;
- tor_addr_t addr_ipv6;
-
- switch (idx) {
- case 0:
- /* Generate the first routerstatus. */
- vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- rs = &vrs->status;
- vrs->version = tor_strdup("0.1.2.14");
- rs->published_on = now-1500;
- strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
- memset(rs->identity_digest, 3, DIGEST_LEN);
- memset(rs->descriptor_digest, 78, DIGEST_LEN);
- rs->addr = 0x99008801;
- rs->or_port = 443;
- rs->dir_port = 8000;
- /* all flags but running cleared */
- rs->is_flagged_running = 1;
- break;
- case 1:
- /* Generate the second routerstatus. */
- vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- rs = &vrs->status;
- vrs->version = tor_strdup("0.2.0.5");
- rs->published_on = now-1000;
- strlcpy(rs->nickname, "router1", sizeof(rs->nickname));
- memset(rs->identity_digest, 5, DIGEST_LEN);
- memset(rs->descriptor_digest, 77, DIGEST_LEN);
- rs->addr = 0x99009901;
- rs->or_port = 443;
- rs->dir_port = 0;
- tor_addr_parse(&addr_ipv6, "[1:2:3::4]");
- tor_addr_copy(&rs->ipv6_addr, &addr_ipv6);
- rs->ipv6_orport = 4711;
- rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running =
- rs->is_valid = rs->is_possible_guard = 1;
- break;
- case 2:
- /* Generate the third routerstatus. */
- vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- rs = &vrs->status;
- vrs->version = tor_strdup("0.1.0.3");
- rs->published_on = now-1000;
- strlcpy(rs->nickname, "router3", sizeof(rs->nickname));
- memset(rs->identity_digest, 33, DIGEST_LEN);
- memset(rs->descriptor_digest, 79, DIGEST_LEN);
- rs->addr = 0xAA009901;
- rs->or_port = 400;
- rs->dir_port = 9999;
- rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
- rs->is_flagged_running = rs->is_valid =
- rs->is_possible_guard = 1;
- break;
- case 3:
- /* Generate a fourth routerstatus that is not running. */
- vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- rs = &vrs->status;
- vrs->version = tor_strdup("0.1.6.3");
- rs->published_on = now-1000;
- strlcpy(rs->nickname, "router4", sizeof(rs->nickname));
- memset(rs->identity_digest, 34, DIGEST_LEN);
- memset(rs->descriptor_digest, 47, DIGEST_LEN);
- rs->addr = 0xC0000203;
- rs->or_port = 500;
- rs->dir_port = 1999;
- /* Running flag (and others) cleared */
- break;
- case 4:
- /* No more for this test; return NULL */
- vrs = NULL;
- break;
- default:
- /* Shouldn't happen */
- tt_assert(0);
- }
- if (vrs) {
- vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
- tor_asprintf(&vrs->microdesc->microdesc_hash_line,
- "m 9,10,11,12,13,14,15,16,17 "
- "sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa%d\n",
- idx);
- }
-
- done:
- return vrs;
-}
-
/** Apply tweaks to the vote list for each voter */
static int
vote_tweaks_for_v3ns(networkstatus_t *v, int voter, time_t now)
@@ -1681,7 +1566,7 @@ vote_tweaks_for_v3ns(networkstatus_t *v, int voter, time_t now)
vrs = smartlist_get(v->routerstatus_list, 0);
memset(vrs->status.descriptor_digest, (int)'Z', DIGEST_LEN);
tt_assert(router_add_to_routerlist(
- generate_ri_from_rs(vrs), &msg,0,0) >= 0);
+ dir_common_generate_ri_from_rs(vrs), &msg,0,0) >= 0);
}
}
@@ -1746,11 +1631,11 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now)
tt_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6));
tt_int_op(rs->ipv6_orport,OP_EQ, 4711);
if (voter == 1) {
- /* all except "authority" (1) and "v2dir" (64) */
- tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(190));
+ /* all except "authority" (1) */
+ tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(254));
} else {
- /* 1023 - authority(1) - madeofcheese(16) - madeoftin(32) - v2dir(256) */
- tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(718));
+ /* 1023 - authority(1) - madeofcheese(16) - madeoftin(32) */
+ tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(974));
}
} else if (tor_memeq(rs->identity_digest,
"\x33\x33\x33\x33\x33\x33\x33\x33\x33\x33"
@@ -1820,6 +1705,7 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
tt_assert(rs->is_flagged_running);
tt_assert(!rs->is_valid);
tt_assert(!rs->is_named);
+ tt_assert(rs->is_v2_dir);
/* XXXX check version */
} else if (tor_memeq(rs->identity_digest,
"\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5"
@@ -1845,6 +1731,7 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
tt_assert(rs->is_stable);
tt_assert(rs->is_flagged_running);
tt_assert(rs->is_valid);
+ tt_assert(rs->is_v2_dir);
tt_assert(!rs->is_named);
/* XXXX check version */
} else {
@@ -1869,7 +1756,6 @@ test_a_networkstatus(
authority_cert_t *cert1=NULL, *cert2=NULL, *cert3=NULL;
crypto_pk_t *sign_skey_1=NULL, *sign_skey_2=NULL, *sign_skey_3=NULL;
crypto_pk_t *sign_skey_leg1=NULL;
- const char *msg=NULL;
/*
* Sum the non-zero returns from vote_tweaks() we've seen; if vote_tweaks()
* returns non-zero, it changed net_params and we should skip the tests for
@@ -1885,8 +1771,7 @@ test_a_networkstatus(
vote_routerstatus_t *vrs;
routerstatus_t *rs;
int idx, n_rs, n_vrs;
- char *v1_text=NULL, *v2_text=NULL, *v3_text=NULL, *consensus_text=NULL,
- *cp=NULL;
+ char *consensus_text=NULL, *cp=NULL;
smartlist_t *votes = smartlist_new();
/* For generating the two other consensuses. */
@@ -1901,79 +1786,13 @@ test_a_networkstatus(
tt_assert(rs_test);
tt_assert(vrs_test);
- /* Parse certificates and keys. */
- cert1 = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
- tt_assert(cert1);
- cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
- tt_assert(cert2);
- cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
- tt_assert(cert3);
- sign_skey_1 = crypto_pk_new();
- sign_skey_2 = crypto_pk_new();
- sign_skey_3 = crypto_pk_new();
+ tt_assert(!dir_common_authority_pk_init(&cert1, &cert2, &cert3,
+ &sign_skey_1, &sign_skey_2,
+ &sign_skey_3));
sign_skey_leg1 = pk_generate(4);
- tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_1,
- AUTHORITY_SIGNKEY_1, -1));
- tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_2,
- AUTHORITY_SIGNKEY_2, -1));
- tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_3,
- AUTHORITY_SIGNKEY_3, -1));
-
- tt_assert(!crypto_pk_cmp_keys(sign_skey_1, cert1->signing_key));
- tt_assert(!crypto_pk_cmp_keys(sign_skey_2, cert2->signing_key));
-
- /*
- * Set up a vote; generate it; try to parse it.
- */
- vote = tor_malloc_zero(sizeof(networkstatus_t));
- vote->type = NS_TYPE_VOTE;
- vote->published = now;
- vote->valid_after = now+1000;
- vote->fresh_until = now+2000;
- vote->valid_until = now+3000;
- vote->vote_seconds = 100;
- vote->dist_seconds = 200;
- vote->supported_methods = smartlist_new();
- smartlist_split_string(vote->supported_methods, "1 2 3", NULL, 0, -1);
- vote->client_versions = tor_strdup("0.1.2.14,0.1.2.15");
- vote->server_versions = tor_strdup("0.1.2.14,0.1.2.15,0.1.2.16");
- vote->known_flags = smartlist_new();
- smartlist_split_string(vote->known_flags,
- "Authority Exit Fast Guard Running Stable V2Dir Valid",
- 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- vote->voters = smartlist_new();
- voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
- voter->nickname = tor_strdup("Voter1");
- voter->address = tor_strdup("1.2.3.4");
- voter->addr = 0x01020304;
- voter->dir_port = 80;
- voter->or_port = 9000;
- voter->contact = tor_strdup("voter@example.com");
- crypto_pk_get_digest(cert1->identity_key, voter->identity_digest);
- smartlist_add(vote->voters, voter);
- vote->cert = authority_cert_dup(cert1);
- vote->net_params = smartlist_new();
- smartlist_split_string(vote->net_params, "circuitwindow=101 foo=990",
- NULL, 0, 0);
- vote->routerstatus_list = smartlist_new();
- /* add routerstatuses */
- idx = 0;
- do {
- vrs = vrs_gen(idx, now);
- if (vrs) {
- smartlist_add(vote->routerstatus_list, vrs);
- tt_assert(router_add_to_routerlist(generate_ri_from_rs(vrs),
- &msg,0,0)>=0);
- ++idx;
- }
- } while (vrs);
- n_vrs = idx;
-
- /* dump the vote and try to parse it. */
- v1_text = format_networkstatus_vote(sign_skey_1, vote);
- tt_assert(v1_text);
- v1 = networkstatus_parse_vote_from_string(v1_text, NULL, NS_TYPE_VOTE);
+ tt_assert(!dir_common_construct_vote_1(&vote, cert1, sign_skey_1, vrs_gen,
+ &v1, &n_vrs, now, 1));
tt_assert(v1);
/* Make sure the parsed thing was right. */
@@ -2000,6 +1819,8 @@ test_a_networkstatus(
tt_str_op(cp,OP_EQ, "Authority:Exit:Fast:Guard:Running:Stable:V2Dir:Valid");
tor_free(cp);
tt_int_op(smartlist_len(v1->routerstatus_list),OP_EQ, n_vrs);
+ networkstatus_vote_free(vote);
+ vote = NULL;
if (vote_tweaks) params_tweaked += vote_tweaks(v1, 1, now);
@@ -2011,33 +1832,10 @@ test_a_networkstatus(
}
/* Generate second vote. It disagrees on some of the times,
- * and doesn't list versions, and knows some crazy flags */
- vote->published = now+1;
- vote->fresh_until = now+3005;
- vote->dist_seconds = 300;
- authority_cert_free(vote->cert);
- vote->cert = authority_cert_dup(cert2);
- SMARTLIST_FOREACH(vote->net_params, char *, c, tor_free(c));
- smartlist_clear(vote->net_params);
- smartlist_split_string(vote->net_params, "bar=2000000000 circuitwindow=20",
- NULL, 0, 0);
- tor_free(vote->client_versions);
- tor_free(vote->server_versions);
- voter = smartlist_get(vote->voters, 0);
- tor_free(voter->nickname);
- tor_free(voter->address);
- voter->nickname = tor_strdup("Voter2");
- voter->address = tor_strdup("2.3.4.5");
- voter->addr = 0x02030405;
- crypto_pk_get_digest(cert2->identity_key, voter->identity_digest);
- smartlist_add(vote->known_flags, tor_strdup("MadeOfCheese"));
- smartlist_add(vote->known_flags, tor_strdup("MadeOfTin"));
- smartlist_sort_strings(vote->known_flags);
-
- /* generate and parse v2. */
- v2_text = format_networkstatus_vote(sign_skey_2, vote);
- tt_assert(v2_text);
- v2 = networkstatus_parse_vote_from_string(v2_text, NULL, NS_TYPE_VOTE);
+ * and doesn't list versions, and knows some crazy flags.
+ * Generate and parse v2. */
+ tt_assert(!dir_common_construct_vote_2(&vote, cert2, sign_skey_2, vrs_gen,
+ &v2, &n_vrs, now, 1));
tt_assert(v2);
if (vote_tweaks) params_tweaked += vote_tweaks(v2, 2, now);
@@ -2055,34 +1853,12 @@ test_a_networkstatus(
tt_assert(vrs);
vrs_test(vrs, 2, now);
}
+ networkstatus_vote_free(vote);
+ vote = NULL;
- /* Generate the third vote. */
- vote->published = now;
- vote->fresh_until = now+2003;
- vote->dist_seconds = 250;
- authority_cert_free(vote->cert);
- vote->cert = authority_cert_dup(cert3);
- SMARTLIST_FOREACH(vote->net_params, char *, c, tor_free(c));
- smartlist_clear(vote->net_params);
- smartlist_split_string(vote->net_params, "circuitwindow=80 foo=660",
- NULL, 0, 0);
- smartlist_add(vote->supported_methods, tor_strdup("4"));
- vote->client_versions = tor_strdup("0.1.2.14,0.1.2.17");
- vote->server_versions = tor_strdup("0.1.2.10,0.1.2.15,0.1.2.16");
- voter = smartlist_get(vote->voters, 0);
- tor_free(voter->nickname);
- tor_free(voter->address);
- voter->nickname = tor_strdup("Voter3");
- voter->address = tor_strdup("3.4.5.6");
- voter->addr = 0x03040506;
- crypto_pk_get_digest(cert3->identity_key, voter->identity_digest);
- /* This one has a legacy id. */
- memset(voter->legacy_id_digest, (int)'A', DIGEST_LEN);
-
- v3_text = format_networkstatus_vote(sign_skey_3, vote);
- tt_assert(v3_text);
-
- v3 = networkstatus_parse_vote_from_string(v3_text, NULL, NS_TYPE_VOTE);
+ /* Generate the third vote with a legacy id. */
+ tt_assert(!dir_common_construct_vote_3(&vote, cert3, sign_skey_3, vrs_gen,
+ &v3, &n_vrs, now, 1));
tt_assert(v3);
if (vote_tweaks) params_tweaked += vote_tweaks(v3, 3, now);
@@ -2153,12 +1929,20 @@ test_a_networkstatus(
/* Check the routerstatuses. */
n_rs = smartlist_len(con->routerstatus_list);
+ tt_assert(n_rs);
for (idx = 0; idx < n_rs; ++idx) {
rs = smartlist_get(con->routerstatus_list, idx);
tt_assert(rs);
rs_test(rs, now);
}
+ n_rs = smartlist_len(con_md->routerstatus_list);
+ tt_assert(n_rs);
+ for (idx = 0; idx < n_rs; ++idx) {
+ rs = smartlist_get(con_md->routerstatus_list, idx);
+ tt_assert(rs);
+ }
+
/* Check signatures. the first voter is a pseudo-entry with a legacy key.
* The second one hasn't signed. The fourth one has signed: validate it. */
voter = smartlist_get(con->voters, 1);
@@ -2215,11 +1999,13 @@ test_a_networkstatus(
tt_assert(con_md3);
/* All three should have the same digest. */
- tt_mem_op(&con->digests,OP_EQ, &con2->digests, sizeof(digests_t));
- tt_mem_op(&con->digests,OP_EQ, &con3->digests, sizeof(digests_t));
+ tt_mem_op(&con->digests,OP_EQ, &con2->digests, sizeof(common_digests_t));
+ tt_mem_op(&con->digests,OP_EQ, &con3->digests, sizeof(common_digests_t));
- tt_mem_op(&con_md->digests,OP_EQ, &con_md2->digests, sizeof(digests_t));
- tt_mem_op(&con_md->digests,OP_EQ, &con_md3->digests, sizeof(digests_t));
+ tt_mem_op(&con_md->digests,OP_EQ, &con_md2->digests,
+ sizeof(common_digests_t));
+ tt_mem_op(&con_md->digests,OP_EQ, &con_md3->digests,
+ sizeof(common_digests_t));
/* Extract a detached signature from con3. */
detached_text1 = get_detached_sigs(con3, con_md3);
@@ -2233,7 +2019,7 @@ test_a_networkstatus(
tt_int_op(dsig1->fresh_until,OP_EQ, con3->fresh_until);
tt_int_op(dsig1->valid_until,OP_EQ, con3->valid_until);
{
- digests_t *dsig_digests = strmap_get(dsig1->digests, "ns");
+ common_digests_t *dsig_digests = strmap_get(dsig1->digests, "ns");
tt_assert(dsig_digests);
tt_mem_op(dsig_digests->d[DIGEST_SHA1], OP_EQ,
con3->digests.d[DIGEST_SHA1], DIGEST_LEN);
@@ -2309,38 +2095,22 @@ test_a_networkstatus(
done:
tor_free(cp);
smartlist_free(votes);
- tor_free(v1_text);
- tor_free(v2_text);
- tor_free(v3_text);
tor_free(consensus_text);
tor_free(consensus_text_md);
- if (vote)
- networkstatus_vote_free(vote);
- if (v1)
- networkstatus_vote_free(v1);
- if (v2)
- networkstatus_vote_free(v2);
- if (v3)
- networkstatus_vote_free(v3);
- if (con)
- networkstatus_vote_free(con);
- if (con_md)
- networkstatus_vote_free(con_md);
- if (sign_skey_1)
- crypto_pk_free(sign_skey_1);
- if (sign_skey_2)
- crypto_pk_free(sign_skey_2);
- if (sign_skey_3)
- crypto_pk_free(sign_skey_3);
- if (sign_skey_leg1)
- crypto_pk_free(sign_skey_leg1);
- if (cert1)
- authority_cert_free(cert1);
- if (cert2)
- authority_cert_free(cert2);
- if (cert3)
- authority_cert_free(cert3);
+ networkstatus_vote_free(vote);
+ networkstatus_vote_free(v1);
+ networkstatus_vote_free(v2);
+ networkstatus_vote_free(v3);
+ networkstatus_vote_free(con);
+ networkstatus_vote_free(con_md);
+ crypto_pk_free(sign_skey_1);
+ crypto_pk_free(sign_skey_2);
+ crypto_pk_free(sign_skey_3);
+ crypto_pk_free(sign_skey_leg1);
+ authority_cert_free(cert1);
+ authority_cert_free(cert2);
+ authority_cert_free(cert3);
tor_free(consensus_text2);
tor_free(consensus_text3);
@@ -2348,18 +2118,13 @@ test_a_networkstatus(
tor_free(consensus_text_md3);
tor_free(detached_text1);
tor_free(detached_text2);
- if (con2)
- networkstatus_vote_free(con2);
- if (con3)
- networkstatus_vote_free(con3);
- if (con_md2)
- networkstatus_vote_free(con_md2);
- if (con_md3)
- networkstatus_vote_free(con_md3);
- if (dsig1)
- ns_detached_signatures_free(dsig1);
- if (dsig2)
- ns_detached_signatures_free(dsig2);
+
+ networkstatus_vote_free(con2);
+ networkstatus_vote_free(con3);
+ networkstatus_vote_free(con_md2);
+ networkstatus_vote_free(con_md3);
+ ns_detached_signatures_free(dsig1);
+ ns_detached_signatures_free(dsig2);
}
/** Run unit tests for generating and parsing V3 consensus networkstatus
@@ -2368,7 +2133,7 @@ static void
test_dir_v3_networkstatus(void *arg)
{
(void)arg;
- test_a_networkstatus(gen_routerstatus_for_v3ns,
+ test_a_networkstatus(dir_common_gen_routerstatus_for_v3ns,
vote_tweaks_for_v3ns,
test_vrs_for_v3ns,
test_consensus_for_v3ns,
@@ -2965,6 +2730,7 @@ test_dir_fmt_control_ns(void *arg)
rs.is_fast = 1;
rs.is_flagged_running = 1;
rs.has_bandwidth = 1;
+ rs.is_v2_dir = 1;
rs.bandwidth_kb = 1000;
s = networkstatus_getinfo_helper_single(&rs);
@@ -3340,12 +3106,33 @@ static void
test_dir_fetch_type(void *arg)
{
(void)arg;
- tt_assert(dir_fetch_type(DIR_PURPOSE_FETCH_MICRODESC, ROUTER_PURPOSE_GENERAL,
- NULL) == MICRODESC_DIRINFO);
- tt_assert(dir_fetch_type(DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_BRIDGE,
- NULL) == BRIDGE_DIRINFO);
- tt_assert(dir_fetch_type(DIR_PURPOSE_FETCH_CONSENSUS, ROUTER_PURPOSE_GENERAL,
- "microdesc") == (V3_DIRINFO | MICRODESC_DIRINFO));
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_EXTRAINFO, ROUTER_PURPOSE_BRIDGE,
+ NULL), OP_EQ, EXTRAINFO_DIRINFO | BRIDGE_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_EXTRAINFO, ROUTER_PURPOSE_GENERAL,
+ NULL), OP_EQ, EXTRAINFO_DIRINFO | V3_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_BRIDGE,
+ NULL), OP_EQ, BRIDGE_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_SERVERDESC,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, V3_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_STATUS_VOTE,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, V3_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, V3_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_CERTIFICATE,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, V3_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_CONSENSUS, ROUTER_PURPOSE_GENERAL,
+ "microdesc"), OP_EQ, V3_DIRINFO|MICRODESC_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_CONSENSUS, ROUTER_PURPOSE_GENERAL,
+ NULL), OP_EQ, V3_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_MICRODESC, ROUTER_PURPOSE_GENERAL,
+ NULL), OP_EQ, MICRODESC_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_RENDDESC_V2,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, NO_DIRINFO);
done: ;
}
@@ -3453,7 +3240,7 @@ test_dir_packages(void *arg)
ADD(4, "clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa");
ADD(5, "clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa");
- /* Five votes for A ... all from the same guy. Three for B. */
+ /* Five votes for A ... all from the same authority. Three for B. */
ADD(0, "cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m");
ADD(1, "cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m");
ADD(3, "cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m");
@@ -3494,6 +3281,768 @@ test_dir_packages(void *arg)
tor_free(res);
}
+static void
+test_dir_download_status_schedule(void *arg)
+{
+ (void)arg;
+ download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC,
+ DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_FAILURE };
+ download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_CONSENSUS,
+ DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_ATTEMPT};
+ download_status_t dls_bridge = { 0, 0, 0, DL_SCHED_BRIDGE,
+ DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_FAILURE};
+ int increment = -1;
+ int expected_increment = -1;
+ time_t current_time = time(NULL);
+ int delay1 = -1;
+ int delay2 = -1;
+ smartlist_t *schedule = smartlist_new();
+
+ /* Make a dummy schedule */
+ smartlist_add(schedule, (void *)&delay1);
+ smartlist_add(schedule, (void *)&delay2);
+
+ /* check a range of values */
+ delay1 = 1000;
+ increment = download_status_schedule_get_delay(&dls_failure,
+ schedule,
+ TIME_MIN);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_failure.next_attempt_at == TIME_MIN + expected_increment);
+
+ delay1 = INT_MAX;
+ increment = download_status_schedule_get_delay(&dls_failure,
+ schedule,
+ -1);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_failure.next_attempt_at == TIME_MAX);
+
+ delay1 = 0;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ 0);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == 0 + expected_increment);
+
+ delay1 = 1000;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ 1);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == 1 + expected_increment);
+
+ delay1 = INT_MAX;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ current_time);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == TIME_MAX);
+
+ delay1 = 1;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ TIME_MAX);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == TIME_MAX);
+
+ /* see what happens when we reach the end */
+ dls_attempt.n_download_attempts++;
+ dls_bridge.n_download_failures++;
+
+ delay2 = 100;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ current_time);
+ expected_increment = delay2;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == current_time + delay2);
+
+ delay2 = 1;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ current_time);
+ expected_increment = delay2;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == current_time + delay2);
+
+ /* see what happens when we try to go off the end */
+ dls_attempt.n_download_attempts++;
+ dls_bridge.n_download_failures++;
+
+ delay2 = 5;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ current_time);
+ expected_increment = delay2;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == current_time + delay2);
+
+ delay2 = 17;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ current_time);
+ expected_increment = delay2;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == current_time + delay2);
+
+ /* see what happens when we reach IMPOSSIBLE_TO_DOWNLOAD */
+ dls_attempt.n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD;
+ dls_bridge.n_download_failures = IMPOSSIBLE_TO_DOWNLOAD;
+
+ delay2 = 35;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ current_time);
+ expected_increment = INT_MAX;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == TIME_MAX);
+
+ delay2 = 99;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ current_time);
+ expected_increment = INT_MAX;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == TIME_MAX);
+
+ done:
+ /* the pointers in schedule are allocated on the stack */
+ smartlist_free(schedule);
+}
+
+static void
+test_dir_download_status_increment(void *arg)
+{
+ (void)arg;
+ download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC,
+ DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_FAILURE };
+ download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_BRIDGE,
+ DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_ATTEMPT};
+ int delay0 = -1;
+ int delay1 = -1;
+ int delay2 = -1;
+ smartlist_t *schedule = smartlist_new();
+ or_options_t test_options;
+ time_t next_at = TIME_MAX;
+ time_t current_time = time(NULL);
+
+ /* Provide some values for the schedule */
+ delay0 = 10;
+ delay1 = 99;
+ delay2 = 20;
+
+ /* Make the schedule */
+ smartlist_add(schedule, (void *)&delay0);
+ smartlist_add(schedule, (void *)&delay1);
+ smartlist_add(schedule, (void *)&delay2);
+
+ /* Put it in the options */
+ mock_options = &test_options;
+ reset_options(mock_options, &mock_get_options_calls);
+ mock_options->TestingClientDownloadSchedule = schedule;
+ mock_options->TestingBridgeDownloadSchedule = schedule;
+
+ MOCK(get_options, mock_get_options);
+
+ /* Check that a failure reset works */
+ mock_get_options_calls = 0;
+ download_status_reset(&dls_failure);
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ != TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 0);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* avoid timing inconsistencies */
+ dls_failure.next_attempt_at = current_time + delay0;
+
+ /* check that a reset schedule becomes ready at the right time */
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay0 - 1,
+ 1) == 0);
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay0,
+ 1) == 1);
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay0 + 1,
+ 1) == 1);
+
+ /* Check that a failure increment works */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
+ current_time);
+ tt_assert(next_at == current_time + delay1);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 1);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 1);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* check that an incremented schedule becomes ready at the right time */
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay1 - 1,
+ 1) == 0);
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay1,
+ 1) == 1);
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay1 + 1,
+ 1) == 1);
+
+ /* check that a schedule isn't ready if it's had too many failures */
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay1 + 10,
+ 0) == 0);
+
+ /* Check that failure increments don't happen on 503 for clients, but that
+ * attempt increments do. */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_failure, 503, "test", 0,
+ current_time);
+ tt_assert(next_at == current_time + delay1);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 1);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 2);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that failure increments do happen on 503 for servers */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_failure, 503, "test", 1,
+ current_time);
+ tt_assert(next_at == current_time + delay2);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 2);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 3);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check what happens when we run off the end of the schedule */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
+ current_time);
+ tt_assert(next_at == current_time + delay2);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 3);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 4);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check what happens when we hit the failure limit */
+ mock_get_options_calls = 0;
+ download_status_mark_impossible(&dls_failure);
+ next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
+ current_time);
+ tt_assert(next_at == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(download_status_get_n_attempts(&dls_failure)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that a failure reset doesn't reset at the limit */
+ mock_get_options_calls = 0;
+ download_status_reset(&dls_failure);
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(download_status_get_n_attempts(&dls_failure)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(mock_get_options_calls == 0);
+
+ /* Check that a failure reset resets just before the limit */
+ mock_get_options_calls = 0;
+ dls_failure.n_download_failures = IMPOSSIBLE_TO_DOWNLOAD - 1;
+ dls_failure.n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD - 1;
+ download_status_reset(&dls_failure);
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ != TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 0);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that failure increments do happen on attempt-based schedules,
+ * but that the retry is set at the end of time */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_attempt, 404, "test", 0,
+ current_time);
+ tt_assert(next_at == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 1);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 0);
+ tt_assert(mock_get_options_calls == 0);
+
+ /* Check that an attempt reset works */
+ mock_get_options_calls = 0;
+ download_status_reset(&dls_attempt);
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ != TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 0);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* avoid timing inconsistencies */
+ dls_attempt.next_attempt_at = current_time + delay0;
+
+ /* check that a reset schedule becomes ready at the right time */
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay0 - 1,
+ 1) == 0);
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay0,
+ 1) == 1);
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay0 + 1,
+ 1) == 1);
+
+ /* Check that an attempt increment works */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_attempt(&dls_attempt, "test",
+ current_time);
+ tt_assert(next_at == current_time + delay1);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 1);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* check that an incremented schedule becomes ready at the right time */
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay1 - 1,
+ 1) == 0);
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay1,
+ 1) == 1);
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay1 + 1,
+ 1) == 1);
+
+ /* check that a schedule isn't ready if it's had too many attempts */
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay1 + 10,
+ 0) == 0);
+
+ /* Check what happens when we reach then run off the end of the schedule */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_attempt(&dls_attempt, "test",
+ current_time);
+ tt_assert(next_at == current_time + delay2);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 2);
+ tt_assert(mock_get_options_calls >= 1);
+
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_attempt(&dls_attempt, "test",
+ current_time);
+ tt_assert(next_at == current_time + delay2);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 3);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check what happens when we hit the attempt limit */
+ mock_get_options_calls = 0;
+ download_status_mark_impossible(&dls_attempt);
+ next_at = download_status_increment_attempt(&dls_attempt, "test",
+ current_time);
+ tt_assert(next_at == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(download_status_get_n_attempts(&dls_attempt)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that an attempt reset doesn't reset at the limit */
+ mock_get_options_calls = 0;
+ download_status_reset(&dls_attempt);
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(download_status_get_n_attempts(&dls_attempt)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(mock_get_options_calls == 0);
+
+ /* Check that an attempt reset resets just before the limit */
+ mock_get_options_calls = 0;
+ dls_attempt.n_download_failures = IMPOSSIBLE_TO_DOWNLOAD - 1;
+ dls_attempt.n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD - 1;
+ download_status_reset(&dls_attempt);
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ != TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 0);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that attempt increments don't happen on failure-based schedules,
+ * and that the attempt is set at the end of time */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_attempt(&dls_failure, "test",
+ current_time);
+ tt_assert(next_at == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 0);
+ tt_assert(mock_get_options_calls == 0);
+
+ done:
+ /* the pointers in schedule are allocated on the stack */
+ smartlist_free(schedule);
+ UNMOCK(get_options);
+ mock_options = NULL;
+ mock_get_options_calls = 0;
+}
+
+static void
+test_dir_authdir_type_to_string(void *data)
+{
+ (void)data;
+ char *res;
+
+ tt_str_op(res = authdir_type_to_string(NO_DIRINFO), OP_EQ,
+ "[Not an authority]");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(EXTRAINFO_DIRINFO), OP_EQ,
+ "[Not an authority]");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(MICRODESC_DIRINFO), OP_EQ,
+ "[Not an authority]");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(V3_DIRINFO), OP_EQ, "V3");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(BRIDGE_DIRINFO), OP_EQ, "Bridge");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(
+ V3_DIRINFO | BRIDGE_DIRINFO | EXTRAINFO_DIRINFO), OP_EQ,
+ "V3, Bridge");
+ done:
+ tor_free(res);
+}
+
+static void
+test_dir_conn_purpose_to_string(void *data)
+{
+ (void)data;
+
+#define EXPECT_CONN_PURPOSE(purpose, expected) \
+ tt_str_op(dir_conn_purpose_to_string(purpose), OP_EQ, expected);
+
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_DIR, "server descriptor upload");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_VOTE, "server vote upload");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_SIGNATURES,
+ "consensus signature upload");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_SERVERDESC, "server descriptor fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_EXTRAINFO, "extra-info fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_CONSENSUS,
+ "consensus network-status fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_CERTIFICATE, "authority cert fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_STATUS_VOTE, "status vote fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES,
+ "consensus signature fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_RENDDESC_V2,
+ "hidden-service v2 descriptor fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_RENDDESC_V2,
+ "hidden-service v2 descriptor upload");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_MICRODESC, "microdescriptor fetch");
+ EXPECT_CONN_PURPOSE(1024, "(unknown)");
+
+ done: ;
+}
+
+NS_DECL(int,
+public_server_mode, (const or_options_t *options));
+
+static int
+NS(public_server_mode)(const or_options_t *options)
+{
+ (void)options;
+
+ if (CALLED(public_server_mode)++ == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+test_dir_should_use_directory_guards(void *data)
+{
+ or_options_t *options;
+ char *errmsg = NULL;
+ (void)data;
+
+ NS_MOCK(public_server_mode);
+
+ options = options_new();
+ options_init(options);
+
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 1);
+
+ options->UseEntryGuardsAsDirGuards = 1;
+ options->UseEntryGuards = 1;
+ options->DownloadExtraInfo = 0;
+ options->FetchDirInfoEarly = 0;
+ options->FetchDirInfoExtraEarly = 0;
+ options->FetchUselessDescriptors = 0;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 1);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 2);
+
+ options->UseEntryGuards = 0;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 3);
+ options->UseEntryGuards = 1;
+
+ options->UseEntryGuardsAsDirGuards = 0;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 4);
+ options->UseEntryGuardsAsDirGuards = 1;
+
+ options->DownloadExtraInfo = 1;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 5);
+ options->DownloadExtraInfo = 0;
+
+ options->FetchDirInfoEarly = 1;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 6);
+ options->FetchDirInfoEarly = 0;
+
+ options->FetchDirInfoExtraEarly = 1;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 7);
+ options->FetchDirInfoExtraEarly = 0;
+
+ options->FetchUselessDescriptors = 1;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 8);
+ options->FetchUselessDescriptors = 0;
+
+ done:
+ NS_UNMOCK(public_server_mode);
+ or_options_free(options);
+ tor_free(errmsg);
+}
+
+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));
+
+static void
+test_dir_should_not_init_request_to_ourselves(void *data)
+{
+ char digest[DIGEST_LEN];
+ dir_server_t *ourself = NULL;
+ crypto_pk_t *key = pk_generate(2);
+ (void) data;
+
+ NS_MOCK(directory_initiate_command_routerstatus);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ set_server_identity_key(key);
+ crypto_pk_get_digest(key, (char*) &digest);
+ ourself = trusted_dir_server_new("ourself", "127.0.0.1", 9059, 9060,
+ NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+
+ tt_assert(ourself);
+ 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);
+
+ directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0,
+ NULL);
+
+ tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0);
+
+ done:
+ NS_UNMOCK(directory_initiate_command_routerstatus);
+ clear_dir_servers();
+ routerlist_free_all();
+ crypto_pk_free(key);
+}
+
+static void
+test_dir_should_not_init_request_to_dir_auths_without_v3_info(void *data)
+{
+ dir_server_t *ds = NULL;
+ dirinfo_type_t dirinfo_type = BRIDGE_DIRINFO | EXTRAINFO_DIRINFO \
+ | MICRODESC_DIRINFO;
+ (void) data;
+
+ NS_MOCK(directory_initiate_command_routerstatus);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ ds = trusted_dir_server_new("ds", "10.0.0.1", 9059, 9060, NULL,
+ "12345678901234567890", NULL, dirinfo_type, 1.0);
+ tt_assert(ds);
+ 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);
+
+ directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0,
+ NULL);
+ tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0);
+
+ done:
+ NS_UNMOCK(directory_initiate_command_routerstatus);
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_should_init_request_to_dir_auths(void *data)
+{
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ NS_MOCK(directory_initiate_command_routerstatus);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ ds = trusted_dir_server_new("ds", "10.0.0.1", 9059, 9060, NULL,
+ "12345678901234567890", NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ 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);
+
+ directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0,
+ NULL);
+ tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 2);
+
+ done:
+ NS_UNMOCK(directory_initiate_command_routerstatus);
+ 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)
+{
+ (void)status;
+ (void)dir_purpose;
+ (void)router_purpose;
+ (void)indirection;
+ (void)resource;
+ (void)payload;
+ (void)payload_len;
+ (void)if_modified_since;
+ CALLED(directory_initiate_command_routerstatus)++;
+}
+
+static void
+test_dir_choose_compression_level(void* data)
+{
+ (void)data;
+
+ /* It starts under_memory_pressure */
+ tt_int_op(have_been_under_memory_pressure(), OP_EQ, 1);
+
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(-1));
+ tt_assert(LOW_COMPRESSION == choose_compression_level(1024-1));
+ tt_assert(MEDIUM_COMPRESSION == choose_compression_level(2048-1));
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(2048));
+
+ /* Reset under_memory_pressure timer */
+ cell_queues_check_size();
+ tt_int_op(have_been_under_memory_pressure(), OP_EQ, 0);
+
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(-1));
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(1024-1));
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(2048-1));
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(2048));
+
+ done: ;
+}
+
+static void
+test_dir_find_dl_schedule(void* data)
+{
+ download_status_t dls;
+ smartlist_t server, client, server_cons, client_cons, bridge;
+ (void)data;
+
+ mock_options = malloc(sizeof(or_options_t));
+ reset_options(mock_options, &mock_get_options_calls);
+ MOCK(get_options, mock_get_options);
+
+ mock_options->TestingServerDownloadSchedule = &server;
+ mock_options->TestingClientDownloadSchedule = &client;
+ mock_options->TestingServerConsensusDownloadSchedule = &server_cons;
+ mock_options->TestingClientConsensusDownloadSchedule = &client_cons;
+ mock_options->TestingBridgeDownloadSchedule = &bridge;
+
+ dls.schedule = DL_SCHED_GENERIC;
+ mock_options->ClientOnly = 1;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client);
+ mock_options->ClientOnly = 0;
+ mock_options->DirPort_set = 1;
+ mock_options->ORPort_set = 1;
+ mock_options->DirCache = 1;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server);
+
+#if 0
+ dls.schedule = DL_SCHED_CONSENSUS;
+ mock_options->ClientOnly = 1;
+ mock_options->DirCache = 0;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client_cons);
+ mock_options->ClientOnly = 0;
+ mock_options->DirCache = 1;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server_cons);
+#endif
+
+ dls.schedule = DL_SCHED_BRIDGE;
+ mock_options->ClientOnly = 1;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge);
+ mock_options->ClientOnly = 0;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge);
+
+ done:
+ UNMOCK(get_options);
+}
+
#define DIR_LEGACY(name) \
{ #name, test_dir_ ## name , TT_FORK, NULL, NULL }
@@ -3525,6 +4074,16 @@ struct testcase_t dir_tests[] = {
DIR(purpose_needs_anonymity, 0),
DIR(fetch_type, 0),
DIR(packages, 0),
+ DIR(download_status_schedule, 0),
+ DIR(download_status_increment, 0),
+ DIR(authdir_type_to_string, 0),
+ DIR(conn_purpose_to_string, 0),
+ DIR(should_use_directory_guards, 0),
+ DIR(should_not_init_request_to_ourselves, TT_FORK),
+ DIR(should_not_init_request_to_dir_auths_without_v3_info, 0),
+ DIR(should_init_request_to_dir_auths, 0),
+ DIR(choose_compression_level, 0),
+ DIR(find_dl_schedule, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c
new file mode 100644
index 0000000000..f9e97c973c
--- /dev/null
+++ b/src/test/test_dir_common.c
@@ -0,0 +1,425 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define DIRVOTE_PRIVATE
+#include "crypto.h"
+#include "test.h"
+#include "container.h"
+#include "or.h"
+#include "dirvote.h"
+#include "nodelist.h"
+#include "routerlist.h"
+#include "test_dir_common.h"
+
+void dir_common_setup_vote(networkstatus_t **vote, time_t now);
+networkstatus_t * dir_common_add_rs_and_parse(networkstatus_t *vote,
+ networkstatus_t **vote_out,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ crypto_pk_t *sign_skey, int *n_vrs,
+ time_t now, int clear_rl);
+
+extern const char AUTHORITY_CERT_1[];
+extern const char AUTHORITY_SIGNKEY_1[];
+extern const char AUTHORITY_CERT_2[];
+extern const char AUTHORITY_SIGNKEY_2[];
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_3[];
+
+/** Initialize and set auth certs and keys
+ * Returns 0 on success, -1 on failure. Clean up handled by caller.
+ */
+int
+dir_common_authority_pk_init(authority_cert_t **cert1,
+ authority_cert_t **cert2,
+ authority_cert_t **cert3,
+ crypto_pk_t **sign_skey_1,
+ crypto_pk_t **sign_skey_2,
+ crypto_pk_t **sign_skey_3)
+{
+ /* Parse certificates and keys. */
+ authority_cert_t *cert;
+ cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ tt_assert(cert);
+ tt_assert(cert->identity_key);
+ *cert1 = cert;
+ tt_assert(*cert1);
+ *cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
+ tt_assert(*cert2);
+ *cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+ tt_assert(*cert3);
+ *sign_skey_1 = crypto_pk_new();
+ *sign_skey_2 = crypto_pk_new();
+ *sign_skey_3 = crypto_pk_new();
+
+ tt_assert(!crypto_pk_read_private_key_from_string(*sign_skey_1,
+ AUTHORITY_SIGNKEY_1, -1));
+ tt_assert(!crypto_pk_read_private_key_from_string(*sign_skey_2,
+ AUTHORITY_SIGNKEY_2, -1));
+ tt_assert(!crypto_pk_read_private_key_from_string(*sign_skey_3,
+ AUTHORITY_SIGNKEY_3, -1));
+
+ tt_assert(!crypto_pk_cmp_keys(*sign_skey_1, (*cert1)->signing_key));
+ tt_assert(!crypto_pk_cmp_keys(*sign_skey_2, (*cert2)->signing_key));
+
+ return 0;
+ done:
+ return -1;
+}
+
+/**
+ * Generate a routerstatus for v3_networkstatus test.
+ */
+vote_routerstatus_t *
+dir_common_gen_routerstatus_for_v3ns(int idx, time_t now)
+{
+ vote_routerstatus_t *vrs=NULL;
+ routerstatus_t *rs = NULL;
+ tor_addr_t addr_ipv6;
+ char *method_list = NULL;
+
+ switch (idx) {
+ case 0:
+ /* Generate the first routerstatus. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.1.2.14");
+ rs->published_on = now-1500;
+ strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
+ memset(rs->identity_digest, TEST_DIR_ROUTER_ID_1, DIGEST_LEN);
+ memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_1, DIGEST_LEN);
+ rs->addr = 0x99008801;
+ rs->or_port = 443;
+ rs->dir_port = 8000;
+ /* all flags but running and v2dir cleared */
+ rs->is_flagged_running = 1;
+ rs->is_v2_dir = 1;
+ break;
+ case 1:
+ /* Generate the second routerstatus. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.2.0.5");
+ rs->published_on = now-1000;
+ strlcpy(rs->nickname, "router1", sizeof(rs->nickname));
+ memset(rs->identity_digest, TEST_DIR_ROUTER_ID_2, DIGEST_LEN);
+ memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_2, DIGEST_LEN);
+ rs->addr = 0x99009901;
+ rs->or_port = 443;
+ rs->dir_port = 0;
+ tor_addr_parse(&addr_ipv6, "[1:2:3::4]");
+ tor_addr_copy(&rs->ipv6_addr, &addr_ipv6);
+ rs->ipv6_orport = 4711;
+ rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running =
+ rs->is_valid = rs->is_possible_guard = rs->is_v2_dir = 1;
+ break;
+ case 2:
+ /* Generate the third routerstatus. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.1.0.3");
+ rs->published_on = now-1000;
+ strlcpy(rs->nickname, "router3", sizeof(rs->nickname));
+ memset(rs->identity_digest, TEST_DIR_ROUTER_ID_3, DIGEST_LEN);
+ memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_3, DIGEST_LEN);
+ rs->addr = 0xAA009901;
+ rs->or_port = 400;
+ rs->dir_port = 9999;
+ rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
+ rs->is_flagged_running = rs->is_valid = rs->is_v2_dir =
+ rs->is_possible_guard = 1;
+ break;
+ case 3:
+ /* Generate a fourth routerstatus that is not running. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.1.6.3");
+ rs->published_on = now-1000;
+ strlcpy(rs->nickname, "router4", sizeof(rs->nickname));
+ memset(rs->identity_digest, TEST_DIR_ROUTER_ID_4, DIGEST_LEN);
+ memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_4, DIGEST_LEN);
+ rs->addr = 0xC0000203;
+ rs->or_port = 500;
+ rs->dir_port = 1999;
+ rs->is_v2_dir = 1;
+ /* Running flag (and others) cleared */
+ break;
+ case 4:
+ /* No more for this test; return NULL */
+ vrs = NULL;
+ break;
+ default:
+ /* Shouldn't happen */
+ tt_assert(0);
+ }
+ if (vrs) {
+ vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
+ method_list = make_consensus_method_list(MIN_SUPPORTED_CONSENSUS_METHOD,
+ MAX_SUPPORTED_CONSENSUS_METHOD,
+ ",");
+ tor_asprintf(&vrs->microdesc->microdesc_hash_line,
+ "m %s "
+ "sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa%d\n",
+ method_list, idx);
+ }
+
+ done:
+ tor_free(method_list);
+ return vrs;
+}
+
+/** Initialize networkstatus vote object attributes. */
+void
+dir_common_setup_vote(networkstatus_t **vote, time_t now)
+{
+ *vote = tor_malloc_zero(sizeof(networkstatus_t));
+ (*vote)->type = NS_TYPE_VOTE;
+ (*vote)->published = now;
+ (*vote)->supported_methods = smartlist_new();
+ (*vote)->known_flags = smartlist_new();
+ (*vote)->net_params = smartlist_new();
+ (*vote)->routerstatus_list = smartlist_new();
+ (*vote)->voters = smartlist_new();
+}
+
+/** Helper: Make a new routerinfo containing the right information for a
+ * given vote_routerstatus_t. */
+routerinfo_t *
+dir_common_generate_ri_from_rs(const vote_routerstatus_t *vrs)
+{
+ routerinfo_t *r;
+ const routerstatus_t *rs = &vrs->status;
+ static time_t published = 0;
+
+ r = tor_malloc_zero(sizeof(routerinfo_t));
+ r->cert_expiration_time = TIME_MAX;
+ memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN);
+ memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest,
+ DIGEST_LEN);
+ r->cache_info.do_not_cache = 1;
+ r->cache_info.routerlist_index = -1;
+ r->cache_info.signed_descriptor_body =
+ tor_strdup("123456789012345678901234567890123");
+ r->cache_info.signed_descriptor_len =
+ strlen(r->cache_info.signed_descriptor_body);
+ r->exit_policy = smartlist_new();
+ r->cache_info.published_on = ++published + time(NULL);
+ if (rs->has_bandwidth) {
+ /*
+ * Multiply by 1000 because the routerinfo_t and the routerstatus_t
+ * seem to use different units (*sigh*) and because we seem stuck on
+ * icky and perverse decimal kilobytes (*double sigh*) - see
+ * router_get_advertised_bandwidth_capped() of routerlist.c and
+ * routerstatus_format_entry() of dirserv.c.
+ */
+ r->bandwidthrate = rs->bandwidth_kb * 1000;
+ r->bandwidthcapacity = rs->bandwidth_kb * 1000;
+ }
+ return r;
+}
+
+/** Create routerstatuses and signed vote.
+ * Create routerstatuses using *vrs_gen* and add them to global routerlist.
+ * Next, create signed vote using *sign_skey* and *vote*, which should have
+ * predefined header fields.
+ * Setting *clear_rl* clears the global routerlist before adding the new
+ * routers.
+ * Return the signed vote, same as *vote_out*. Save the number of routers added
+ * in *n_vrs*.
+ */
+networkstatus_t *
+dir_common_add_rs_and_parse(networkstatus_t *vote, networkstatus_t **vote_out,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ crypto_pk_t *sign_skey, int *n_vrs, time_t now,
+ int clear_rl)
+{
+ vote_routerstatus_t *vrs;
+ char *v_text=NULL;
+ const char *msg=NULL;
+ int idx;
+ was_router_added_t router_added = -1;
+ *vote_out = NULL;
+
+ if (clear_rl) {
+ nodelist_free_all();
+ routerlist_free_all();
+ }
+
+ idx = 0;
+ do {
+ vrs = vrs_gen(idx, now);
+ if (vrs) {
+ smartlist_add(vote->routerstatus_list, vrs);
+ router_added =
+ router_add_to_routerlist(dir_common_generate_ri_from_rs(vrs),
+ &msg,0,0);
+ tt_assert(router_added >= 0);
+ ++idx;
+ }
+ } while (vrs);
+ *n_vrs = idx;
+
+ /* dump the vote and try to parse it. */
+ v_text = format_networkstatus_vote(sign_skey, vote);
+ tt_assert(v_text);
+ *vote_out = networkstatus_parse_vote_from_string(v_text, NULL, NS_TYPE_VOTE);
+
+ done:
+ if (v_text)
+ tor_free(v_text);
+
+ return *vote_out;
+}
+
+/** Create a fake *vote* where *cert* describes the signer, *sign_skey*
+ * is the signing key, and *vrs_gen* is the function we'll use to create the
+ * routers on which we're voting.
+ * We pass *vote_out*, *n_vrs*, and *clear_rl* directly to vrs_gen().
+ * Return 0 on success, return -1 on failure.
+ */
+int
+dir_common_construct_vote_1(networkstatus_t **vote, authority_cert_t *cert,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs,
+ time_t now, int clear_rl)
+{
+ networkstatus_voter_info_t *voter;
+
+ dir_common_setup_vote(vote, now);
+ (*vote)->valid_after = now+1000;
+ (*vote)->fresh_until = now+2000;
+ (*vote)->valid_until = now+3000;
+ (*vote)->vote_seconds = 100;
+ (*vote)->dist_seconds = 200;
+ smartlist_split_string((*vote)->supported_methods, "1 2 3", NULL, 0, -1);
+ (*vote)->client_versions = tor_strdup("0.1.2.14,0.1.2.15");
+ (*vote)->server_versions = tor_strdup("0.1.2.14,0.1.2.15,0.1.2.16");
+ smartlist_split_string((*vote)->known_flags,
+ "Authority Exit Fast Guard Running Stable V2Dir Valid",
+ 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+ voter->nickname = tor_strdup("Voter1");
+ voter->address = tor_strdup("1.2.3.4");
+ voter->addr = 0x01020304;
+ voter->dir_port = 80;
+ voter->or_port = 9000;
+ voter->contact = tor_strdup("voter@example.com");
+ crypto_pk_get_digest(cert->identity_key, voter->identity_digest);
+ /*
+ * Set up a vote; generate it; try to parse it.
+ */
+ smartlist_add((*vote)->voters, voter);
+ (*vote)->cert = authority_cert_dup(cert);
+ smartlist_split_string((*vote)->net_params, "circuitwindow=101 foo=990",
+ NULL, 0, 0);
+ *n_vrs = 0;
+ /* add routerstatuses */
+ if (!dir_common_add_rs_and_parse(*vote, vote_out, vrs_gen, sign_skey,
+ n_vrs, now, clear_rl))
+ return -1;
+
+ return 0;
+}
+
+/** See dir_common_construct_vote_1.
+ * Produces a vote with slightly different values.
+ */
+int
+dir_common_construct_vote_2(networkstatus_t **vote, authority_cert_t *cert,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs,
+ time_t now, int clear_rl)
+{
+ networkstatus_voter_info_t *voter;
+
+ dir_common_setup_vote(vote, now);
+ (*vote)->type = NS_TYPE_VOTE;
+ (*vote)->published += 1;
+ (*vote)->valid_after = now+1000;
+ (*vote)->fresh_until = now+3005;
+ (*vote)->valid_until = now+3000;
+ (*vote)->vote_seconds = 100;
+ (*vote)->dist_seconds = 300;
+ smartlist_split_string((*vote)->supported_methods, "1 2 3", NULL, 0, -1);
+ smartlist_split_string((*vote)->known_flags,
+ "Authority Exit Fast Guard MadeOfCheese MadeOfTin "
+ "Running Stable V2Dir Valid", 0,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+ voter->nickname = tor_strdup("Voter2");
+ voter->address = tor_strdup("2.3.4.5");
+ voter->addr = 0x02030405;
+ voter->dir_port = 80;
+ voter->or_port = 9000;
+ voter->contact = tor_strdup("voter@example.com");
+ crypto_pk_get_digest(cert->identity_key, voter->identity_digest);
+ /*
+ * Set up a vote; generate it; try to parse it.
+ */
+ smartlist_add((*vote)->voters, voter);
+ (*vote)->cert = authority_cert_dup(cert);
+ if (! (*vote)->net_params)
+ (*vote)->net_params = smartlist_new();
+ smartlist_split_string((*vote)->net_params,
+ "bar=2000000000 circuitwindow=20",
+ NULL, 0, 0);
+ /* add routerstatuses */
+ /* dump the vote and try to parse it. */
+ dir_common_add_rs_and_parse(*vote, vote_out, vrs_gen, sign_skey,
+ n_vrs, now, clear_rl);
+
+ return 0;
+}
+
+/** See dir_common_construct_vote_1.
+ * Produces a vote with slightly different values. Adds a legacy key.
+ */
+int
+dir_common_construct_vote_3(networkstatus_t **vote, authority_cert_t *cert,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs,
+ time_t now, int clear_rl)
+{
+ networkstatus_voter_info_t *voter;
+
+ dir_common_setup_vote(vote, now);
+ (*vote)->valid_after = now+1000;
+ (*vote)->fresh_until = now+2003;
+ (*vote)->valid_until = now+3000;
+ (*vote)->vote_seconds = 100;
+ (*vote)->dist_seconds = 250;
+ smartlist_split_string((*vote)->supported_methods, "1 2 3 4", NULL, 0, -1);
+ (*vote)->client_versions = tor_strdup("0.1.2.14,0.1.2.17");
+ (*vote)->server_versions = tor_strdup("0.1.2.10,0.1.2.15,0.1.2.16");
+ smartlist_split_string((*vote)->known_flags,
+ "Authority Exit Fast Guard Running Stable V2Dir Valid",
+ 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+ voter->nickname = tor_strdup("Voter2");
+ voter->address = tor_strdup("3.4.5.6");
+ voter->addr = 0x03040506;
+ voter->dir_port = 80;
+ voter->or_port = 9000;
+ voter->contact = tor_strdup("voter@example.com");
+ crypto_pk_get_digest(cert->identity_key, voter->identity_digest);
+ memset(voter->legacy_id_digest, (int)'A', DIGEST_LEN);
+ /*
+ * Set up a vote; generate it; try to parse it.
+ */
+ smartlist_add((*vote)->voters, voter);
+ (*vote)->cert = authority_cert_dup(cert);
+ smartlist_split_string((*vote)->net_params, "circuitwindow=80 foo=660",
+ NULL, 0, 0);
+ /* add routerstatuses */
+ /* dump the vote and try to parse it. */
+ dir_common_add_rs_and_parse(*vote, vote_out, vrs_gen, sign_skey,
+ n_vrs, now, clear_rl);
+
+ return 0;
+}
+
diff --git a/src/test/test_dir_common.h b/src/test/test_dir_common.h
new file mode 100644
index 0000000000..9557cb7157
--- /dev/null
+++ b/src/test/test_dir_common.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "networkstatus.h"
+#include "routerparse.h"
+
+#define TEST_DIR_ROUTER_ID_1 3
+#define TEST_DIR_ROUTER_ID_2 5
+#define TEST_DIR_ROUTER_ID_3 33
+#define TEST_DIR_ROUTER_ID_4 34
+
+#define TEST_DIR_ROUTER_DD_1 78
+#define TEST_DIR_ROUTER_DD_2 77
+#define TEST_DIR_ROUTER_DD_3 79
+#define TEST_DIR_ROUTER_DD_4 44
+
+int dir_common_authority_pk_init(authority_cert_t **cert1,
+ authority_cert_t **cert2,
+ authority_cert_t **cert3,
+ crypto_pk_t **sign_skey_1,
+ crypto_pk_t **sign_skey_2,
+ crypto_pk_t **sign_skey_3);
+
+routerinfo_t * dir_common_generate_ri_from_rs(const vote_routerstatus_t *vrs);
+
+vote_routerstatus_t * dir_common_gen_routerstatus_for_v3ns(int idx,
+ time_t now);
+
+int dir_common_construct_vote_1(networkstatus_t **vote,
+ authority_cert_t *cert1,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs, time_t now,
+ int clear_rl);
+
+int dir_common_construct_vote_2(networkstatus_t **vote,
+ authority_cert_t *cert2,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs, time_t now,
+ int clear_rl);
+
+int dir_common_construct_vote_3(networkstatus_t **vote,
+ authority_cert_t *cert3,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs, time_t now,
+ int clear_rl);
+
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
new file mode 100644
index 0000000000..46d76734e0
--- /dev/null
+++ b/src/test/test_dir_handle_get.c
@@ -0,0 +1,2552 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define RENDCOMMON_PRIVATE
+#define GEOIP_PRIVATE
+#define CONNECTION_PRIVATE
+#define CONFIG_PRIVATE
+#define RENDCACHE_PRIVATE
+
+#include "or.h"
+#include "config.h"
+#include "connection.h"
+#include "directory.h"
+#include "test.h"
+#include "connection.h"
+#include "rendcommon.h"
+#include "rendcache.h"
+#include "router.h"
+#include "routerlist.h"
+#include "rend_test_helpers.h"
+#include "microdesc.h"
+#include "test_helpers.h"
+#include "nodelist.h"
+#include "entrynodes.h"
+#include "routerparse.h"
+#include "networkstatus.h"
+#include "geoip.h"
+#include "dirserv.h"
+#include "torgzip.h"
+#include "dirvote.h"
+
+#ifdef _WIN32
+/* For mkdir() */
+#include <direct.h>
+#else
+#include <dirent.h>
+#endif
+
+#include "vote_descriptors.inc"
+
+#define NS_MODULE dir_handle_get
+
+static void
+connection_write_to_buf_mock(const char *string, size_t len,
+ connection_t *conn, int zlib)
+{
+ (void) zlib;
+
+ tor_assert(string);
+ tor_assert(conn);
+
+ write_to_buf(string, len, conn->outbuf);
+}
+
+#define GET(path) "GET " path " HTTP/1.0\r\n\r\n"
+#define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n"
+#define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n"
+#define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again later\r\n\r\n"
+#define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \
+ "Consensus not signed by sufficient number of requested authorities\r\n\r\n"
+
+static tor_addr_t MOCK_TOR_ADDR;
+
+static void
+test_dir_handle_get_bad_request(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(directory_handle_command_get(conn, "", NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_v1_command_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // no frontpage configured
+ tt_ptr_op(get_dirportfrontpage(), OP_EQ, NULL);
+
+ /* V1 path */
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0),
+ OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static const char*
+mock_get_dirportfrontpage(void)
+{
+ return "HELLO FROM FRONTPAGE";
+}
+
+static void
+test_dir_handle_get_v1_command(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0, body_len = 0;
+ const char *exp_body = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ MOCK(get_dirportfrontpage, mock_get_dirportfrontpage);
+
+ exp_body = get_dirportfrontpage();
+ body_len = strlen(exp_body);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0),
+ OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, body_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/html\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 20\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, exp_body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_dirportfrontpage);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ /* Unrecognized path */
+ tt_int_op(directory_handle_command_get(conn, GET("/anything"), NULL, 0),
+ OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_robots_txt(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/robots.txt"),
+ NULL, 0), OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, 29, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 28\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, "User-agent: *\r\nDisallow: /\r\n");
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_bytes_txt(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0, body_len = 0;
+ char buff[30];
+ char *exp_body = NULL;
+ (void) data;
+
+ exp_body = directory_dump_request_log();
+ body_len = strlen(exp_body);
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/bytes.txt"), NULL, 0),
+ OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, body_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+
+ sprintf(buff, "Content-Length: %ld\r\n", (long) body_len);
+ tt_assert(strstr(header, buff));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, exp_body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ tor_free(exp_body);
+}
+
+#define RENDEZVOUS2_GET(descid) GET("/tor/rendezvous2/" descid)
+static void
+test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is not encrypted
+ tt_assert(!connection_dir_is_encrypted(conn))
+
+ tt_int_op(directory_handle_command_get(conn, RENDEZVOUS2_GET(), NULL, 0),
+ OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id(
+ void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ tt_int_op(directory_handle_command_get(conn,
+ RENDEZVOUS2_GET("invalid-desc-id"), NULL, 0), OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ //TODO: this cant be reached because rend_valid_descriptor_id() prevents this
+ //case to happen. This test is the same as
+ //test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id
+ //We should refactor to remove the case from the switch.
+
+ const char *req = RENDEZVOUS2_GET("1bababababababababababababababab");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ rend_cache_init();
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ const char *req = RENDEZVOUS2_GET("3xqunszqnaolrrfmtzgaki7mxelgvkje");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ rend_cache_free_all();
+}
+
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static routerinfo_t *mock_routerinfo;
+static int hid_serv_responsible_for_desc_id_response;
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+ if (!mock_routerinfo) {
+ mock_routerinfo = tor_malloc_zero(sizeof(routerinfo_t));
+ }
+
+ return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ char buff[30];
+ char req[70];
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ size_t body_len = 0;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+ hid_serv_responsible_for_desc_id_response = 1;
+
+ /* create a valid rend service descriptor */
+ #define RECENT_TIME -10
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+ tt_int_op(rend_cache_store_v2_desc_as_dir(desc_holder->desc_str),
+ OP_EQ, RCS_OKAY);
+
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ sprintf(req, RENDEZVOUS2_GET("%s"), desc_id_base32);
+
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ body_len = strlen(desc_holder->desc_str);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, body_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+ sprintf(buff, "Content-Length: %ld\r\n", (long) body_len);
+ tt_assert(strstr(header, buff));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, desc_holder->desc_str);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
+ tor_free(mock_routerinfo);
+
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+}
+
+#define MICRODESC_GET(digest) GET("/tor/micro/d/" digest)
+static void
+test_dir_handle_get_micro_d_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ #define B64_256_1 "8/Pz8/u7vz8/Pz+7vz8/Pz+7u/Pz8/P7u/Pz8/P7u78"
+ #define B64_256_2 "zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMw"
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = MICRODESC_GET(B64_256_1 "-" B64_256_2);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static or_options_t *mock_options = NULL;
+static void
+init_mock_options(void)
+{
+ mock_options = malloc(sizeof(or_options_t));
+ memset(mock_options, 0, sizeof(or_options_t));
+ mock_options->TestingTorNetwork = 1;
+}
+
+static const or_options_t *
+mock_get_options(void)
+{
+ tor_assert(mock_options);
+ return mock_options;
+}
+
+static const char microdesc[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMjlHH/daN43cSVRaHBwgUfnszzAhg98EvivJ9Qxfv51mvQUxPjQ07es\n"
+ "gV/3n8fyh3Kqr/ehi9jxkdgSRfSnmF7giaHL1SLZ29kA7KtST+pBvmTpDtHa3ykX\n"
+ "Xorc7hJvIyTZoc1HU+5XSynj3gsBE5IGK1ZRzrNS688LnuZMVp1tAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n";
+
+static void
+test_dir_handle_get_micro_d(void *data)
+{
+ dir_connection_t *conn = NULL;
+ microdesc_cache_t *mc = NULL ;
+ smartlist_t *list = NULL;
+ char digest[DIGEST256_LEN];
+ char digest_base64[128];
+ char path[80];
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ const char *fn = get_fname("dir_handle_datadir_test1");
+ mock_options->DataDirectory = tor_strdup(fn);
+
+#ifdef _WIN32
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory));
+#else
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700));
+#endif
+
+ /* Add microdesc to cache */
+ crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256);
+ base64_encode_nopad(digest_base64, sizeof(digest_base64),
+ (uint8_t *) digest, DIGEST256_LEN);
+
+ mc = get_microdesc_cache();
+ list = microdescs_add_to_cache(mc, microdesc, NULL, SAVED_NOWHERE, 0,
+ time(NULL), NULL);
+ tt_int_op(1, OP_EQ, smartlist_len(list));
+
+ /* Make the request */
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ sprintf(path, MICRODESC_GET("%s"), digest_base64);
+ tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(microdesc)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, microdesc);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ smartlist_free(list);
+ microdesc_free_all();
+}
+
+static void
+test_dir_handle_get_micro_d_server_busy(void *data)
+{
+ dir_connection_t *conn = NULL;
+ microdesc_cache_t *mc = NULL ;
+ smartlist_t *list = NULL;
+ char digest[DIGEST256_LEN];
+ char digest_base64[128];
+ char path[80];
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ const char *fn = get_fname("dir_handle_datadir_test2");
+ mock_options->DataDirectory = tor_strdup(fn);
+
+#ifdef _WIN32
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory));
+#else
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700));
+#endif
+
+ /* Add microdesc to cache */
+ crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256);
+ base64_encode_nopad(digest_base64, sizeof(digest_base64),
+ (uint8_t *) digest, DIGEST256_LEN);
+
+ mc = get_microdesc_cache();
+ list = microdescs_add_to_cache(mc, microdesc, NULL, SAVED_NOWHERE, 0,
+ time(NULL), NULL);
+ tt_int_op(1, OP_EQ, smartlist_len(list));
+
+ //Make it busy
+ mock_options->CountPrivateBandwidth = 1;
+
+ /* Make the request */
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ sprintf(path, MICRODESC_GET("%s"), digest_base64);
+ tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ smartlist_free(list);
+ microdesc_free_all();
+}
+
+#define BRIDGES_PATH "/tor/networkstatus-bridges"
+static void
+test_dir_handle_get_networkstatus_bridges_not_found_without_auth(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ mock_options->BridgeAuthoritativeDir = 1;
+ mock_options->BridgePassword_AuthDigest_ = tor_strdup("digest");
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->linked = 1;
+
+ const char *req = GET(BRIDGES_PATH);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_networkstatus_bridges(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ mock_options->BridgeAuthoritativeDir = 1;
+ mock_options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN);
+ crypto_digest256(mock_options->BridgePassword_AuthDigest_,
+ "abcdefghijklm12345", 18, DIGEST_SHA256);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->linked = 1;
+
+ const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
+ "Authorization: Basic abcdefghijklm12345\r\n\r\n";
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 0\r\n"));
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_networkstatus_bridges_not_found_wrong_auth(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ mock_options->BridgeAuthoritativeDir = 1;
+ mock_options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN);
+ crypto_digest256(mock_options->BridgePassword_AuthDigest_,
+ "abcdefghijklm12345", 18, DIGEST_SHA256);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->linked = 1;
+
+ const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
+ "Authorization: Basic NOTSAMEDIGEST\r\n\r\n";
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+#define SERVER_DESC_GET(id) GET("/tor/server/" id)
+static void
+test_dir_handle_get_server_descriptors_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = SERVER_DESC_GET("invalid");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ 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);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_descriptors_all(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ /* Setup fake routerlist. */
+ helper_setup_fake_routerlist();
+
+ //TODO: change to router_get_my_extrainfo when testing "extra" path
+ NS_MOCK(router_get_my_routerinfo);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ // We are one of the routers
+ routerlist_t *our_routerlist = router_get_routerlist();
+ 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);
+
+ /* Treat "all" requests as if they were unencrypted */
+ mock_routerinfo->cache_info.send_unencrypted = 1;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = SERVER_DESC_GET("all");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(signed_descriptor_len)+1 as body_len but returns a body
+ //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);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ //TODO: Is this a BUG?
+ //This is what should be expected: tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_int_op(body_used, OP_EQ,
+ mock_routerinfo->cache_info.signed_descriptor_len);
+
+ 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);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+
+ routerlist_free_all();
+ nodelist_free_all();
+ entry_guards_free_all();
+}
+
+static char
+TEST_DESCRIPTOR[] =
+"@uploaded-at 2014-06-08 19:20:11\n"
+"@source \"127.0.0.1\"\n"
+"router test000a 127.0.0.1 5000 0 7000\n"
+"platform Tor 0.2.5.3-alpha-dev on Linux\n"
+"protocols Link 1 2 Circuit 1\n"
+"published 2014-06-08 19:20:11\n"
+"fingerprint C7E7 CCB8 179F 8CC3 7F5C 8A04 2B3A 180B 934B 14BA\n"
+"uptime 0\n"
+"bandwidth 1073741824 1073741824 0\n"
+"extra-info-digest 67A152A4C7686FB07664F872620635F194D76D95\n"
+"caches-extra-info\n"
+"onion-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAOuBUIEBARMkkka/TGyaQNgUEDLP0KG7sy6KNQTNOlZHUresPr/vlVjo\n"
+"HPpLMfu9M2z18c51YX/muWwY9x4MyQooD56wI4+AqXQcJRwQfQlPn3Ay82uZViA9\n"
+"DpBajRieLlKKkl145KjArpD7F5BVsqccvjErgFYXvhhjSrx7BVLnAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAN6NLnSxWQnFXxqZi5D3b0BMgV6y9NJLGjYQVP+eWtPZWgqyv4zeYsqv\n"
+"O9y6c5lvxyUxmNHfoAbe/s8f2Vf3/YaC17asAVSln4ktrr3e9iY74a9RMWHv1Gzk\n"
+"3042nMcqj3PEhRN0PoLkcOZNjjmNbaqki6qy9bWWZDNTdo+uI44dAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"hidden-service-dir\n"
+"contact auth0@test.test\n"
+"ntor-onion-key pK4bs08ERYN591jj7ca17Rn9Q02TIEfhnjR6hSq+fhU=\n"
+"reject *:*\n"
+"router-signature\n"
+"-----BEGIN SIGNATURE-----\n"
+"rx88DuM3Y7tODlHNDDEVzKpwh3csaG1or+T4l2Xs1oq3iHHyPEtB6QTLYrC60trG\n"
+"aAPsj3DEowGfjga1b248g2dtic8Ab+0exfjMm1RHXfDam5TXXZU3A0wMyoHjqHuf\n"
+"eChGPgFNUvEc+5YtD27qEDcUjcinYztTs7/dzxBT4PE=\n"
+"-----END SIGNATURE-----\n";
+
+static void
+test_dir_handle_get_server_descriptors_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ NS_MOCK(router_get_my_routerinfo);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* init mock */
+ router_get_my_routerinfo();
+ crypto_pk_get_digest(identity_pkey,
+ mock_routerinfo->cache_info.identity_digest);
+
+ // the digest is mine (the channel is unnecrypted, so we must allow sending)
+ set_server_identity_key(identity_pkey);
+ mock_routerinfo->cache_info.send_unencrypted = 1;
+
+ /* Setup descriptor */
+ long annotation_len = strstr(TEST_DESCRIPTOR, "router ") - TEST_DESCRIPTOR;
+ mock_routerinfo->cache_info.signed_descriptor_body =
+ tor_strdup(TEST_DESCRIPTOR);
+ mock_routerinfo->cache_info.signed_descriptor_len =
+ strlen(TEST_DESCRIPTOR) - annotation_len;;
+ mock_routerinfo->cache_info.annotations_len = annotation_len;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = SERVER_DESC_GET("authority");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(TEST_DESCRIPTOR)+1 as body_len but returns a body which
+ //is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_DESCRIPTOR)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ tt_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);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ crypto_pk_free(identity_pkey);
+}
+
+static void
+test_dir_handle_get_server_descriptors_fp(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ NS_MOCK(router_get_my_routerinfo);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* init mock */
+ router_get_my_routerinfo();
+ crypto_pk_get_digest(identity_pkey,
+ mock_routerinfo->cache_info.identity_digest);
+
+ // the digest is mine (the channel is unnecrypted, so we must allow sending)
+ set_server_identity_key(identity_pkey);
+ mock_routerinfo->cache_info.send_unencrypted = 1;
+
+ /* Setup descriptor */
+ long annotation_len = strstr(TEST_DESCRIPTOR, "router ") - TEST_DESCRIPTOR;
+ mock_routerinfo->cache_info.signed_descriptor_body =
+ tor_strdup(TEST_DESCRIPTOR);
+ mock_routerinfo->cache_info.signed_descriptor_len =
+ strlen(TEST_DESCRIPTOR) - annotation_len;
+ mock_routerinfo->cache_info.annotations_len = annotation_len;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ #define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+ #define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+ const char *hex_digest = hex_str(mock_routerinfo->cache_info.identity_digest,
+ DIGEST_LEN);
+
+ char req[155];
+ sprintf(req, SERVER_DESC_GET("fp/%s+" HEX1 "+" HEX2), hex_digest);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(TEST_DESCRIPTOR)+1 as body_len but returns a body which
+ //is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_DESCRIPTOR)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ tt_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);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ crypto_pk_free(identity_pkey);
+}
+
+#define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+#define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+
+static void
+test_dir_handle_get_server_descriptors_d(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ /* Setup fake routerlist. */
+ helper_setup_fake_routerlist();
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* Get one router's signed_descriptor_digest */
+ routerlist_t *our_routerlist = router_get_routerlist();
+ tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+ routerinfo_t *router = smartlist_get(our_routerlist->routers, 0);
+ const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
+ DIGEST_LEN);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ char req_header[155];
+ sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+ tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(signed_descriptor_len)+1 as body_len but returns a body
+ //which is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used,
+ router->cache_info.signed_descriptor_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ //TODO: Is this a BUG?
+ //This is what should be expected:
+ //tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_int_op(body_used, OP_EQ, router->cache_info.signed_descriptor_len);
+
+ 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);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ crypto_pk_free(identity_pkey);
+
+ routerlist_free_all();
+ nodelist_free_all();
+ entry_guards_free_all();
+}
+
+static void
+test_dir_handle_get_server_descriptors_busy(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ /* Setup fake routerlist. */
+ helper_setup_fake_routerlist();
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ //Make it busy
+ MOCK(get_options, mock_get_options);
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ /* Get one router's signed_descriptor_digest */
+ routerlist_t *our_routerlist = router_get_routerlist();
+ tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+ routerinfo_t *router = smartlist_get(our_routerlist->routers, 0);
+ const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
+ DIGEST_LEN);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ #define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+ #define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+ char req_header[155];
+ sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+ tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ crypto_pk_free(identity_pkey);
+
+ routerlist_free_all();
+ nodelist_free_all();
+ entry_guards_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_bad_req(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(BAD_REQUEST, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_all_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/all");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+#define TEST_CERTIFICATE AUTHORITY_CERT_3
+#define TEST_SIGNING_KEY AUTHORITY_SIGNKEY_A_DIGEST
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
+
+static const char TEST_CERT_IDENT_KEY[] =
+ "D867ACF56A9D229B35C25F0090BC9867E906BE69";
+
+static void
+test_dir_handle_get_server_keys_all(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ const char digest[DIGEST_LEN] = "";
+
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/all");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_authority_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/authority");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static authority_cert_t * mock_cert = NULL;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+ tor_assert(mock_cert);
+ return mock_cert;
+}
+
+static void
+test_dir_handle_get_server_keys_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/authority");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(get_my_v3_authority_cert);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+}
+
+static void
+test_dir_handle_get_server_keys_fp_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/fp/somehex");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_fp(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ char req[71];
+ sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_sk_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/sk/somehex");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_sk(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ char req[71];
+ sprintf(req, GET("/tor/keys/sk/%s"), TEST_SIGNING_KEY);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(get_my_v3_authority_cert);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_server_keys_fpsk_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/fp-sk/somehex");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_fpsk(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ dir_server_add(ds);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ char req[115];
+ sprintf(req, GET("/tor/keys/fp-sk/%s-%s"),
+ TEST_CERT_IDENT_KEY, TEST_SIGNING_KEY);
+
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_busy(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ dir_server_add(ds);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* setup busy server */
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ char req[71];
+ sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static networkstatus_t *mock_ns_val = NULL;
+static networkstatus_t *
+mock_ns_get_by_flavor(consensus_flavor_t f)
+{
+ (void)f;
+ return mock_ns_val;
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *stats = NULL;
+ (void) d;
+
+ /* init mock */
+ mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
+ mock_ns_val->flavor = FLAV_NS;
+ mock_ns_val->voters = smartlist_new();
+
+ /* init mock */
+ init_mock_options();
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor);
+
+ /* start gathering stats */
+ mock_options->DirReqStatistics = 1;
+ geoip_dirreq_stats_init(time(NULL));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/consensus-ns/" HEX1 "+" HEX2), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_ENOUGH_CONSENSUS_SIGNATURES, OP_EQ, header);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+ tt_assert(strstr(stats, "not-enough-sigs=8"));
+
+ done:
+ UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_options);
+
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(stats);
+ smartlist_free(mock_ns_val->voters);
+ tor_free(mock_ns_val);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *stats = NULL;
+ (void) data;
+
+ init_mock_options();
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* start gathering stats */
+ mock_options->DirReqStatistics = 1;
+ geoip_dirreq_stats_init(time(NULL));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/consensus-ns"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+ tt_assert(strstr(stats, "not-found=8"));
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_options);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(stats);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+NS_DECL(int, geoip_get_country_by_addr, (const tor_addr_t *addr));
+
+int
+NS(geoip_get_country_by_addr)(const tor_addr_t *addr)
+{
+ (void)addr;
+ CALLED(geoip_get_country_by_addr)++;
+ return 1;
+}
+
+static void
+status_vote_current_consensus_ns_test(char **header, char **body,
+ size_t *body_len)
+{
+ common_digests_t digests;
+ dir_connection_t *conn = NULL;
+
+ #define NETWORK_STATUS "some network status string"
+ dirserv_set_cached_consensus_networkstatus(NETWORK_STATUS, "ns", &digests,
+ time(NULL));
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ tt_assert(mock_options);
+ mock_options->DirReqStatistics = 1;
+ geoip_dirreq_stats_init(time(NULL));
+
+ /* init geoip database */
+ geoip_parse_entry("10,50,AB", AF_INET);
+ tt_str_op("ab", OP_EQ, geoip_get_country_name(1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->address = tor_strdup("127.0.0.1");
+
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/consensus-ns"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_len, strlen(NETWORK_STATUS)+7, 0);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns(void* data)
+{
+ char *header = NULL;
+ char *body = NULL, *comp_body = NULL;
+ size_t body_used = 0, comp_body_used = 0;
+ char *stats = NULL, *hist = NULL;
+ (void) data;
+
+ dirserv_free_all();
+ clear_geoip_db();
+
+ NS_MOCK(geoip_get_country_by_addr);
+ MOCK(get_options, mock_get_options);
+
+ init_mock_options();
+
+ status_vote_current_consensus_ns_test(&header, &comp_body, &comp_body_used);
+ tt_assert(header);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+
+ compress_method_t compression = detect_compression_method(comp_body,
+ 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);
+
+ tt_str_op(NETWORK_STATUS, OP_EQ, body);
+ tt_int_op(strlen(NETWORK_STATUS), OP_EQ, body_used);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+
+ tt_assert(strstr(stats, "ok=8"));
+ tt_assert(strstr(stats, "dirreq-v3-ips ab=8"));
+ tt_assert(strstr(stats, "dirreq-v3-reqs ab=8"));
+ tt_assert(strstr(stats, "dirreq-v3-direct-dl"
+ " complete=0,timeout=0,running=4"));
+
+ hist = geoip_get_request_history();
+ tt_assert(hist);
+ tt_str_op("ab=8", OP_EQ, hist);
+
+ done:
+ NS_UNMOCK(geoip_get_country_by_addr);
+ UNMOCK(get_options);
+ tor_free(header);
+ tor_free(comp_body);
+ tor_free(body);
+ tor_free(stats);
+ tor_free(hist);
+ or_options_free(mock_options); mock_options = NULL;
+
+ dirserv_free_all();
+ clear_geoip_db();
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_busy(void* data)
+{
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ char *stats = NULL;
+ (void) data;
+
+ dirserv_free_all();
+ clear_geoip_db();
+
+ MOCK(get_options, mock_get_options);
+
+ // Make it busy
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ status_vote_current_consensus_ns_test(&header, &body, &body_used);
+ tt_assert(header);
+
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+ tt_assert(strstr(stats, "busy=8"));
+
+ done:
+ UNMOCK(get_options);
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+
+ tor_free(stats);
+ dirserv_free_all();
+ clear_geoip_db();
+}
+
+static void
+test_dir_handle_get_status_vote_current_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/" HEX1), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+#define VOTE_DIGEST "312A4890D4D832597ABBD3089C782DBBFB81E48D"
+
+static void
+status_vote_current_d_test(char **header, char **body, size_t *body_l)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/d/" VOTE_DIGEST), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_l, strlen(VOTE_BODY_V3)+1, 0);
+ tt_assert(header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+status_vote_next_d_test(char **header, char **body, size_t *body_l)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/d/" VOTE_DIGEST), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_l, strlen(VOTE_BODY_V3)+1, 0);
+ tt_assert(header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+test_dir_handle_get_status_vote_current_d_not_found(void* data)
+{
+ char *header = NULL;
+ (void) data;
+
+ status_vote_current_d_test(&header, NULL, NULL);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_next_d_not_found(void* data)
+{
+ char *header = NULL;
+ (void) data;
+
+ status_vote_next_d_test(&header, NULL, NULL);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_d(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ clear_dir_servers();
+ dirvote_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+ mock_options->TestingV3AuthVotingStartOffset = 0;
+ mock_options->TestingV3AuthInitialVotingInterval = 1;
+ mock_options->TestingV3AuthInitialVoteDelay = 1;
+ mock_options->TestingV3AuthInitialDistDelay = 1;
+
+ time_t now = 1441223455 -1;
+ dirvote_recalculate_timing(mock_options, now);
+
+ const char *msg_out = NULL;
+ int status_out = 0;
+ struct pending_vote_t *pv = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+ &status_out);
+ tt_assert(pv);
+
+ status_vote_current_d_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ tor_free(header);
+ tor_free(body);
+
+ status_vote_next_d_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ dirvote_free_all();
+}
+
+static void
+test_dir_handle_get_status_vote_next_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/" HEX1), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+status_vote_next_consensus_test(char **header, char **body, size_t *body_used)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/consensus"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_used, 18, 0);
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_not_found(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used;
+ (void) data;
+
+ status_vote_next_consensus_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_current_authority_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_next_authority_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+NS_DECL(const char*,
+dirvote_get_pending_consensus, (consensus_flavor_t flav));
+
+const char*
+NS(dirvote_get_pending_consensus)(consensus_flavor_t flav)
+{
+ (void)flav;
+ return "pending consensus";
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ NS_MOCK(dirvote_get_pending_consensus);
+
+ status_vote_next_consensus_test(&header, &body, &body_used);
+ tt_assert(header);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 17\r\n"));
+
+ tt_str_op("pending consensus", OP_EQ, body);
+
+ done:
+ NS_UNMOCK(dirvote_get_pending_consensus);
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_busy(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ NS_MOCK(dirvote_get_pending_consensus);
+
+ //Make it busy
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ status_vote_next_consensus_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ NS_UNMOCK(dirvote_get_pending_consensus);
+ UNMOCK(get_options);
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+static void
+status_vote_next_consensus_signatures_test(char **header, char **body,
+ size_t *body_used)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/consensus-signatures"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_used, 22, 0);
+
+ done:
+ connection_free_(TO_CONN(conn));
+ UNMOCK(connection_write_to_buf_impl_);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures_not_found(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used;
+ (void) data;
+
+ status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ tor_free(header);
+ tor_free(body);
+}
+
+NS_DECL(const char*,
+dirvote_get_pending_detached_signatures, (void));
+
+const char*
+NS(dirvote_get_pending_detached_signatures)(void)
+{
+ return "pending detached sigs";
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ NS_MOCK(dirvote_get_pending_detached_signatures);
+
+ status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+ tt_assert(header);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 21\r\n"));
+
+ tt_str_op("pending detached sigs", OP_EQ, body);
+
+ done:
+ NS_UNMOCK(dirvote_get_pending_detached_signatures);
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures_busy(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used;
+ (void) data;
+
+ NS_MOCK(dirvote_get_pending_detached_signatures);
+ MOCK(get_options, mock_get_options);
+
+ //Make it busy
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ NS_UNMOCK(dirvote_get_pending_detached_signatures);
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+static void
+test_dir_handle_get_status_vote_next_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ const char *msg_out = NULL;
+ int status_out = 0;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+ mock_options->TestingV3AuthVotingStartOffset = 0;
+ mock_options->TestingV3AuthInitialVotingInterval = 1;
+ mock_options->TestingV3AuthInitialVoteDelay = 1;
+ mock_options->TestingV3AuthInitialDistDelay = 1;
+
+ time_t now = 1441223455 -1;
+ dirvote_recalculate_timing(mock_options, now);
+
+ struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+ &status_out);
+ tt_assert(vote);
+
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(VOTE_BODY_V3)+1, 0);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_my_v3_authority_cert);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+}
+
+static void
+test_dir_handle_get_status_vote_current_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ const char *msg_out = NULL;
+ int status_out = 0;
+ size_t body_used = 0;
+ const char digest[DIGEST_LEN] = "";
+
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+ mock_options->TestingV3AuthVotingStartOffset = 0;
+ mock_options->TestingV3AuthInitialVotingInterval = 1;
+ mock_options->TestingV3AuthInitialVoteDelay = 1;
+ mock_options->TestingV3AuthInitialDistDelay = 1;
+
+ time_t now = 1441223455;
+ dirvote_recalculate_timing(mock_options, now-1);
+
+ struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+ &status_out);
+ tt_assert(vote);
+
+ // move the pending vote to previous vote
+ dirvote_act(mock_options, now+1);
+
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(VOTE_BODY_V3)+1, 0);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_my_v3_authority_cert);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+}
+
+#define DIR_HANDLE_CMD(name,flags) \
+ { #name, test_dir_handle_get_##name, (flags), NULL, NULL }
+
+struct testcase_t dir_handle_get_tests[] = {
+ DIR_HANDLE_CMD(not_found, 0),
+ DIR_HANDLE_CMD(bad_request, 0),
+ DIR_HANDLE_CMD(v1_command_not_found, 0),
+ DIR_HANDLE_CMD(v1_command, 0),
+ DIR_HANDLE_CMD(robots_txt, 0),
+ DIR_HANDLE_CMD(bytes_txt, 0),
+ DIR_HANDLE_CMD(rendezvous2_not_found_if_not_encrypted, 0),
+ DIR_HANDLE_CMD(rendezvous2_not_found, 0),
+ DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_with_invalid_desc_id, 0),
+ DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_not_well_formed, 0),
+ DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_success, 0),
+ DIR_HANDLE_CMD(micro_d_not_found, 0),
+ DIR_HANDLE_CMD(micro_d_server_busy, 0),
+ DIR_HANDLE_CMD(micro_d, 0),
+ DIR_HANDLE_CMD(networkstatus_bridges_not_found_without_auth, 0),
+ DIR_HANDLE_CMD(networkstatus_bridges_not_found_wrong_auth, 0),
+ DIR_HANDLE_CMD(networkstatus_bridges, 0),
+ DIR_HANDLE_CMD(server_descriptors_not_found, 0),
+ DIR_HANDLE_CMD(server_descriptors_busy, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_all, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_authority, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_fp, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_d, TT_FORK),
+ DIR_HANDLE_CMD(server_keys_bad_req, 0),
+ DIR_HANDLE_CMD(server_keys_busy, 0),
+ DIR_HANDLE_CMD(server_keys_all_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_all, 0),
+ DIR_HANDLE_CMD(server_keys_authority_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_authority, 0),
+ DIR_HANDLE_CMD(server_keys_fp_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_fp, 0),
+ DIR_HANDLE_CMD(server_keys_sk_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_sk, 0),
+ DIR_HANDLE_CMD(server_keys_fpsk_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_fpsk, 0),
+ DIR_HANDLE_CMD(status_vote_current_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_current_authority_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_current_authority, 0),
+ DIR_HANDLE_CMD(status_vote_next_authority_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_authority, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns_busy, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns, 0),
+ DIR_HANDLE_CMD(status_vote_current_d_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_d_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_d, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_busy, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus, 0),
+ 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),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index ad81914ccb..5289ca58ff 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -5,9 +5,14 @@
#include "dns.h"
#include "connection.h"
+#include "router.h"
+
+#define NS_MODULE dns
+
+#define NS_SUBMODULE clip_ttl
static void
-test_dns_clip_ttl(void *arg)
+NS(test_main)(void *arg)
{
(void)arg;
@@ -21,8 +26,12 @@ test_dns_clip_ttl(void *arg)
return;
}
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE expiry_ttl
+
static void
-test_dns_expiry_ttl(void *arg)
+NS(test_main)(void *arg)
{
(void)arg;
@@ -36,6 +45,10 @@ test_dns_expiry_ttl(void *arg)
return;
}
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE resolve
+
static int resolve_retval = 0;
static int resolve_made_conn_pending = 0;
static char *resolved_name = NULL;
@@ -43,6 +56,11 @@ static cached_resolve_t *cache_entry = NULL;
static int n_fake_impl = 0;
+NS_DECL(int, dns_resolve_impl, (edge_connection_t *exitconn, int is_resolve,
+ or_circuit_t *oncirc, char **hostname_out,
+ int *made_connection_pending_out,
+ cached_resolve_t **resolve_out));
+
/** This will be our configurable substitute for <b>dns_resolve_impl</b> in
* dns.c. It will return <b>resolve_retval</b>,
* and set <b>resolve_made_conn_pending</b> to
@@ -52,10 +70,10 @@ static int n_fake_impl = 0;
* 1.
*/
static int
-dns_resolve_fake_impl(edge_connection_t *exitconn, int is_resolve,
- or_circuit_t *oncirc, char **hostname_out,
- int *made_connection_pending_out,
- cached_resolve_t **resolve_out)
+NS(dns_resolve_impl)(edge_connection_t *exitconn, int is_resolve,
+ or_circuit_t *oncirc, char **hostname_out,
+ int *made_connection_pending_out,
+ cached_resolve_t **resolve_out)
{
(void)oncirc;
(void)exitconn;
@@ -82,8 +100,8 @@ static uint8_t last_answer_type = 0;
static cached_resolve_t *last_resolved;
static void
-send_resolved_cell_replacement(edge_connection_t *conn, uint8_t answer_type,
- const cached_resolve_t *resolved)
+NS(send_resolved_cell)(edge_connection_t *conn, uint8_t answer_type,
+ const cached_resolve_t *resolved)
{
conn_for_resolved_cell = conn;
@@ -98,8 +116,8 @@ static int n_send_resolved_hostname_cell_replacement = 0;
static char *last_resolved_hostname = NULL;
static void
-send_resolved_hostname_cell_replacement(edge_connection_t *conn,
- const char *hostname)
+NS(send_resolved_hostname_cell)(edge_connection_t *conn,
+ const char *hostname)
{
conn_for_resolved_cell = conn;
@@ -112,7 +130,7 @@ send_resolved_hostname_cell_replacement(edge_connection_t *conn,
static int n_dns_cancel_pending_resolve_replacement = 0;
static void
-dns_cancel_pending_resolve_replacement(const char *address)
+NS(dns_cancel_pending_resolve)(const char *address)
{
(void) address;
n_dns_cancel_pending_resolve_replacement++;
@@ -122,7 +140,7 @@ static int n_connection_free = 0;
static connection_t *last_freed_conn = NULL;
static void
-connection_free_replacement(connection_t *conn)
+NS(connection_free)(connection_t *conn)
{
n_connection_free++;
@@ -130,7 +148,7 @@ connection_free_replacement(connection_t *conn)
}
static void
-test_dns_resolve_outer(void *arg)
+NS(test_main)(void *arg)
{
(void) arg;
int retval;
@@ -149,9 +167,9 @@ test_dns_resolve_outer(void *arg)
memset(exitconn,0,sizeof(edge_connection_t));
memset(nextconn,0,sizeof(edge_connection_t));
- MOCK(dns_resolve_impl,dns_resolve_fake_impl);
- MOCK(send_resolved_cell,send_resolved_cell_replacement);
- MOCK(send_resolved_hostname_cell,send_resolved_hostname_cell_replacement);
+ NS_MOCK(dns_resolve_impl);
+ NS_MOCK(send_resolved_cell);
+ NS_MOCK(send_resolved_hostname_cell);
/*
* CASE 1: dns_resolve_impl returns 1 and sets a hostname. purpose is
@@ -264,8 +282,8 @@ test_dns_resolve_outer(void *arg)
* on exitconn with type being RESOLVED_TYPE_ERROR.
*/
- MOCK(dns_cancel_pending_resolve,dns_cancel_pending_resolve_replacement);
- MOCK(connection_free,connection_free_replacement);
+ NS_MOCK(dns_cancel_pending_resolve);
+ NS_MOCK(connection_free);
exitconn->on_circuit = &(on_circuit->base_);
exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE;
@@ -288,11 +306,11 @@ test_dns_resolve_outer(void *arg)
tt_assert(last_freed_conn == TO_CONN(exitconn));
done:
- UNMOCK(dns_resolve_impl);
- UNMOCK(send_resolved_cell);
- UNMOCK(send_resolved_hostname_cell);
- UNMOCK(dns_cancel_pending_resolve);
- UNMOCK(connection_free);
+ NS_UNMOCK(dns_resolve_impl);
+ NS_UNMOCK(send_resolved_cell);
+ NS_UNMOCK(send_resolved_hostname_cell);
+ NS_UNMOCK(dns_cancel_pending_resolve);
+ NS_UNMOCK(connection_free);
tor_free(on_circuit);
tor_free(exitconn);
tor_free(nextconn);
@@ -302,10 +320,446 @@ test_dns_resolve_outer(void *arg)
return;
}
+#undef NS_SUBMODULE
+
+/** Create an <b>edge_connection_t</b> instance that is considered a
+ * valid exit connection by asserts in dns_resolve_impl.
+ */
+static edge_connection_t *
+create_valid_exitconn(void)
+{
+ edge_connection_t *exitconn = tor_malloc_zero(sizeof(edge_connection_t));
+ TO_CONN(exitconn)->type = CONN_TYPE_EXIT;
+ TO_CONN(exitconn)->magic = EDGE_CONNECTION_MAGIC;
+ TO_CONN(exitconn)->purpose = EXIT_PURPOSE_RESOLVE;
+ TO_CONN(exitconn)->state = EXIT_CONN_STATE_RESOLVING;
+ exitconn->base_.s = TOR_INVALID_SOCKET;
+
+ return exitconn;
+}
+
+#define NS_SUBMODULE ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve)
+
+/*
+ * Given that <b>exitconn->base_.address</b> is IP address string, we
+ * want dns_resolve_impl() to parse it and store in
+ * <b>exitconn->base_.addr</b>. We expect dns_resolve_impl to return 1.
+ * Lastly, we want it to set the TTL value to default one for DNS queries.
+ */
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+ const tor_addr_t *resolved_addr;
+ tor_addr_t addr_to_compare;
+
+ (void)arg;
+
+ tor_addr_parse(&addr_to_compare, "8.8.8.8");
+
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+
+ TO_CONN(exitconn)->address = tor_strdup("8.8.8.8");
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ resolved_addr = &(exitconn->base_.addr);
+
+ tt_int_op(retval,==,1);
+ tt_assert(tor_addr_eq(resolved_addr, (const tor_addr_t *)&addr_to_compare));
+ tt_int_op(exitconn->address_ttl,==,DEFAULT_DNS_TTL);
+
+ done:
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, non_exit)
+
+/** Given that Tor instance is not configured as an exit node, we want
+ * dns_resolve_impl() to fail with return value -1.
+ */
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 1;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ done:
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ tor_free(on_circ);
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, addr_is_invalid_dest)
+
+/** Given that address is not a valid destination (as judged by
+ * address_is_invalid_destination() function), we want dns_resolve_impl()
+ * function to fail with return value -1.
+ */
+
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ (void)arg;
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ TO_CONN(exitconn)->address = tor_strdup("invalid#@!.org");
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ tor_free(on_circ);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, malformed_ptr)
+
+/** Given that address is a malformed PTR name, we want dns_resolve_impl to
+ * fail.
+ */
+
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("1.0.0.127.in-addr.arpa");
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ tor_free(TO_CONN(exitconn)->address);
+
+ TO_CONN(exitconn)->address =
+ tor_strdup("z01234567890123456789.in-addr.arpa");
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ tor_free(on_circ);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_pending)
+
+/* Given that there is already a pending resolve for the given address,
+ * we want dns_resolve_impl to append our exit connection to list
+ * of pending connections for the pending DNS request and return 0.
+ */
+
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending = 0;
+
+ pending_connection_t *pending_conn = NULL;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ cached_resolve_t *cache_entry = tor_malloc_zero(sizeof(cached_resolve_t));
+ cache_entry->magic = CACHED_RESOLVE_MAGIC;
+ cache_entry->state = CACHE_STATE_PENDING;
+ cache_entry->minheap_idx = -1;
+ cache_entry->expire = time(NULL) + 60 * 60;
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ strlcpy(cache_entry->address, TO_CONN(exitconn)->address,
+ sizeof(cache_entry->address));
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ dns_init();
+
+ dns_insert_cache_entry(cache_entry);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,0);
+ tt_int_op(made_pending,==,1);
+
+ pending_conn = cache_entry->pending_connections;
+
+ tt_assert(pending_conn != NULL);
+ tt_assert(pending_conn->conn == exitconn);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(cache_entry->pending_connections);
+ tor_free(cache_entry);
+ tor_free(exitconn);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_cached)
+
+/* Given that a finished DNS resolve is available in our cache, we want
+ * dns_resolve_impl() return it to called via resolve_out and pass the
+ * handling to set_exitconn_info_from_resolve function.
+ */
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static edge_connection_t *last_exitconn = NULL;
+static cached_resolve_t *last_resolve = NULL;
+
+static int
+NS(set_exitconn_info_from_resolve)(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out)
+{
+ last_exitconn = exitconn;
+ last_resolve = (cached_resolve_t *)resolve;
+
+ (void)hostname_out;
+
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending = 0;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ cached_resolve_t *resolve_out = NULL;
+
+ cached_resolve_t *cache_entry = tor_malloc_zero(sizeof(cached_resolve_t));
+ cache_entry->magic = CACHED_RESOLVE_MAGIC;
+ cache_entry->state = CACHE_STATE_CACHED;
+ cache_entry->minheap_idx = -1;
+ cache_entry->expire = time(NULL) + 60 * 60;
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ strlcpy(cache_entry->address, TO_CONN(exitconn)->address,
+ sizeof(cache_entry->address));
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+ NS_MOCK(set_exitconn_info_from_resolve);
+
+ dns_init();
+
+ dns_insert_cache_entry(cache_entry);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ &resolve_out);
+
+ tt_int_op(retval,==,0);
+ tt_int_op(made_pending,==,0);
+ tt_assert(resolve_out == cache_entry);
+
+ tt_assert(last_exitconn == exitconn);
+ tt_assert(last_resolve == cache_entry);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ NS_UNMOCK(set_exitconn_info_from_resolve);
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(cache_entry->pending_connections);
+ tor_free(cache_entry);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, cache_miss)
+
+/* Given that there are neither pending nor pre-cached resolve for a given
+ * address, we want dns_resolve_impl() to create a new cached_resolve_t
+ * object, mark it as pending, insert it into the cache, attach the exit
+ * connection to list of pending connections and call launch_resolve()
+ * with the cached_resolve_t object it created.
+ */
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static cached_resolve_t *last_launched_resolve = NULL;
+
+static int
+NS(launch_resolve)(cached_resolve_t *resolve)
+{
+ last_launched_resolve = resolve;
+
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending = 0;
+
+ pending_connection_t *pending_conn = NULL;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ cached_resolve_t *cache_entry = NULL;
+ cached_resolve_t query;
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ strlcpy(query.address, TO_CONN(exitconn)->address, sizeof(query.address));
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+ NS_MOCK(launch_resolve);
+
+ dns_init();
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,0);
+ tt_int_op(made_pending,==,1);
+
+ cache_entry = dns_get_cache_entry(&query);
+
+ tt_assert(cache_entry);
+
+ pending_conn = cache_entry->pending_connections;
+
+ tt_assert(pending_conn != NULL);
+ tt_assert(pending_conn->conn == exitconn);
+
+ tt_assert(last_launched_resolve == cache_entry);
+ tt_str_op(cache_entry->address,==,TO_CONN(exitconn)->address);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ NS_UNMOCK(launch_resolve);
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ if (cache_entry)
+ tor_free(cache_entry->pending_connections);
+ tor_free(cache_entry);
+ tor_free(exitconn);
+ return;
+}
+
+#undef NS_SUBMODULE
+
struct testcase_t dns_tests[] = {
- { "clip_ttl", test_dns_clip_ttl, 0, NULL, NULL },
- { "expiry_ttl", test_dns_expiry_ttl, 0, NULL, NULL },
- { "resolve_outer", test_dns_resolve_outer, TT_FORK, NULL, NULL },
+ TEST_CASE(clip_ttl),
+ TEST_CASE(expiry_ttl),
+ TEST_CASE(resolve),
+ TEST_CASE_ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve),
+ TEST_CASE_ASPECT(resolve_impl, non_exit),
+ TEST_CASE_ASPECT(resolve_impl, addr_is_invalid_dest),
+ TEST_CASE_ASPECT(resolve_impl, malformed_ptr),
+ TEST_CASE_ASPECT(resolve_impl, cache_hit_pending),
+ TEST_CASE_ASPECT(resolve_impl, cache_hit_cached),
+ TEST_CASE_ASPECT(resolve_impl, cache_miss),
END_OF_TESTCASES
};
+#undef NS_MODULE
+
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index 0011d3698a..fd19db095d 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -9,14 +9,16 @@
#include "or.h"
#include "test.h"
+
+#include "config.h"
#include "entrynodes.h"
-#include "routerparse.h"
#include "nodelist.h"
-#include "util.h"
+#include "policies.h"
#include "routerlist.h"
+#include "routerparse.h"
#include "routerset.h"
#include "statefile.h"
-#include "config.h"
+#include "util.h"
#include "test_helpers.h"
@@ -70,6 +72,14 @@ fake_network_setup(const struct testcase_t *testcase)
return dummy_state;
}
+static or_options_t mocked_options;
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mocked_options;
+}
+
/** Test choose_random_entry() with none of our routers being guard nodes. */
static void
test_choose_random_entry_no_guards(void *arg)
@@ -78,6 +88,14 @@ test_choose_random_entry_no_guards(void *arg)
(void) arg;
+ MOCK(get_options, mock_get_options);
+
+ /* Check that we get a guard if it passes preferred
+ * address settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientPreferIPv6ORPort = 0;
+
/* Try to pick an entry even though none of our routers are guards. */
chosen_entry = choose_random_entry(NULL);
@@ -86,8 +104,55 @@ test_choose_random_entry_no_guards(void *arg)
can't find a proper entry guard. */
tt_assert(chosen_entry);
+ /* And with the other IP version active */
+ mocked_options.ClientUseIPv6 = 1;
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
+ /* And with the preference on auto */
+ mocked_options.ClientPreferIPv6ORPort = -1;
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
+ /* Check that we don't get a guard if it doesn't pass mandatory address
+ * settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 0;
+ mocked_options.ClientPreferIPv6ORPort = 0;
+
+ chosen_entry = choose_random_entry(NULL);
+
+ /* If we don't allow IPv4 at all, we don't get a guard*/
+ tt_assert(!chosen_entry);
+
+ /* Check that we get a guard if it passes allowed but not preferred address
+ * settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 1;
+ mocked_options.ClientPreferIPv6ORPort = 1;
+
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
+ /* Check that we get a guard if it passes preferred address settings when
+ * they're auto */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientPreferIPv6ORPort = -1;
+
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
+ /* And with IPv6 active */
+ mocked_options.ClientUseIPv6 = 1;
+
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
done:
- ;
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ UNMOCK(get_options);
}
/** Test choose_random_entry() with only one of our routers being a
@@ -101,17 +166,78 @@ test_choose_random_entry_one_possible_guard(void *arg)
(void) arg;
+ MOCK(get_options, mock_get_options);
+
/* Set one of the nodes to be a guard. */
our_nodelist = nodelist_get_list();
the_guard = smartlist_get(our_nodelist, 4); /* chosen by fair dice roll */
the_guard->is_possible_guard = 1;
+ /* Check that we get the guard if it passes preferred
+ * address settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientPreferIPv6ORPort = 0;
+
/* Pick an entry. Make sure we pick the node we marked as guard. */
chosen_entry = choose_random_entry(NULL);
tt_ptr_op(chosen_entry, OP_EQ, the_guard);
+ /* And with the other IP version active */
+ mocked_options.ClientUseIPv6 = 1;
+ chosen_entry = choose_random_entry(NULL);
+ tt_ptr_op(chosen_entry, OP_EQ, the_guard);
+
+ /* And with the preference on auto */
+ mocked_options.ClientPreferIPv6ORPort = -1;
+ chosen_entry = choose_random_entry(NULL);
+ tt_ptr_op(chosen_entry, OP_EQ, the_guard);
+
+ /* Check that we don't get a guard if it doesn't pass mandatory address
+ * settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 0;
+ mocked_options.ClientPreferIPv6ORPort = 0;
+
+ chosen_entry = choose_random_entry(NULL);
+
+ /* If we don't allow IPv4 at all, we don't get a guard*/
+ tt_assert(!chosen_entry);
+
+ /* Check that we get a node if it passes allowed but not preferred
+ * address settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 1;
+ mocked_options.ClientPreferIPv6ORPort = 1;
+
+ chosen_entry = choose_random_entry(NULL);
+
+ /* We disable the guard check and the preferred address check at the same
+ * time, so we can't be sure we get the guard */
+ tt_assert(chosen_entry);
+
+ /* Check that we get a node if it is allowed but not preferred when settings
+ * are auto */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientPreferIPv6ORPort = -1;
+
+ chosen_entry = choose_random_entry(NULL);
+
+ /* We disable the guard check and the preferred address check at the same
+ * time, so we can't be sure we get the guard */
+ tt_assert(chosen_entry);
+
+ /* and with IPv6 active */
+ mocked_options.ClientUseIPv6 = 1;
+
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
done:
- ;
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ UNMOCK(get_options);
}
/** Helper to conduct tests for populate_live_entry_guards().
@@ -624,6 +750,93 @@ test_entry_is_live(void *arg)
; /* XXX */
}
+#define TEST_IPV4_ADDR "123.45.67.89"
+#define TEST_IPV6_ADDR "[1234:5678:90ab:cdef::]"
+
+static void
+test_node_preferred_orport(void *arg)
+{
+ (void)arg;
+ tor_addr_t ipv4_addr;
+ const uint16_t ipv4_port = 4444;
+ tor_addr_t ipv6_addr;
+ const uint16_t ipv6_port = 6666;
+ routerinfo_t node_ri;
+ node_t node;
+ tor_addr_port_t ap;
+
+ /* Setup options */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ /* We don't test ClientPreferIPv6ORPort here, because it's used in
+ * nodelist_set_consensus to setup node.ipv6_preferred, which we set
+ * directly. */
+ MOCK(get_options, mock_get_options);
+
+ /* Setup IP addresses */
+ tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR);
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR);
+
+ /* Setup node_ri */
+ memset(&node_ri, 0, sizeof(node_ri));
+ node_ri.addr = tor_addr_to_ipv4h(&ipv4_addr);
+ node_ri.or_port = ipv4_port;
+ tor_addr_copy(&node_ri.ipv6_addr, &ipv6_addr);
+ node_ri.ipv6_orport = ipv6_port;
+
+ /* Setup node */
+ memset(&node, 0, sizeof(node));
+ node.ri = &node_ri;
+
+ /* Check the preferred address is IPv4 if we're only using IPv4, regardless
+ * of whether we prefer it or not */
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 0;
+ node.ipv6_preferred = 0;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
+ tt_assert(ap.port == ipv4_port);
+
+ node.ipv6_preferred = 1;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
+ tt_assert(ap.port == ipv4_port);
+
+ /* Check the preferred address is IPv4 if we're using IPv4 and IPv6, but
+ * don't prefer the IPv6 address */
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 1;
+ node.ipv6_preferred = 0;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
+ tt_assert(ap.port == ipv4_port);
+
+ /* Check the preferred address is IPv6 if we prefer it and
+ * ClientUseIPv6 is 1, regardless of ClientUseIPv4 */
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 1;
+ node.ipv6_preferred = 1;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
+ tt_assert(ap.port == ipv6_port);
+
+ mocked_options.ClientUseIPv4 = 0;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
+ tt_assert(ap.port == ipv6_port);
+
+ /* Check the preferred address is IPv6 if we don't prefer it, but
+ * ClientUseIPv4 is 0 */
+ mocked_options.ClientUseIPv4 = 0;
+ mocked_options.ClientUseIPv6 = 1;
+ node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(&mocked_options);
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
+ tt_assert(ap.port == ipv6_port);
+
+ done:
+ UNMOCK(get_options);
+}
+
static const struct testcase_setup_t fake_network = {
fake_network_setup, fake_network_cleanup
};
@@ -654,6 +867,9 @@ struct testcase_t entrynodes_tests[] = {
{ "entry_is_live",
test_entry_is_live,
TT_FORK, &fake_network, NULL },
+ { "node_preferred_orport",
+ test_node_preferred_orport,
+ 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c
index 2e5a32eef3..5d38ed8fa2 100644
--- a/src/test/test_extorport.c
+++ b/src/test/test_extorport.c
@@ -309,15 +309,14 @@ test_ext_or_cookie_auth(void *arg)
tor_free(client_hash2);
}
-static int
+static void
crypto_rand_return_tse_str(char *to, size_t n)
{
if (n != 32) {
TT_FAIL(("Asked for %d bytes, not 32", (int)n));
- return -1;
+ return;
}
memcpy(to, "te road There is always another ", 32);
- return 0;
}
static void
diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c
index a8693ec9b5..b0cb87dc60 100644
--- a/src/test/test_nodelist.c
+++ b/src/test/test_nodelist.c
@@ -60,12 +60,53 @@ test_nodelist_node_get_verbose_nickname_not_named(void *arg)
return;
}
+/** A node should be considered a directory server if it has an open dirport
+ * of it accepts tunnelled directory requests.
+ */
+static void
+test_nodelist_node_is_dir(void *arg)
+{
+ (void)arg;
+
+ routerstatus_t rs;
+ routerinfo_t ri;
+ node_t node;
+ memset(&node, 0, sizeof(node_t));
+ memset(&rs, 0, sizeof(routerstatus_t));
+ memset(&ri, 0, sizeof(routerinfo_t));
+
+ tt_assert(!node_is_dir(&node));
+
+ node.rs = &rs;
+ tt_assert(!node_is_dir(&node));
+
+ rs.is_v2_dir = 1;
+ tt_assert(node_is_dir(&node));
+
+ rs.is_v2_dir = 0;
+ rs.dir_port = 1;
+ tt_assert(! node_is_dir(&node));
+
+ node.rs = NULL;
+ tt_assert(!node_is_dir(&node));
+ node.ri = &ri;
+ ri.supports_tunnelled_dir_requests = 1;
+ tt_assert(node_is_dir(&node));
+ ri.supports_tunnelled_dir_requests = 0;
+ ri.dir_port = 1;
+ tt_assert(! node_is_dir(&node));
+
+ done:
+ return;
+}
+
#define NODE(name, flags) \
{ #name, test_nodelist_##name, (flags), NULL, NULL }
struct testcase_t nodelist_tests[] = {
NODE(node_get_verbose_nickname_by_id_null_node, TT_FORK),
NODE(node_get_verbose_nickname_not_named, TT_FORK),
+ NODE(node_is_dir, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c
index bfbf13a476..915a5d0a7c 100644
--- a/src/test/test_ntor_cl.c
+++ b/src/test/test_ntor_cl.c
@@ -106,6 +106,7 @@ server1(int argc, char **argv)
done:
tor_free(keys);
tor_free(hexkeys);
+ dimap_free(keymap, NULL);
return result;
}
diff --git a/src/test/test_options.c b/src/test/test_options.c
index a8ebadb14b..b2be8a9c65 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -8,6 +8,18 @@
#include "confparse.h"
#include "config.h"
#include "test.h"
+#include "geoip.h"
+
+#define ROUTERSET_PRIVATE
+#include "routerset.h"
+
+#include "log_test_helpers.h"
+
+#include "sandbox.h"
+#include "memarea.h"
+#include "policies.h"
+
+#define NS_MODULE test_options
typedef struct {
int severity;
@@ -38,6 +50,7 @@ setup_log_callback(void)
lst.masks[LOG_WARN - LOG_ERR] = ~0;
lst.masks[LOG_NOTICE - LOG_ERR] = ~0;
add_callback_log(&lst, log_cback);
+ mark_logs_temp();
}
static char *
@@ -69,22 +82,41 @@ clear_log_messages(void)
messages = NULL;
}
+#define setup_options(opt,dflt) \
+ do { \
+ opt = options_new(); \
+ opt->command = CMD_RUN_TOR; \
+ options_init(opt); \
+ \
+ dflt = config_dup(&options_format, opt); \
+ clear_log_messages(); \
+ } while (0)
+
+#define VALID_DIR_AUTH "DirAuthority dizum orport=443 v3ident=E8A9C45" \
+ "EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212:80 7EA6 EAD6 FD83" \
+ " 083C 538F 4403 8BBF A077 587D D755\n"
+#define VALID_ALT_BRIDGE_AUTH \
+ "AlternateBridgeAuthority dizum orport=443 v3ident=E8A9C45" \
+ "EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212:80 7EA6 EAD6 FD83" \
+ " 083C 538F 4403 8BBF A077 587D D755\n"
+#define VALID_ALT_DIR_AUTH \
+ "AlternateDirAuthority dizum orport=443 v3ident=E8A9C45" \
+ "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)
{
- or_options_t *opt = options_new();
+ or_options_t *opt=NULL;
or_options_t *dflt;
config_line_t *cl=NULL;
char *msg=NULL;
int r;
- opt->command = CMD_RUN_TOR;
- options_init(opt);
- dflt = config_dup(&options_format, opt);
- clear_log_messages();
+ setup_options(opt, dflt);
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
@@ -126,6 +158,9 @@ test_options_validate_impl(const char *configuration,
}
done:
+ memarea_clear_freelist();
+ escaped(NULL);
+ policies_free_all();
config_free_lines(cl);
or_options_free(opt);
or_options_free(dflt);
@@ -147,6 +182,7 @@ test_options_validate(void *arg)
{
(void)arg;
setup_log_callback();
+ sandbox_disable_getaddrinfo_cache();
WANT_ERR("ExtORPort 500000", "Invalid ExtORPort");
@@ -159,12 +195,4224 @@ test_options_validate(void *arg)
"ServerTransportOptions did not parse",
LOG_WARN, "\"slingsnappy\" is not a k=v");
+ WANT_ERR("DirPort 8080\nDirCache 0",
+ "DirPort configured but DirCache disabled.");
+ WANT_ERR("BridgeRelay 1\nDirCache 0",
+ "We're a bridge but DirCache is disabled.");
+
+ close_temp_logs();
+ clear_log_messages();
+ return;
+}
+
+#define MEGABYTEIFY(mb) (U64_LITERAL(mb) << 20)
+static void
+test_have_enough_mem_for_dircache(void *arg)
+{
+ (void)arg;
+ or_options_t *opt=NULL;
+ or_options_t *dflt;
+ config_line_t *cl=NULL;
+ char *msg=NULL;;
+ int r;
+ const char *configuration = "ORPort 8080\nDirCache 1", *expect_errmsg;
+
+ setup_options(opt, dflt);
+ setup_log_callback();
+ (void)dflt;
+
+ r = config_get_lines(configuration, &cl, 1);
+ tt_int_op(r, OP_EQ, 0);
+
+ r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ /* 300 MB RAM available, DirCache enabled */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_assert(!msg);
+
+ /* 200 MB RAM available, DirCache enabled */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg);
+ tt_int_op(r, OP_EQ, -1);
+ expect_errmsg = "Being a directory cache (default) with less than ";
+ if (!strstr(msg, expect_errmsg)) {
+ TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.",
+ expect_errmsg, configuration, msg));
+ }
+ tor_free(msg);
+
+ config_free_lines(cl); cl = NULL;
+ configuration = "ORPort 8080\nDirCache 1\nBridgeRelay 1";
+ r = config_get_lines(configuration, &cl, 1);
+ tt_int_op(r, OP_EQ, 0);
+
+ r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ /* 300 MB RAM available, DirCache enabled, Bridge */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_assert(!msg);
+
+ /* 200 MB RAM available, DirCache enabled, Bridge */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg);
+ tt_int_op(r, OP_EQ, -1);
+ expect_errmsg = "Running a Bridge with less than ";
+ if (!strstr(msg, expect_errmsg)) {
+ TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.",
+ expect_errmsg, configuration, msg));
+ }
+ tor_free(msg);
+
+ config_free_lines(cl); cl = NULL;
+ configuration = "ORPort 8080\nDirCache 0";
+ r = config_get_lines(configuration, &cl, 1);
+ tt_int_op(r, OP_EQ, 0);
+
+ r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ /* 200 MB RAM available, DirCache disabled */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_assert(!msg);
+
+ /* 300 MB RAM available, DirCache disabled */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg);
+ tt_int_op(r, OP_EQ, -1);
+ expect_errmsg = "DirCache is disabled and we are configured as a ";
+ if (!strstr(msg, expect_errmsg)) {
+ TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.",
+ expect_errmsg, configuration, msg));
+ }
+ tor_free(msg);
+
clear_log_messages();
+
+ done:
+ if (msg)
+ tor_free(msg);
+ tor_free(dflt);
+ or_options_free(opt);
+ config_free_lines(cl);
return;
}
+static const char *fixed_get_uname_result = NULL;
+
+static const char *
+fixed_get_uname(void)
+{
+ return fixed_get_uname_result;
+}
+
+#define TEST_OPTIONS_OLD_VALUES "TestingV3AuthInitialVotingInterval 1800\n" \
+ "TestingClientBootstrapConsensusMaxDownloadTries 7\n" \
+ "TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries 4\n" \
+ "TestingClientBootstrapConsensusMaxInProgressTries 3\n" \
+ "TestingV3AuthInitialVoteDelay 300\n" \
+ "TestingV3AuthInitialDistDelay 300\n" \
+ "TestingClientMaxIntervalWithoutRequest 600\n" \
+ "TestingDirConnectionMaxStall 600\n" \
+ "TestingConsensusMaxDownloadTries 8\n" \
+ "TestingDescriptorMaxDownloadTries 8\n" \
+ "TestingMicrodescMaxDownloadTries 8\n" \
+ "TestingCertMaxDownloadTries 8\n"
+
+#define TEST_OPTIONS_DEFAULT_VALUES TEST_OPTIONS_OLD_VALUES \
+ "MaxClientCircuitsPending 1\n" \
+ "RendPostPeriod 1000\n" \
+ "KeepAlivePeriod 1\n" \
+ "ConnLimit 1\n" \
+ "V3AuthVotingInterval 300\n" \
+ "V3AuthVoteDelay 20\n" \
+ "V3AuthDistDelay 20\n" \
+ "V3AuthNIntervalsValid 3\n" \
+ "ClientUseIPv4 1\n" \
+ "VirtualAddrNetworkIPv4 127.192.0.0/10\n" \
+ "VirtualAddrNetworkIPv6 [FE80::]/10\n" \
+ "SchedulerHighWaterMark__ 42\n" \
+ "SchedulerLowWaterMark__ 10\n"
+
+typedef struct {
+ or_options_t *old_opt;
+ or_options_t *opt;
+ or_options_t *def_opt;
+} options_test_data_t;
+
+static void free_options_test_data(options_test_data_t *td);
+
+static options_test_data_t *
+get_options_test_data(const char *conf)
+{
+ int rv = -1;
+ char *msg = NULL;
+ config_line_t *cl=NULL;
+ options_test_data_t *result = tor_malloc(sizeof(options_test_data_t));
+ result->opt = options_new();
+ result->old_opt = options_new();
+ result->def_opt = options_new();
+ rv = config_get_lines(conf, &cl, 1);
+ tt_assert(rv == 0);
+ rv = config_assign(&options_format, result->opt, cl, 0, 0, &msg);
+ if (msg) {
+ /* Display the parse error message by comparing it with an empty string */
+ tt_str_op(msg, OP_EQ, "");
+ }
+ tt_assert(rv == 0);
+ config_free_lines(cl);
+ result->opt->LogTimeGranularity = 1;
+ result->opt->TokenBucketRefillInterval = 1;
+ rv = config_get_lines(TEST_OPTIONS_OLD_VALUES, &cl, 1);
+ tt_assert(rv == 0);
+ rv = config_assign(&options_format, result->def_opt, cl, 0, 0, &msg);
+ if (msg) {
+ /* Display the parse error message by comparing it with an empty string */
+ tt_str_op(msg, OP_EQ, "");
+ }
+ tt_assert(rv == 0);
+
+ done:
+ config_free_lines(cl);
+ if (rv != 0) {
+ free_options_test_data(result);
+ result = NULL;
+ /* Callers expect a non-NULL result, so just die if we can't provide one.
+ */
+ tor_assert(0);
+ }
+ return result;
+}
+
+static void
+free_options_test_data(options_test_data_t *td)
+{
+ if (!td) return;
+ or_options_free(td->old_opt);
+ or_options_free(td->opt);
+ or_options_free(td->def_opt);
+ tor_free(td);
+}
+
+#define expect_log_msg(str) \
+ tt_assert_msg(mock_saved_log_has_message(str), \
+ "expected log to contain " # str);
+
+#define expect_no_log_msg(str) \
+ tt_assert_msg(!mock_saved_log_has_message(str), \
+ "expected log to not contain " # str);
+
+static void
+test_options_validate__uname_for_server(void *ignored)
+{
+ (void)ignored;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "ORListenAddress 127.0.0.1:5555");
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ MOCK(get_uname, fixed_get_uname);
+ fixed_get_uname_result = "Windows 95";
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Tor is running as a server, but you"
+ " are running Windows 95; this probably won't work. See https://www"
+ ".torproject.org/docs/faq.html#BestOSForRelay for details.\n");
+ tor_free(msg);
+
+ fixed_get_uname_result = "Windows 98";
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Tor is running as a server, but you"
+ " are running Windows 98; this probably won't work. See https://www"
+ ".torproject.org/docs/faq.html#BestOSForRelay for details.\n");
+ tor_free(msg);
+
+ fixed_get_uname_result = "Windows Me";
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Tor is running as a server, but you"
+ " are running Windows Me; this probably won't work. See https://www"
+ ".torproject.org/docs/faq.html#BestOSForRelay for details.\n");
+ tor_free(msg);
+
+ fixed_get_uname_result = "Windows 2000";
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_entry();
+ tor_free(msg);
+
+ done:
+ UNMOCK(get_uname);
+ free_options_test_data(tdata);
+ tor_free(msg);
+ teardown_capture_of_logs(previous_log);
+}
+
+static void
+test_options_validate__outbound_addresses(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "OutboundBindAddress xxyy!!!sdfaf");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__data_directory(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "DataDirectory longreallyl"
+ "ongLONGLONGlongreallylong"
+ "LONGLONGlongreallylongLON"
+ "GLONGlongreallylongLONGLO"
+ "NGlongreallylongLONGLONGl"
+ "ongreallylongLONGLONGlong"
+ "reallylongLONGLONGlongrea"
+ "llylongLONGLONGlongreally"
+ "longLONGLONGlongreallylon"
+ "gLONGLONGlongreallylongLO"
+ "NGLONGlongreallylongLONGL"
+ "ONGlongreallylongLONGLONG"
+ "longreallylongLONGLONGlon"
+ "greallylongLONGLONGlongre"
+ "allylongLONGLONGlongreall"
+ "ylongLONGLONGlongreallylo"
+ "ngLONGLONGlongreallylongL"
+ "ONGLONGlongreallylongLONG"
+ "LONG"); // 440 characters
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Invalid DataDirectory");
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__nickname(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "Nickname ThisNickNameIsABitTooLong");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Nickname 'ThisNickNameIsABitTooLong' is wrong length or"
+ " contains illegal characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("Nickname AMoreValidNick");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("DataDirectory /tmp/somewhere");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__contactinfo(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "ORListenAddress 127.0.0.1:5555\nORPort 955");
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ tdata->opt->ContactInfo = NULL;
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg(
+ "Your ContactInfo config option is not"
+ " set. Please consider setting it, so we can contact you if your"
+ " server is misconfigured or something else goes wrong.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ORListenAddress 127.0.0.1:5555\nORPort 955\n"
+ "ContactInfo hella@example.org");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg(
+ "Your ContactInfo config option is not"
+ " set. Please consider setting it, so we can contact you if your"
+ " server is misconfigured or something else goes wrong.\n");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+extern int quiet_level;
+
+static void
+test_options_validate__logs(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ (void)ret;
+ char *msg;
+ int orig_quiet_level = quiet_level;
+ options_test_data_t *tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 0;
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(tdata->opt->Logs->key, OP_EQ, "Log");
+ tt_str_op(tdata->opt->Logs->value, OP_EQ, "notice stdout");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 0;
+ quiet_level = 1;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(tdata->opt->Logs->key, OP_EQ, "Log");
+ tt_str_op(tdata->opt->Logs->value, OP_EQ, "warn stdout");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 0;
+ quiet_level = 2;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_assert(!tdata->opt->Logs);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 0;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 1, &msg);
+ tt_assert(!tdata->opt->Logs);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 1;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_assert(!tdata->opt->Logs);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->RunAsDaemon = 0;
+ config_line_t *cl=NULL;
+ config_get_lines("Log foo", &cl, 1);
+ tdata->opt->Logs = cl;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op((intptr_t)tdata->opt->Logs, OP_EQ, (intptr_t)cl);
+
+ done:
+ quiet_level = orig_quiet_level;
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+/* static config_line_t * */
+/* mock_config_line(const char *key, const char *val) */
+/* { */
+/* config_line_t *config_line = tor_malloc(sizeof(config_line_t)); */
+/* memset(config_line, 0, sizeof(config_line_t)); */
+/* config_line->key = tor_strdup(key); */
+/* config_line->value = tor_strdup(val); */
+/* return config_line; */
+/* } */
+
+static void
+test_options_validate__authdir(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+ options_test_data_t *tdata = get_options_test_data(
+ "AuthoritativeDirectory 1\n"
+ "Address this.should.not_exist.example.org");
+
+ sandbox_disable_getaddrinfo_cache();
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Failed to resolve/guess local address. See logs for"
+ " details.");
+ expect_log_msg("Could not resolve local Address "
+ "'this.should.not_exist.example.org'. Failing.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Authoritative directory servers must set ContactInfo");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "TestingTorNetwork 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
+ "AuthoritativeDir is set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
+ "AuthoritativeDir is set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "RecommendedVersions 1.2, 3.14\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(tdata->opt->RecommendedClientVersions->value, OP_EQ, "1.2, 3.14");
+ tt_str_op(tdata->opt->RecommendedServerVersions->value, OP_EQ, "1.2, 3.14");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "RecommendedVersions 1.2, 3.14\n"
+ "RecommendedClientVersions 25\n"
+ "RecommendedServerVersions 4.18\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(tdata->opt->RecommendedClientVersions->value, OP_EQ, "25");
+ tt_str_op(tdata->opt->RecommendedServerVersions->value, OP_EQ, "4.18");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "VersioningAuthoritativeDirectory 1\n"
+ "RecommendedVersions 1.2, 3.14\n"
+ "RecommendedClientVersions 25\n"
+ "RecommendedServerVersions 4.18\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
+ "AuthoritativeDir is set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "VersioningAuthoritativeDirectory 1\n"
+ "RecommendedServerVersions 4.18\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ, "Versioning authoritative dir servers must set "
+ "Recommended*Versions.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "VersioningAuthoritativeDirectory 1\n"
+ "RecommendedClientVersions 4.18\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ, "Versioning authoritative dir servers must set "
+ "Recommended*Versions.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "UseEntryGuards 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Authoritative directory servers "
+ "can't set UseEntryGuards. Disabling.\n");
+ tt_int_op(tdata->opt->UseEntryGuards, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "V3AuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Authoritative directories always try"
+ " to download extra-info documents. Setting DownloadExtraInfo.\n");
+ tt_int_op(tdata->opt->DownloadExtraInfo, OP_EQ, 1);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "DownloadExtraInfo 1\n"
+ "V3AuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_no_log_msg("Authoritative directories always try"
+ " to download extra-info documents. Setting DownloadExtraInfo.\n");
+ tt_int_op(tdata->opt->DownloadExtraInfo, OP_EQ, 1);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
+ "AuthoritativeDir is set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "V3BandwidthsFile non-existant-file\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "V3BandwidthsFile non-existant-file\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "GuardfractionFile non-existant-file\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "GuardfractionFile non-existant-file\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "DirPort 999\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no ORPort set.");
+ tor_free(msg);
+
+ // TODO: This case can't be reached, since clientonly is used to
+ // check when parsing port lines as well.
+ /* free_options_test_data(tdata); */
+ /* tdata = get_options_test_data("AuthoritativeDirectory 1\n" */
+ /* "Address 100.200.10.1\n" */
+ /* "DirPort 999\n" */
+ /* "ORPort 888\n" */
+ /* "ClientOnly 1\n" */
+ /* "BridgeAuthoritativeDir 1\n" */
+ /* "ContactInfo hello@hello.com\n" */
+ /* "SchedulerHighWaterMark__ 42\n" */
+ /* "SchedulerLowWaterMark__ 10\n"); */
+ /* mock_clean_saved_logs(); */
+ /* ret = options_validate(tdata->old_opt, tdata->opt, */
+ /* tdata->def_opt, 0, &msg); */
+ /* tt_int_op(ret, OP_EQ, -1); */
+ /* tt_str_op(msg, OP_EQ, "Running as authoritative directory, " */
+ /* "but ClientOnly also set."); */
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ // sandbox_free_getaddrinfo_cache();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__relay_with_hidden_services(void *ignored)
+{
+ (void)ignored;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ options_test_data_t *tdata = get_options_test_data(
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "HiddenServiceDir "
+ "/Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg(
+ "Tor is currently configured as a relay and a hidden service. "
+ "That's not very secure: you should probably run your hidden servi"
+ "ce in a separate Tor process, at least -- see "
+ "https://trac.torproject.org/8742\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+// TODO: it doesn't seem possible to hit the case of having no port lines at
+// all, since there will be a default created for SocksPort
+/* static void */
+/* test_options_validate__ports(void *ignored) */
+/* { */
+/* (void)ignored; */
+/* int ret; */
+/* char *msg; */
+/* int previous_log = setup_capture_of_logs(LOG_WARN); */
+/* options_test_data_t *tdata = get_options_test_data(""); */
+/* ret = options_validate(tdata->old_opt, tdata->opt, */
+/* tdata->def_opt, 0, &msg); */
+/* expect_log_msg("SocksPort, TransPort, NATDPort, DNSPort, and ORPort " */
+/* "are all undefined, and there aren't any hidden services " */
+/* "configured. " */
+/* " Tor will still run, but probably won't do anything.\n"); */
+/* done: */
+/* teardown_capture_of_logs(previous_log); */
+/* free_options_test_data(tdata); */
+/* tor_free(msg); */
+/* } */
+
+static void
+test_options_validate__transproxy(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata;
+
+#ifdef USE_TRANSPARENT
+ // Test default trans proxy
+ tdata = get_options_test_data("TransProxyType default\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_DEFAULT);
+ tor_free(msg);
+
+ // Test pf-divert trans proxy
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TransProxyType pf-divert\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+
+#if !defined(__OpenBSD__) && !defined( DARWIN )
+ tt_str_op(msg, OP_EQ,
+ "pf-divert is a OpenBSD-specific and OS X/Darwin-specific feature.");
+#else
+ tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_PF_DIVERT);
+ tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without "
+ "any valid TransPort or TransListenAddress.");
+#endif
+ tor_free(msg);
+
+ // Test tproxy trans proxy
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TransProxyType tproxy\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+
+#if !defined(__linux__)
+ tt_str_op(msg, OP_EQ, "TPROXY is a Linux-specific feature.");
+#else
+ tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_TPROXY);
+ tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without any valid "
+ "TransPort or TransListenAddress.");
+#endif
+ tor_free(msg);
+
+ // Test ipfw trans proxy
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TransProxyType ipfw\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+
+#if !defined(__FreeBSD__) && !defined( DARWIN )
+ tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specificand OS X/Darwin-specific "
+ "feature.");
+#else
+ tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_IPFW);
+ tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without any valid "
+ "TransPort or TransListenAddress.");
+#endif
+ tor_free(msg);
+
+ // Test unknown trans proxy
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TransProxyType non-existant\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Unrecognized value for TransProxyType");
+ tor_free(msg);
+
+ // Test trans proxy success
+ free_options_test_data(tdata);
+
+#if defined(linux)
+ tdata = get_options_test_data("TransProxyType tproxy\n"
+ "TransPort 127.0.0.1:123\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+#endif
+#if defined(__FreeBSD__) || defined( DARWIN )
+ tdata = get_options_test_data("TransProxyType ipfw\n"
+ "TransPort 127.0.0.1:123\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+#endif
+#if defined(__OpenBSD__)
+ tdata = get_options_test_data("TransProxyType pf-divert\n"
+ "TransPort 127.0.0.1:123\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+#endif
+
+#else
+ tdata = get_options_test_data("TransPort 127.0.0.1:555\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TransPort and TransListenAddress are disabled in "
+ "this build.");
+ tor_free(msg);
+#endif
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+NS_DECL(country_t, geoip_get_country, (const char *country));
+
+static country_t
+NS(geoip_get_country)(const char *countrycode)
+{
+ (void)countrycode;
+ CALLED(geoip_get_country)++;
+
+ return 1;
+}
+
+static void
+test_options_validate__exclude_nodes(void *ignored)
+{
+ (void)ignored;
+
+ NS_MOCK(geoip_get_country);
+
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ options_test_data_t *tdata = get_options_test_data(
+ "ExcludeExitNodes {us}\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(smartlist_len(tdata->opt->ExcludeExitNodesUnion_->list), OP_EQ, 1);
+ tt_str_op((char *)
+ (smartlist_get(tdata->opt->ExcludeExitNodesUnion_->list, 0)),
+ OP_EQ, "{us}");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ExcludeNodes {cn}\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(smartlist_len(tdata->opt->ExcludeExitNodesUnion_->list), OP_EQ, 1);
+ tt_str_op((char *)
+ (smartlist_get(tdata->opt->ExcludeExitNodesUnion_->list, 0)),
+ OP_EQ, "{cn}");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ExcludeNodes {cn}\n"
+ "ExcludeExitNodes {us} {cn}\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(smartlist_len(tdata->opt->ExcludeExitNodesUnion_->list), OP_EQ, 2);
+ tt_str_op((char *)
+ (smartlist_get(tdata->opt->ExcludeExitNodesUnion_->list, 0)),
+ OP_EQ, "{us} {cn}");
+ tt_str_op((char *)
+ (smartlist_get(tdata->opt->ExcludeExitNodesUnion_->list, 1)),
+ OP_EQ, "{cn}");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ExcludeNodes {cn}\n"
+ "StrictNodes 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg(
+ "You have asked to exclude certain relays from all positions "
+ "in your circuits. Expect hidden services and other Tor "
+ "features to be broken in unpredictable ways.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ExcludeNodes {cn}\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg(
+ "You have asked to exclude certain relays from all positions "
+ "in your circuits. Expect hidden services and other Tor "
+ "features to be broken in unpredictable ways.\n");
+ tor_free(msg);
+
+ done:
+ NS_UNMOCK(geoip_get_country);
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__scheduler(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ options_test_data_t *tdata = get_options_test_data(
+ "SchedulerLowWaterMark__ 0\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Bad SchedulerLowWaterMark__ option\n");
+ tor_free(msg);
+
+ // TODO: this test cannot run on platforms where UINT32_MAX == UINT64_MAX.
+ // I suspect it's unlikely this branch can actually happen
+ /* free_options_test_data(tdata); */
+ /* tdata = get_options_test_data( */
+ /* "SchedulerLowWaterMark 10000000000000000000\n"); */
+ /* tdata->opt->SchedulerLowWaterMark__ = (uint64_t)UINT32_MAX; */
+ /* tdata->opt->SchedulerLowWaterMark__++; */
+ /* mock_clean_saved_logs(); */
+ /* ret = options_validate(tdata->old_opt, tdata->opt, */
+ /* tdata->def_opt, 0, &msg); */
+ /* tt_int_op(ret, OP_EQ, -1); */
+ /* expect_log_msg("Bad SchedulerLowWaterMark__ option\n"); */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SchedulerLowWaterMark__ 42\n"
+ "SchedulerHighWaterMark__ 42\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Bad SchedulerHighWaterMark option\n");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__node_families(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "NodeFamily flux, flax\n"
+ "NodeFamily somewhere\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->NodeFamilySets);
+ tt_int_op(smartlist_len(tdata->opt->NodeFamilySets), OP_EQ, 2);
+ tt_str_op((char *)(smartlist_get(
+ ((routerset_t *)smartlist_get(tdata->opt->NodeFamilySets, 0))->list, 0)),
+ OP_EQ, "flux");
+ tt_str_op((char *)(smartlist_get(
+ ((routerset_t *)smartlist_get(tdata->opt->NodeFamilySets, 0))->list, 1)),
+ OP_EQ, "flax");
+ tt_str_op((char *)(smartlist_get(
+ ((routerset_t *)smartlist_get(tdata->opt->NodeFamilySets, 1))->list, 0)),
+ OP_EQ, "somewhere");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!tdata->opt->NodeFamilySets);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("NodeFamily !flux\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->NodeFamilySets);
+ tt_int_op(smartlist_len(tdata->opt->NodeFamilySets), OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__tlsec(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ options_test_data_t *tdata = get_options_test_data(
+ "TLSECGroup ed25519\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Unrecognized TLSECGroup: Falling back to the default.\n");
+ tt_assert(!tdata->opt->TLSECGroup);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TLSECGroup P224\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg(
+ "Unrecognized TLSECGroup: Falling back to the default.\n");
+ tt_assert(tdata->opt->TLSECGroup);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TLSECGroup P256\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg(
+ "Unrecognized TLSECGroup: Falling back to the default.\n");
+ tt_assert(tdata->opt->TLSECGroup);
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__token_bucket(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data("");
+
+ tdata->opt->TokenBucketRefillInterval = 0;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TokenBucketRefillInterval must be between 1 and 1000 inclusive.");
+ tor_free(msg);
+
+ tdata->opt->TokenBucketRefillInterval = 1001;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TokenBucketRefillInterval must be between 1 and 1000 inclusive.");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__recommended_packages(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ options_test_data_t *tdata = get_options_test_data(
+ "RecommendedPackages foo 1.2 http://foo.com sha1=123123123123\n"
+ "RecommendedPackages invalid-package-line\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg("Invalid RecommendedPackage line "
+ "invalid-package-line will be ignored\n");
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__fetch_dir(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "FetchDirInfoExtraEarly 1\n"
+ "FetchDirInfoEarly 0\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "FetchDirInfoExtraEarly requires that you"
+ " also set FetchDirInfoEarly");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FetchDirInfoExtraEarly 1\n"
+ "FetchDirInfoEarly 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_NE, "FetchDirInfoExtraEarly requires that you"
+ " also set FetchDirInfoEarly");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__conn_limit(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "ConnLimit 0\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "ConnLimit must be greater than 0, but was set to 0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "MaxClientCircuitsPending must be between 1 and 1024, "
+ "but was set to 0");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__paths_needed(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ options_test_data_t *tdata = get_options_test_data(
+ "PathsNeededToBuildCircuits 0.1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->PathsNeededToBuildCircuits > 0.24 &&
+ tdata->opt->PathsNeededToBuildCircuits < 0.26);
+ expect_log_msg("PathsNeededToBuildCircuits is too low. "
+ "Increasing to 0.25\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("PathsNeededToBuildCircuits 0.99\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->PathsNeededToBuildCircuits > 0.94 &&
+ tdata->opt->PathsNeededToBuildCircuits < 0.96);
+ expect_log_msg("PathsNeededToBuildCircuits is "
+ "too high. Decreasing to 0.95\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("PathsNeededToBuildCircuits 0.91\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->PathsNeededToBuildCircuits > 0.90 &&
+ tdata->opt->PathsNeededToBuildCircuits < 0.92);
+ expect_no_log_entry();
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__max_client_circuits(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "MaxClientCircuitsPending 0\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "MaxClientCircuitsPending must be between 1 and 1024,"
+ " but was set to 0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("MaxClientCircuitsPending 1025\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "MaxClientCircuitsPending must be between 1 and 1024,"
+ " but was set to 1025");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "KeepalivePeriod option must be positive.");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__ports(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "FirewallPorts 65537\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Port '65537' out of range in FirewallPorts");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FirewallPorts 1\n"
+ "LongLivedPorts 124444\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Port '124444' out of range in LongLivedPorts");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FirewallPorts 1\n"
+ "LongLivedPorts 2\n"
+ "RejectPlaintextPorts 112233\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Port '112233' out of range in RejectPlaintextPorts");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FirewallPorts 1\n"
+ "LongLivedPorts 2\n"
+ "RejectPlaintextPorts 3\n"
+ "WarnPlaintextPorts 65536\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Port '65536' out of range in WarnPlaintextPorts");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FirewallPorts 1\n"
+ "LongLivedPorts 2\n"
+ "RejectPlaintextPorts 3\n"
+ "WarnPlaintextPorts 4\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "KeepalivePeriod option must be positive.");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__reachable_addresses(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_NOTICE);
+ options_test_data_t *tdata = get_options_test_data(
+ "FascistFirewall 1\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Converting FascistFirewall config "
+ "option to new format: \"ReachableDirAddresses *:80\"\n");
+ tt_str_op(tdata->opt->ReachableDirAddresses->value, OP_EQ, "*:80");
+ expect_log_msg("Converting FascistFirewall config "
+ "option to new format: \"ReachableORAddresses *:443\"\n");
+ tt_str_op(tdata->opt->ReachableORAddresses->value, OP_EQ, "*:443");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("FascistFirewall 1\n"
+ "ReachableDirAddresses *:81\n"
+ "ReachableORAddresses *:444\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ tdata->opt->FirewallPorts = smartlist_new();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_entry();
+ tt_str_op(tdata->opt->ReachableDirAddresses->value, OP_EQ, "*:81");
+ tt_str_op(tdata->opt->ReachableORAddresses->value, OP_EQ, "*:444");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("FascistFirewall 1\n"
+ "FirewallPort 123\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Converting FascistFirewall and "
+ "FirewallPorts config options to new format: "
+ "\"ReachableAddresses *:123\"\n");
+ tt_str_op(tdata->opt->ReachableAddresses->value, OP_EQ, "*:123");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("FascistFirewall 1\n"
+ "ReachableAddresses *:82\n"
+ "ReachableAddresses *:83\n"
+ "ReachableAddresses reject *:*\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_entry();
+ tt_str_op(tdata->opt->ReachableAddresses->value, OP_EQ, "*:82");
+ tor_free(msg);
+
+#define SERVERS_REACHABLE_MSG "Servers must be able to freely connect to" \
+ " the rest of the Internet, so they must not set Reachable*Addresses or" \
+ " FascistFirewall or FirewallPorts or ClientUseIPv4 0."
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ReachableAddresses *:82\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, SERVERS_REACHABLE_MSG);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ReachableORAddresses *:82\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, SERVERS_REACHABLE_MSG);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ReachableDirAddresses *:82\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, SERVERS_REACHABLE_MSG);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ClientUseIPv4 0\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, SERVERS_REACHABLE_MSG);
+ tor_free(msg);
+
+ /* Test IPv4-only clients setting IPv6 preferences */
+
+#define WARN_PLEASE_USE_IPV6_OR_LOG_MSG \
+ "ClientPreferIPv6ORPort 1 is ignored unless tor is using IPv6. " \
+ "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges.\n"
+
+#define WARN_PLEASE_USE_IPV6_DIR_LOG_MSG \
+ "ClientPreferIPv6DirPort 1 is ignored unless tor is using IPv6. " \
+ "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges.\n"
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv4 1\n"
+ "ClientUseIPv6 0\n"
+ "UseBridges 0\n"
+ "ClientPreferIPv6ORPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(WARN_PLEASE_USE_IPV6_OR_LOG_MSG);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv4 1\n"
+ "ClientUseIPv6 0\n"
+ "UseBridges 0\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(WARN_PLEASE_USE_IPV6_DIR_LOG_MSG);
+ tor_free(msg);
+
+ /* Now test an IPv4/IPv6 client setting IPv6 preferences */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv4 1\n"
+ "ClientUseIPv6 1\n"
+ "ClientPreferIPv6ORPort 1\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ /* Now test an IPv6 client setting IPv6 preferences */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv6 1\n"
+ "ClientPreferIPv6ORPort 1\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ /* And an implicit (IPv4 disabled) IPv6 client setting IPv6 preferences */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv4 0\n"
+ "ClientPreferIPv6ORPort 1\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ /* And an implicit (bridge) client setting IPv6 preferences */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "UseBridges 1\n"
+ "Bridge 127.0.0.1:12345\n"
+ "ClientPreferIPv6ORPort 1\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__use_bridges(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "UseBridges 1\n"
+ "ClientUseIPv4 1\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Servers must be able to freely connect to the rest of"
+ " the Internet, so they must not set UseBridges.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("UseBridges 1\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_NE, "Servers must be able to freely connect to the rest of"
+ " the Internet, so they must not set UseBridges.");
+ tor_free(msg);
+
+ NS_MOCK(geoip_get_country);
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("UseBridges 1\n"
+ "EntryNodes {cn}\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "You cannot set both UseBridges and EntryNodes.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "UseBridges 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "If you set UseBridges, you must specify at least one bridge.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "UseBridges 1\n"
+ "Bridge 10.0.0.1\n"
+ "Bridge !!!\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bridge line did not parse. See logs for details.");
+ tor_free(msg);
+
+ done:
+ NS_UNMOCK(geoip_get_country);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__entry_nodes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ NS_MOCK(geoip_get_country);
+ options_test_data_t *tdata = get_options_test_data(
+ "EntryNodes {cn}\n"
+ "UseEntryGuards 0\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "If EntryNodes is set, UseEntryGuards must be enabled.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("EntryNodes {cn}\n"
+ "UseEntryGuards 1\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "KeepalivePeriod option must be positive.");
+ tor_free(msg);
+
+ done:
+ NS_UNMOCK(geoip_get_country);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__invalid_nodes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "AllowInvalidNodes something_stupid\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Unrecognized value 'something_stupid' in AllowInvalidNodes");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AllowInvalidNodes entry, middle, exit\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->AllowInvalid_, OP_EQ, ALLOW_INVALID_ENTRY |
+ ALLOW_INVALID_EXIT | ALLOW_INVALID_MIDDLE);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AllowInvalidNodes introduction, rendezvous\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->AllowInvalid_, OP_EQ, ALLOW_INVALID_INTRODUCTION |
+ ALLOW_INVALID_RENDEZVOUS);
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__safe_logging(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->SafeLogging_, OP_EQ, SAFELOG_SCRUB_NONE);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SafeLogging 0\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->SafeLogging_, OP_EQ, SAFELOG_SCRUB_NONE);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SafeLogging Relay\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->SafeLogging_, OP_EQ, SAFELOG_SCRUB_RELAY);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SafeLogging 1\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->SafeLogging_, OP_EQ, SAFELOG_SCRUB_ALL);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SafeLogging stuffy\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Unrecognized value '\"stuffy\"' in SafeLogging");
+ tor_free(msg);
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__publish_server_descriptor(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ options_test_data_t *tdata = get_options_test_data(
+ "PublishServerDescriptor bridge\n" TEST_OPTIONS_DEFAULT_VALUES
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("PublishServerDescriptor humma\n"
+ TEST_OPTIONS_DEFAULT_VALUES);
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Unrecognized value in PublishServerDescriptor");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("PublishServerDescriptor bridge, v3\n"
+ TEST_OPTIONS_DEFAULT_VALUES);
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bridges are not supposed to publish router "
+ "descriptors to the directory authorities. Please correct your "
+ "PublishServerDescriptor line.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("BridgeRelay 1\n"
+ "PublishServerDescriptor v3\n"
+ TEST_OPTIONS_DEFAULT_VALUES);
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bridges are not supposed to publish router "
+ "descriptors to the directory authorities. Please correct your "
+ "PublishServerDescriptor line.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("BridgeRelay 1\n" TEST_OPTIONS_DEFAULT_VALUES);
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_NE, "Bridges are not supposed to publish router "
+ "descriptors to the directory authorities. Please correct your "
+ "PublishServerDescriptor line.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("BridgeRelay 1\n"
+ "DirPort 999\n" TEST_OPTIONS_DEFAULT_VALUES);
+
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Can't set a DirPort on a bridge "
+ "relay; disabling DirPort\n");
+ tt_assert(!tdata->opt->DirPort_lines);
+ tt_assert(!tdata->opt->DirPort_set);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__testing(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+#define ENSURE_DEFAULT(varname, varval) \
+ STMT_BEGIN \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ #varname " " #varval "\n"); \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ tt_str_op(msg, OP_EQ, \
+ #varname " may only be changed in testing Tor networks!"); \
+ tt_int_op(ret, OP_EQ, -1); \
+ tor_free(msg); \
+ \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ #varname " " #varval "\n" \
+ VALID_DIR_AUTH \
+ "TestingTorNetwork 1\n"); \
+ \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ if (msg) { \
+ tt_str_op(msg, OP_NE, \
+ #varname " may only be changed in testing Tor networks!"); \
+ tor_free(msg); \
+ } \
+ \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ #varname " " #varval "\n" \
+ "___UsingTestNetworkDefaults 1\n"); \
+ \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ if (msg) { \
+ tt_str_op(msg, OP_NE, \
+ #varname " may only be changed in testing Tor networks!"); \
+ tor_free(msg); \
+ } \
+ STMT_END
+
+ ENSURE_DEFAULT(TestingV3AuthInitialVotingInterval, 3600);
+ ENSURE_DEFAULT(TestingV3AuthInitialVoteDelay, 3000);
+ ENSURE_DEFAULT(TestingV3AuthInitialDistDelay, 3000);
+ ENSURE_DEFAULT(TestingV3AuthVotingStartOffset, 3000);
+ ENSURE_DEFAULT(TestingAuthDirTimeToLearnReachability, 3000);
+ ENSURE_DEFAULT(TestingEstimatedDescriptorPropagationTime, 3000);
+ ENSURE_DEFAULT(TestingServerDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingClientDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingServerConsensusDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingClientConsensusDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingBridgeDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingClientMaxIntervalWithoutRequest, 3000);
+ ENSURE_DEFAULT(TestingDirConnectionMaxStall, 3000);
+ ENSURE_DEFAULT(TestingConsensusMaxDownloadTries, 3000);
+ ENSURE_DEFAULT(TestingDescriptorMaxDownloadTries, 3000);
+ ENSURE_DEFAULT(TestingMicrodescMaxDownloadTries, 3000);
+ ENSURE_DEFAULT(TestingCertMaxDownloadTries, 3000);
+ ENSURE_DEFAULT(TestingAuthKeyLifetime, 3000);
+ ENSURE_DEFAULT(TestingLinkCertLifetime, 3000);
+ ENSURE_DEFAULT(TestingSigningKeySlop, 3000);
+ ENSURE_DEFAULT(TestingAuthKeySlop, 3000);
+ ENSURE_DEFAULT(TestingLinkKeySlop, 3000);
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__hidserv(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ options_test_data_t *tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES);
+ tdata->opt->MinUptimeHidServDirectoryV2 = -1;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("MinUptimeHidServDirectoryV2 "
+ "option must be at least 0 seconds. Changing to 0.\n");
+ tt_int_op(tdata->opt->MinUptimeHidServDirectoryV2, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RendPostPeriod 1\n" );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("RendPostPeriod option is too short;"
+ " raising to 600 seconds.\n");
+ tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 600);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RendPostPeriod 302401\n" );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("RendPostPeriod is too large; "
+ "clipping to 302400s.\n");
+ tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 302400);
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__predicted_ports(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ options_test_data_t *tdata = get_options_test_data(
+ "PredictedPortsRelevanceTime 100000000\n"
+ TEST_OPTIONS_DEFAULT_VALUES);
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("PredictedPortsRelevanceTime is too "
+ "large; clipping to 3600s.\n");
+ tt_int_op(tdata->opt->PredictedPortsRelevanceTime, OP_EQ, 3600);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__path_bias(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+
+ options_test_data_t *tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasNoticeRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasNoticeRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasWarnRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasWarnRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasExtremeRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasExtremeRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasNoticeUseRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasNoticeUseRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasExtremeUseRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasExtremeUseRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__bandwidth(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+#define ENSURE_BANDWIDTH_PARAM(p) \
+ STMT_BEGIN \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES #p " 3Gb\n"); \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ tt_int_op(ret, OP_EQ, -1); \
+ tt_mem_op(msg, OP_EQ, #p " (3221225471) must be at most 2147483647", 40); \
+ tor_free(msg); \
+ STMT_END
+
+ ENSURE_BANDWIDTH_PARAM(BandwidthRate);
+ ENSURE_BANDWIDTH_PARAM(BandwidthBurst);
+ ENSURE_BANDWIDTH_PARAM(MaxAdvertisedBandwidth);
+ ENSURE_BANDWIDTH_PARAM(RelayBandwidthRate);
+ ENSURE_BANDWIDTH_PARAM(RelayBandwidthBurst);
+ ENSURE_BANDWIDTH_PARAM(PerConnBWRate);
+ ENSURE_BANDWIDTH_PARAM(PerConnBWBurst);
+ ENSURE_BANDWIDTH_PARAM(AuthDirFastGuarantee);
+ ENSURE_BANDWIDTH_PARAM(AuthDirGuardBWGuarantee);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthRate 1000\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(tdata->opt->RelayBandwidthBurst, OP_EQ, 1000);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthBurst 1001\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(tdata->opt->RelayBandwidthRate, OP_EQ, 1001);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthRate 1001\n"
+ "RelayBandwidthBurst 1000\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "RelayBandwidthBurst must be at least equal to "
+ "RelayBandwidthRate.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "BandwidthRate 1001\n"
+ "BandwidthBurst 1000\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "BandwidthBurst must be at least equal to BandwidthRate.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthRate 1001\n"
+ "BandwidthRate 1000\n"
+ "BandwidthBurst 1000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(tdata->opt->BandwidthRate, OP_EQ, 1001);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthRate 1001\n"
+ "BandwidthRate 1000\n"
+ "RelayBandwidthBurst 1001\n"
+ "BandwidthBurst 1000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(tdata->opt->BandwidthBurst, OP_EQ, 1001);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "BandwidthRate is set to 1 bytes/second. For servers,"
+ " it must be at least 76800.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76800\n"
+ "MaxAdvertisedBandwidth 30000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "MaxAdvertisedBandwidth is set to 30000 bytes/second."
+ " For servers, it must be at least 38400.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76800\n"
+ "RelayBandwidthRate 1\n"
+ "MaxAdvertisedBandwidth 38400\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "RelayBandwidthRate is set to 1 bytes/second. For "
+ "servers, it must be at least 76800.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76800\n"
+ "BandwidthBurst 76800\n"
+ "RelayBandwidthRate 76800\n"
+ "MaxAdvertisedBandwidth 38400\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__circuits(void *ignored)
+{
+ (void)ignored;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "MaxCircuitDirtiness 2592001\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("MaxCircuitDirtiness option is too "
+ "high; setting to 30 days.\n");
+ tt_int_op(tdata->opt->MaxCircuitDirtiness, OP_EQ, 2592000);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CircuitStreamTimeout 1\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("CircuitStreamTimeout option is too"
+ " short; raising to 10 seconds.\n");
+ tt_int_op(tdata->opt->CircuitStreamTimeout, OP_EQ, 10);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CircuitStreamTimeout 111\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_no_log_msg("CircuitStreamTimeout option is too"
+ " short; raising to 10 seconds.\n");
+ tt_int_op(tdata->opt->CircuitStreamTimeout, OP_EQ, 111);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HeartbeatPeriod 1\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("HeartbeatPeriod option is too short;"
+ " raising to 1800 seconds.\n");
+ tt_int_op(tdata->opt->HeartbeatPeriod, OP_EQ, 1800);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HeartbeatPeriod 1982\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_no_log_msg("HeartbeatPeriod option is too short;"
+ " raising to 1800 seconds.\n");
+ tt_int_op(tdata->opt->HeartbeatPeriod, OP_EQ, 1982);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CircuitBuildTimeout 1\n"
+ );
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("CircuitBuildTimeout is shorter (1"
+ " seconds) than the recommended minimum (10 seconds), and "
+ "LearnCircuitBuildTimeout is disabled. If tor isn't working, "
+ "raise this value or enable LearnCircuitBuildTimeout.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CircuitBuildTimeout 11\n"
+ );
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_no_log_msg("CircuitBuildTimeout is shorter (1 "
+ "seconds) than the recommended minimum (10 seconds), and "
+ "LearnCircuitBuildTimeout is disabled. If tor isn't working, "
+ "raise this value or enable LearnCircuitBuildTimeout.\n");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__port_forwarding(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PortForwarding 1\nSandbox 1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "PortForwarding is not compatible with Sandbox;"
+ " at most one can be set");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PortForwarding 1\nSandbox 0\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ memarea_clear_freelist();
+ policies_free_all();
+ tor_free(msg);
+}
+
+static void
+test_options_validate__tor2web(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Tor2webRendezvousPoints 1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Tor2webRendezvousPoints cannot be set without Tor2webMode.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Tor2webRendezvousPoints 1\nTor2webMode 1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__rend(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "UseEntryGuards 0\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("UseEntryGuards is disabled, but you"
+ " have configured one or more hidden services on this Tor "
+ "instance. Your hidden services will be very easy to locate using"
+ " a well-known attack -- see http://freehaven.net/anonbib/#hs-"
+ "attack06 for details.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "UseEntryGuards 1\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("UseEntryGuards is disabled, but you"
+ " have configured one or more hidden services on this Tor "
+ "instance. Your hidden services will be very easy to locate using"
+ " a well-known attack -- see http://freehaven.net/anonbib/#hs-"
+ "attack06 for details.\n");
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Failed to configure rendezvous options. See logs for details.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HidServAuth failed\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Failed to configure client authorization for hidden "
+ "services. See logs for details.");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__accounting(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingRule something_bad\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "AccountingRule must be 'sum', 'max', 'in', or 'out'");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingRule sum\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->AccountingRule, OP_EQ, ACCT_SUM);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingRule max\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->AccountingRule, OP_EQ, ACCT_MAX);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingStart fail\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Failed to parse accounting options. See logs for details.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingMax 10\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76800\n"
+ "BandwidthBurst 76800\n"
+ "MaxAdvertisedBandwidth 38400\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ "AccountingMax 10\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("Using accounting with a hidden "
+ "service and an ORPort is risky: your hidden service(s) and "
+ "your public address will all turn off at the same time, "
+ "which may alert observers that they are being run by the "
+ "same party.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ "AccountingMax 10\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("Using accounting with a hidden "
+ "service and an ORPort is risky: your hidden service(s) and "
+ "your public address will all turn off at the same time, "
+ "which may alert observers that they are being run by the "
+ "same party.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service2/\n"
+ "HiddenServicePort 81 127.0.0.1:8081\n"
+ "AccountingMax 10\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("Using accounting with multiple "
+ "hidden services is risky: they will all turn off at the same"
+ " time, which may alert observers that they are being run by "
+ "the same party.\n");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__proxy(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ sandbox_disable_getaddrinfo_cache();
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 127.0.42.1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HTTPProxyPort, OP_EQ, 80);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 127.0.42.1:444\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HTTPProxyPort, OP_EQ, 444);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy not_so_valid!\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HTTPProxy failed to parse or resolve. Please fix.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxyAuthenticator "
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreeonetwothreeonetwothree"
+
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HTTPProxyAuthenticator is too long (>= 512 chars).");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxyAuthenticator validauth\n"
+
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxy 127.0.42.1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HTTPSProxyPort, OP_EQ, 443);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxy 127.0.42.1:444\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HTTPSProxyPort, OP_EQ, 444);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxy not_so_valid!\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HTTPSProxy failed to parse or resolve. Please fix.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxyAuthenticator "
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreeonetwothreeonetwothree"
+
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HTTPSProxyAuthenticator is too long (>= 512 chars).");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxyAuthenticator validauth\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks4Proxy 127.0.42.1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->Socks4ProxyPort, OP_EQ, 1080);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks4Proxy 127.0.42.1:444\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->Socks4ProxyPort, OP_EQ, 444);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks4Proxy not_so_valid!\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Socks4Proxy failed to parse or resolve. Please fix.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5Proxy 127.0.42.1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->Socks5ProxyPort, OP_EQ, 1080);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5Proxy 127.0.42.1:444\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->Socks5ProxyPort, OP_EQ, 444);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5Proxy not_so_valid!\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Socks5Proxy failed to parse or resolve. Please fix.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks4Proxy 215.1.1.1\n"
+ "Socks5Proxy 215.1.1.2\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "You have configured more than one proxy type. "
+ "(Socks4Proxy|Socks5Proxy|HTTPSProxy)");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 215.1.1.1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("HTTPProxy configured, but no SOCKS "
+ "proxy or HTTPS proxy configured. Watch out: this configuration "
+ "will proxy unencrypted directory connections only.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 215.1.1.1\n"
+ "Socks4Proxy 215.1.1.1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("HTTPProxy configured, but no SOCKS "
+ "proxy or HTTPS proxy configured. Watch out: this configuration "
+ "will proxy unencrypted directory connections only.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 215.1.1.1\n"
+ "Socks5Proxy 215.1.1.1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("HTTPProxy configured, but no SOCKS "
+ "proxy or HTTPS proxy configured. Watch out: this configuration "
+ "will proxy unencrypted directory connections only.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 215.1.1.1\n"
+ "HttpsProxy 215.1.1.1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "HTTPProxy configured, but no SOCKS proxy or HTTPS proxy "
+ "configured. Watch out: this configuration will proxy "
+ "unencrypted directory connections only.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ );
+ tdata->opt->Socks5ProxyUsername = tor_strdup("");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Socks5ProxyUsername must be between 1 and 255 characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ );
+ tdata->opt->Socks5ProxyUsername =
+ tor_strdup("ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789AB"
+ "CDEABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCD"
+ "EABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEA"
+ "BCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEABC"
+ "DE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Socks5ProxyUsername must be between 1 and 255 characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyUsername hello_world\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Socks5ProxyPassword must be included with "
+ "Socks5ProxyUsername.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyUsername hello_world\n"
+ );
+ tdata->opt->Socks5ProxyPassword = tor_strdup("");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Socks5ProxyPassword must be between 1 and 255 characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyUsername hello_world\n"
+ );
+ tdata->opt->Socks5ProxyPassword =
+ tor_strdup("ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789AB"
+ "CDEABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCD"
+ "EABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEA"
+ "BCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEABC"
+ "DE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Socks5ProxyPassword must be between 1 and 255 characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyUsername hello_world\n"
+ "Socks5ProxyPassword world_hello\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyPassword hello_world\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Socks5ProxyPassword must be included with "
+ "Socks5ProxyUsername.");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ memarea_clear_freelist();
+ policies_free_all();
+ // sandbox_free_getaddrinfo_cache();
+ tor_free(msg);
+}
+
+static void
+test_options_validate__control(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HashedControlPassword something_incorrect\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Bad HashedControlPassword: wrong length or bad encoding");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HashedControlPassword 16:872860B76453A77D60CA"
+ "2BB8C1A7042072093276A3D701AD684053EC4C\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "__HashedControlSessionPassword something_incorrect\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bad HashedControlSessionPassword: wrong length or "
+ "bad encoding");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "__HashedControlSessionPassword 16:872860B7645"
+ "3A77D60CA2BB8C1A7042072093276A3D701AD684053EC"
+ "4C\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "__OwningControllerProcess something_incorrect\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bad OwningControllerProcess: invalid PID");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "__OwningControllerProcess 123\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlPort 127.0.0.1:1234\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "ControlPort is open, but no authentication method has been "
+ "configured. This means that any program on your computer can "
+ "reconfigure your Tor. That's bad! You should upgrade your Tor"
+ " controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlPort 127.0.0.1:1234\n"
+ "HashedControlPassword 16:872860B76453A77D60CA"
+ "2BB8C1A7042072093276A3D701AD684053EC4C\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlPort is open, but no authentication method has been "
+ "configured. This means that any program on your computer can "
+ "reconfigure your Tor. That's bad! You should upgrade your Tor "
+ "controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlPort 127.0.0.1:1234\n"
+ "__HashedControlSessionPassword 16:872860B7645"
+ "3A77D60CA2BB8C1A7042072093276A3D701AD684053EC"
+ "4C\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlPort is open, but no authentication method has been "
+ "configured. This means that any program on your computer can "
+ "reconfigure your Tor. That's bad! You should upgrade your Tor "
+ "controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlPort 127.0.0.1:1234\n"
+ "CookieAuthentication 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlPort is open, but no authentication method has been "
+ "configured. This means that any program on your computer can "
+ "reconfigure your Tor. That's bad! You should upgrade your Tor "
+ "controller as soon as possible.\n");
+ tor_free(msg);
+
+#ifdef HAVE_SYS_UN_H
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlSocket unix:/tmp WorldWritable\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "ControlSocket is world writable, but no authentication method has"
+ " been configured. This means that any program on your computer "
+ "can reconfigure your Tor. That's bad! You should upgrade your "
+ "Tor controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlSocket unix:/tmp WorldWritable\n"
+ "HashedControlPassword 16:872860B76453A77D60CA"
+ "2BB8C1A7042072093276A3D701AD684053EC4C\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlSocket is world writable, but no authentication method has"
+ " been configured. This means that any program on your computer "
+ "can reconfigure your Tor. That's bad! You should upgrade your "
+ "Tor controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlSocket unix:/tmp WorldWritable\n"
+ "__HashedControlSessionPassword 16:872860B7645"
+ "3A77D60CA2BB8C1A7042072093276A3D701AD684053EC"
+ "4C\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlSocket is world writable, but no authentication method has"
+ " been configured. This means that any program on your computer "
+ "can reconfigure your Tor. That's bad! You should upgrade your "
+ "Tor controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlSocket unix:/tmp WorldWritable\n"
+ "CookieAuthentication 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlSocket is world writable, but no authentication method has"
+ " been configured. This means that any program on your computer "
+ "can reconfigure your Tor. That's bad! You should upgrade your "
+ "Tor controller as soon as possible.\n");
+ tor_free(msg);
+#endif
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CookieAuthFileGroupReadable 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "CookieAuthFileGroupReadable is set, but will have no effect: you "
+ "must specify an explicit CookieAuthFile to have it "
+ "group-readable.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CookieAuthFileGroupReadable 1\n"
+ "CookieAuthFile /tmp/somewhere\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "CookieAuthFileGroupReadable is set, but will have no effect: you "
+ "must specify an explicit CookieAuthFile to have it "
+ "group-readable.\n");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__families(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "MyFamily home\n"
+ "BridgeRelay 1\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 51300\n"
+ "BandwidthBurst 51300\n"
+ "MaxAdvertisedBandwidth 25700\n"
+ "DirCache 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "Listing a family for a bridge relay is not supported: it can "
+ "reveal bridge fingerprints to censors. You should also make sure "
+ "you aren't listing this bridge's fingerprint in any other "
+ "MyFamily.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "MyFamily home\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "Listing a family for a bridge relay is not supported: it can "
+ "reveal bridge fingerprints to censors. You should also make sure "
+ "you aren't listing this bridge's fingerprint in any other "
+ "MyFamily.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "MyFamily !\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Invalid nickname '!' in MyFamily line");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "NodeFamily foo\n"
+ "NodeFamily !\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__addr_policies(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ExitPolicy !!!\n"
+ "ExitRelay 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Error in ExitPolicy entry.");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__dir_auth(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ VALID_ALT_DIR_AUTH
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Directory authority/fallback line did not parse. See logs for "
+ "details.");
+ expect_log_msg(
+ "You cannot set both DirAuthority and Alternate*Authority.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TestingTorNetwork may only be configured in combination with a "
+ "non-default set of DirAuthority or both of AlternateDirAuthority "
+ "and AlternateBridgeAuthority configured.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingTorNetwork 1\n"
+ VALID_ALT_DIR_AUTH
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TestingTorNetwork may only be configured in combination with a "
+ "non-default set of DirAuthority or both of AlternateDirAuthority "
+ "and AlternateBridgeAuthority configured.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingTorNetwork 1\n"
+ VALID_ALT_BRIDGE_AUTH
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingTorNetwork may only be configured in "
+ "combination with a non-default set of DirAuthority or both of "
+ "AlternateDirAuthority and AlternateBridgeAuthority configured.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_ALT_DIR_AUTH
+ VALID_ALT_BRIDGE_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__transport(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_NOTICE);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientTransportPlugin !!\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Invalid client transport line. See logs for details.");
+ expect_log_msg(
+ "Too few arguments on ClientTransportPlugin line.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientTransportPlugin foo exec bar\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportPlugin !!\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Invalid server transport line. See logs for details.");
+ expect_log_msg(
+ "Too few arguments on ServerTransportPlugin line.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportPlugin foo exec bar\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "Tor is not configured as a relay but you specified a "
+ "ServerTransportPlugin line (\"foo exec bar\"). The "
+ "ServerTransportPlugin line will be ignored.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportPlugin foo exec bar\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76900\n"
+ "BandwidthBurst 76900\n"
+ "MaxAdvertisedBandwidth 38500\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "Tor is not configured as a relay but you specified a "
+ "ServerTransportPlugin line (\"foo exec bar\"). The "
+ "ServerTransportPlugin line will be ignored.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportListenAddr foo 127.0.0.42:55\n"
+ "ServerTransportListenAddr !\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "ServerTransportListenAddr did not parse. See logs for details.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportListenAddr foo 127.0.0.42:55\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "You need at least a single managed-proxy to specify a transport "
+ "listen address. The ServerTransportListenAddr line will be "
+ "ignored.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportListenAddr foo 127.0.0.42:55\n"
+ "ServerTransportPlugin foo exec bar\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76900\n"
+ "BandwidthBurst 76900\n"
+ "MaxAdvertisedBandwidth 38500\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "You need at least a single managed-proxy to specify a transport "
+ "listen address. The ServerTransportListenAddr line will be "
+ "ignored.\n");
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__constrained_sockets(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 0\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "ConstrainedSockSize is invalid. Must be a value "
+ "between 2048 and 262144 in 1024 byte increments.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 263168\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "ConstrainedSockSize is invalid. Must be a value "
+ "between 2048 and 262144 in 1024 byte increments.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 2047\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "ConstrainedSockSize is invalid. Must be a value "
+ "between 2048 and 262144 in 1024 byte increments.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 2048\n"
+ "DirPort 999\n"
+ "DirCache 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("You have requested constrained "
+ "socket buffers while also serving directory entries via DirPort."
+ " It is strongly suggested that you disable serving directory"
+ " requests when system TCP buffer resources are scarce.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 2048\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "You have requested constrained socket buffers while also serving"
+ " directory entries via DirPort. It is strongly suggested that "
+ "you disable serving directory requests when system TCP buffer "
+ "resources are scarce.\n");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__v3_auth(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 1000\n"
+ "V3AuthDistDelay 1000\n"
+ "V3AuthVotingInterval 1000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "V3AuthVoteDelay plus V3AuthDistDelay must be less than half "
+ "V3AuthVotingInterval");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthVoteDelay is way too low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 1\n"
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthVoteDelay is way too low.");
+ tor_free(msg);
+
+ // TODO: we can't reach the case of v3authvotedelay lower
+ // than MIN_VOTE_SECONDS but not lower than MIN_VOTE_SECONDS_TESTING,
+ // since they are the same
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthDistDelay 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthDistDelay is way too low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthDistDelay 1\n"
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthDistDelay is way too low.");
+ tor_free(msg);
+
+ // TODO: we can't reach the case of v3authdistdelay lower than
+ // MIN_DIST_SECONDS but not lower than MIN_DIST_SECONDS_TESTING,
+ // since they are the same
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthNIntervalsValid 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthNIntervalsValid must be at least 2.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 200\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthVotingInterval is insanely low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 200000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthVotingInterval is insanely high.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 1441\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("V3AuthVotingInterval does not divide"
+ " evenly into 24 hours.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 1440\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("V3AuthVotingInterval does not divide"
+ " evenly into 24 hours.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 299\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("V3AuthVotingInterval is very low. "
+ "This may lead to failure to synchronise for a consensus.\n");
+ tor_free(msg);
+
+ // TODO: It is impossible to reach the case of testingtor network, with
+ // v3authvotinginterval too low
+ /* free_options_test_data(tdata); */
+ /* tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES */
+ /* "V3AuthVoteDelay 1\n" */
+ /* "V3AuthDistDelay 1\n" */
+ /* "V3AuthVotingInterval 9\n" */
+ /* VALID_DIR_AUTH */
+ /* "TestingTorNetwork 1\n" */
+ /* ); */
+ /* ret = options_validate(tdata->old_opt, tdata->opt, */
+ /* tdata->def_opt, 0, &msg); */
+ /* tt_int_op(ret, OP_EQ, -1); */
+ /* tt_str_op(msg, OP_EQ, "V3AuthVotingInterval is insanely low."); */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingV3AuthInitialVoteDelay 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingV3AuthInitialVoteDelay is way too low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingV3AuthInitialDistDelay 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingV3AuthInitialDistDelay is way too low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ tdata->opt->TestingV3AuthVotingStartOffset = 100000;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingV3AuthVotingStartOffset is higher than the "
+ "voting interval.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ tdata->opt->TestingV3AuthVotingStartOffset = -1;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TestingV3AuthVotingStartOffset must be non-negative.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ "TestingV3AuthInitialVotingInterval 4\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingV3AuthInitialVotingInterval is insanely low.");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__virtual_addr(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "VirtualAddrNetworkIPv4 !!"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Error parsing VirtualAddressNetwork !!");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "VirtualAddrNetworkIPv6 !!"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Error parsing VirtualAddressNetworkIPv6 !!");
+ tor_free(msg);
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__exits(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AllowSingleHopExits 1"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("You have set AllowSingleHopExits; "
+ "now your relay will allow others to make one-hop exits. However,"
+ " since by default most clients avoid relays that set this option,"
+ " most clients will ignore you.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AllowSingleHopExits 1\n"
+ VALID_DIR_AUTH
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("You have set AllowSingleHopExits; "
+ "now your relay will allow others to make one-hop exits. However,"
+ " since by default most clients avoid relays that set this option,"
+ " most clients will ignore you.\n");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__testing_options(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+#define TEST_TESTING_OPTION(name, low_val, high_val, err_low) \
+ STMT_BEGIN \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ VALID_DIR_AUTH \
+ "TestingTorNetwork 1\n" \
+ ); \
+ tdata->opt-> name = low_val; \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ tt_int_op(ret, OP_EQ, -1); \
+ tt_str_op(msg, OP_EQ, #name " " err_low); \
+ tor_free(msg); \
+ \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ VALID_DIR_AUTH \
+ "TestingTorNetwork 1\n" \
+ ); \
+ tdata->opt-> name = high_val; \
+ mock_clean_saved_logs(); \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ tt_int_op(ret, OP_EQ, 0); \
+ expect_log_msg( #name " is insanely high.\n"); \
+ tor_free(msg); \
+ STMT_END
+
+ TEST_TESTING_OPTION(TestingAuthDirTimeToLearnReachability, -1, 8000,
+ "must be non-negative.");
+ TEST_TESTING_OPTION(TestingEstimatedDescriptorPropagationTime, -1, 3601,
+ "must be non-negative.");
+ TEST_TESTING_OPTION(TestingClientMaxIntervalWithoutRequest, -1, 3601,
+ "is way too low.");
+ TEST_TESTING_OPTION(TestingDirConnectionMaxStall, 1, 3601,
+ "is way too low.");
+ // TODO: I think this points to a bug/regression in options_validate
+ TEST_TESTING_OPTION(TestingConsensusMaxDownloadTries, 1, 801,
+ "must be greater than 2.");
+ TEST_TESTING_OPTION(TestingDescriptorMaxDownloadTries, 1, 801,
+ "must be greater than 1.");
+ TEST_TESTING_OPTION(TestingMicrodescMaxDownloadTries, 1, 801,
+ "must be greater than 1.");
+ TEST_TESTING_OPTION(TestingCertMaxDownloadTries, 1, 801,
+ "must be greater than 1.");
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableConnBwEvent 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingEnableConnBwEvent may only be changed in "
+ "testing Tor networks!");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableConnBwEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ "___UsingTestNetworkDefaults 0\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableConnBwEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 0\n"
+ "___UsingTestNetworkDefaults 1\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableCellStatsEvent 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingEnableCellStatsEvent may only be changed in "
+ "testing Tor networks!");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableCellStatsEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ "___UsingTestNetworkDefaults 0\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableCellStatsEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 0\n"
+ "___UsingTestNetworkDefaults 1\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableTbEmptyEvent 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingEnableTbEmptyEvent may only be changed "
+ "in testing Tor networks!");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableTbEmptyEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ "___UsingTestNetworkDefaults 0\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableTbEmptyEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 0\n"
+ "___UsingTestNetworkDefaults 1\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__accel(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccelName foo\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HardwareAccel, OP_EQ, 1);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccelName foo\n"
+ );
+ tdata->opt->HardwareAccel = 2;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HardwareAccel, OP_EQ, 2);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccelDir 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Can't use hardware crypto accelerator dir without engine name.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccelDir 1\n"
+ "AccelName something\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+#define LOCAL_VALIDATE_TEST(name) \
+ { "validate__" #name, test_options_validate__ ## name, TT_FORK, NULL, NULL }
+
struct testcase_t options_tests[] = {
{ "validate", test_options_validate, TT_FORK, NULL, NULL },
- END_OF_TESTCASES
+ { "mem_dircache", test_have_enough_mem_for_dircache, TT_FORK, NULL, NULL },
+ LOCAL_VALIDATE_TEST(uname_for_server),
+ LOCAL_VALIDATE_TEST(outbound_addresses),
+ LOCAL_VALIDATE_TEST(data_directory),
+ LOCAL_VALIDATE_TEST(nickname),
+ LOCAL_VALIDATE_TEST(contactinfo),
+ LOCAL_VALIDATE_TEST(logs),
+ LOCAL_VALIDATE_TEST(authdir),
+ LOCAL_VALIDATE_TEST(relay_with_hidden_services),
+ LOCAL_VALIDATE_TEST(transproxy),
+ LOCAL_VALIDATE_TEST(exclude_nodes),
+ LOCAL_VALIDATE_TEST(scheduler),
+ LOCAL_VALIDATE_TEST(node_families),
+ LOCAL_VALIDATE_TEST(tlsec),
+ LOCAL_VALIDATE_TEST(token_bucket),
+ LOCAL_VALIDATE_TEST(recommended_packages),
+ LOCAL_VALIDATE_TEST(fetch_dir),
+ LOCAL_VALIDATE_TEST(conn_limit),
+ LOCAL_VALIDATE_TEST(paths_needed),
+ LOCAL_VALIDATE_TEST(max_client_circuits),
+ LOCAL_VALIDATE_TEST(ports),
+ LOCAL_VALIDATE_TEST(reachable_addresses),
+ LOCAL_VALIDATE_TEST(use_bridges),
+ LOCAL_VALIDATE_TEST(entry_nodes),
+ LOCAL_VALIDATE_TEST(invalid_nodes),
+ LOCAL_VALIDATE_TEST(safe_logging),
+ LOCAL_VALIDATE_TEST(publish_server_descriptor),
+ LOCAL_VALIDATE_TEST(testing),
+ LOCAL_VALIDATE_TEST(hidserv),
+ LOCAL_VALIDATE_TEST(predicted_ports),
+ LOCAL_VALIDATE_TEST(path_bias),
+ LOCAL_VALIDATE_TEST(bandwidth),
+ LOCAL_VALIDATE_TEST(circuits),
+ LOCAL_VALIDATE_TEST(port_forwarding),
+ LOCAL_VALIDATE_TEST(tor2web),
+ LOCAL_VALIDATE_TEST(rend),
+ LOCAL_VALIDATE_TEST(accounting),
+ LOCAL_VALIDATE_TEST(proxy),
+ LOCAL_VALIDATE_TEST(control),
+ LOCAL_VALIDATE_TEST(families),
+ LOCAL_VALIDATE_TEST(addr_policies),
+ LOCAL_VALIDATE_TEST(dir_auth),
+ LOCAL_VALIDATE_TEST(transport),
+ LOCAL_VALIDATE_TEST(constrained_sockets),
+ LOCAL_VALIDATE_TEST(v3_auth),
+ LOCAL_VALIDATE_TEST(virtual_addr),
+ LOCAL_VALIDATE_TEST(exits),
+ LOCAL_VALIDATE_TEST(testing_options),
+ LOCAL_VALIDATE_TEST(accel),
+ END_OF_TESTCASES /* */
};
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index 37c36fed99..84a7081588 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -2,8 +2,11 @@
/* See LICENSE for licensing information */
#include "or.h"
+#define CONFIG_PRIVATE
+#include "config.h"
#include "router.h"
#include "routerparse.h"
+#define POLICIES_PRIVATE
#include "policies.h"
#include "test.h"
@@ -49,7 +52,7 @@ test_policy_summary_helper(const char *policy_str,
r = policies_parse_exit_policy(&line, &policy,
EXIT_POLICY_IPV6_ENABLED |
- EXIT_POLICY_ADD_DEFAULT, 0, NULL, 0);
+ EXIT_POLICY_ADD_DEFAULT, NULL);
tt_int_op(r,OP_EQ, 0);
summary = policy_summarize(policy, AF_INET);
@@ -80,7 +83,8 @@ test_policies_general(void *arg)
*policy7 = NULL, *policy8 = NULL, *policy9 = NULL,
*policy10 = NULL, *policy11 = NULL, *policy12 = NULL;
addr_policy_t *p;
- tor_addr_t tar;
+ tor_addr_t tar, tar2;
+ smartlist_t *addr_list = NULL;
config_line_t line;
smartlist_t *sm = NULL;
char *policy_str = NULL;
@@ -115,17 +119,22 @@ test_policies_general(void *arg)
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy2,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT, NULL));
tt_assert(policy2);
- tor_addr_parse(&tar, "[2000::1234]");
+ tor_addr_from_ipv4h(&tar, 0x0306090cu);
+ tor_addr_parse(&tar2, "[2000::1234]");
+ addr_list = smartlist_new();
+ smartlist_add(addr_list, &tar);
+ smartlist_add(addr_list, &tar2);
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy12,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
EXIT_POLICY_ADD_DEFAULT,
- 0x0306090cu, &tar, 1));
+ addr_list));
+ smartlist_free(addr_list);
+ addr_list = NULL;
tt_assert(policy12);
@@ -206,15 +215,15 @@ test_policies_general(void *arg)
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy8,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT,
+ NULL));
tt_assert(policy8);
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy9,
EXIT_POLICY_REJECT_PRIVATE |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT,
+ NULL));
tt_assert(policy9);
@@ -261,6 +270,93 @@ test_policies_general(void *arg)
addr_policy_list_free(policy);
policy = NULL;
+ /* make sure assume_action works */
+ malformed_list = 0;
+ p = router_parse_addr_policy_item_from_string("127.0.0.1",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("127.0.0.1:*",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("[::]",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("[::]:*",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("[face::b]",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("[b::aaaa]",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("*",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("*4",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("*6",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ /* These are all ambiguous IPv6 addresses, it's good that we reject them */
+ p = router_parse_addr_policy_item_from_string("acce::abcd",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(!p);
+ tt_assert(malformed_list);
+ malformed_list = 0;
+
+ p = router_parse_addr_policy_item_from_string("7:1234",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(!p);
+ tt_assert(malformed_list);
+ malformed_list = 0;
+
+ p = router_parse_addr_policy_item_from_string("::",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(!p);
+ tt_assert(malformed_list);
+ malformed_list = 0;
+
/* make sure compacting logic works. */
policy = NULL;
line.key = (char*)"foo";
@@ -268,8 +364,7 @@ test_policies_general(void *arg)
line.next = NULL;
tt_int_op(0, OP_EQ, policies_parse_exit_policy(&line,&policy,
EXIT_POLICY_IPV6_ENABLED |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT, NULL));
tt_assert(policy);
//test_streq(policy->string, "accept *:80");
@@ -489,6 +584,324 @@ test_policies_general(void *arg)
short_policy_free(short_parsed);
}
+/** Helper: Check that policy_list contains address */
+static int
+test_policy_has_address_helper(const smartlist_t *policy_list,
+ const tor_addr_t *addr)
+{
+ int found = 0;
+
+ tt_assert(policy_list);
+ tt_assert(addr);
+
+ SMARTLIST_FOREACH_BEGIN(policy_list, addr_policy_t*, p) {
+ if (tor_addr_eq(&p->addr, addr)) {
+ found = 1;
+ }
+ } SMARTLIST_FOREACH_END(p);
+
+ return found;
+
+ done:
+ return 0;
+}
+
+#define TEST_IPV4_ADDR (0x01020304)
+#define TEST_IPV6_ADDR ("2002::abcd")
+
+/** Run unit tests for rejecting the configured addresses on this exit relay
+ * using policies_parse_exit_policy_reject_private */
+static void
+test_policies_reject_exit_address(void *arg)
+{
+ smartlist_t *policy = NULL;
+ tor_addr_t ipv4_addr, ipv6_addr;
+ smartlist_t *ipv4_list, *ipv6_list, *both_list, *dupl_list;
+ (void)arg;
+
+ tor_addr_from_ipv4h(&ipv4_addr, TEST_IPV4_ADDR);
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR);
+
+ ipv4_list = smartlist_new();
+ ipv6_list = smartlist_new();
+ both_list = smartlist_new();
+ dupl_list = smartlist_new();
+
+ smartlist_add(ipv4_list, &ipv4_addr);
+ smartlist_add(both_list, &ipv4_addr);
+ smartlist_add(dupl_list, &ipv4_addr);
+ smartlist_add(dupl_list, &ipv4_addr);
+ smartlist_add(dupl_list, &ipv4_addr);
+
+ smartlist_add(ipv6_list, &ipv6_addr);
+ smartlist_add(both_list, &ipv6_addr);
+ smartlist_add(dupl_list, &ipv6_addr);
+ smartlist_add(dupl_list, &ipv6_addr);
+
+ /* IPv4-Only Exits */
+
+ /* test that IPv4 addresses are rejected on an IPv4-only exit */
+ policies_parse_exit_policy_reject_private(&policy, 0, ipv4_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv6 addresses are NOT rejected on an IPv4-only exit
+ * (all IPv6 addresses are rejected by policies_parse_exit_policy_internal
+ * on IPv4-only exits, so policies_parse_exit_policy_reject_private doesn't
+ * need to do anything) */
+ policies_parse_exit_policy_reject_private(&policy, 0, ipv6_list, 0, 0);
+ tt_assert(policy == NULL);
+
+ /* test that only IPv4 addresses are rejected on an IPv4-only exit */
+ policies_parse_exit_policy_reject_private(&policy, 0, both_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* Test that lists with duplicate entries produce the same results */
+ policies_parse_exit_policy_reject_private(&policy, 0, dupl_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* IPv4/IPv6 Exits */
+
+ /* test that IPv4 addresses are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, ipv4_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv6 addresses are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, ipv6_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv4 and IPv6 addresses are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, both_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 2);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* Test that lists with duplicate entries produce the same results */
+ policies_parse_exit_policy_reject_private(&policy, 1, dupl_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 2);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ done:
+ addr_policy_list_free(policy);
+ smartlist_free(ipv4_list);
+ smartlist_free(ipv6_list);
+ smartlist_free(both_list);
+ smartlist_free(dupl_list);
+}
+
+static smartlist_t *test_configured_ports = NULL;
+const smartlist_t *mock_get_configured_ports(void);
+
+/** Returns test_configured_ports */
+const smartlist_t *
+mock_get_configured_ports(void)
+{
+ return test_configured_ports;
+}
+
+/** Run unit tests for rejecting publicly routable configured port addresses
+ * on this exit relay using policies_parse_exit_policy_reject_private */
+static void
+test_policies_reject_port_address(void *arg)
+{
+ smartlist_t *policy = NULL;
+ port_cfg_t *ipv4_port = NULL;
+ port_cfg_t *ipv6_port = NULL;
+ (void)arg;
+
+ test_configured_ports = smartlist_new();
+
+ ipv4_port = port_cfg_new(0);
+ tor_addr_from_ipv4h(&ipv4_port->addr, TEST_IPV4_ADDR);
+ smartlist_add(test_configured_ports, ipv4_port);
+
+ ipv6_port = port_cfg_new(0);
+ tor_addr_parse(&ipv6_port->addr, TEST_IPV6_ADDR);
+ smartlist_add(test_configured_ports, ipv6_port);
+
+ MOCK(get_configured_ports, mock_get_configured_ports);
+
+ /* test that an IPv4 port is rejected on an IPv4-only exit, but an IPv6 port
+ * is NOT rejected (all IPv6 addresses are rejected by
+ * policies_parse_exit_policy_internal on IPv4-only exits, so
+ * policies_parse_exit_policy_reject_private doesn't need to do anything
+ * with IPv6 addresses on IPv4-only exits) */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 1);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_port->addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv4 and IPv6 ports are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, NULL, 0, 1);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 2);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_port->addr));
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_port->addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ done:
+ addr_policy_list_free(policy);
+ if (test_configured_ports) {
+ SMARTLIST_FOREACH(test_configured_ports,
+ port_cfg_t *, p, port_cfg_free(p));
+ smartlist_free(test_configured_ports);
+ test_configured_ports = NULL;
+ }
+ UNMOCK(get_configured_ports);
+}
+
+smartlist_t *mock_ipv4_addrs = NULL;
+smartlist_t *mock_ipv6_addrs = NULL;
+
+/* mock get_interface_address6_list, returning a deep copy of the template
+ * address list ipv4_interface_address_list or ipv6_interface_address_list */
+static smartlist_t *
+mock_get_interface_address6_list(int severity,
+ sa_family_t family,
+ int include_internal)
+{
+ (void)severity;
+ (void)include_internal;
+ smartlist_t *clone_list = smartlist_new();
+ smartlist_t *template_list = NULL;
+
+ if (family == AF_INET) {
+ template_list = mock_ipv4_addrs;
+ } else if (family == AF_INET6) {
+ template_list = mock_ipv6_addrs;
+ } else {
+ return NULL;
+ }
+
+ tt_assert(template_list);
+
+ SMARTLIST_FOREACH_BEGIN(template_list, tor_addr_t *, src_addr) {
+ tor_addr_t *dest_addr = malloc(sizeof(tor_addr_t));
+ memset(dest_addr, 0, sizeof(*dest_addr));
+ tor_addr_copy_tight(dest_addr, src_addr);
+ smartlist_add(clone_list, dest_addr);
+ } SMARTLIST_FOREACH_END(src_addr);
+
+ return clone_list;
+
+ done:
+ free_interface_address6_list(clone_list);
+ return NULL;
+}
+
+/** Run unit tests for rejecting publicly routable interface addresses on this
+ * exit relay using policies_parse_exit_policy_reject_private */
+static void
+test_policies_reject_interface_address(void *arg)
+{
+ smartlist_t *policy = NULL;
+ smartlist_t *public_ipv4_addrs =
+ get_interface_address6_list(LOG_INFO, AF_INET, 0);
+ smartlist_t *public_ipv6_addrs =
+ get_interface_address6_list(LOG_INFO, AF_INET6, 0);
+ tor_addr_t ipv4_addr, ipv6_addr;
+ (void)arg;
+
+ /* test that no addresses are rejected when none are supplied/requested */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 0);
+ tt_assert(policy == NULL);
+
+ /* test that only IPv4 interface addresses are rejected on an IPv4-only exit
+ * (and allow for duplicates)
+ */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 1, 0);
+ if (policy) {
+ tt_assert(smartlist_len(policy) <= smartlist_len(public_ipv4_addrs));
+ addr_policy_list_free(policy);
+ policy = NULL;
+ }
+
+ /* test that IPv4 and IPv6 interface addresses are rejected on an IPv4/IPv6
+ * exit (and allow for duplicates) */
+ policies_parse_exit_policy_reject_private(&policy, 1, NULL, 1, 0);
+ if (policy) {
+ tt_assert(smartlist_len(policy) <= (smartlist_len(public_ipv4_addrs)
+ + smartlist_len(public_ipv6_addrs)));
+ addr_policy_list_free(policy);
+ policy = NULL;
+ }
+
+ /* Now do it all again, but mocked */
+ tor_addr_from_ipv4h(&ipv4_addr, TEST_IPV4_ADDR);
+ mock_ipv4_addrs = smartlist_new();
+ smartlist_add(mock_ipv4_addrs, (void *)&ipv4_addr);
+
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR);
+ mock_ipv6_addrs = smartlist_new();
+ smartlist_add(mock_ipv6_addrs, (void *)&ipv6_addr);
+
+ MOCK(get_interface_address6_list, mock_get_interface_address6_list);
+
+ /* test that no addresses are rejected when none are supplied/requested */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 0);
+ tt_assert(policy == NULL);
+
+ /* test that only IPv4 interface addresses are rejected on an IPv4-only exit
+ */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 1, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == smartlist_len(mock_ipv4_addrs));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv4 and IPv6 interface addresses are rejected on an IPv4/IPv6
+ * exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, NULL, 1, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == (smartlist_len(mock_ipv4_addrs)
+ + smartlist_len(mock_ipv6_addrs)));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ done:
+ addr_policy_list_free(policy);
+ free_interface_address6_list(public_ipv4_addrs);
+ free_interface_address6_list(public_ipv6_addrs);
+
+ UNMOCK(get_interface_address6_list);
+ /* we don't use free_interface_address6_list on these lists because their
+ * address pointers are stack-based */
+ smartlist_free(mock_ipv4_addrs);
+ smartlist_free(mock_ipv6_addrs);
+}
+
+#undef TEST_IPV4_ADDR
+#undef TEST_IPV6_ADDR
+
static void
test_dump_exit_policy_to_string(void *arg)
{
@@ -578,10 +991,861 @@ test_dump_exit_policy_to_string(void *arg)
tor_free(ep);
}
+static routerinfo_t *mock_desc_routerinfo = NULL;
+static const routerinfo_t *
+mock_router_get_my_routerinfo(void)
+{
+ return mock_desc_routerinfo;
+}
+
+#define DEFAULT_POLICY_STRING "reject *:*"
+#define TEST_IPV4_ADDR (0x02040608)
+#define TEST_IPV6_ADDR ("2003::ef01")
+
+static or_options_t mock_options;
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mock_options;
+}
+
+/** Run unit tests for generating summary lines of exit policies */
+static void
+test_policies_getinfo_helper_policies(void *arg)
+{
+ (void)arg;
+ int rv = 0;
+ size_t ipv4_len = 0, ipv6_len = 0;
+ char *answer = NULL;
+ const char *errmsg = NULL;
+ routerinfo_t mock_my_routerinfo;
+
+ memset(&mock_my_routerinfo, 0, sizeof(mock_my_routerinfo));
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/default", &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/default",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ memset(&mock_my_routerinfo, 0, sizeof(routerinfo_t));
+ MOCK(router_get_my_routerinfo, mock_router_get_my_routerinfo);
+ mock_my_routerinfo.exit_policy = smartlist_new();
+ mock_desc_routerinfo = &mock_my_routerinfo;
+
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(get_options, mock_get_options);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) == 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv4_len = strlen(answer);
+ tt_assert(ipv4_len == 0 || ipv4_len == strlen(DEFAULT_POLICY_STRING));
+ tt_assert(ipv4_len == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv6", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv6_len = strlen(answer);
+ tt_assert(ipv6_len == 0 || ipv6_len == strlen(DEFAULT_POLICY_STRING));
+ tt_assert(ipv6_len == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/full", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ /* It's either empty or it's the default */
+ tt_assert(strlen(answer) == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
+ tor_free(answer);
+
+ mock_my_routerinfo.addr = TEST_IPV4_ADDR;
+ tor_addr_parse(&mock_my_routerinfo.ipv6_addr, TEST_IPV6_ADDR);
+ append_exit_policy_string(&mock_my_routerinfo.exit_policy, "accept *4:*");
+ append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*");
+
+ mock_options.IPv6Exit = 1;
+ mock_options.ExitPolicyRejectPrivate = 1;
+ tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR);
+ tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv4_len = strlen(answer);
+ tt_assert(ipv4_len > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv6", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv6_len = strlen(answer);
+ tt_assert(ipv6_len > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/full", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tt_assert(strlen(answer) == ipv4_len + ipv6_len + 1);
+ tor_free(answer);
+
+ done:
+ tor_free(answer);
+ UNMOCK(get_options);
+ UNMOCK(router_get_my_routerinfo);
+ addr_policy_list_free(mock_my_routerinfo.exit_policy);
+}
+
+#undef DEFAULT_POLICY_STRING
+#undef TEST_IPV4_ADDR
+#undef TEST_IPV6_ADDR
+
+#define TEST_IPV4_ADDR_STR "1.2.3.4"
+#define TEST_IPV6_ADDR_STR "[1002::4567]"
+#define REJECT_IPv4_FINAL_STR "reject 0.0.0.0/0:*"
+#define REJECT_IPv6_FINAL_STR "reject [::]/0:*"
+
+#define OTHER_IPV4_ADDR_STR "6.7.8.9"
+#define OTHER_IPV6_ADDR_STR "[afff::]"
+
+/** Run unit tests for fascist_firewall_allows_address */
+static void
+test_policies_fascist_firewall_allows_address(void *arg)
+{
+ (void)arg;
+ tor_addr_t ipv4_addr, ipv6_addr, r_ipv4_addr, r_ipv6_addr;
+ tor_addr_t n_ipv4_addr, n_ipv6_addr;
+ const uint16_t port = 1234;
+ smartlist_t *policy = NULL;
+ smartlist_t *e_policy = NULL;
+ addr_policy_t *item = NULL;
+ int malformed_list = 0;
+
+ /* Setup the options and the items in the policies */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(get_options, mock_get_options);
+
+ policy = smartlist_new();
+ item = router_parse_addr_policy_item_from_string("accept "
+ TEST_IPV4_ADDR_STR ":*",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(item);
+ tt_assert(!malformed_list);
+ smartlist_add(policy, item);
+ item = router_parse_addr_policy_item_from_string("accept "
+ TEST_IPV6_ADDR_STR,
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(item);
+ tt_assert(!malformed_list);
+ smartlist_add(policy, item);
+ /* Normally, policy_expand_unspec would do this for us */
+ item = router_parse_addr_policy_item_from_string(REJECT_IPv4_FINAL_STR,
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(item);
+ tt_assert(!malformed_list);
+ smartlist_add(policy, item);
+ item = router_parse_addr_policy_item_from_string(REJECT_IPv6_FINAL_STR,
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(item);
+ tt_assert(!malformed_list);
+ smartlist_add(policy, item);
+ item = NULL;
+
+ e_policy = smartlist_new();
+
+ /*
+ char *polstr = policy_dump_to_string(policy, 1, 1);
+ printf("%s\n", polstr);
+ tor_free(polstr);
+ */
+
+ /* Parse the addresses */
+ tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR_STR);
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR_STR);
+ tor_addr_parse(&r_ipv4_addr, OTHER_IPV4_ADDR_STR);
+ tor_addr_parse(&r_ipv6_addr, OTHER_IPV6_ADDR_STR);
+ tor_addr_make_null(&n_ipv4_addr, AF_INET);
+ tor_addr_make_null(&n_ipv6_addr, AF_INET6);
+
+ /* Test the function's address matching with IPv4 and IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 0;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Preferring IPv4 */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 0)
+ == 0);
+
+ /* Preferring IPv6 */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 1)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 1)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 1)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 1)
+ == 0);
+
+ /* Test the function's address matching with UseBridges on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 1;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Preferring IPv4 */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 0)
+ == 0);
+
+ /* Preferring IPv6 */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 1)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 1)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 1)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 1)
+ == 0);
+
+ /* bridge clients always use IPv6, regardless of ClientUseIPv6 */
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 0;
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Test the function's address matching with IPv4 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 0;
+ mock_options.UseBridges = 0;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Test the function's address matching with IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 0;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Test the function's address matching with ClientUseIPv4 0.
+ * This means "use IPv6" regardless of the other settings. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 0;
+ mock_options.UseBridges = 0;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Test the function's address matching for unusual inputs */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 1;
+
+ /* NULL and tor_addr_is_null addresses are rejected */
+ tt_assert(fascist_firewall_allows_address(NULL, port, policy, 0, 0) == 0);
+ tt_assert(fascist_firewall_allows_address(&n_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&n_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* zero ports are rejected */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, 0, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, 0, policy, 0, 0)
+ == 0);
+
+ /* NULL and empty policies accept everything */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, NULL, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, NULL, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, e_policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, e_policy, 0, 0)
+ == 1);
+
+ done:
+ addr_policy_free(item);
+ addr_policy_list_free(policy);
+ addr_policy_list_free(e_policy);
+ UNMOCK(get_options);
+}
+
+#undef REJECT_IPv4_FINAL_STR
+#undef REJECT_IPv6_FINAL_STR
+#undef OTHER_IPV4_ADDR_STR
+#undef OTHER_IPV6_ADDR_STR
+
+#define TEST_IPV4_OR_PORT 1234
+#define TEST_IPV4_DIR_PORT 2345
+#define TEST_IPV6_OR_PORT 61234
+#define TEST_IPV6_DIR_PORT 62345
+
+/* Check that fascist_firewall_choose_address_rs() returns the expected
+ * results. */
+#define CHECK_CHOSEN_ADDR_RS(fake_rs, fw_connection, pref_only, expect_rv, \
+ expect_ap) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_rs_ap; \
+ tor_addr_make_null(&chosen_rs_ap.addr, AF_INET); \
+ chosen_rs_ap.port = 0; \
+ tt_int_op(fascist_firewall_choose_address_rs(&(fake_rs), \
+ (fw_connection), \
+ (pref_only), \
+ &chosen_rs_ap), \
+ OP_EQ, (expect_rv)); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_rs_ap.addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, chosen_rs_ap.port); \
+ STMT_END
+
+/* Check that fascist_firewall_choose_address_node() returns the expected
+ * results. */
+#define CHECK_CHOSEN_ADDR_NODE(fake_node, fw_connection, pref_only, \
+ expect_rv, expect_ap) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_node_ap; \
+ tor_addr_make_null(&chosen_node_ap.addr, AF_INET); \
+ chosen_node_ap.port = 0; \
+ tt_int_op(fascist_firewall_choose_address_node(&(fake_node), \
+ (fw_connection), \
+ (pref_only), \
+ &chosen_node_ap), \
+ OP_EQ, (expect_rv)); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_node_ap.addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, chosen_node_ap.port); \
+ STMT_END
+
+/* Check that fascist_firewall_choose_address_rs and
+ * fascist_firewall_choose_address_node() both return the expected results. */
+#define CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, fw_connection, pref_only, \
+ expect_rv, expect_ap) \
+ STMT_BEGIN \
+ CHECK_CHOSEN_ADDR_RS(fake_rs, fw_connection, pref_only, expect_rv, \
+ expect_ap); \
+ CHECK_CHOSEN_ADDR_NODE(fake_node, fw_connection, pref_only, expect_rv, \
+ expect_ap); \
+ STMT_END
+
+/** Run unit tests for fascist_firewall_choose_address */
+static void
+test_policies_fascist_firewall_choose_address(void *arg)
+{
+ (void)arg;
+ tor_addr_port_t ipv4_or_ap, ipv4_dir_ap, ipv6_or_ap, ipv6_dir_ap;
+ tor_addr_port_t n_ipv4_ap, n_ipv6_ap;
+
+ /* Setup the options */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(get_options, mock_get_options);
+
+ /* Parse the addresses */
+ tor_addr_parse(&ipv4_or_ap.addr, TEST_IPV4_ADDR_STR);
+ ipv4_or_ap.port = TEST_IPV4_OR_PORT;
+ tor_addr_parse(&ipv4_dir_ap.addr, TEST_IPV4_ADDR_STR);
+ ipv4_dir_ap.port = TEST_IPV4_DIR_PORT;
+
+ tor_addr_parse(&ipv6_or_ap.addr, TEST_IPV6_ADDR_STR);
+ ipv6_or_ap.port = TEST_IPV6_OR_PORT;
+ tor_addr_parse(&ipv6_dir_ap.addr, TEST_IPV6_ADDR_STR);
+ ipv6_dir_ap.port = TEST_IPV6_DIR_PORT;
+
+ tor_addr_make_null(&n_ipv4_ap.addr, AF_INET);
+ n_ipv4_ap.port = 0;
+ tor_addr_make_null(&n_ipv6_ap.addr, AF_INET6);
+ n_ipv6_ap.port = 0;
+
+ /* Sanity check fascist_firewall_choose_address with IPv4 and IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 0;
+
+ /* Prefer IPv4 */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 0, 0)
+ == &ipv4_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 1, 0)
+ == &ipv4_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 1,
+ FIREWALL_DIR_CONNECTION, 0, 0)
+ == &ipv4_dir_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 1,
+ FIREWALL_DIR_CONNECTION, 1, 0)
+ == &ipv4_dir_ap);
+
+ /* Prefer IPv6 */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+ FIREWALL_OR_CONNECTION, 0, 1)
+ == &ipv6_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+ FIREWALL_OR_CONNECTION, 1, 1)
+ == &ipv6_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+ FIREWALL_DIR_CONNECTION, 0, 1)
+ == &ipv6_dir_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+ FIREWALL_DIR_CONNECTION, 1, 1)
+ == &ipv6_dir_ap);
+
+ /* Unusual inputs */
+
+ /* null preferred OR addresses */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &n_ipv6_ap, 0,
+ FIREWALL_OR_CONNECTION, 0, 1)
+ == &ipv4_or_ap);
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 0, 0)
+ == &ipv6_or_ap);
+
+ /* null both OR addresses */
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0,
+ FIREWALL_OR_CONNECTION, 0, 1)
+ == NULL);
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1,
+ FIREWALL_OR_CONNECTION, 0, 0)
+ == NULL);
+
+ /* null preferred Dir addresses */
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &n_ipv6_ap, 0,
+ FIREWALL_DIR_CONNECTION, 0, 1)
+ == &ipv4_dir_ap);
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &ipv6_dir_ap, 1,
+ FIREWALL_DIR_CONNECTION, 0, 0)
+ == &ipv6_dir_ap);
+
+ /* null both Dir addresses */
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0,
+ FIREWALL_DIR_CONNECTION, 0, 1)
+ == NULL);
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1,
+ FIREWALL_DIR_CONNECTION, 0, 0)
+ == NULL);
+
+ /* Prefer IPv4 but want IPv6 (contradictory) */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+ FIREWALL_OR_CONNECTION, 0, 0)
+ == &ipv4_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+ FIREWALL_OR_CONNECTION, 1, 0)
+ == &ipv4_or_ap);
+
+ /* Prefer IPv6 but want IPv4 (contradictory) */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 0, 1)
+ == &ipv6_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 1, 1)
+ == &ipv6_or_ap);
+
+ /* Make a fake rs. There will be no corresponding node.
+ * This is what happens when there's no consensus and we're bootstrapping
+ * from authorities / fallbacks. */
+ routerstatus_t fake_rs;
+ memset(&fake_rs, 0, sizeof(routerstatus_t));
+ /* In a routerstatus, the OR and Dir addresses are the same */
+ fake_rs.addr = tor_addr_to_ipv4h(&ipv4_or_ap.addr);
+ fake_rs.or_port = ipv4_or_ap.port;
+ fake_rs.dir_port = ipv4_dir_ap.port;
+
+ tor_addr_copy(&fake_rs.ipv6_addr, &ipv6_or_ap.addr);
+ fake_rs.ipv6_orport = ipv6_or_ap.port;
+ /* In a routerstatus, the IPv4 and IPv6 DirPorts are the same.*/
+ ipv6_dir_ap.port = TEST_IPV4_DIR_PORT;
+
+ /* Make a fake node. Even though it contains the fake_rs, a lookup won't
+ * find the node from the rs, because they're not in the hash table. */
+ node_t fake_node;
+ memset(&fake_node, 0, sizeof(node_t));
+ fake_node.rs = &fake_rs;
+
+ /* Choose an address with IPv4 and IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 0;
+
+ /* Preferring IPv4 */
+ mock_options.ClientPreferIPv6ORPort = 0;
+ mock_options.ClientPreferIPv6DirPort = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Auto (Preferring IPv4) */
+ mock_options.ClientPreferIPv6ORPort = -1;
+ mock_options.ClientPreferIPv6DirPort = -1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Preferring IPv6 */
+ mock_options.ClientPreferIPv6ORPort = 1;
+ mock_options.ClientPreferIPv6DirPort = 1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* Preferring IPv4 OR / IPv6 Dir */
+ mock_options.ClientPreferIPv6ORPort = 0;
+ mock_options.ClientPreferIPv6DirPort = 1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* Preferring IPv6 OR / IPv4 Dir */
+ mock_options.ClientPreferIPv6ORPort = 1;
+ mock_options.ClientPreferIPv6DirPort = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Choose an address with UseBridges on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.UseBridges = 1;
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+
+ /* Preferring IPv4 */
+ mock_options.ClientPreferIPv6ORPort = 0;
+ mock_options.ClientPreferIPv6DirPort = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Auto:
+ * - bridge clients prefer the configured bridge OR address from the node,
+ * (the configured address family sets node.ipv6_preferred)
+ * - other clients prefer IPv4 OR by default (see above),
+ * - all clients, including bridge clients, prefer IPv4 Dir by default.
+ */
+ mock_options.ClientPreferIPv6ORPort = -1;
+ mock_options.ClientPreferIPv6DirPort = -1;
+
+ /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge
+ * configured with an IPv4 address */
+ fake_node.ipv6_preferred = 0;
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge
+ * configured with an IPv6 address */
+ fake_node.ipv6_preferred = 1;
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* When a rs has no node, it defaults to IPv4 under auto. */
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 0, 1, ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 1, 1, ipv4_dir_ap);
+
+ /* Preferring IPv6 */
+ mock_options.ClientPreferIPv6ORPort = 1;
+ mock_options.ClientPreferIPv6DirPort = 1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* In the default configuration (Auto / IPv6 off), bridge clients should
+ * use both IPv4 and IPv6, but only prefer IPv6 for bridges configured with
+ * an IPv6 address, regardless of ClientUseIPv6. (See above.) */
+ mock_options.ClientUseIPv6 = 0;
+ mock_options.ClientPreferIPv6ORPort = -1;
+ mock_options.ClientPreferIPv6DirPort = -1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge
+ * configured with an IPv4 address */
+ fake_node.ipv6_preferred = 0;
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge
+ * configured with an IPv6 address */
+ fake_node.ipv6_preferred = 1;
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* When a rs has no node, it defaults to IPv4 under auto. */
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 0, 1, ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 1, 1, ipv4_dir_ap);
+
+ /* Choose an address with IPv4 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Choose an address with IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* Choose an address with ClientUseIPv4 0.
+ * This means "use IPv6" regardless of the other settings. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* Choose an address with ORPort_set 1 (server mode).
+ * This means "use IPv4" regardless of the other settings. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ORPort_set = 1;
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.ClientPreferIPv6ORPort = 1;
+ mock_options.ClientPreferIPv6DirPort = 1;
+
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ done:
+ UNMOCK(get_options);
+}
+
+#undef TEST_IPV4_ADDR_STR
+#undef TEST_IPV6_ADDR_STR
+#undef TEST_IPV4_OR_PORT
+#undef TEST_IPV4_DIR_PORT
+#undef TEST_IPV6_OR_PORT
+#undef TEST_IPV6_DIR_PORT
+
+#undef CHECK_CHOSEN_ADDR_RS
+#undef CHECK_CHOSEN_ADDR_NODE
+#undef CHECK_CHOSEN_ADDR_RN
+
struct testcase_t policy_tests[] = {
{ "router_dump_exit_policy_to_string", test_dump_exit_policy_to_string, 0,
NULL, NULL },
{ "general", test_policies_general, 0, NULL, NULL },
+ { "getinfo_helper_policies", test_policies_getinfo_helper_policies, 0, NULL,
+ NULL },
+ { "reject_exit_address", test_policies_reject_exit_address, 0, NULL, NULL },
+ { "reject_interface_address", test_policies_reject_interface_address, 0,
+ NULL, NULL },
+ { "reject_port_address", test_policies_reject_port_address, 0, NULL, NULL },
+ { "fascist_firewall_allows_address",
+ test_policies_fascist_firewall_allows_address, 0, NULL, NULL },
+ { "fascist_firewall_choose_address",
+ test_policies_fascist_firewall_choose_address, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_procmon.c b/src/test/test_procmon.c
new file mode 100644
index 0000000000..2855178788
--- /dev/null
+++ b/src/test/test_procmon.c
@@ -0,0 +1,58 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define PROCMON_PRIVATE
+#include "orconfig.h"
+#include "or.h"
+#include "test.h"
+
+#include "procmon.h"
+
+#include "log_test_helpers.h"
+
+#define NS_MODULE procmon
+
+struct event_base;
+
+static void
+test_procmon_tor_process_monitor_new(void *ignored)
+{
+ (void)ignored;
+ tor_process_monitor_t *res;
+ const char *msg;
+
+ res = tor_process_monitor_new(NULL, "probably invalid", 0, NULL, NULL, &msg);
+ tt_assert(!res);
+ tt_str_op(msg, OP_EQ, "invalid PID");
+
+ res = tor_process_monitor_new(NULL, "243443535345454", 0, NULL, NULL, &msg);
+ tt_assert(!res);
+ tt_str_op(msg, OP_EQ, "invalid PID");
+
+ res = tor_process_monitor_new(tor_libevent_get_base(), "43", 0,
+ NULL, NULL, &msg);
+ tt_assert(res);
+ tt_assert(!msg);
+ tor_process_monitor_free(res);
+
+ res = tor_process_monitor_new(tor_libevent_get_base(), "44 hello", 0,
+ NULL, NULL, &msg);
+ tt_assert(res);
+ tt_assert(!msg);
+ tor_process_monitor_free(res);
+
+ res = tor_process_monitor_new(tor_libevent_get_base(), "45:hello", 0,
+ NULL, NULL, &msg);
+ tt_assert(res);
+ tt_assert(!msg);
+
+ done:
+ tor_process_monitor_free(res);
+}
+
+struct testcase_t procmon_tests[] = {
+ { "tor_process_monitor_new", test_procmon_tor_process_monitor_new,
+ TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c
new file mode 100644
index 0000000000..7e04799db2
--- /dev/null
+++ b/src/test/test_rendcache.c
@@ -0,0 +1,1340 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+#define RENDCACHE_PRIVATE
+#include "rendcache.h"
+#include "router.h"
+#include "routerlist.h"
+#include "config.h"
+#include <openssl/rsa.h>
+#include "rend_test_helpers.h"
+
+#define NS_MODULE rend_cache
+
+static const int RECENT_TIME = -10;
+static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + \
+ REND_CACHE_MAX_SKEW + 10);
+static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 10;
+
+extern strmap_t *rend_cache;
+extern digestmap_t *rend_cache_v2_dir;
+extern strmap_t *rend_cache_failure;
+extern size_t rend_cache_total_allocation;
+
+static rend_data_t *
+mock_rend_data(const char *onion_address)
+{
+ rend_data_t *rend_query = tor_malloc_zero(sizeof(rend_data_t));
+
+ strlcpy(rend_query->onion_address, onion_address,
+ sizeof(rend_query->onion_address));
+ rend_query->auth_type = REND_NO_AUTH;
+ rend_query->hsdirs_fp = smartlist_new();
+ smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa",
+ DIGEST_LEN));
+
+ return rend_query;
+}
+
+static void
+test_rend_cache_lookup_entry(void *data)
+{
+ int ret;
+ rend_data_t *mock_rend_query = NULL;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_cache_entry_t *entry = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ (void)data;
+
+ rend_cache_init();
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+ ret = rend_cache_lookup_entry("abababababababab", 0, NULL);
+ tt_int_op(ret, OP_EQ, -ENOENT);
+
+ ret = rend_cache_lookup_entry("invalid query", 2, NULL);
+ tt_int_op(ret, OP_EQ, -EINVAL);
+
+ ret = rend_cache_lookup_entry("abababababababab", 2, NULL);
+ tt_int_op(ret, OP_EQ, -ENOENT);
+
+ ret = rend_cache_lookup_entry("abababababababab", 4224, NULL);
+ tt_int_op(ret, OP_EQ, -ENOENT);
+
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32,
+ mock_rend_query, NULL);
+
+ ret = rend_cache_lookup_entry(service_id, 2, NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = rend_cache_lookup_entry(service_id, 2, &entry);
+ tt_assert(entry);
+ tt_int_op(entry->len, OP_EQ, strlen(desc_holder->desc_str));
+ tt_str_op(entry->desc, OP_EQ, desc_holder->desc_str);
+
+ done:
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_client(void *data)
+{
+ rend_cache_store_status_t ret;
+ rend_data_t *mock_rend_query;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_cache_entry_t *entry = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ char client_cookie[REND_DESC_COOKIE_LEN];
+ (void)data;
+
+ rend_cache_init();
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+ // Test success
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ &entry);
+
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+ tt_assert(entry);
+ tt_int_op(entry->len, OP_EQ, strlen(desc_holder->desc_str));
+ tt_str_op(entry->desc, OP_EQ, desc_holder->desc_str);
+
+ // Test various failure modes
+
+ // TODO: a too long desc_id_base32 argument crashes the function
+ /* ret = rend_cache_store_v2_desc_as_client( */
+ /* desc_holder->desc_str, */
+ /* "3TOOLONG3TOOLONG3TOOLONG3TOOLONG3TOOLONG3TOOLONG", */
+ /* &mock_rend_query, NULL); */
+ /* tt_int_op(ret, OP_EQ, RCS_BADDESC); */
+
+ // Test bad base32 failure
+ // This causes an assertion failure if we're running with assertions.
+ // But when building without asserts, we can test it.
+#ifdef DISABLE_ASSERTS_IN_UNIT_TESTS
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ "!xqunszqnaolrrfmtzgaki7mxelgvkj", mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+#endif
+
+ // Test invalid descriptor
+ ret = rend_cache_store_v2_desc_as_client("invalid descriptor",
+ "3xqunszqnaolrrfmtzgaki7mxelgvkje", mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ // TODO: it doesn't seem to be possible to test invalid service ID condition.
+ // that means it is likely not possible to have that condition without
+ // earlier conditions failing first (such as signature checking of the desc)
+
+ rend_cache_free_all();
+
+ // Test mismatch between service ID and onion address
+ rend_cache_init();
+ strncpy(mock_rend_query->onion_address, "abc", REND_SERVICE_ID_LEN_BASE32+1);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32,
+ mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+
+ // Test incorrect descriptor ID
+ rend_cache_init();
+ mock_rend_query = mock_rend_data(service_id);
+ desc_id_base32[0]++;
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ desc_id_base32[0]--;
+ rend_cache_free_all();
+
+ // Test too old descriptor
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(TIME_IN_THE_PAST, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32,
+ mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+
+ // Test too new descriptor (in the future)
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+
+ // Test when a descriptor is already in the cache
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32,
+ mock_rend_query, NULL);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ &entry);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+ tt_assert(entry);
+ rend_cache_free_all();
+
+ // Test unsuccessful decrypting of introduction points
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ mock_rend_query->auth_type = REND_BASIC_AUTH;
+ client_cookie[0] = 'A';
+ memcpy(mock_rend_query->descriptor_cookie, client_cookie,
+ REND_DESC_COOKIE_LEN);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+ rend_cache_free_all();
+
+ // Test successful run when we have REND_BASIC_AUTH but not cookie
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ mock_rend_query->auth_type = REND_BASIC_AUTH;
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_cache_free_all();
+
+ // Test when we have no introduction points
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 0);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+
+ // Test when we have too many intro points
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, MAX_INTRO_POINTS+1);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ done:
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_client_with_different_time(void *data)
+{
+ rend_cache_store_status_t ret;
+ rend_data_t *mock_rend_query;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t t;
+ char *service_id = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_newer;
+ rend_encoded_v2_service_descriptor_t *desc_holder_older;
+
+ t = time(NULL);
+ rend_cache_init();
+
+ create_descriptor(&generated, &service_id, 3);
+
+ generated->timestamp = t + RECENT_TIME;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_newer = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ descs = smartlist_new();
+
+ generated->timestamp = (t + RECENT_TIME) - 20;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_older = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+ (void)data;
+
+ // Test when a descriptor is already in the cache and it is newer than the
+ // one we submit
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32),
+ desc_holder_newer->desc_id, DIGEST_LEN);
+ rend_cache_store_v2_desc_as_client(desc_holder_newer->desc_str,
+ desc_id_base32, mock_rend_query, NULL);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder_older->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_cache_free_all();
+
+ // Test when an old descriptor is in the cache and we submit a newer one
+ rend_cache_init();
+ rend_cache_store_v2_desc_as_client(desc_holder_older->desc_str,
+ desc_id_base32, mock_rend_query, NULL);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder_newer->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ rend_encoded_v2_service_descriptor_free(desc_holder_newer);
+ rend_encoded_v2_service_descriptor_free(desc_holder_older);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_service_descriptor_free(generated);
+ tor_free(service_id);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+}
+
+#define NS_SUBMODULE lookup_v2_desc_as_dir
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static routerinfo_t *mock_routerinfo;
+static int hid_serv_responsible_for_desc_id_response;
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+ if (!mock_routerinfo) {
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ }
+
+ return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_lookup_v2_desc_as_dir(void *data)
+{
+ int ret;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ const char *ret_desc = NULL;
+
+ (void)data;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ // Test invalid base32
+ ret = rend_cache_lookup_v2_desc_as_dir("!bababababababab", NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test non-existent descriptor but well formed
+ ret = rend_cache_lookup_v2_desc_as_dir("3xqunszqnaolrrfmtzgaki7mxelgvkje",
+ NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test existing descriptor
+ hid_serv_responsible_for_desc_id_response = 1;
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_lookup_v2_desc_as_dir(desc_id_base32, &ret_desc);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_assert(ret_desc);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ tor_free(mock_routerinfo);
+ rend_cache_free_all();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE store_v2_desc_as_dir
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+ return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir(void *data)
+{
+ (void)data;
+ rend_cache_store_status_t ret;
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ // Test when we are not an HS dir
+ mock_routerinfo = NULL;
+ ret = rend_cache_store_v2_desc_as_dir("");
+ tt_int_op(ret, OP_EQ, RCS_NOTDIR);
+
+ // Test when we can't parse the descriptor
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ hid_serv_responsible_for_desc_id_response = 1;
+ ret = rend_cache_store_v2_desc_as_dir("unparseable");
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ // Test when we are not responsible for an HS
+ hid_serv_responsible_for_desc_id_response = 0;
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when we have an old descriptor
+ hid_serv_responsible_for_desc_id_response = 1;
+ generate_desc(TIME_IN_THE_PAST, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when we have a descriptor in the future
+ generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when two descriptors
+ generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when asking for hidden service statistics HiddenServiceStatistics
+ rend_cache_purge();
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ get_options_mutable()->HiddenServiceStatistics = 1;
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+ tor_free(mock_routerinfo);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir_with_different_time(void *data)
+{
+ (void)data;
+
+ rend_cache_store_status_t ret;
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t t;
+ char *service_id = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_newer;
+ rend_encoded_v2_service_descriptor_t *desc_holder_older;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ t = time(NULL);
+
+ create_descriptor(&generated, &service_id, 3);
+ generated->timestamp = t + RECENT_TIME;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_newer = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ descs = smartlist_new();
+
+ generated->timestamp = (t + RECENT_TIME) - 20;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_older = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ // Test when we have a newer descriptor stored
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_store_v2_desc_as_dir(desc_holder_newer->desc_str);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder_older->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ // Test when we have an old descriptor stored
+ rend_cache_purge();
+ rend_cache_store_v2_desc_as_dir(desc_holder_older->desc_str);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder_newer->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_free_all();
+ rend_service_descriptor_free(generated);
+ tor_free(service_id);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_encoded_v2_service_descriptor_free(desc_holder_newer);
+ rend_encoded_v2_service_descriptor_free(desc_holder_older);
+ tor_free(mock_routerinfo);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir_with_different_content(void *data)
+{
+ (void)data;
+
+ rend_cache_store_status_t ret;
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t t;
+ char *service_id = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_one = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_two = NULL;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ t = time(NULL);
+
+ create_descriptor(&generated, &service_id, 3);
+ generated->timestamp = t + RECENT_TIME;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_one = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ descs = smartlist_new();
+
+ generated->timestamp = t + RECENT_TIME;
+ generated->protocols = 41;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_two = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ // Test when we have another descriptor stored, with a different descriptor
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_store_v2_desc_as_dir(desc_holder_one->desc_str);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder_two->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_free_all();
+ rend_service_descriptor_free(generated);
+ tor_free(service_id);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_encoded_v2_service_descriptor_free(desc_holder_one);
+ rend_encoded_v2_service_descriptor_free(desc_holder_two);
+}
+
+#undef NS_SUBMODULE
+
+static void
+test_rend_cache_init(void *data)
+{
+ (void)data;
+
+ tt_assert_msg(!rend_cache, "rend_cache should be NULL when starting");
+ tt_assert_msg(!rend_cache_v2_dir, "rend_cache_v2_dir should be NULL "
+ "when starting");
+ tt_assert_msg(!rend_cache_failure, "rend_cache_failure should be NULL when "
+ "starting");
+
+ rend_cache_init();
+
+ tt_assert_msg(rend_cache, "rend_cache should not be NULL after initing");
+ tt_assert_msg(rend_cache_v2_dir, "rend_cache_v2_dir should not be NULL "
+ "after initing");
+ tt_assert_msg(rend_cache_failure, "rend_cache_failure should not be NULL "
+ "after initing");
+
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_decrement_allocation(void *data)
+{
+ (void)data;
+
+ // Test when the cache has enough allocations
+ rend_cache_total_allocation = 10;
+ rend_cache_decrement_allocation(3);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 7);
+
+ // Test when there are not enough allocations
+ rend_cache_total_allocation = 1;
+ rend_cache_decrement_allocation(2);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
+
+ // And again
+ rend_cache_decrement_allocation(2);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
+
+ done:
+ (void)0;
+}
+
+static void
+test_rend_cache_increment_allocation(void *data)
+{
+ (void)data;
+
+ // Test when the cache is not overflowing
+ rend_cache_total_allocation = 5;
+ rend_cache_increment_allocation(3);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 8);
+
+ // Test when there are too many allocations
+ rend_cache_total_allocation = SIZE_MAX-1;
+ rend_cache_increment_allocation(2);
+ tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
+
+ // And again
+ rend_cache_increment_allocation(2);
+ tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
+
+ done:
+ (void)0;
+}
+
+static void
+test_rend_cache_failure_intro_entry_new(void *data)
+{
+ time_t now;
+ rend_cache_failure_intro_t *entry;
+ rend_intro_point_failure_t failure;
+
+ (void)data;
+
+ failure = INTRO_POINT_FAILURE_TIMEOUT;
+ now = time(NULL);
+ entry = rend_cache_failure_intro_entry_new(failure);
+
+ tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_TIMEOUT);
+ tt_int_op(entry->created_ts, OP_GE, now-5);
+ tt_int_op(entry->created_ts, OP_LE, now+5);
+
+ done:
+ tor_free(entry);
+}
+
+static void
+test_rend_cache_failure_intro_lookup(void *data)
+{
+ (void)data;
+ int ret;
+ rend_cache_failure_t *failure;
+ rend_cache_failure_intro_t *ip;
+ rend_cache_failure_intro_t *entry;
+ const char key_ip_one[DIGEST_LEN] = "ip1";
+ const char key_ip_two[DIGEST_LEN] = "ip2";
+ const char key_foo[DIGEST_LEN] = "foo1";
+
+ rend_cache_init();
+
+ failure = rend_cache_failure_entry_new();
+ ip = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ digestmap_set(failure->intro_failures, key_ip_one, ip);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+
+ // Test not found
+ ret = cache_failure_intro_lookup((const uint8_t *) key_foo, "foo2", NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test found with no intro failures in it
+ ret = cache_failure_intro_lookup((const uint8_t *) key_ip_two, "foo1", NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test found
+ ret = cache_failure_intro_lookup((const uint8_t *) key_ip_one, "foo1", NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ // Test found and asking for entry
+ cache_failure_intro_lookup((const uint8_t *) key_ip_one, "foo1", &entry);
+ tt_assert(entry);
+ tt_assert(entry == ip);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_clean(void *data)
+{
+ rend_cache_entry_t *one, *two;
+ rend_service_descriptor_t *desc_one, *desc_two;
+ strmap_iter_t *iter = NULL;
+ const char *key;
+ void *val;
+
+ (void)data;
+
+ rend_cache_init();
+
+ // Test with empty rendcache
+ rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+ // Test with two old entries
+ one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ two = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc_two = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ one->parsed = desc_one;
+ two->parsed = desc_two;
+
+ desc_one->timestamp = time(NULL) + TIME_IN_THE_PAST;
+ desc_two->timestamp = (time(NULL) + TIME_IN_THE_PAST) - 10;
+ desc_one->pk = pk_generate(0);
+ desc_two->pk = pk_generate(1);
+
+ strmap_set_lc(rend_cache, "foo1", one);
+ strmap_set_lc(rend_cache, "foo2", two);
+
+ rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+ // Test with one old entry and one newer entry
+ one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ two = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc_two = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ one->parsed = desc_one;
+ two->parsed = desc_two;
+
+ desc_one->timestamp = (time(NULL) + TIME_IN_THE_PAST) - 10;
+ desc_two->timestamp = time(NULL) - 100;
+ desc_one->pk = pk_generate(0);
+ desc_two->pk = pk_generate(1);
+
+ strmap_set_lc(rend_cache, "foo1", one);
+ strmap_set_lc(rend_cache, "foo2", two);
+
+ rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 1);
+
+ iter = strmap_iter_init(rend_cache);
+ strmap_iter_get(iter, &key, &val);
+ tt_str_op(key, OP_EQ, "foo2");
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_failure_entry_new(void *data)
+{
+ rend_cache_failure_t *failure;
+
+ (void)data;
+
+ failure = rend_cache_failure_entry_new();
+ tt_assert(failure);
+ tt_int_op(digestmap_size(failure->intro_failures), OP_EQ, 0);
+
+ done:
+ rend_cache_failure_entry_free(failure);
+}
+
+static void
+test_rend_cache_failure_entry_free(void *data)
+{
+ (void)data;
+
+ // Test that it can deal with a NULL argument
+ rend_cache_failure_entry_free(NULL);
+
+ /* done: */
+ /* (void)0; */
+}
+
+static void
+test_rend_cache_failure_clean(void *data)
+{
+ rend_cache_failure_t *failure;
+ rend_cache_failure_intro_t *ip_one, *ip_two;
+
+ const char key_one[DIGEST_LEN] = "ip1";
+ const char key_two[DIGEST_LEN] = "ip2";
+
+ (void)data;
+
+ rend_cache_init();
+
+ // Test with empty failure cache
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ // Test with one empty failure entry
+ failure = rend_cache_failure_entry_new();
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ // Test with one new intro point
+ failure = rend_cache_failure_entry_new();
+ ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ digestmap_set(failure->intro_failures, key_one, ip_one);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 1);
+
+ // Test with one old intro point
+ rend_cache_failure_purge();
+ failure = rend_cache_failure_entry_new();
+ ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ ip_one->created_ts = time(NULL) - 7*60;
+ digestmap_set(failure->intro_failures, key_one, ip_one);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ // Test with one old intro point and one new one
+ rend_cache_failure_purge();
+ failure = rend_cache_failure_entry_new();
+ ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ ip_one->created_ts = time(NULL) - 7*60;
+ digestmap_set(failure->intro_failures, key_one, ip_one);
+ ip_two = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ ip_two->created_ts = time(NULL) - 2*60;
+ digestmap_set(failure->intro_failures, key_two, ip_two);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 1);
+ tt_int_op(digestmap_size(failure->intro_failures), OP_EQ, 1);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_failure_remove(void *data)
+{
+ rend_service_descriptor_t *desc;
+ (void)data;
+
+ rend_cache_init();
+
+ // Test that it deals well with a NULL desc
+ rend_cache_failure_remove(NULL);
+
+ // Test a descriptor that isn't in the cache
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->pk = pk_generate(0);
+ rend_cache_failure_remove(desc);
+
+ // There seems to not exist any way of getting rend_cache_failure_remove()
+ // to fail because of a problem with rend_get_service_id from here
+ rend_cache_free_all();
+
+ rend_service_descriptor_free(desc);
+ /* done: */
+ /* (void)0; */
+}
+
+static void
+test_rend_cache_free_all(void *data)
+{
+ rend_cache_failure_t *failure;
+ rend_cache_entry_t *one;
+ rend_service_descriptor_t *desc_one;
+
+ (void)data;
+
+ rend_cache_init();
+
+ failure = rend_cache_failure_entry_new();
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+
+ one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ one->parsed = desc_one;
+ desc_one->timestamp = time(NULL) + TIME_IN_THE_PAST;
+ desc_one->pk = pk_generate(0);
+ strmap_set_lc(rend_cache, "foo1", one);
+
+ rend_cache_free_all();
+
+ tt_assert(!rend_cache);
+ tt_assert(!rend_cache_v2_dir);
+ tt_assert(!rend_cache_failure);
+ tt_assert(!rend_cache_total_allocation);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_entry_free(void *data)
+{
+ (void)data;
+ rend_cache_entry_t *e;
+
+ // Handles NULL correctly
+ rend_cache_entry_free(NULL);
+
+ // Handles NULL descriptor correctly
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ rend_cache_entry_free(e);
+
+ // Handles non-NULL descriptor correctly
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->desc = (char *)malloc(10);
+ rend_cache_entry_free(e);
+
+ /* done: */
+ /* (void)0; */
+}
+
+static void
+test_rend_cache_purge(void *data)
+{
+ (void)data;
+
+ // Deals with a NULL rend_cache
+ rend_cache_purge();
+ tt_assert(rend_cache);
+ tt_assert(strmap_size(rend_cache) == 0);
+
+ // Deals with existing rend_cache
+ rend_cache_free_all();
+ rend_cache_init();
+ tt_assert(rend_cache);
+ tt_assert(strmap_size(rend_cache) == 0);
+
+ rend_cache_purge();
+ tt_assert(rend_cache);
+ tt_assert(strmap_size(rend_cache) == 0);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_failure_intro_add(void *data)
+{
+ (void)data;
+ rend_cache_failure_t *fail_entry;
+ rend_cache_failure_intro_t *entry;
+ const char identity[DIGEST_LEN] = "foo1";
+
+ rend_cache_init();
+
+ // Adds non-existing entry
+ cache_failure_intro_add((const uint8_t *) identity, "foo2",
+ INTRO_POINT_FAILURE_TIMEOUT);
+ fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+ tt_assert(fail_entry);
+ tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+ entry = digestmap_get(fail_entry->intro_failures, identity);
+ tt_assert(entry);
+
+ // Adds existing entry
+ cache_failure_intro_add((const uint8_t *) identity, "foo2",
+ INTRO_POINT_FAILURE_TIMEOUT);
+ fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+ tt_assert(fail_entry);
+ tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+ entry = digestmap_get(fail_entry->intro_failures, identity);
+ tt_assert(entry);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_intro_failure_note(void *data)
+{
+ (void)data;
+ rend_cache_failure_t *fail_entry;
+ rend_cache_failure_intro_t *entry;
+ const char key[DIGEST_LEN] = "foo1";
+
+ rend_cache_init();
+
+ // Test not found
+ rend_cache_intro_failure_note(INTRO_POINT_FAILURE_TIMEOUT,
+ (const uint8_t *) key, "foo2");
+ fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+ tt_assert(fail_entry);
+ tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+ entry = digestmap_get(fail_entry->intro_failures, key);
+ tt_assert(entry);
+ tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_TIMEOUT);
+
+ // Test found
+ rend_cache_intro_failure_note(INTRO_POINT_FAILURE_UNREACHABLE,
+ (const uint8_t *) key, "foo2");
+ tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_UNREACHABLE);
+
+ done:
+ rend_cache_free_all();
+}
+
+#define NS_SUBMODULE clean_v2_descs_as_dir
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_clean_v2_descs_as_dir(void *data)
+{
+ rend_cache_entry_t *e;
+ time_t now;
+ rend_service_descriptor_t *desc;
+ now = time(NULL);
+ const char key[DIGEST_LEN] = "abcde";
+
+ (void)data;
+
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_init();
+
+ // Test running with an empty cache
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test with only one new entry
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now;
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, key, e);
+
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
+
+ // Test with one old entry
+ desc->timestamp = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000);
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test with one entry that is not under the responsibility of this
+ // hidden service
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now;
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, key, e);
+
+ hid_serv_responsible_for_desc_id_response = 0;
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test with one entry that has an old last served
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000);
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, key, e);
+
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test a run through asking for a large force_remove
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now;
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, key, e);
+
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_clean_v2_descs_as_dir(now, 20000);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
+
+ done:
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_free_all();
+}
+
+#undef NS_SUBMODULE
+
+static void
+test_rend_cache_entry_allocation(void *data)
+{
+ (void)data;
+
+ size_t ret;
+ rend_cache_entry_t *e = NULL;
+
+ // Handles a null argument
+ ret = rend_cache_entry_allocation(NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Handles a non-null argument
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ ret = rend_cache_entry_allocation(e);
+ tt_int_op(ret, OP_GT, sizeof(rend_cache_entry_t));
+
+ done:
+ tor_free(e);
+}
+
+static void
+test_rend_cache_failure_intro_entry_free(void *data)
+{
+ (void)data;
+ rend_cache_failure_intro_t *entry;
+
+ // Handles a null argument
+ rend_cache_failure_intro_entry_free(NULL);
+
+ // Handles a non-null argument
+ entry = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ rend_cache_failure_intro_entry_free(entry);
+}
+
+static void
+test_rend_cache_failure_purge(void *data)
+{
+ (void)data;
+
+ // Handles a null failure cache
+ strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
+ rend_cache_failure = NULL;
+
+ rend_cache_failure_purge();
+
+ tt_ptr_op(rend_cache_failure, OP_NE, NULL);
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_validate_intro_point_failure(void *data)
+{
+ (void)data;
+ rend_service_descriptor_t *desc = NULL;
+ char *service_id = NULL;
+ rend_intro_point_t *intro = NULL;
+ const char *identity = NULL;
+ rend_cache_failure_t *failure;
+ rend_cache_failure_intro_t *ip;
+
+ rend_cache_init();
+
+ create_descriptor(&desc, &service_id, 3);
+ desc->timestamp = time(NULL) + RECENT_TIME;
+
+ intro = (rend_intro_point_t *)smartlist_get(desc->intro_nodes, 0);
+ identity = intro->extend_info->identity_digest;
+
+ failure = rend_cache_failure_entry_new();
+ ip = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ digestmap_set(failure->intro_failures, identity, ip);
+ strmap_set_lc(rend_cache_failure, service_id, failure);
+
+ // Test when we have an intro point in our cache
+ validate_intro_point_failure(desc, service_id);
+ tt_int_op(smartlist_len(desc->intro_nodes), OP_EQ, 2);
+
+ done:
+ rend_cache_free_all();
+ rend_service_descriptor_free(desc);
+ tor_free(service_id);
+}
+
+struct testcase_t rend_cache_tests[] = {
+ { "init", test_rend_cache_init, 0, NULL, NULL },
+ { "decrement_allocation", test_rend_cache_decrement_allocation, 0,
+ NULL, NULL },
+ { "increment_allocation", test_rend_cache_increment_allocation, 0,
+ NULL, NULL },
+ { "clean", test_rend_cache_clean, TT_FORK, NULL, NULL },
+ { "clean_v2_descs_as_dir", test_rend_cache_clean_v2_descs_as_dir, 0,
+ NULL, NULL },
+ { "entry_allocation", test_rend_cache_entry_allocation, 0, NULL, NULL },
+ { "entry_free", test_rend_cache_entry_free, 0, NULL, NULL },
+ { "failure_intro_entry_free", test_rend_cache_failure_intro_entry_free, 0,
+ NULL, NULL },
+ { "free_all", test_rend_cache_free_all, 0, NULL, NULL },
+ { "purge", test_rend_cache_purge, 0, NULL, NULL },
+ { "failure_clean", test_rend_cache_failure_clean, 0, NULL, NULL },
+ { "failure_entry_new", test_rend_cache_failure_entry_new, 0, NULL, NULL },
+ { "failure_entry_free", test_rend_cache_failure_entry_free, 0, NULL, NULL },
+ { "failure_intro_add", test_rend_cache_failure_intro_add, 0, NULL, NULL },
+ { "failure_intro_entry_new", test_rend_cache_failure_intro_entry_new, 0,
+ NULL, NULL },
+ { "failure_intro_lookup", test_rend_cache_failure_intro_lookup, 0,
+ NULL, NULL },
+ { "failure_purge", test_rend_cache_failure_purge, 0, NULL, NULL },
+ { "failure_remove", test_rend_cache_failure_remove, 0, NULL, NULL },
+ { "intro_failure_note", test_rend_cache_intro_failure_note, 0, NULL, NULL },
+ { "lookup", test_rend_cache_lookup_entry, 0, NULL, NULL },
+ { "lookup_v2_desc_as_dir", test_rend_cache_lookup_v2_desc_as_dir, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_client", test_rend_cache_store_v2_desc_as_client, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_client_with_different_time",
+ test_rend_cache_store_v2_desc_as_client_with_different_time, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_dir", test_rend_cache_store_v2_desc_as_dir, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_dir_with_different_time",
+ test_rend_cache_store_v2_desc_as_dir_with_different_time, 0, NULL, NULL },
+ { "store_v2_desc_as_dir_with_different_content",
+ test_rend_cache_store_v2_desc_as_dir_with_different_content, 0,
+ NULL, NULL },
+ { "validate_intro_point_failure",
+ test_rend_cache_validate_intro_point_failure, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_replay.c b/src/test/test_replay.c
index a02c160365..7a0f098776 100644
--- a/src/test/test_replay.c
+++ b/src/test/test_replay.c
@@ -17,6 +17,20 @@ static const char *test_buffer =
" occaecat cupidatat non proident, sunt in culpa qui officia deserunt"
" mollit anim id est laborum.";
+static const char *test_buffer_2 =
+ "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis"
+ " praesentium voluptatum deleniti atque corrupti quos dolores et quas"
+ " molestias excepturi sint occaecati cupiditate non provident, similique"
+ " sunt in culpa qui officia deserunt mollitia animi, id est laborum et"
+ " dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio."
+ " Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil"
+ " impedit quo minus id quod maxime placeat facere possimus, omnis voluptas"
+ " assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut"
+ " officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates"
+ " repudiandae sint et molestiae non recusandae. Itaque earum rerum hic"
+ " tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias"
+ " consequatur aut perferendis doloribus asperiores repellat.";
+
static void
test_replaycache_alloc(void *arg)
{
@@ -83,6 +97,12 @@ test_replaycache_miss(void *arg)
strlen(test_buffer), NULL);
tt_int_op(result,OP_EQ, 0);
+ /* make sure a different buffer misses as well */
+ result =
+ replaycache_add_and_test_internal(1200, NULL, test_buffer_2,
+ strlen(test_buffer_2), NULL);
+ tt_int_op(result,OP_EQ, 0);
+
/* poke the bad-parameter error case too */
result =
replaycache_add_and_test_internal(1200, NULL, test_buffer,
@@ -115,6 +135,18 @@ test_replaycache_hit(void *arg)
strlen(test_buffer), NULL);
tt_int_op(result,OP_EQ, 1);
+ /* make sure a different buffer misses then hits as well */
+
+ result =
+ replaycache_add_and_test_internal(1200, r, test_buffer_2,
+ strlen(test_buffer_2), NULL);
+ tt_int_op(result,OP_EQ, 0);
+
+ result =
+ replaycache_add_and_test_internal(1300, r, test_buffer_2,
+ strlen(test_buffer_2), NULL);
+ tt_int_op(result,OP_EQ, 1);
+
done:
if (r) replaycache_free(r);
@@ -245,7 +277,7 @@ test_replaycache_scrub(void *arg)
/* Make sure we hit the aging-out case too */
replaycache_scrub_if_needed_internal(1500, r);
/* Assert that we aged it */
- tt_int_op(digestmap_size(r->digests_seen),OP_EQ, 0);
+ tt_int_op(digest256map_size(r->digests_seen),OP_EQ, 0);
done:
if (r) replaycache_free(r);
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 381a592c5b..31533d070f 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -1,22 +1,49 @@
/* Copyright (c) 2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#include "orconfig.h"
+#include <math.h>
+#include <time.h>
+
+#define DIRVOTE_PRIVATE
+#define NETWORKSTATUS_PRIVATE
#define ROUTERLIST_PRIVATE
+#define TOR_UNIT_TESTING
#include "or.h"
-#include "routerlist.h"
+#include "config.h"
+#include "connection.h"
+#include "container.h"
#include "directory.h"
+#include "dirvote.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "policies.h"
+#include "routerlist.h"
+#include "routerparse.h"
#include "test.h"
+#include "test_dir_common.h"
+
+extern const char AUTHORITY_CERT_1[];
+extern const char AUTHORITY_SIGNKEY_1[];
+extern const char AUTHORITY_CERT_2[];
+extern const char AUTHORITY_SIGNKEY_2[];
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_3[];
+
+void construct_consensus(const char **consensus_text_md);
/* 4 digests + 3 sep + pre + post + NULL */
static char output[4*BASE64_DIGEST256_LEN+3+2+2+1];
static void
mock_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
- const char *resource, int pds_flags)
+ const char *resource, int pds_flags,
+ download_want_authority_t want_authority)
{
(void)dir_purpose;
(void)router_purpose;
(void)pds_flags;
+ (void)want_authority;
tt_assert(resource);
strlcpy(output, resource, sizeof(output));
done:
@@ -92,12 +119,335 @@ test_routerlist_launch_descriptor_downloads(void *arg)
smartlist_free(downloadable);
}
+void
+construct_consensus(const char **consensus_text_md)
+{
+ networkstatus_t *vote = NULL;
+ networkstatus_t *v1 = NULL, *v2 = NULL, *v3 = NULL;
+ networkstatus_voter_info_t *voter = NULL;
+ authority_cert_t *cert1=NULL, *cert2=NULL, *cert3=NULL;
+ crypto_pk_t *sign_skey_1=NULL, *sign_skey_2=NULL, *sign_skey_3=NULL;
+ crypto_pk_t *sign_skey_leg=NULL;
+ time_t now = time(NULL);
+ smartlist_t *votes = NULL;
+ int n_vrs;
+
+ tt_assert(!dir_common_authority_pk_init(&cert1, &cert2, &cert3,
+ &sign_skey_1, &sign_skey_2,
+ &sign_skey_3));
+ sign_skey_leg = pk_generate(4);
+
+ dir_common_construct_vote_1(&vote, cert1, sign_skey_1,
+ &dir_common_gen_routerstatus_for_v3ns,
+ &v1, &n_vrs, now, 1);
+ networkstatus_vote_free(vote);
+ tt_assert(v1);
+ tt_int_op(n_vrs, ==, 4);
+ tt_int_op(smartlist_len(v1->routerstatus_list), ==, 4);
+
+ dir_common_construct_vote_2(&vote, cert2, sign_skey_2,
+ &dir_common_gen_routerstatus_for_v3ns,
+ &v2, &n_vrs, now, 1);
+ networkstatus_vote_free(vote);
+ tt_assert(v2);
+ tt_int_op(n_vrs, ==, 4);
+ tt_int_op(smartlist_len(v2->routerstatus_list), ==, 4);
+
+ dir_common_construct_vote_3(&vote, cert3, sign_skey_3,
+ &dir_common_gen_routerstatus_for_v3ns,
+ &v3, &n_vrs, now, 1);
+
+ tt_assert(v3);
+ tt_int_op(n_vrs, ==, 4);
+ tt_int_op(smartlist_len(v3->routerstatus_list), ==, 4);
+ networkstatus_vote_free(vote);
+ votes = smartlist_new();
+ smartlist_add(votes, v1);
+ smartlist_add(votes, v2);
+ smartlist_add(votes, v3);
+
+ *consensus_text_md = networkstatus_compute_consensus(votes, 3,
+ cert1->identity_key,
+ sign_skey_1,
+ "AAAAAAAAAAAAAAAAAAAA",
+ sign_skey_leg,
+ FLAV_MICRODESC);
+
+ tt_assert(*consensus_text_md);
+
+ done:
+ tor_free(voter);
+ networkstatus_vote_free(v1);
+ networkstatus_vote_free(v2);
+ networkstatus_vote_free(v3);
+}
+
+static void
+test_router_pick_directory_server_impl(void *arg)
+{
+ (void)arg;
+
+ networkstatus_t *con_md = NULL;
+ const char *consensus_text_md = NULL;
+ int flags = PDS_IGNORE_FASCISTFIREWALL|PDS_RETRY_IF_NO_SERVERS;
+ or_options_t *options = get_options_mutable();
+ const routerstatus_t *rs = NULL;
+ options->UseMicrodescriptors = 1;
+ char *router1_id = NULL, *router2_id = NULL, *router3_id = NULL;
+ node_t *node_router1 = NULL, *node_router2 = NULL, *node_router3 = NULL;
+ config_line_t *policy_line = NULL;
+ time_t now = time(NULL);
+ int tmp_dirport1, tmp_dirport3;
+
+ (void)arg;
+
+ /* No consensus available, fail early */
+ rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
+ tt_assert(rs == NULL);
+
+ construct_consensus(&consensus_text_md);
+ tt_assert(consensus_text_md);
+ con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
+ NS_TYPE_CONSENSUS);
+ tt_assert(con_md);
+ tt_int_op(con_md->flavor,==, FLAV_MICRODESC);
+ tt_assert(con_md->routerstatus_list);
+ tt_int_op(smartlist_len(con_md->routerstatus_list), ==, 3);
+ tt_assert(!networkstatus_set_current_consensus_from_ns(con_md,
+ "microdesc"));
+ nodelist_set_consensus(con_md);
+ nodelist_assert_ok();
+
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ /* We should not fail now we have a consensus and routerstatus_list
+ * and nodelist are populated. */
+ tt_assert(rs != NULL);
+
+ /* Manipulate the nodes so we get the dir server we expect */
+ router1_id = tor_malloc(DIGEST_LEN);
+ memset(router1_id, TEST_DIR_ROUTER_ID_1, DIGEST_LEN);
+ router2_id = tor_malloc(DIGEST_LEN);
+ memset(router2_id, TEST_DIR_ROUTER_ID_2, DIGEST_LEN);
+ router3_id = tor_malloc(DIGEST_LEN);
+ memset(router3_id, TEST_DIR_ROUTER_ID_3, DIGEST_LEN);
+
+ node_router1 = node_get_mutable_by_id(router1_id);
+ node_router2 = node_get_mutable_by_id(router2_id);
+ node_router3 = node_get_mutable_by_id(router3_id);
+
+ node_router1->is_possible_guard = 1;
+
+ node_router1->is_running = 0;
+ node_router3->is_running = 0;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
+ rs = NULL;
+ node_router1->is_running = 1;
+ node_router3->is_running = 1;
+
+ node_router1->rs->is_v2_dir = 0;
+ node_router3->rs->is_v2_dir = 0;
+ tmp_dirport1 = node_router1->rs->dir_port;
+ tmp_dirport3 = node_router3->rs->dir_port;
+ node_router1->rs->dir_port = 0;
+ node_router3->rs->dir_port = 0;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
+ rs = NULL;
+ node_router1->rs->is_v2_dir = 1;
+ node_router3->rs->is_v2_dir = 1;
+ node_router1->rs->dir_port = tmp_dirport1;
+ node_router3->rs->dir_port = tmp_dirport3;
+
+ node_router1->is_valid = 0;
+ node_router3->is_valid = 0;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
+ rs = NULL;
+ node_router1->is_valid = 1;
+ node_router3->is_valid = 1;
+
+ flags |= PDS_FOR_GUARD;
+ node_router1->using_as_guard = 1;
+ node_router2->using_as_guard = 1;
+ node_router3->using_as_guard = 1;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs == NULL);
+ node_router1->using_as_guard = 0;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+ rs = NULL;
+ node_router2->using_as_guard = 0;
+ node_router3->using_as_guard = 0;
+
+ /* One not valid, one guard. This should leave one remaining */
+ node_router1->is_valid = 0;
+ node_router2->using_as_guard = 1;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN));
+ rs = NULL;
+ node_router1->is_valid = 1;
+ node_router2->using_as_guard = 0;
+
+ /* Manipulate overloaded */
+
+ node_router2->rs->last_dir_503_at = now;
+ node_router3->rs->last_dir_503_at = now;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+ node_router2->rs->last_dir_503_at = 0;
+ node_router3->rs->last_dir_503_at = 0;
+
+ /* Set a Fascist firewall */
+ flags &= ~ PDS_IGNORE_FASCISTFIREWALL;
+ policy_line = tor_malloc_zero(sizeof(config_line_t));
+ policy_line->key = tor_strdup("ReachableORAddresses");
+ policy_line->value = tor_strdup("accept *:442, reject *:*");
+ options->ReachableORAddresses = policy_line;
+ policies_parse_from_options(options);
+
+ node_router1->rs->or_port = 444;
+ node_router2->rs->or_port = 443;
+ node_router3->rs->or_port = 442;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN));
+ node_router1->rs->or_port = 442;
+ node_router2->rs->or_port = 443;
+ node_router3->rs->or_port = 444;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+
+ /* Fascist firewall and overloaded */
+ node_router1->rs->or_port = 442;
+ node_router2->rs->or_port = 443;
+ node_router3->rs->or_port = 442;
+ node_router3->rs->last_dir_503_at = now;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+ node_router3->rs->last_dir_503_at = 0;
+
+ /* Fascists against OR and Dir */
+ policy_line = tor_malloc_zero(sizeof(config_line_t));
+ policy_line->key = tor_strdup("ReachableAddresses");
+ policy_line->value = tor_strdup("accept *:80, reject *:*");
+ options->ReachableDirAddresses = policy_line;
+ policies_parse_from_options(options);
+ node_router1->rs->or_port = 442;
+ node_router2->rs->or_port = 441;
+ node_router3->rs->or_port = 443;
+ node_router1->rs->dir_port = 80;
+ node_router2->rs->dir_port = 80;
+ node_router3->rs->dir_port = 81;
+ node_router1->rs->last_dir_503_at = now;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+ node_router1->rs->last_dir_503_at = 0;
+
+ done:
+ if (router1_id)
+ tor_free(router1_id);
+ if (router2_id)
+ tor_free(router2_id);
+ if (router3_id)
+ tor_free(router3_id);
+ if (options->ReachableORAddresses ||
+ options->ReachableDirAddresses)
+ policies_free_all();
+}
+
+connection_t *mocked_connection = NULL;
+
+/* Mock connection_get_by_type_addr_port_purpose by returning
+ * mocked_connection. */
+static connection_t *
+mock_connection_get_by_type_addr_port_purpose(int type,
+ const tor_addr_t *addr,
+ uint16_t port, int purpose)
+{
+ (void)type;
+ (void)addr;
+ (void)port;
+ (void)purpose;
+
+ return mocked_connection;
+}
+
+#define TEST_ADDR_STR "127.0.0.1"
+#define TEST_DIR_PORT 12345
+
+static void
+test_routerlist_router_is_already_dir_fetching(void *arg)
+{
+ (void)arg;
+ tor_addr_port_t test_ap, null_addr_ap, zero_port_ap;
+
+ /* Setup */
+ tor_addr_parse(&test_ap.addr, TEST_ADDR_STR);
+ test_ap.port = TEST_DIR_PORT;
+ tor_addr_make_null(&null_addr_ap.addr, AF_INET6);
+ null_addr_ap.port = TEST_DIR_PORT;
+ tor_addr_parse(&zero_port_ap.addr, TEST_ADDR_STR);
+ zero_port_ap.port = 0;
+ MOCK(connection_get_by_type_addr_port_purpose,
+ mock_connection_get_by_type_addr_port_purpose);
+
+ /* Test that we never get 1 from a NULL connection */
+ mocked_connection = NULL;
+ tt_assert(router_is_already_dir_fetching(&test_ap, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 1, 0) == 0);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 0, 1) == 0);
+ /* We always expect 0 in these cases */
+ tt_assert(router_is_already_dir_fetching(&test_ap, 0, 0) == 0);
+ tt_assert(router_is_already_dir_fetching(NULL, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&null_addr_ap, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&zero_port_ap, 1, 1) == 0);
+
+ /* Test that we get 1 with a connection in the appropriate circumstances */
+ mocked_connection = connection_new(CONN_TYPE_DIR, AF_INET);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 1, 1) == 1);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 1, 0) == 1);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 0, 1) == 1);
+
+ /* Test that we get 0 even with a connection in the appropriate
+ * circumstances */
+ tt_assert(router_is_already_dir_fetching(&test_ap, 0, 0) == 0);
+ tt_assert(router_is_already_dir_fetching(NULL, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&null_addr_ap, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&zero_port_ap, 1, 1) == 0);
+
+ done:
+ /* If a connection is never set up, connection_free chokes on it. */
+ if (mocked_connection) {
+ buf_free(mocked_connection->inbuf);
+ buf_free(mocked_connection->outbuf);
+ }
+ tor_free(mocked_connection);
+ UNMOCK(connection_get_by_type_addr_port_purpose);
+}
+
+#undef TEST_ADDR_STR
+#undef TEST_DIR_PORT
+
#define NODE(name, flags) \
{ #name, test_routerlist_##name, (flags), NULL, NULL }
+#define ROUTER(name,flags) \
+ { #name, test_router_##name, (flags), NULL, NULL }
struct testcase_t routerlist_tests[] = {
NODE(initiate_descriptor_downloads, 0),
NODE(launch_descriptor_downloads, 0),
+ NODE(router_is_already_dir_fetching, TT_FORK),
+ ROUTER(pick_directory_server_impl, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c
index 90dfb28c6b..74b39c0486 100644
--- a/src/test/test_routerset.c
+++ b/src/test/test_routerset.c
@@ -423,10 +423,10 @@ NS(test_main)(void *arg)
}
#undef NS_SUBMODULE
-#define NS_SUBMODULE ASPECT(routerset_parse, policy)
+#define NS_SUBMODULE ASPECT(routerset_parse, policy_wildcard)
/*
- * Structural test for routerset_parse, when given a valid policy.
+ * Structural test for routerset_parse, when given a valid wildcard policy.
*/
NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
@@ -470,6 +470,100 @@ NS(router_parse_addr_policy_item_from_string)(const char *s,
}
#undef NS_SUBMODULE
+#define NS_SUBMODULE ASPECT(routerset_parse, policy_ipv4)
+
+/*
+ * Structural test for routerset_parse, when given a valid IPv4 address
+ * literal policy.
+ */
+
+NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
+ (const char *s, int assume_action, int *bogus));
+
+addr_policy_t *NS(mock_addr_policy);
+
+static void
+NS(test_main)(void *arg)
+{
+ routerset_t *set;
+ const char *s;
+ int r;
+ (void)arg;
+
+ NS_MOCK(router_parse_addr_policy_item_from_string);
+ NS(mock_addr_policy) = tor_malloc_zero(sizeof(addr_policy_t));
+
+ set = routerset_new();
+ s = "127.0.0.1";
+ r = routerset_parse(set, s, "");
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(smartlist_len(set->policies), OP_NE, 0);
+ tt_int_op(CALLED(router_parse_addr_policy_item_from_string), OP_EQ, 1);
+
+ done:
+ routerset_free(set);
+}
+
+addr_policy_t *
+NS(router_parse_addr_policy_item_from_string)(const char *s, int assume_action,
+ int *bogus)
+{
+ (void)s;
+ (void)assume_action;
+ CALLED(router_parse_addr_policy_item_from_string)++;
+ *bogus = 0;
+
+ return NS(mock_addr_policy);
+}
+
+#undef NS_SUBMODULE
+#define NS_SUBMODULE ASPECT(routerset_parse, policy_ipv6)
+
+/*
+ * Structural test for routerset_parse, when given a valid IPv6 address
+ * literal policy.
+ */
+
+NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
+ (const char *s, int assume_action, int *bad));
+
+addr_policy_t *NS(mock_addr_policy);
+
+static void
+NS(test_main)(void *arg)
+{
+ routerset_t *set;
+ const char *s;
+ int r;
+ (void)arg;
+
+ NS_MOCK(router_parse_addr_policy_item_from_string);
+ NS(mock_addr_policy) = tor_malloc_zero(sizeof(addr_policy_t));
+
+ set = routerset_new();
+ s = "::1";
+ r = routerset_parse(set, s, "");
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(smartlist_len(set->policies), OP_NE, 0);
+ tt_int_op(CALLED(router_parse_addr_policy_item_from_string), OP_EQ, 1);
+
+ done:
+ routerset_free(set);
+}
+
+addr_policy_t *
+NS(router_parse_addr_policy_item_from_string)(const char *s,
+ int assume_action, int *bad)
+{
+ (void)s;
+ (void)assume_action;
+ CALLED(router_parse_addr_policy_item_from_string)++;
+ *bad = 0;
+
+ return NS(mock_addr_policy);
+}
+
+#undef NS_SUBMODULE
#define NS_SUBMODULE ASPECT(routerset_union, source_bad)
/*
@@ -2109,7 +2203,9 @@ struct testcase_t routerset_tests[] = {
TEST_CASE_ASPECT(routerset_parse, valid_hexdigest),
TEST_CASE_ASPECT(routerset_parse, valid_nickname),
TEST_CASE_ASPECT(routerset_parse, get_countryname),
- TEST_CASE_ASPECT(routerset_parse, policy),
+ TEST_CASE_ASPECT(routerset_parse, policy_wildcard),
+ TEST_CASE_ASPECT(routerset_parse, policy_ipv4),
+ TEST_CASE_ASPECT(routerset_parse, policy_ipv6),
TEST_CASE(routerset_subtract_nodes),
TEST_CASE_ASPECT(routerset_subtract_nodes, null_routerset),
TEST_CASE(routerset_to_string),
diff --git a/src/test/test_status.c b/src/test/test_status.c
index cbc8af188c..84a0f6c024 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -707,15 +707,18 @@ NS(logv)(int severity, log_domain_mask_t domain,
tt_ptr_op(strstr(funcname, "log_accounting"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
- "Heartbeat: Accounting enabled. Sent: %s / %s, Received: %s / %s. "
- "The current accounting interval ends on %s, in %s.");
+ "Heartbeat: Accounting enabled. Sent: %s, Received: %s, Used: %s / "
+ "%s, Rule: %s. The current accounting interval ends on %s, in %s.");
tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_sent */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_max */
tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_rcvd */
+ tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_used */
tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_max */
- /* format_local_iso_time uses local tz, just check mins and secs. */
- tt_ptr_op(strstr(va_arg(ap, char *), ":01:00"),
- OP_NE, NULL); /* end_buf */
+ tt_str_op(va_arg(ap, char *), OP_EQ, "max"); /* acc_rule */
+ /* format_local_iso_time uses local tz, so we can't just compare
+ * the string against a constant */
+ char datetime[ISO_TIME_LEN+1];
+ format_local_iso_time(datetime, 60);
+ tt_str_op(va_arg(ap, char *), OP_EQ, datetime); /* end_buf */
tt_str_op(va_arg(ap, char *), OP_EQ, "0:01 hours"); /* remaining */
break;
case 2:
diff --git a/src/test/test_switch_id.c b/src/test/test_switch_id.c
new file mode 100644
index 0000000000..3d9c1c8bba
--- /dev/null
+++ b/src/test/test_switch_id.c
@@ -0,0 +1,191 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+#define TEST_BUILT_WITH_CAPS 0
+#define TEST_HAVE_CAPS 1
+#define TEST_ROOT_CAN_BIND_LOW 2
+#define TEST_SETUID 3
+#define TEST_SETUID_KEEPCAPS 4
+#define TEST_SETUID_STRICT 5
+
+static const struct {
+ const char *name;
+ int test_id;
+} which_test[] = {
+ { "built-with-caps", TEST_BUILT_WITH_CAPS },
+ { "have-caps", TEST_HAVE_CAPS },
+ { "root-bind-low", TEST_ROOT_CAN_BIND_LOW },
+ { "setuid", TEST_SETUID },
+ { "setuid-keepcaps", TEST_SETUID_KEEPCAPS },
+ { "setuid-strict", TEST_SETUID_STRICT },
+ { NULL, 0 }
+};
+
+#if !defined(_WIN32)
+/* 0 on no, 1 on yes, -1 on failure. */
+static int
+check_can_bind_low_ports(void)
+{
+ int port;
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+
+ for (port = 600; port < 1024; ++port) {
+ sin.sin_port = htons(port);
+ tor_socket_t fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (! SOCKET_OK(fd)) {
+ perror("socket");
+ return -1;
+ }
+
+ int one = 1;
+ if (setsockopt(fd, SOL_SOCKET,SO_REUSEADDR, (void*)&one,
+ (socklen_t)sizeof(one))) {
+ perror("setsockopt");
+ tor_close_socket_simple(fd);
+ return -1;
+ }
+
+ int res = bind(fd, (struct sockaddr *)&sin, sizeof(sin));
+ tor_close_socket_simple(fd);
+
+ if (res == 0) {
+ /* bind was successful */
+ return 1;
+ } else if (errno == EACCES || errno == EPERM) {
+ /* Got a permission-denied error. */
+ return 0;
+ } else if (errno == EADDRINUSE) {
+ /* Huh; somebody is using that port. */
+ } else {
+ perror("bind");
+ }
+ }
+
+ return -1;
+}
+#endif
+
+int
+main(int argc, char **argv)
+{
+#if defined(_WIN32)
+ (void) argc;
+ (void) argv;
+
+ fprintf(stderr, "This test is not supported on your OS.\n");
+ return 77;
+#else
+ const char *username;
+ const char *testname;
+ if (argc != 3) {
+ fprintf(stderr, "I want 2 arguments: a username and a command.\n");
+ return 1;
+ }
+ if (getuid() != 0) {
+ fprintf(stderr, "This test only works when it's run as root.\n");
+ return 1;
+ }
+ username = argv[1];
+ testname = argv[2];
+ int test_id = -1;
+ int i;
+ for (i = 0; which_test[i].name; ++i) {
+ if (!strcmp(which_test[i].name, testname)) {
+ test_id = which_test[i].test_id;
+ break;
+ }
+ }
+ if (test_id == -1) {
+ fprintf(stderr, "Unrecognized test '%s'\n", testname);
+ return 1;
+ }
+
+#ifdef HAVE_LINUX_CAPABILITIES
+ const int have_cap_support = 1;
+#else
+ const int have_cap_support = 0;
+#endif
+
+ int okay;
+
+ init_logging(1);
+ log_severity_list_t sev;
+ memset(&sev, 0, sizeof(sev));
+ set_log_severity_config(LOG_WARN, LOG_ERR, &sev);
+ add_stream_log(&sev, "", fileno(stderr));
+
+ switch (test_id)
+ {
+ case TEST_BUILT_WITH_CAPS:
+ /* Succeed if we were built with capability support. */
+ okay = have_cap_support;
+ break;
+ case TEST_HAVE_CAPS:
+ /* Succeed if "capabilities work" == "we were built with capability
+ * support." */
+ okay = have_cap_support == have_capability_support();
+ break;
+ case TEST_ROOT_CAN_BIND_LOW:
+ /* Succeed if root can bind low ports. */
+ okay = check_can_bind_low_ports() == 1;
+ break;
+ case TEST_SETUID:
+ /* Succeed if we can do a setuid with no capability retention, and doing
+ * so makes us lose the ability to bind low ports */
+ case TEST_SETUID_KEEPCAPS:
+ /* Succeed if we can do a setuid with capability retention, and doing so
+ * does not make us lose the ability to bind low ports */
+ {
+ int keepcaps = (test_id == TEST_SETUID_KEEPCAPS);
+ okay = switch_id(username, keepcaps ? SWITCH_ID_KEEP_BINDLOW : 0) == 0;
+ if (okay) {
+ okay = check_can_bind_low_ports() == keepcaps;
+ }
+ break;
+ }
+ case TEST_SETUID_STRICT:
+ /* Succeed if, after a setuid, we cannot setuid back, and we cannot
+ * re-grab any capabilities. */
+ okay = switch_id(username, SWITCH_ID_KEEP_BINDLOW) == 0;
+ if (okay) {
+ /* We'd better not be able to setuid back! */
+ if (setuid(0) == 0 || errno != EPERM) {
+ okay = 0;
+ }
+ }
+#ifdef HAVE_LINUX_CAPABILITIES
+ if (okay) {
+ cap_t caps = cap_get_proc();
+ const cap_value_t caplist[] = {
+ CAP_SETUID,
+ };
+ cap_set_flag(caps, CAP_PERMITTED, 1, caplist, CAP_SET);
+ if (cap_set_proc(caps) == 0 || errno != EPERM) {
+ okay = 0;
+ }
+ cap_free(caps);
+ }
+#endif
+ break;
+ default:
+ fprintf(stderr, "Unsupported test '%s'\n", testname);
+ okay = 0;
+ break;
+ }
+
+ if (!okay) {
+ fprintf(stderr, "Test %s failed!\n", testname);
+ }
+
+ return (okay ? 0 : 1);
+#endif
+}
+
diff --git a/src/test/test_switch_id.sh b/src/test/test_switch_id.sh
new file mode 100755
index 0000000000..1b4e0998b5
--- /dev/null
+++ b/src/test/test_switch_id.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+if test "`id -u`" != '0'; then
+ echo "This test only works when run as root. Skipping." >&2
+ exit 77
+fi
+
+if test "`id -u nobody`" = ""; then
+ echo "This test requires that your system have a 'nobody' user. Sorry." >&2
+ exit 1
+fi
+
+"${builddir:-.}/src/test/test-switch-id" nobody setuid || exit 1
+"${builddir:-.}/src/test/test-switch-id" nobody root-bind-low || exit 1
+"${builddir:-.}/src/test/test-switch-id" nobody setuid-strict || exit 1
+"${builddir:-.}/src/test/test-switch-id" nobody built-with-caps || exit 0
+# ... Go beyond this point only if we were built with capability support.
+
+"${builddir:-.}/src/test/test-switch-id" nobody have-caps || exit 1
+"${builddir:-.}/src/test/test-switch-id" nobody setuid-keepcaps || exit 1
+
+
+echo "All okay"
+
+exit 0
diff --git a/src/test/test_threads.c b/src/test/test_threads.c
index 35f5dc8ea3..fe88c9470f 100644
--- a/src/test/test_threads.c
+++ b/src/test/test_threads.c
@@ -73,6 +73,8 @@ thread_test_func_(void* _s)
++thread_fns_failed;
tor_mutex_release(thread_test_mutex_);
+ tor_free(mycount);
+
tor_mutex_release(m);
spawn_exit();
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
new file mode 100644
index 0000000000..28847d4611
--- /dev/null
+++ b/src/test/test_tortls.c
@@ -0,0 +1,2834 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define TORTLS_PRIVATE
+#define LOG_PRIVATE
+#include "orconfig.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#endif
+
+#if __GNUC__ && GCC_VERSION >= 402
+#if GCC_VERSION >= 406
+#pragma GCC diagnostic push
+#endif
+/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in
+ * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+#include <openssl/opensslv.h>
+
+#include <openssl/ssl.h>
+#include <openssl/ssl3.h>
+#include <openssl/err.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+
+#if __GNUC__ && GCC_VERSION >= 402
+#if GCC_VERSION >= 406
+#pragma GCC diagnostic pop
+#else
+#pragma GCC diagnostic warning "-Wredundant-decls"
+#endif
+#endif
+
+#include "or.h"
+#include "torlog.h"
+#include "config.h"
+#include "tortls.h"
+
+#include "test.h"
+#include "log_test_helpers.h"
+#define NS_MODULE tortls
+
+extern tor_tls_context_t *server_tls_context;
+extern tor_tls_context_t *client_tls_context;
+
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) \
+ && !defined(LIBRESSL_VERSION_NUMBER)
+#define OPENSSL_OPAQUE
+#define SSL_STATE_STR "before SSL initialization"
+#else
+#define SSL_STATE_STR "before/accept initialization"
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static SSL_METHOD *
+give_me_a_test_method(void)
+{
+ SSL_METHOD *method = tor_malloc_zero(sizeof(SSL_METHOD));
+ memcpy(method, TLSv1_method(), sizeof(SSL_METHOD));
+ return method;
+}
+
+static int
+fake_num_ciphers(void)
+{
+ return 0;
+}
+#endif
+
+static void
+test_tortls_errno_to_tls_error(void *data)
+{
+ (void) data;
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ECONNRESET)),OP_EQ,
+ TOR_TLS_ERROR_CONNRESET);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ETIMEDOUT)),OP_EQ,
+ TOR_TLS_ERROR_TIMEOUT);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(EHOSTUNREACH)),OP_EQ,
+ TOR_TLS_ERROR_NO_ROUTE);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ENETUNREACH)),OP_EQ,
+ TOR_TLS_ERROR_NO_ROUTE);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ECONNREFUSED)),OP_EQ,
+ TOR_TLS_ERROR_CONNREFUSED);
+ tt_int_op(tor_errno_to_tls_error(0),OP_EQ,TOR_TLS_ERROR_MISC);
+ done:
+ (void)1;
+}
+
+static void
+test_tortls_err_to_string(void *data)
+{
+ (void) data;
+ tt_str_op(tor_tls_err_to_string(1),OP_EQ,"[Not an error.]");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_MISC),OP_EQ,"misc error");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_IO),OP_EQ,"unexpected close");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_CONNREFUSED),OP_EQ,
+ "connection refused");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_CONNRESET),OP_EQ,
+ "connection reset");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_NO_ROUTE),OP_EQ,
+ "host unreachable");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_TIMEOUT),OP_EQ,
+ "connection timed out");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_CLOSE),OP_EQ,"closed");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_WANTREAD),OP_EQ,"want to read");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_WANTWRITE),OP_EQ,"want to write");
+ tt_str_op(tor_tls_err_to_string(-100),OP_EQ,"(unknown error code)");
+ done:
+ (void)1;
+}
+
+static int
+mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert)
+{
+ (void) tls;
+ (void) cert; // XXXX look at this.
+ return 1;
+}
+
+static void
+test_tortls_tor_tls_new(void *data)
+{
+ (void) data;
+ MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
+ crypto_pk_t *key1 = NULL, *key2 = NULL;
+ SSL_METHOD *method = NULL;
+
+ key1 = pk_generate(2);
+ key2 = pk_generate(3);
+
+ tor_tls_t *tls = NULL;
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ key1, key2, 86400), OP_EQ, 0);
+ tls = tor_tls_new(-1, 0);
+ tt_want(tls);
+ tor_tls_free(tls); tls = NULL;
+
+ SSL_CTX_free(client_tls_context->ctx);
+ client_tls_context->ctx = NULL;
+ tls = tor_tls_new(-1, 0);
+ tt_assert(!tls);
+
+#ifndef OPENSSL_OPAQUE
+ method = give_me_a_test_method();
+ SSL_CTX *ctx = SSL_CTX_new(method);
+ method->num_ciphers = fake_num_ciphers;
+ client_tls_context->ctx = ctx;
+ tls = tor_tls_new(-1, 0);
+ tt_assert(!tls);
+#endif
+
+ done:
+ UNMOCK(tor_tls_cert_matches_key);
+ crypto_pk_free(key1);
+ crypto_pk_free(key2);
+ tor_tls_free(tls);
+ tor_free(method);
+ tor_tls_free_all();
+}
+
+#define NS_MODULE tortls
+NS_DECL(void, logv, (int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap));
+
+static void
+NS(logv)(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix, const char *format,
+ va_list ap)
+{
+ (void) severity;
+ (void) domain;
+ (void) funcname;
+ (void) suffix;
+ (void) format;
+ (void) ap; // XXXX look at this.
+ CALLED(logv)++;
+}
+
+static void
+test_tortls_tor_tls_get_error(void *data)
+{
+ (void) data;
+ MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
+ crypto_pk_t *key1 = NULL, *key2 = NULL;
+ key1 = pk_generate(2);
+ key2 = pk_generate(3);
+
+ tor_tls_t *tls = NULL;
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ key1, key2, 86400), OP_EQ, 0);
+ tls = tor_tls_new(-1, 0);
+ NS_MOCK(logv);
+ tt_int_op(CALLED(logv), OP_EQ, 0);
+ tor_tls_get_error(tls, 0, 0,
+ (const char *)"test", 0, 0);
+ tt_int_op(CALLED(logv), OP_EQ, 1);
+
+ done:
+ UNMOCK(tor_tls_cert_matches_key);
+ NS_UNMOCK(logv);
+ crypto_pk_free(key1);
+ crypto_pk_free(key2);
+ tor_tls_free(tls);
+}
+
+static void
+test_tortls_get_state_description(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ char *buf;
+ SSL_CTX *ctx;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+
+ buf = tor_malloc_zero(1000);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tor_tls_get_state_description(NULL, buf, 20);
+ tt_str_op(buf, OP_EQ, "(No SSL object)");
+
+ SSL_free(tls->ssl);
+ tls->ssl = NULL;
+ tor_tls_get_state_description(tls, buf, 20);
+ tt_str_op(buf, OP_EQ, "(No SSL object)");
+
+ tls->ssl = SSL_new(ctx);
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in HANDSHAKE");
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in OPEN");
+
+ tls->state = TOR_TLS_ST_GOTCLOSE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in GOTCLOSE");
+
+ tls->state = TOR_TLS_ST_SENTCLOSE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in SENTCLOSE");
+
+ tls->state = TOR_TLS_ST_CLOSED;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in CLOSED");
+
+ tls->state = TOR_TLS_ST_RENEGOTIATE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in RENEGOTIATE");
+
+ tls->state = TOR_TLS_ST_BUFFEREVENT;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR);
+
+ tls->state = 7;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in unknown TLS state");
+
+ done:
+ SSL_CTX_free(ctx);
+ SSL_free(tls->ssl);
+ tor_free(buf);
+ tor_free(tls);
+}
+
+extern int tor_tls_object_ex_data_index;
+
+static void
+test_tortls_get_by_ssl(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ tor_tls_t *res;
+ SSL_CTX *ctx;
+ SSL *ssl;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+
+ ssl = SSL_new(ctx);
+
+ res = tor_tls_get_by_ssl(ssl);
+ tt_assert(!res);
+
+ SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
+
+ res = tor_tls_get_by_ssl(ssl);
+ tt_assert(res == tls);
+
+ done:
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+
+static void
+test_tortls_allocate_tor_tls_object_ex_data_index(void *ignored)
+{
+ (void)ignored;
+ int first;
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ first = tor_tls_object_ex_data_index;
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+ tt_int_op(first, OP_EQ, tor_tls_object_ex_data_index);
+
+ done:
+ (void)0;
+}
+
+static void
+test_tortls_log_one_error(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL *ssl = NULL;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+
+ tor_tls_log_one_error(NULL, 0, LOG_WARN, 0, "something");
+ expect_log_msg("TLS error while something: "
+ "(null) (in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error: (null) "
+ "(in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tls->address = tor_strdup("127.hello");
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error with 127.hello: "
+ "(null) (in (null):(null):---)\n");
+ tor_free(tls->address);
+
+ mock_clean_saved_logs();
+ tls->address = tor_strdup("127.hello");
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, "blarg");
+ expect_log_msg("TLS error while blarg with "
+ "127.hello: (null) (in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, 3), LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error with 127.hello: "
+ "BN lib (in unknown library:(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTP_REQUEST),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTPS_PROXY_REQUEST),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_LENGTH_MISMATCH),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_TOO_LARGE),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNKNOWN_PROTOCOL),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNSUPPORTED_PROTOCOL),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ tls->ssl = SSL_new(ctx);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error with 127.hello: (null)"
+ " (in (null):(null):" SSL_STATE_STR ")\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ if (tls && tls->ssl)
+ SSL_free(tls->ssl);
+ if (tls)
+ tor_free(tls->address);
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_error(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ int ret;
+ SSL_CTX *ctx;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_IO);
+ expect_log_msg("TLS error: unexpected close while"
+ " something (before/accept initialization)\n");
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 2, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 0, 1, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -11);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ expect_log_msg("TLS error while something: (null)"
+ " (in bignum routines:(null):before/accept initialization)\n");
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = 0;
+ tls->ssl->shutdown = SSL_RECEIVED_SHUTDOWN;
+ tls->ssl->s3->warn_alert =SSL_AD_CLOSE_NOTIFY;
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
+ expect_log_entry();
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 0, 2, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -10);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -9);
+ expect_log_msg("TLS error while something: (null) (in system library:"
+ "connect:before/accept initialization)\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+}
+#endif
+
+static void
+test_tortls_always_accept_verify_cb(void *ignored)
+{
+ (void)ignored;
+ int ret;
+
+ ret = always_accept_verify_cb(0, NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ (void)0;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_x509_cert_free(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert;
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ tor_x509_cert_free(cert);
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ cert->cert = tor_malloc_zero(sizeof(X509));
+ cert->encoded = tor_malloc_zero(1);
+ tor_x509_cert_free(cert);
+}
+#endif
+
+static void
+test_tortls_x509_cert_get_id_digests(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert;
+ common_digests_t *d;
+ const common_digests_t *res;
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ d = tor_malloc_zero(sizeof(common_digests_t));
+ d->d[0][0] = 42;
+
+ res = tor_x509_cert_get_id_digests(cert);
+ tt_assert(!res);
+
+ cert->pkey_digests_set = 1;
+ cert->pkey_digests = *d;
+ res = tor_x509_cert_get_id_digests(cert);
+ tt_int_op(res->d[0][0], OP_EQ, 42);
+
+ done:
+ tor_free(cert);
+ tor_free(d);
+}
+
+#ifndef OPENSSL_OPAQUE
+static int
+fixed_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+ (void) a; (void) b;
+ return 1;
+}
+
+static void
+fake_x509_free(X509 *cert)
+{
+ if (cert) {
+ if (cert->cert_info) {
+ if (cert->cert_info->key) {
+ if (cert->cert_info->key->pkey) {
+ tor_free(cert->cert_info->key->pkey);
+ }
+ tor_free(cert->cert_info->key);
+ }
+ tor_free(cert->cert_info);
+ }
+ tor_free(cert);
+ }
+}
+
+static void
+test_tortls_cert_matches_key(void *ignored)
+{
+ (void)ignored;
+ int res;
+ tor_tls_t *tls;
+ tor_x509_cert_t *cert;
+ X509 *one = NULL, *two = NULL;
+ EVP_PKEY_ASN1_METHOD *meth = EVP_PKEY_asn1_new(999, 0, NULL, NULL);
+ EVP_PKEY_asn1_set_public(meth, NULL, NULL, fixed_pub_cmp, NULL, NULL, NULL);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ one = tor_malloc_zero(sizeof(X509));
+ one->references = 1;
+ two = tor_malloc_zero(sizeof(X509));
+ two->references = 1;
+
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->peer = one;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ cert->cert = two;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ one->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ one->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ one->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ one->cert_info->key->pkey->references = 1;
+ one->cert_info->key->pkey->ameth = meth;
+ one->cert_info->key->pkey->type = 1;
+
+ two->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ two->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ two->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ two->cert_info->key->pkey->references = 1;
+ two->cert_info->key->pkey->ameth = meth;
+ two->cert_info->key->pkey->type = 2;
+
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ one->cert_info->key->pkey->type = 1;
+ two->cert_info->key->pkey->type = 1;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 1);
+
+ done:
+ EVP_PKEY_asn1_free(meth);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(cert);
+ fake_x509_free(one);
+ fake_x509_free(two);
+}
+
+static void
+test_tortls_cert_get_key(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert = NULL;
+ crypto_pk_t *res = NULL;
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ X509 *key = NULL;
+ key = tor_malloc_zero(sizeof(X509));
+ key->references = 1;
+
+ res = tor_tls_cert_get_key(cert);
+ tt_assert(!res);
+
+ cert->cert = key;
+ key->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ key->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ key->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ key->cert_info->key->pkey->references = 1;
+ key->cert_info->key->pkey->type = 2;
+ res = tor_tls_cert_get_key(cert);
+ tt_assert(!res);
+
+ done:
+ fake_x509_free(key);
+ tor_free(cert);
+ crypto_pk_free(res);
+}
+#endif
+
+static void
+test_tortls_get_my_client_auth_key(void *ignored)
+{
+ (void)ignored;
+ crypto_pk_t *ret;
+ crypto_pk_t *expected;
+ tor_tls_context_t *ctx;
+ RSA *k = tor_malloc_zero(sizeof(RSA));
+
+ ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
+ expected = crypto_new_pk_from_rsa_(k);
+ ctx->auth_key = expected;
+
+ client_tls_context = NULL;
+ ret = tor_tls_get_my_client_auth_key();
+ tt_assert(!ret);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_client_auth_key();
+ tt_assert(ret == expected);
+
+ done:
+ tor_free(expected);
+ tor_free(k);
+ tor_free(ctx);
+}
+
+static void
+test_tortls_get_my_certs(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_context_t *ctx;
+ const tor_x509_cert_t *link_cert_out = NULL;
+ const tor_x509_cert_t *id_cert_out = NULL;
+
+ ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
+
+ client_tls_context = NULL;
+ ret = tor_tls_get_my_certs(0, NULL, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ server_tls_context = NULL;
+ ret = tor_tls_get_my_certs(1, NULL, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_certs(0, NULL, NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_certs(0, &link_cert_out, &id_cert_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ server_tls_context = ctx;
+ ret = tor_tls_get_my_certs(1, &link_cert_out, &id_cert_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ (void)1;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_ciphersuite_name(void *ignored)
+{
+ (void)ignored;
+ const char *ret;
+ tor_tls_t *ctx;
+ ctx = tor_malloc_zero(sizeof(tor_tls_t));
+ ctx->ssl = tor_malloc_zero(sizeof(SSL));
+
+ ret = tor_tls_get_ciphersuite_name(ctx);
+ tt_str_op(ret, OP_EQ, "(NONE)");
+
+ done:
+ tor_free(ctx->ssl);
+ tor_free(ctx);
+}
+
+static SSL_CIPHER *
+get_cipher_by_name(const char *name)
+{
+ int i;
+ const SSL_METHOD *method = SSLv23_method();
+ int num = method->num_ciphers();
+ for (i = 0; i < num; ++i) {
+ const SSL_CIPHER *cipher = method->get_cipher(i);
+ const char *ciphername = SSL_CIPHER_get_name(cipher);
+ if (!strcmp(ciphername, name)) {
+ return (SSL_CIPHER *)cipher;
+ }
+ }
+
+ return NULL;
+}
+
+static SSL_CIPHER *
+get_cipher_by_id(uint16_t id)
+{
+ int i;
+ const SSL_METHOD *method = SSLv23_method();
+ int num = method->num_ciphers();
+ for (i = 0; i < num; ++i) {
+ const SSL_CIPHER *cipher = method->get_cipher(i);
+ if (id == (SSL_CIPHER_get_id(cipher) & 0xffff)) {
+ return (SSL_CIPHER *)cipher;
+ }
+ }
+
+ return NULL;
+}
+
+extern uint16_t v2_cipher_list[];
+
+static void
+test_tortls_classify_client_ciphers(void *ignored)
+{
+ (void)ignored;
+ int i;
+ int ret;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ tor_tls_t *tls;
+ STACK_OF(SSL_CIPHER) *ciphers;
+ SSL_CIPHER *tmp_cipher;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+ tls->ssl = ssl;
+
+ ciphers = sk_SSL_CIPHER_new_null();
+
+ ret = tor_tls_classify_client_ciphers(ssl, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
+ tls->client_cipher_list_type = 42;
+
+ ret = tor_tls_classify_client_ciphers(ssl, NULL);
+ tt_int_op(ret, OP_EQ, 42);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, SSL_get_ciphers(ssl));
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ SSL_CIPHER *one = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_128_SHA),
+ *two = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA),
+ *three = get_cipher_by_name(SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA),
+ *four = NULL;
+ sk_SSL_CIPHER_push(ciphers, one);
+ sk_SSL_CIPHER_push(ciphers, two);
+ sk_SSL_CIPHER_push(ciphers, three);
+ sk_SSL_CIPHER_push(ciphers, four);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
+
+ sk_SSL_CIPHER_zero(ciphers);
+
+ one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ two = get_cipher_by_name("ECDH-RSA-AES128-GCM-SHA256");
+ two->id = 0x0000;
+ sk_SSL_CIPHER_push(ciphers, one);
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ sk_SSL_CIPHER_push(ciphers, two);
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ one->id = 0xC00A;
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ sk_SSL_CIPHER_zero(ciphers);
+ for (i=0; v2_cipher_list[i]; i++) {
+ tmp_cipher = get_cipher_by_id(v2_cipher_list[i]);
+ tt_assert(tmp_cipher);
+ sk_SSL_CIPHER_push(ciphers, tmp_cipher);
+ }
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 2);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 2);
+
+ done:
+ sk_SSL_CIPHER_free(ciphers);
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+}
+#endif
+
+static void
+test_tortls_client_is_using_v2_ciphers(void *ignored)
+{
+ (void)ignored;
+
+#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
+ tt_skip();
+ done:
+ (void)1;
+#else
+ int ret;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ SSL_SESSION *sess;
+ STACK_OF(SSL_CIPHER) *ciphers;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+ sess = SSL_SESSION_new();
+
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, -1);
+
+ ssl->session = sess;
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ciphers = sk_SSL_CIPHER_new_null();
+ SSL_CIPHER *one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ sk_SSL_CIPHER_push(ciphers, one);
+ sess->ciphers = ciphers;
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, 1);
+ done:
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+#endif
+}
+
+#ifndef OPENSSL_OPAQUE
+static X509 *fixed_try_to_extract_certs_from_tls_cert_out_result = NULL;
+static X509 *fixed_try_to_extract_certs_from_tls_id_cert_out_result = NULL;
+
+static void
+fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
+ X509 **cert_out, X509 **id_cert_out)
+{
+ (void) severity;
+ (void) tls;
+ *cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result;
+ *id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result;
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static const char* notCompletelyValidCertString =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICVjCCAb8CAg37MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n"
+ "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n"
+ "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n"
+ "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n"
+ "ODIyMDUyNzIzWhcNMTcwODIxMDUyNzIzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n"
+ "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n"
+ "ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYBBrx5PlP0WNI/ZdzD\n"
+ "+6Pktmurn+F2kQYbtc7XQh8/LTBvCo+P6iZoLEmUA9e7EXLRxgU1CVqeAi7QcAn9\n"
+ "MwBlc8ksFJHB0rtf9pmf8Oza9E0Bynlq/4/Kb1x+d+AyhL7oK9tQwB24uHOueHi1\n"
+ "C/iVv8CSWKiYe6hzN1txYe8rAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAASPdjigJ\n"
+ "kXCqKWpnZ/Oc75EUcMi6HztaW8abUMlYXPIgkV2F7YanHOB7K4f7OOLjiz8DTPFf\n"
+ "jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n"
+ "evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n"
+ "-----END CERTIFICATE-----\n";
+#endif
+
+static const char* validCertString = "-----BEGIN CERTIFICATE-----\n"
+ "MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n"
+ "VQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwLVG9yIFRl\n"
+ "c3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkwNjEzMzk1OVoXDTQz\n"
+ "MDEyMjEzMzk1OVowVjELMAkGA1UEBhMCVVMxEDAOBgNVBAcMB0NoaWNhZ28xFDAS\n"
+ "BgNVBAoMC1RvciBUZXN0aW5nMR8wHQYDVQQDDBZ0ZXN0aW5nLnRvcnByb2plY3Qu\n"
+ "b3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoT6uyVVhWyOF3wkHjjYbd\n"
+ "nKaykyRv4JVtKQdZ4OpEErmX1zw4MmyzpQNV6iR4bQnWiyLfzyVJMZDIC/WILBfX\n"
+ "w2Pza/yuLgUvDc3twMuhOACzOQVO8PrEF/aVv2+hbCCy2udXvKhnYn+CCXl3ozc8\n"
+ "XcKYvujTXDyvGWY3xwAjlQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCUvnhzQWuQ\n"
+ "MrN+pERkE+zcTI/9dGS90rUMMLgu8VDNqTa0TUQh8uO0EQ6uDvI8Js6e8tgwS0BR\n"
+ "UBahqb7ZHv+rejGCBr5OudqD+x4STiiuPNJVs86JTLN8SpM9CHjIBH5WCCN2KOy3\n"
+ "mevNoRcRRyYJzSFULCunIK6FGulszigMYGscrO4oiTkZiHPh9KvWT40IMiHfL+Lw\n"
+ "EtEWiLex6064LcA2YQ1AMuSZyCexks63lcfaFmQbkYOKqXa1oLkIRuDsOaSVjTfe\n"
+ "vec+X6jvf12cFTKS5WIeqkKF2Irt+dJoiHEGTe5RscUMN/f+gqHPzfFz5dR23sxo\n"
+ "g+HC6MZHlFkLAOx3wW6epPS8A/m1mw3zMPoTnb2U2YYt8T0dJMMlUn/7Y1sEAa+a\n"
+ "dSTMaeUf6VnJ//11m454EZl1to9Z7oJOgqmFffSrdD4BGIWe8f7hhW6L1Enmqe/J\n"
+ "BKL3wbzZh80O1W0bndAwhnEEhlzneFY84cbBo9pmVxpODHkUcStpr5Z7pBDrcL21\n"
+ "Ss/aB/1YrsVXhdvJdOGxl3Mnl9dUY57CympLGlT8f0pPS6GAKOelECOhFMHmJd8L\n"
+ "dj3XQSmKtYHevZ6IvuMXSlB/fJvSjSlkCuLo5+kJoaqPuRu+i/S1qxeRy3CBwmnE\n"
+ "LdSNdcX4N79GQJ996PA8+mUCQG7YRtK+WA==\n"
+ "-----END CERTIFICATE-----\n";
+
+static const char* caCertString = "-----BEGIN CERTIFICATE-----\n"
+ "MIIFjzCCA3egAwIBAgIJAKd5WgyfPMYRMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n"
+ "BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIG\n"
+ "A1UECgwLVG9yIFRlc3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkw\n"
+ "NjEzMzc0MVoXDTQzMDEyMjEzMzc0MVowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM\n"
+ "CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtUb3IgVGVzdGlu\n"
+ "ZzEUMBIGA1UEAwwLVG9yIFRlc3RpbmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n"
+ "ggIKAoICAQCpLMUEiLW5leUgBZoEJms2V7lZRhIAjnJBhVMHD0e3UubNknmaQoxf\n"
+ "ARz3rvqOaRd0JlV+qM9qE0DjiYcCVP1cAfqAo9d83uS1vwY3YMVJzADlaIiHfyVW\n"
+ "uEgBy0vvkeUBqaua24dYlcwsemOiXYLu41yM1wkcGHW1AhBNHppY6cznb8TyLgNM\n"
+ "2x3SGUdzc5XMyAFx51faKGBA3wjs+Hg1PLY7d30nmCgEOBavpm5I1disM/0k+Mcy\n"
+ "YmAKEo/iHJX/rQzO4b9znP69juLlR8PDBUJEVIG/CYb6+uw8MjjUyiWXYoqfVmN2\n"
+ "hm/lH8b6rXw1a2Aa3VTeD0DxaWeacMYHY/i01fd5n7hCoDTRNdSw5KJ0L3Z0SKTu\n"
+ "0lzffKzDaIfyZGlpW5qdouACkWYzsaitQOePVE01PIdO30vUfzNTFDfy42ccx3Di\n"
+ "59UCu+IXB+eMtrBfsok0Qc63vtF1linJgjHW1z/8ujk8F7/qkOfODhk4l7wngc2A\n"
+ "EmwWFIFoGaiTEZHB9qteXr4unbXZ0AHpM02uGGwZEGohjFyebEb73M+J57WKKAFb\n"
+ "PqbLcGUksL1SHNBNAJcVLttX55sO4nbidOS/kA3m+F1R04MBTyQF9qA6YDDHqdI3\n"
+ "h/3pw0Z4fxVouTYT4/NfRnX4JTP4u+7Mpcoof28VME0qWqD1LnRhFQIDAQABo1Aw\n"
+ "TjAdBgNVHQ4EFgQUMoAgIXH7pZ3QMRwTjT+DM9Yo/v0wHwYDVR0jBBgwFoAUMoAg\n"
+ "IXH7pZ3QMRwTjT+DM9Yo/v0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n"
+ "AgEAUJxacjXR9sT+Xs6ISFiUsyd0T6WVKMnV46xrYJHirGfx+krWHrjxMY+ZtxYD\n"
+ "DBDGlo11Qc4v6QrclNf5QUBfIiGQsP9Cm6hHcQ+Tpg9HHCgSqG1YNPwCPReCR4br\n"
+ "BLvLfrfkcBL2IWM0PdQdCze+59DBfipsULD2mEn9fjYRXQEwb2QWtQ9qRc20Yb/x\n"
+ "Q4b/+CvUodLkaq7B8MHz0BV8HHcBoph6DYaRmO/N+hPauIuSp6XyaGYcEefGKVKj\n"
+ "G2+fcsdyXsoijNdL8vNKwm4j2gVwCBnw16J00yfFoV46YcbfqEdJB2je0XSvwXqt\n"
+ "14AOTngxso2h9k9HLtrfpO1ZG/B5AcCMs1lzbZ2fp5DPHtjvvmvA2RJqgo3yjw4W\n"
+ "4DHAuTglYFlC3mDHNfNtcGP20JvepcQNzNP2UzwcpOc94hfKikOFw+gf9Vf1qd0y\n"
+ "h/Sk6OZHn2+JVUPiWHIQV98Vtoh4RmUZDJD+b55ia3fQGTGzt4z1XFzQYSva5sfs\n"
+ "wocS/papthqWldQU7x+3wofNd5CNU1x6WKXG/yw30IT/4F8ADJD6GeygNT8QJYvt\n"
+ "u/8lAkbOy6B9xGmSvr0Kk1oq9P2NshA6kalxp1Oz/DTNDdL4AeBXV3JmM6WWCjGn\n"
+ "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n"
+ "-----END CERTIFICATE-----\n";
+
+static X509 *
+read_cert_from(const char *str)
+{
+ BIO *bio = BIO_new(BIO_s_mem());
+ BIO_write(bio, str, (int) strlen(str));
+ X509 *res = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ return res;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_verify(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ crypto_pk_t *k = NULL;
+ X509 *cert1 = NULL, *cert2 = NULL, *invalidCert = NULL,
+ *validCert = NULL, *caCert = NULL;
+
+ cert1 = tor_malloc_zero(sizeof(X509));
+ cert1->references = 10;
+
+ cert2 = tor_malloc_zero(sizeof(X509));
+ cert2->references = 10;
+
+ validCert = read_cert_from(validCertString);
+ caCert = read_cert_from(caCertString);
+ invalidCert = read_cert_from(notCompletelyValidCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ MOCK(try_to_extract_certs_from_tls, fixed_try_to_extract_certs_from_tls);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = cert1;
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = cert2;
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = invalidCert;
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = invalidCert;
+
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = validCert;
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = caCert;
+
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(k);
+
+ done:
+ UNMOCK(try_to_extract_certs_from_tls);
+ tor_free(cert1);
+ tor_free(cert2);
+ tor_free(tls);
+ tor_free(k);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_check_lifetime(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ X509 *validCert = read_cert_from(validCertString);
+ time_t now = time(NULL);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->peer = validCert;
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ASN1_STRING_free(validCert->cert_info->validity->notBefore);
+ validCert->cert_info->validity->notBefore = ASN1_TIME_set(NULL, now-10);
+ ASN1_STRING_free(validCert->cert_info->validity->notAfter);
+ validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60);
+
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, -1000);
+ tt_int_op(ret, OP_EQ, -1);
+
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, -1000, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(validCert);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_pending_result = 0;
+
+static int
+fixed_ssl_pending(const SSL *ignored)
+{
+ (void)ignored;
+ return fixed_ssl_pending_result;
+}
+
+static void
+test_tortls_get_pending_bytes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ method = tor_malloc_zero(sizeof(SSL_METHOD));
+ method->ssl_pending = fixed_ssl_pending;
+ tls->ssl->method = method;
+
+ fixed_ssl_pending_result = 42;
+ ret = tor_tls_get_pending_bytes(tls);
+ tt_int_op(ret, OP_EQ, 42);
+
+ done:
+ tor_free(method);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+static void
+test_tortls_get_forced_write_size(void *ignored)
+{
+ (void)ignored;
+ long ret;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->wantwrite_n = 43;
+ ret = tor_tls_get_forced_write_size(tls);
+ tt_int_op(ret, OP_EQ, 43);
+
+ done:
+ tor_free(tls);
+}
+
+extern uint64_t total_bytes_written_over_tls;
+extern uint64_t total_bytes_written_by_tls;
+
+static void
+test_tortls_get_write_overhead_ratio(void *ignored)
+{
+ (void)ignored;
+ double ret;
+
+ total_bytes_written_over_tls = 0;
+ ret = tls_get_write_overhead_ratio();
+ tt_int_op(ret, OP_EQ, 1.0);
+
+ total_bytes_written_by_tls = 10;
+ total_bytes_written_over_tls = 1;
+ ret = tls_get_write_overhead_ratio();
+ tt_int_op(ret, OP_EQ, 10.0);
+
+ total_bytes_written_by_tls = 10;
+ total_bytes_written_over_tls = 2;
+ ret = tls_get_write_overhead_ratio();
+ tt_int_op(ret, OP_EQ, 5.0);
+
+ done:
+ (void)0;
+}
+
+static void
+test_tortls_used_v1_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ // These tests assume both V2 handshake server and client are enabled
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_used_v1_handshake(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ ret = tor_tls_used_v1_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ tor_free(tls);
+}
+
+static void
+test_tortls_get_num_server_handshakes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->server_handshake_count = 3;
+ ret = tor_tls_get_num_server_handshakes(tls);
+ tt_int_op(ret, OP_EQ, 3);
+
+ done:
+ tor_free(tls);
+}
+
+static void
+test_tortls_server_got_renegotiate(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->got_renegotiate = 1;
+ ret = tor_tls_server_got_renegotiate(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_SSL_SESSION_get_master_key(void *ignored)
+{
+ (void)ignored;
+ size_t ret;
+ tor_tls_t *tls;
+ uint8_t *out;
+ out = tor_malloc_zero(1);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->master_key_length = 1;
+
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+ tls->ssl->session->master_key[0] = 43;
+ ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 0);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(out[0], OP_EQ, 0);
+
+ ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 1);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(out[0], OP_EQ, 43);
+
+ done:
+#endif
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(out);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_tlssecrets(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ uint8_t *secret_out = tor_malloc_zero(DIGEST256_LEN);;
+ tor_tls_t *tls;
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->master_key_length = 1;
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+
+ ret = tor_tls_get_tlssecrets(tls, secret_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ tor_free(secret_out);
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_buffer_sizes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ size_t rbuf_c=-1, rbuf_b=-1, wbuf_c=-1, wbuf_b=-1;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+
+ tls->ssl->s3->rbuf.buf = NULL;
+ tls->ssl->s3->rbuf.len = 1;
+ tls->ssl->s3->rbuf.offset = 0;
+ tls->ssl->s3->rbuf.left = 42;
+
+ tls->ssl->s3->wbuf.buf = NULL;
+ tls->ssl->s3->wbuf.len = 2;
+ tls->ssl->s3->wbuf.offset = 0;
+ tls->ssl->s3->wbuf.left = 43;
+
+ ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(rbuf_c, OP_EQ, 0);
+ tt_int_op(wbuf_c, OP_EQ, 0);
+ tt_int_op(rbuf_b, OP_EQ, 42);
+ tt_int_op(wbuf_b, OP_EQ, 43);
+
+ tls->ssl->s3->rbuf.buf = tor_malloc_zero(1);
+ tls->ssl->s3->wbuf.buf = tor_malloc_zero(1);
+ ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(rbuf_c, OP_EQ, 1);
+ tt_int_op(wbuf_c, OP_EQ, 2);
+
+#endif
+
+ done:
+ tor_free(tls->ssl->s3->rbuf.buf);
+ tor_free(tls->ssl->s3->wbuf.buf);
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+static void
+test_tortls_evaluate_ecgroup_for_tls(void *ignored)
+{
+ (void)ignored;
+ int ret;
+
+ ret = evaluate_ecgroup_for_tls(NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = evaluate_ecgroup_for_tls("foobar");
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = evaluate_ecgroup_for_tls("P256");
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = evaluate_ecgroup_for_tls("P224");
+ // tt_int_op(ret, OP_EQ, 1); This varies between machines
+
+ done:
+ (void)0;
+}
+
+#ifndef OPENSSL_OPAQUE
+typedef struct cert_pkey_st_local
+{
+ X509 *x509;
+ EVP_PKEY *privatekey;
+ const EVP_MD *digest;
+} CERT_PKEY_local;
+
+typedef struct sess_cert_st_local
+{
+ STACK_OF(X509) *cert_chain;
+ int peer_cert_type;
+ CERT_PKEY_local *peer_key;
+ CERT_PKEY_local peer_pkeys[8];
+ int references;
+} SESS_CERT_local;
+
+static void
+test_tortls_try_to_extract_certs_from_tls(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ X509 *cert = NULL, *id_cert = NULL, *c1 = NULL, *c2 = NULL;
+ SESS_CERT_local *sess = NULL;
+
+ c1 = read_cert_from(validCertString);
+ c2 = read_cert_from(caCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ sess = tor_malloc_zero(sizeof(SESS_CERT_local));
+ tls->ssl->session->sess_cert = (void *)sess;
+
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(!cert);
+ tt_assert(!id_cert);
+
+ tls->ssl->session->peer = c1;
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(!id_cert);
+ X509_free(cert); /* decrease refcnt */
+
+ sess->cert_chain = sk_X509_new_null();
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(!id_cert);
+ X509_free(cert); /* decrease refcnt */
+
+ sk_X509_push(sess->cert_chain, c1);
+ sk_X509_push(sess->cert_chain, c2);
+
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(id_cert);
+ X509_free(cert); /* decrease refcnt */
+
+ done:
+ sk_X509_free(sess->cert_chain);
+ tor_free(sess);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(c1);
+ X509_free(c2);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_peer_cert(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *ret;
+ tor_tls_t *tls;
+ X509 *cert = NULL;
+
+ cert = read_cert_from(validCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+
+ ret = tor_tls_get_peer_cert(tls);
+ tt_assert(!ret);
+
+ tls->ssl->session->peer = cert;
+ ret = tor_tls_get_peer_cert(tls);
+ tt_assert(ret);
+ tt_assert(ret->cert == cert);
+
+ done:
+ tor_x509_cert_free(ret);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(cert);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_peer_has_cert(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ X509 *cert = NULL;
+
+ cert = read_cert_from(validCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+
+ ret = tor_tls_peer_has_cert(tls);
+ tt_assert(!ret);
+
+ tls->ssl->session->peer = cert;
+ ret = tor_tls_peer_has_cert(tls);
+ tt_assert(ret);
+
+ done:
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(cert);
+}
+#endif
+
+static void
+test_tortls_is_server(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ int ret;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->isServer = 1;
+ ret = tor_tls_is_server(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_session_secret_cb(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ STACK_OF(SSL_CIPHER) *ciphers = NULL;
+ SSL_CIPHER *one;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->magic = TOR_TLS_MAGIC;
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ tls->ssl = SSL_new(ctx);
+ SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
+
+ SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
+
+ tor_tls_session_secret_cb(tls->ssl, NULL, NULL, NULL, NULL, NULL);
+ tt_assert(!tls->ssl->tls_session_secret_cb);
+
+ one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ ciphers = sk_SSL_CIPHER_new_null();
+ sk_SSL_CIPHER_push(ciphers, one);
+
+ tls->client_cipher_list_type = 0;
+ tor_tls_session_secret_cb(tls->ssl, NULL, NULL, ciphers, NULL, NULL);
+ tt_assert(!tls->ssl->tls_session_secret_cb);
+
+ done:
+ sk_SSL_CIPHER_free(ciphers);
+ SSL_free(tls->ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+/* TODO: It seems block_renegotiation and unblock_renegotiation and
+ * using different blags. This might not be correct */
+static void
+test_tortls_block_renegotiation(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+#ifndef SUPPORT_UNSAFE_RENEGOTIATION_FLAG
+#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0
+#endif
+
+ tls->ssl->s3->flags = SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+
+ tor_tls_block_renegotiation(tls);
+
+#ifndef OPENSSL_1_1_API
+ tt_assert(!(tls->ssl->s3->flags &
+ SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
+#endif
+
+ done:
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+
+static void
+test_tortls_unblock_renegotiation(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tor_tls_unblock_renegotiation(tls);
+
+ tt_uint_op(SSL_get_options(tls->ssl) &
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, OP_EQ,
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+
+ done:
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_assert_renegotiation_unblocked(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tor_tls_unblock_renegotiation(tls);
+ tor_tls_assert_renegotiation_unblocked(tls);
+ /* No assertion here - this test will fail if tor_assert is turned on
+ * and things are bad. */
+
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+static void
+test_tortls_set_logged_address(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tor_tls_set_logged_address(tls, "foo bar");
+
+ tt_str_op(tls->address, OP_EQ, "foo bar");
+
+ tor_tls_set_logged_address(tls, "foo bar 2");
+ tt_str_op(tls->address, OP_EQ, "foo bar 2");
+
+ done:
+ tor_free(tls->address);
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+example_cb(tor_tls_t *t, void *arg)
+{
+ (void)t;
+ (void)arg;
+}
+
+static void
+test_tortls_set_renegotiate_callback(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ const char *arg = "hello";
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+
+ tor_tls_set_renegotiate_callback(tls, example_cb, (void*)arg);
+ tt_assert(tls->negotiated_callback == example_cb);
+ tt_assert(tls->callback_arg == arg);
+ tt_assert(!tls->got_renegotiate);
+
+ /* Assumes V2_HANDSHAKE_SERVER */
+ tt_assert(tls->ssl->info_callback == tor_tls_server_info_callback);
+
+ tor_tls_set_renegotiate_callback(tls, NULL, (void*)arg);
+ tt_assert(tls->ssl->info_callback == tor_tls_debug_state_callback);
+
+ done:
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static SSL_CIPHER *fixed_cipher1 = NULL;
+static SSL_CIPHER *fixed_cipher2 = NULL;
+static const SSL_CIPHER *
+fake_get_cipher(unsigned ncipher)
+{
+
+ switch (ncipher) {
+ case 1:
+ return fixed_cipher1;
+ case 2:
+ return fixed_cipher2;
+ default:
+ return NULL;
+ }
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_find_cipher_by_id(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ SSL *ssl;
+ SSL_CTX *ctx;
+ const SSL_METHOD *m = TLSv1_method();
+ SSL_METHOD *empty_method = tor_malloc_zero(sizeof(SSL_METHOD));
+
+ fixed_cipher1 = tor_malloc_zero(sizeof(SSL_CIPHER));
+ fixed_cipher2 = tor_malloc_zero(sizeof(SSL_CIPHER));
+ fixed_cipher2->id = 0xC00A;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(m);
+ ssl = SSL_new(ctx);
+
+ ret = find_cipher_by_id(ssl, NULL, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, m, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, m, 0xFFFF);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, empty_method, 0xFFFF);
+#ifdef HAVE_SSL_CIPHER_FIND
+ tt_int_op(ret, OP_EQ, 0);
+#else
+ tt_int_op(ret, OP_EQ, 1);
+#endif
+
+ empty_method->get_cipher = fake_get_cipher;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->get_cipher = m->get_cipher;
+ empty_method->num_ciphers = m->num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->get_cipher = fake_get_cipher;
+ empty_method->num_ciphers = m->num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->num_ciphers = fake_num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+#ifdef HAVE_SSL_CIPHER_FIND
+ tt_int_op(ret, OP_EQ, 1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+ done:
+ tor_free(empty_method);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(fixed_cipher1);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_debug_state_callback(void *ignored)
+{
+ (void)ignored;
+ SSL *ssl;
+ char *buf = tor_malloc_zero(1000);
+ int n;
+
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+
+ ssl = tor_malloc_zero(sizeof(SSL));
+
+ tor_tls_debug_state_callback(ssl, 32, 45);
+
+ n = tor_snprintf(buf, 1000, "SSL %p is now in state unknown"
+ " state [type=32,val=45].\n", ssl);
+ /* tor's snprintf returns -1 on error */
+ tt_int_op(n, OP_NE, -1);
+ expect_log_msg(buf);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(buf);
+ tor_free(ssl);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_server_info_callback(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+ tls->ssl = ssl;
+
+ tor_tls_server_info_callback(NULL, 0, 0);
+
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ expect_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
+
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ expect_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
+
+ SSL_set_state(ssl, 99);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ expect_no_log_entry();
+
+ SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
+ tls->negotiated_callback = 0;
+ tls->server_handshake_count = 120;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->server_handshake_count, OP_EQ, 121);
+
+ tls->server_handshake_count = 127;
+ tls->negotiated_callback = (void *)1;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->server_handshake_count, OP_EQ, 127);
+ tt_int_op(tls->got_renegotiate, OP_EQ, 1);
+
+ tls->ssl->session = SSL_SESSION_new();
+ tls->wasV2Handshake = 0;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_read_result_index;
+static int fixed_ssl_read_result[5];
+static int fixed_ssl_shutdown_result;
+
+static int
+fixed_ssl_read(SSL *s, void *buf, int len)
+{
+ (void)s;
+ (void)buf;
+ (void)len;
+ return fixed_ssl_read_result[fixed_ssl_read_result_index++];
+}
+
+static int
+fixed_ssl_shutdown(SSL *s)
+{
+ (void)s;
+ return fixed_ssl_shutdown_result;
+}
+
+#ifndef LIBRESSL_VERSION_NUMBER
+static int fixed_ssl_state_to_set;
+static tor_tls_t *fixed_tls;
+
+static int
+setting_version_ssl_shutdown(SSL *s)
+{
+ s->version = SSL2_VERSION;
+ return fixed_ssl_shutdown_result;
+}
+
+static int
+setting_version_and_state_ssl_shutdown(SSL *s)
+{
+ fixed_tls->state = fixed_ssl_state_to_set;
+ s->version = SSL2_VERSION;
+ return fixed_ssl_shutdown_result;
+}
+#endif
+
+static int
+dummy_handshake_func(SSL *s)
+{
+ (void)s;
+ return 1;
+}
+
+static void
+test_tortls_shutdown(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method = give_me_a_test_method();
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->method = method;
+ method->ssl_read = fixed_ssl_read;
+ method->ssl_shutdown = fixed_ssl_shutdown;
+
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->state = TOR_TLS_ST_SENTCLOSE;
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = -1;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+#ifndef LIBRESSL_VERSION_NUMBER
+ tls->ssl->handshake_func = dummy_handshake_func;
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 1;
+ ERR_clear_error();
+ tls->ssl->version = SSL2_VERSION;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ fixed_tls = tls;
+ fixed_ssl_state_to_set = TOR_TLS_ST_GOTCLOSE;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_read_result[3] = -1;
+ fixed_ssl_shutdown_result = 0;
+ fixed_tls = tls;
+ fixed_ssl_state_to_set = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+#endif
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(method);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+
+static int negotiated_callback_called;
+
+static void
+negotiated_callback_setter(tor_tls_t *t, void *arg)
+{
+ (void)t;
+ (void)arg;
+ negotiated_callback_called++;
+}
+
+static void
+test_tortls_read(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ char buf[100];
+ SSL_METHOD *method = give_me_a_test_method();
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, -9);
+
+ /* These tests assume that V2_HANDSHAKE_SERVER is set */
+ tls->ssl->handshake_func = dummy_handshake_func;
+ tls->ssl->method = method;
+ method->ssl_read = fixed_ssl_read;
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 42;
+ tls->state = TOR_TLS_ST_OPEN;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, 42);
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tls->got_renegotiate = 1;
+ fixed_ssl_read_result_index = 0;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(tls->got_renegotiate, OP_EQ, 0);
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tls->got_renegotiate = 1;
+ negotiated_callback_called = 0;
+ tls->negotiated_callback = negotiated_callback_setter;
+ fixed_ssl_read_result_index = 0;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(negotiated_callback_called, OP_EQ, 1);
+
+#ifndef LIBRESSL_VERSION_NUMBER
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 0;
+ tls->ssl->version = SSL2_VERSION;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+#endif
+ // TODO: fill up
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(method);
+}
+
+static int fixed_ssl_write_result;
+
+static int
+fixed_ssl_write(SSL *s, const void *buf, int len)
+{
+ (void)s;
+ (void)buf;
+ (void)len;
+ return fixed_ssl_write_result;
+}
+
+static void
+test_tortls_write(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method = give_me_a_test_method();
+ char buf[100];
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_write(tls, buf, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->ssl->method = method;
+ tls->wantwrite_n = 1;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(tls->wantwrite_n, OP_EQ, 0);
+
+ method->ssl_write = fixed_ssl_write;
+ tls->ssl->handshake_func = dummy_handshake_func;
+ fixed_ssl_write_result = 1;
+ ERR_clear_error();
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, 1);
+
+ fixed_ssl_write_result = -1;
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
+
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ BIO_free(tls->ssl->rbio);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(method);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_accept_result;
+static int fixed_ssl_connect_result;
+
+static int
+setting_error_ssl_accept(SSL *ssl)
+{
+ (void)ssl;
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ return fixed_ssl_accept_result;
+}
+
+static int
+setting_error_ssl_connect(SSL *ssl)
+{
+ (void)ssl;
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ return fixed_ssl_connect_result;
+}
+
+static int
+fixed_ssl_accept(SSL *ssl)
+{
+ (void) ssl;
+ return fixed_ssl_accept_result;
+}
+
+static void
+test_tortls_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL_METHOD *method = give_me_a_test_method();
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->isServer = 1;
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->ssl->method = method;
+ method->ssl_accept = fixed_ssl_accept;
+ fixed_ssl_accept_result = 2;
+ ERR_clear_error();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_OPEN);
+
+ method->ssl_accept = setting_error_ssl_accept;
+ fixed_ssl_accept_result = 1;
+ ERR_clear_error();
+ mock_clean_saved_logs();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ expect_log_entry();
+ /* This fails on jessie. Investigate why! */
+#if 0
+ expect_log_msg("TLS error while handshaking: (null) (in bignum routines:"
+ "(null):SSLv3 write client hello B)\n");
+ expect_log_msg("TLS error while handshaking: (null) (in system library:"
+ "connect:SSLv3 write client hello B)\n");
+#endif
+ expect_log_severity(LOG_INFO);
+
+ tls->isServer = 0;
+ method->ssl_connect = setting_error_ssl_connect;
+ fixed_ssl_connect_result = 1;
+ ERR_clear_error();
+ mock_clean_saved_logs();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ expect_log_entry();
+#if 0
+ /* See above */
+ expect_log_msg("TLS error while handshaking: "
+ "(null) (in bignum routines:(null):SSLv3 write client hello B)\n");
+ expect_log_msg("TLS error while handshaking: "
+ "(null) (in system library:connect:SSLv3 write client hello B)\n");
+#endif
+ expect_log_severity(LOG_WARN);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(tls->ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+ tor_free(method);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_finish_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL_METHOD *method = give_me_a_test_method();
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ X509 *c1 = read_cert_from(validCertString);
+ SESS_CERT_local *sess = NULL;
+
+ ctx = SSL_CTX_new(method);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tls->isServer = 1;
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ tls->ssl->session = SSL_SESSION_new();
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
+
+ tls->isServer = 0;
+
+ sess = tor_malloc_zero(sizeof(SESS_CERT_local));
+ tls->ssl->session->sess_cert = (void *)sess;
+ sess->cert_chain = sk_X509_new_null();
+ sk_X509_push(sess->cert_chain, c1);
+ tls->ssl->session->peer = c1;
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ method->num_ciphers = fake_num_ciphers;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ done:
+ if (sess)
+ sk_X509_free(sess->cert_chain);
+ if (tls->ssl && tls->ssl->session) {
+ tor_free(tls->ssl->session->sess_cert);
+ }
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+ tor_free(method);
+}
+#endif
+
+static int fixed_crypto_pk_new_result_index;
+static crypto_pk_t *fixed_crypto_pk_new_result[5];
+
+static crypto_pk_t *
+fixed_crypto_pk_new(void)
+{
+ return fixed_crypto_pk_new_result[fixed_crypto_pk_new_result_index++];
+}
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_crypto_pk_generate_key_with_bits_result_index;
+static int fixed_crypto_pk_generate_key_with_bits_result[5];
+static int fixed_tor_tls_create_certificate_result_index;
+static X509 *fixed_tor_tls_create_certificate_result[5];
+static int fixed_tor_x509_cert_new_result_index;
+static tor_x509_cert_t *fixed_tor_x509_cert_new_result[5];
+
+static int
+fixed_crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits)
+{
+ (void)env;
+ (void)bits;
+ return fixed_crypto_pk_generate_key_with_bits_result[
+ fixed_crypto_pk_generate_key_with_bits_result_index++];
+}
+
+static X509 *
+fixed_tor_tls_create_certificate(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime)
+{
+ (void)rsa;
+ (void)rsa_sign;
+ (void)cname;
+ (void)cname_sign;
+ (void)cert_lifetime;
+ return fixed_tor_tls_create_certificate_result[
+ fixed_tor_tls_create_certificate_result_index++];
+}
+
+static tor_x509_cert_t *
+fixed_tor_x509_cert_new(X509 *x509_cert)
+{
+ (void) x509_cert;
+ return fixed_tor_x509_cert_new_result[
+ fixed_tor_x509_cert_new_result_index++];
+}
+
+static void
+test_tortls_context_new(void *ignored)
+{
+ (void)ignored;
+ tor_tls_context_t *ret;
+ crypto_pk_t *pk1, *pk2, *pk3, *pk4, *pk5, *pk6, *pk7, *pk8, *pk9, *pk10,
+ *pk11, *pk12, *pk13, *pk14, *pk15, *pk16, *pk17, *pk18;
+
+ pk1 = crypto_pk_new();
+ pk2 = crypto_pk_new();
+ pk3 = crypto_pk_new();
+ pk4 = crypto_pk_new();
+ pk5 = crypto_pk_new();
+ pk6 = crypto_pk_new();
+ pk7 = crypto_pk_new();
+ pk8 = crypto_pk_new();
+ pk9 = crypto_pk_new();
+ pk10 = crypto_pk_new();
+ pk11 = crypto_pk_new();
+ pk12 = crypto_pk_new();
+ pk13 = crypto_pk_new();
+ pk14 = crypto_pk_new();
+ pk15 = crypto_pk_new();
+ pk16 = crypto_pk_new();
+ pk17 = crypto_pk_new();
+ pk18 = crypto_pk_new();
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = NULL;
+ MOCK(crypto_pk_new, fixed_crypto_pk_new);
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(crypto_pk_generate_key_with_bits,
+ fixed_crypto_pk_generate_key_with_bits);
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk1;
+ fixed_crypto_pk_new_result[1] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = -1;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk2;
+ fixed_crypto_pk_new_result[1] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk3;
+ fixed_crypto_pk_new_result[1] = pk4;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
+ fixed_crypto_pk_generate_key_with_bits_result[1] = -1;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(tor_tls_create_certificate, fixed_tor_tls_create_certificate);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk5;
+ fixed_crypto_pk_new_result[1] = pk6;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_crypto_pk_generate_key_with_bits_result[1] = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = NULL;
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk7;
+ fixed_crypto_pk_new_result[1] = pk8;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = NULL;
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk9;
+ fixed_crypto_pk_new_result[1] = pk10;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(tor_x509_cert_new, fixed_tor_x509_cert_new);
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk11;
+ fixed_crypto_pk_new_result[1] = pk12;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = NULL;
+ fixed_tor_x509_cert_new_result[1] = NULL;
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk13;
+ fixed_crypto_pk_new_result[1] = pk14;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = NULL;
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk15;
+ fixed_crypto_pk_new_result[1] = pk16;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk17;
+ fixed_crypto_pk_new_result[1] = pk18;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[2] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ done:
+ UNMOCK(tor_x509_cert_new);
+ UNMOCK(tor_tls_create_certificate);
+ UNMOCK(crypto_pk_generate_key_with_bits);
+ UNMOCK(crypto_pk_new);
+}
+#endif
+
+static int fixed_crypto_pk_get_evp_pkey_result_index = 0;
+static EVP_PKEY *fixed_crypto_pk_get_evp_pkey_result[5];
+
+static EVP_PKEY *
+fixed_crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private)
+{
+ (void) env;
+ (void) private;
+ return fixed_crypto_pk_get_evp_pkey_result[
+ fixed_crypto_pk_get_evp_pkey_result_index++];
+}
+
+static void
+test_tortls_create_certificate(void *ignored)
+{
+ (void)ignored;
+ X509 *ret;
+ crypto_pk_t *pk1, *pk2;
+
+ pk1 = crypto_pk_new();
+ pk2 = crypto_pk_new();
+
+ MOCK(crypto_pk_get_evp_pkey_, fixed_crypto_pk_get_evp_pkey_);
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = NULL;
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new();
+ fixed_crypto_pk_get_evp_pkey_result[1] = NULL;
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new();
+ fixed_crypto_pk_get_evp_pkey_result[1] = EVP_PKEY_new();
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ done:
+ UNMOCK(crypto_pk_get_evp_pkey_);
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk2);
+}
+
+static void
+test_tortls_cert_new(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *ret;
+ X509 *cert = read_cert_from(validCertString);
+
+ ret = tor_x509_cert_new(NULL);
+ tt_assert(!ret);
+
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+ tor_x509_cert_free(ret);
+ ret = NULL;
+
+#if 0
+ cert = read_cert_from(validCertString);
+ /* XXX this doesn't do what you think: it alters a copy of the pubkey. */
+ X509_get_pubkey(cert)->type = EVP_PKEY_DSA;
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+#endif
+
+#ifndef OPENSSL_OPAQUE
+ cert = read_cert_from(validCertString);
+ X509_CINF_free(cert->cert_info);
+ cert->cert_info = NULL;
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+#endif
+
+ done:
+ tor_x509_cert_free(ret);
+}
+
+static void
+test_tortls_cert_is_valid(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_x509_cert_t *cert = NULL, *scert = NULL;
+
+ scert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(scert);
+ tor_free(cert);
+
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+#ifndef OPENSSL_OPAQUE
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ ASN1_TIME_free(cert->cert->cert_info->validity->notAfter);
+ cert->cert->cert_info->validity->notAfter =
+ ASN1_TIME_set(NULL, time(NULL)-1000000);
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ X509_PUBKEY_free(cert->cert->cert_info->key);
+ cert->cert->cert_info->key = NULL;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1);
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+#if 0
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ BN_one(EVP_PKEY_get1_RSA(X509_get_pubkey(cert->cert))->n);
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ X509_get_pubkey(cert->cert)->ameth = NULL;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+ done:
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+}
+
+static void
+test_tortls_context_init_one(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_context_t *old = NULL;
+
+ MOCK(crypto_pk_new, fixed_crypto_pk_new);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = NULL;
+ ret = tor_tls_context_init_one(&old, NULL, 0, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ UNMOCK(crypto_pk_new);
+}
+
+#define LOCAL_TEST_CASE(name, flags) \
+ { #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL }
+
+#ifdef OPENSSL_OPAQUE
+#define INTRUSIVE_TEST_CASE(name, flags) \
+ { #name, NULL, TT_SKIP, NULL, NULL }
+#else
+#define INTRUSIVE_TEST_CASE(name, flags) LOCAL_TEST_CASE(name, flags)
+#endif
+
+struct testcase_t tortls_tests[] = {
+ LOCAL_TEST_CASE(errno_to_tls_error, 0),
+ LOCAL_TEST_CASE(err_to_string, 0),
+ LOCAL_TEST_CASE(tor_tls_new, TT_FORK),
+ LOCAL_TEST_CASE(tor_tls_get_error, 0),
+ LOCAL_TEST_CASE(get_state_description, TT_FORK),
+ LOCAL_TEST_CASE(get_by_ssl, TT_FORK),
+ LOCAL_TEST_CASE(allocate_tor_tls_object_ex_data_index, TT_FORK),
+ LOCAL_TEST_CASE(log_one_error, TT_FORK),
+ INTRUSIVE_TEST_CASE(get_error, TT_FORK),
+ LOCAL_TEST_CASE(always_accept_verify_cb, 0),
+ INTRUSIVE_TEST_CASE(x509_cert_free, 0),
+ LOCAL_TEST_CASE(x509_cert_get_id_digests, 0),
+ INTRUSIVE_TEST_CASE(cert_matches_key, 0),
+ INTRUSIVE_TEST_CASE(cert_get_key, 0),
+ LOCAL_TEST_CASE(get_my_client_auth_key, TT_FORK),
+ LOCAL_TEST_CASE(get_my_certs, TT_FORK),
+ INTRUSIVE_TEST_CASE(get_ciphersuite_name, 0),
+ INTRUSIVE_TEST_CASE(classify_client_ciphers, 0),
+ LOCAL_TEST_CASE(client_is_using_v2_ciphers, 0),
+ INTRUSIVE_TEST_CASE(verify, 0),
+ INTRUSIVE_TEST_CASE(check_lifetime, 0),
+ INTRUSIVE_TEST_CASE(get_pending_bytes, 0),
+ LOCAL_TEST_CASE(get_forced_write_size, 0),
+ LOCAL_TEST_CASE(get_write_overhead_ratio, TT_FORK),
+ LOCAL_TEST_CASE(used_v1_handshake, TT_FORK),
+ LOCAL_TEST_CASE(get_num_server_handshakes, 0),
+ LOCAL_TEST_CASE(server_got_renegotiate, 0),
+ INTRUSIVE_TEST_CASE(SSL_SESSION_get_master_key, 0),
+ INTRUSIVE_TEST_CASE(get_tlssecrets, 0),
+ INTRUSIVE_TEST_CASE(get_buffer_sizes, 0),
+ LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0),
+ INTRUSIVE_TEST_CASE(try_to_extract_certs_from_tls, 0),
+ INTRUSIVE_TEST_CASE(get_peer_cert, 0),
+ INTRUSIVE_TEST_CASE(peer_has_cert, 0),
+ INTRUSIVE_TEST_CASE(shutdown, 0),
+ INTRUSIVE_TEST_CASE(finish_handshake, 0),
+ INTRUSIVE_TEST_CASE(handshake, 0),
+ INTRUSIVE_TEST_CASE(write, 0),
+ INTRUSIVE_TEST_CASE(read, 0),
+ INTRUSIVE_TEST_CASE(server_info_callback, 0),
+ LOCAL_TEST_CASE(is_server, 0),
+ INTRUSIVE_TEST_CASE(assert_renegotiation_unblocked, 0),
+ INTRUSIVE_TEST_CASE(block_renegotiation, 0),
+ INTRUSIVE_TEST_CASE(unblock_renegotiation, 0),
+ INTRUSIVE_TEST_CASE(set_renegotiate_callback, 0),
+ LOCAL_TEST_CASE(set_logged_address, 0),
+ INTRUSIVE_TEST_CASE(find_cipher_by_id, 0),
+ INTRUSIVE_TEST_CASE(session_secret_cb, 0),
+ INTRUSIVE_TEST_CASE(debug_state_callback, 0),
+ INTRUSIVE_TEST_CASE(context_new, 0),
+ LOCAL_TEST_CASE(create_certificate, 0),
+ LOCAL_TEST_CASE(cert_new, 0),
+ LOCAL_TEST_CASE(cert_is_valid, 0),
+ LOCAL_TEST_CASE(context_init_one, 0),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 0a5783e9f5..37f7d938ea 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -14,11 +14,21 @@
#include "memarea.h"
#include "util_process.h"
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
#ifdef _WIN32
#include <tchar.h>
#endif
#include <math.h>
#include <ctype.h>
+#include <float.h>
/* XXXX this is a minimal wrapper to make the unit tests compile with the
* changed tor_timegm interface. */
@@ -4097,6 +4107,9 @@ test_util_round_to_next_multiple_of(void *arg)
tt_u64_op(round_uint64_to_next_multiple_of(99,7), ==, 105);
tt_u64_op(round_uint64_to_next_multiple_of(99,9), ==, 99);
+ tt_u64_op(round_uint64_to_next_multiple_of(UINT64_MAX,2), ==,
+ UINT64_MAX);
+
tt_i64_op(round_int64_to_next_multiple_of(0,1), ==, 0);
tt_i64_op(round_int64_to_next_multiple_of(0,7), ==, 0);
@@ -4110,7 +4123,27 @@ test_util_round_to_next_multiple_of(void *arg)
tt_i64_op(round_int64_to_next_multiple_of(INT64_MIN,2), ==, INT64_MIN);
tt_i64_op(round_int64_to_next_multiple_of(INT64_MAX,2), ==,
- INT64_MAX-INT64_MAX%2);
+ INT64_MAX);
+
+ tt_int_op(round_uint32_to_next_multiple_of(0,1), ==, 0);
+ tt_int_op(round_uint32_to_next_multiple_of(0,7), ==, 0);
+
+ tt_int_op(round_uint32_to_next_multiple_of(99,1), ==, 99);
+ tt_int_op(round_uint32_to_next_multiple_of(99,7), ==, 105);
+ tt_int_op(round_uint32_to_next_multiple_of(99,9), ==, 99);
+
+ tt_int_op(round_uint32_to_next_multiple_of(UINT32_MAX,2), ==,
+ UINT32_MAX);
+
+ tt_uint_op(round_to_next_multiple_of(0,1), ==, 0);
+ tt_uint_op(round_to_next_multiple_of(0,7), ==, 0);
+
+ tt_uint_op(round_to_next_multiple_of(99,1), ==, 99);
+ tt_uint_op(round_to_next_multiple_of(99,7), ==, 105);
+ tt_uint_op(round_to_next_multiple_of(99,9), ==, 99);
+
+ tt_uint_op(round_to_next_multiple_of(UINT_MAX,2), ==,
+ UINT_MAX);
done:
;
}
@@ -4143,6 +4176,7 @@ test_util_laplace(void *arg)
*/
tt_i64_op(INT64_MIN + 20, ==,
add_laplace_noise(20, 0.0, delta_f, epsilon));
+
tt_i64_op(-60, ==, add_laplace_noise(20, 0.1, delta_f, epsilon));
tt_i64_op(-14, ==, add_laplace_noise(20, 0.25, delta_f, epsilon));
tt_i64_op(20, ==, add_laplace_noise(20, 0.5, delta_f, epsilon));
@@ -4150,15 +4184,146 @@ test_util_laplace(void *arg)
tt_i64_op(100, ==, add_laplace_noise(20, 0.9, delta_f, epsilon));
tt_i64_op(215, ==, add_laplace_noise(20, 0.99, delta_f, epsilon));
+ /* Test extreme values of signal with maximally negative values of noise
+ * 1.0000000000000002 is the smallest number > 1
+ * 0.0000000000000002 is the double epsilon (error when calculating near 1)
+ * this is approximately 1/(2^52)
+ * per https://en.wikipedia.org/wiki/Double_precision
+ * (let's not descend into the world of subnormals)
+ * >>> laplace.ppf([0, 0.0000000000000002], loc = 0, scale = 1)
+ * array([ -inf, -35.45506713])
+ */
+ const double noscale_df = 1.0, noscale_eps = 1.0;
+
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, 0.0, noscale_df, noscale_eps));
+
+ /* is it clipped to INT64_MIN? */
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(-1, 0.0, noscale_df, noscale_eps));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, 0.0,
+ noscale_df, noscale_eps));
+ /* ... even when scaled? */
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, 0.0, delta_f, epsilon));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, 0.0,
+ DBL_MAX, 1));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, 0.0,
+ DBL_MAX, 1));
+
+ /* does it play nice with INT64_MAX? */
+ tt_i64_op((INT64_MIN + INT64_MAX), ==,
+ add_laplace_noise(INT64_MAX, 0.0,
+ noscale_df, noscale_eps));
+
+ /* do near-zero fractional values work? */
+ const double min_dbl_error = 0.0000000000000002;
+
+ tt_i64_op(-35, ==,
+ add_laplace_noise(0, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_i64_op((-35 + INT64_MAX), ==,
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, min_dbl_error,
+ DBL_MAX, 1));
+ tt_i64_op((INT64_MAX + INT64_MIN), ==,
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ DBL_MAX, 1));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, min_dbl_error,
+ DBL_MAX, 1));
+
+ /* does it play nice with INT64_MAX? */
+ tt_i64_op((INT64_MAX - 35), ==,
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ noscale_df, noscale_eps));
+
+ /* Test extreme values of signal with maximally positive values of noise
+ * 1.0000000000000002 is the smallest number > 1
+ * 0.9999999999999998 is the greatest number < 1 by calculation
+ * per https://en.wikipedia.org/wiki/Double_precision
+ * >>> laplace.ppf([1.0, 0.9999999999999998], loc = 0, scale = 1)
+ * array([inf, 35.35050621])
+ * but the function rejects p == 1.0, so we just use max_dbl_lt_one
+ */
+ const double max_dbl_lt_one = 0.9999999999999998;
+
+ /* do near-one fractional values work? */
+ tt_i64_op(35, ==,
+ add_laplace_noise(0, max_dbl_lt_one, noscale_df, noscale_eps));
+
+ /* is it clipped to INT64_MAX? */
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX - 35, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX - 34, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ /* ... even when scaled? */
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ delta_f, epsilon));
+ tt_i64_op((INT64_MIN + INT64_MAX), ==,
+ add_laplace_noise(INT64_MIN, max_dbl_lt_one,
+ DBL_MAX, 1));
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ DBL_MAX, 1));
+ /* does it play nice with INT64_MIN? */
+ tt_i64_op((INT64_MIN + 35), ==,
+ add_laplace_noise(INT64_MIN, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+
done:
;
}
-#define UTIL_LEGACY(name) \
- { #name, test_util_ ## name , 0, NULL, NULL }
+static void
+test_util_clamp_double_to_int64(void *arg)
+{
+ (void)arg;
-#define UTIL_TEST(name, flags) \
- { #name, test_util_ ## name, flags, NULL, NULL }
+ tt_i64_op(INT64_MIN, ==, clamp_double_to_int64(-INFINITY));
+ tt_i64_op(INT64_MIN, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 64.0) - 1.0));
+ tt_i64_op(INT64_MIN, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 63.0) - 1.0));
+ tt_i64_op(((uint64_t) -1) << 53, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 53.0)));
+ tt_i64_op((((uint64_t) -1) << 53) + 1, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 53.0) + 1.0));
+ tt_i64_op(-1, ==, clamp_double_to_int64(-1.0));
+ tt_i64_op(0, ==, clamp_double_to_int64(-0.9));
+ tt_i64_op(0, ==, clamp_double_to_int64(-0.1));
+ tt_i64_op(0, ==, clamp_double_to_int64(0.0));
+ tt_i64_op(0, ==, clamp_double_to_int64(NAN));
+ tt_i64_op(0, ==, clamp_double_to_int64(0.1));
+ tt_i64_op(0, ==, clamp_double_to_int64(0.9));
+ tt_i64_op(1, ==, clamp_double_to_int64(1.0));
+ tt_i64_op((((int64_t) 1) << 53) - 1, ==,
+ clamp_double_to_int64(pow(2.0, 53.0) - 1.0));
+ tt_i64_op(((int64_t) 1) << 53, ==,
+ clamp_double_to_int64(pow(2.0, 53.0)));
+ tt_i64_op(INT64_MAX, ==,
+ clamp_double_to_int64(pow(2.0, 63.0)));
+ tt_i64_op(INT64_MAX, ==,
+ clamp_double_to_int64(pow(2.0, 64.0)));
+ tt_i64_op(INT64_MAX, ==, clamp_double_to_int64(INFINITY));
+
+ done:
+ ;
+}
#ifdef FD_CLOEXEC
#define CAN_CHECK_CLOEXEC
@@ -4180,9 +4345,14 @@ fd_is_nonblocking(tor_socket_t fd)
}
#endif
+#define ERRNO_IS_EPROTO(e) (e == SOCK_ERRNO(EPROTONOSUPPORT))
+#define SOCK_ERR_IS_EPROTO(s) ERRNO_IS_EPROTO(tor_socket_errno(s))
+
+/* Test for tor_open_socket*, using IPv4 or IPv6 depending on arg. */
static void
test_util_socket(void *arg)
{
+ const int domain = !strcmp(arg, "4") ? AF_INET : AF_INET6;
tor_socket_t fd1 = TOR_INVALID_SOCKET;
tor_socket_t fd2 = TOR_INVALID_SOCKET;
tor_socket_t fd3 = TOR_INVALID_SOCKET;
@@ -4193,15 +4363,19 @@ test_util_socket(void *arg)
(void)arg;
- fd1 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 0, 0);
- fd2 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 0, 1);
+ fd1 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 0, 0);
+ if (SOCK_ERR_IS_EPROTO(fd1)) {
+ /* Assume we're on an IPv4-only or IPv6-only system, and give up now. */
+ goto done;
+ }
+ fd2 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 0, 1);
tt_assert(SOCKET_OK(fd1));
tt_assert(SOCKET_OK(fd2));
tt_int_op(get_n_open_sockets(), OP_EQ, n + 2);
- //fd3 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 1, 0);
- //fd4 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 1, 1);
- fd3 = tor_open_socket(AF_INET, SOCK_STREAM, 0);
- fd4 = tor_open_socket_nonblocking(AF_INET, SOCK_STREAM, 0);
+ //fd3 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 1, 0);
+ //fd4 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 1, 1);
+ fd3 = tor_open_socket(domain, SOCK_STREAM, 0);
+ fd4 = tor_open_socket_nonblocking(domain, SOCK_STREAM, 0);
tt_assert(SOCKET_OK(fd3));
tt_assert(SOCKET_OK(fd4));
tt_int_op(get_n_open_sockets(), OP_EQ, n + 4);
@@ -4250,8 +4424,20 @@ test_util_socketpair(void *arg)
int n = get_n_open_sockets();
tor_socket_t fds[2] = {TOR_INVALID_SOCKET, TOR_INVALID_SOCKET};
const int family = AF_UNIX;
+ int socketpair_result = 0;
+
+ socketpair_result = tor_socketpair_fn(family, SOCK_STREAM, 0, fds);
+ /* If there is no 127.0.0.1 or ::1, tor_ersatz_socketpair will and must fail.
+ * Otherwise, we risk exposing a socketpair on a routable IP address. (Some
+ * BSD jails use a routable address for localhost. Fortunately, they have
+ * the real AF_UNIX socketpair.) */
+ if (ersatz && ERRNO_IS_EPROTO(-socketpair_result)) {
+ /* In my testing, an IPv6-only FreeBSD jail without ::1 returned EINVAL.
+ * Assume we're on a machine without 127.0.0.1 or ::1 and give up now. */
+ goto done;
+ }
+ tt_int_op(0, OP_EQ, socketpair_result);
- tt_int_op(0, OP_EQ, tor_socketpair_fn(family, SOCK_STREAM, 0, fds));
tt_assert(SOCKET_OK(fds[0]));
tt_assert(SOCKET_OK(fds[1]));
tt_int_op(get_n_open_sockets(), OP_EQ, n + 2);
@@ -4271,6 +4457,8 @@ test_util_socketpair(void *arg)
tor_close_socket(fds[1]);
}
+#undef SOCKET_EPROTO
+
static void
test_util_max_mem(void *arg)
{
@@ -4414,6 +4602,92 @@ test_util_get_avail_disk_space(void *arg)
;
}
+static void
+test_util_touch_file(void *arg)
+{
+ (void) arg;
+ const char *fname = get_fname("touch");
+
+ const time_t now = time(NULL);
+ struct stat st;
+ write_bytes_to_file(fname, "abc", 3, 1);
+ tt_int_op(0, OP_EQ, stat(fname, &st));
+ /* A subtle point: the filesystem time is not necessarily equal to the
+ * system clock time, since one can be using a monotonic clock, or coarse
+ * monotonic clock, or whatever. So we might wind up with an mtime a few
+ * microseconds ago. Let's just give it a lot of wiggle room. */
+ tt_i64_op(st.st_mtime, OP_GE, now - 1);
+
+ const time_t five_sec_ago = now - 5;
+ struct utimbuf u = { five_sec_ago, five_sec_ago };
+ tt_int_op(0, OP_EQ, utime(fname, &u));
+ tt_int_op(0, OP_EQ, stat(fname, &st));
+ /* Let's hope that utime/stat give the same second as a round-trip? */
+ tt_i64_op(st.st_mtime, OP_EQ, five_sec_ago);
+
+ /* Finally we can touch the file */
+ tt_int_op(0, OP_EQ, touch_file(fname));
+ tt_int_op(0, OP_EQ, stat(fname, &st));
+ tt_i64_op(st.st_mtime, OP_GE, now-1);
+
+ done:
+ ;
+}
+
+#ifndef _WIN32
+static void
+test_util_pwdb(void *arg)
+{
+ (void) arg;
+ const struct passwd *me = NULL, *me2, *me3;
+ char *name = NULL;
+ char *dir = NULL;
+
+ /* Uncached case. */
+ /* Let's assume that we exist. */
+ me = tor_getpwuid(getuid());
+ tt_assert(me != NULL);
+ name = tor_strdup(me->pw_name);
+
+ /* Uncached case */
+ me2 = tor_getpwnam(name);
+ tt_assert(me2 != NULL);
+ tt_int_op(me2->pw_uid, OP_EQ, getuid());
+
+ /* Cached case */
+ me3 = tor_getpwuid(getuid());
+ tt_assert(me3 != NULL);
+ tt_str_op(me3->pw_name, OP_EQ, name);
+
+ me3 = tor_getpwnam(name);
+ tt_assert(me3 != NULL);
+ tt_int_op(me3->pw_uid, OP_EQ, getuid());
+
+ dir = get_user_homedir(name);
+ tt_assert(dir != NULL);
+
+ done:
+ tor_free(name);
+ tor_free(dir);
+}
+#endif
+
+#define UTIL_LEGACY(name) \
+ { #name, test_util_ ## name , 0, NULL, NULL }
+
+#define UTIL_TEST(name, flags) \
+ { #name, test_util_ ## name, flags, NULL, NULL }
+
+#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))
+#define UTIL_LEGACY_NO_WIN(n) UTIL_TEST_NO_WIN(n, 0)
+#else
+#define UTIL_TEST_NO_WIN(n, f) UTIL_TEST(n, (f))
+#define UTIL_TEST_WIN_ONLY(n, f) { #n, NULL, TT_SKIP, NULL, NULL }
+#define UTIL_LEGACY_NO_WIN(n) UTIL_LEGACY(n)
+#endif
+
struct testcase_t util_tests[] = {
UTIL_LEGACY(time),
UTIL_TEST(parse_http_time, 0),
@@ -4421,9 +4695,7 @@ struct testcase_t util_tests[] = {
UTIL_LEGACY(config_line_quotes),
UTIL_LEGACY(config_line_comment_character),
UTIL_LEGACY(config_line_escaped_content),
-#ifndef _WIN32
- UTIL_LEGACY(expand_filename),
-#endif
+ UTIL_LEGACY_NO_WIN(expand_filename),
UTIL_LEGACY(escape_string_socks),
UTIL_LEGACY(string_is_key_value),
UTIL_LEGACY(strmisc),
@@ -4441,19 +4713,16 @@ struct testcase_t util_tests[] = {
UTIL_TEST(di_map, 0),
UTIL_TEST(round_to_next_multiple_of, 0),
UTIL_TEST(laplace, 0),
+ UTIL_TEST(clamp_double_to_int64, 0),
UTIL_TEST(find_str_at_start_of_line, 0),
UTIL_TEST(string_is_C_identifier, 0),
UTIL_TEST(asprintf, 0),
UTIL_TEST(listdir, 0),
UTIL_TEST(parent_dir, 0),
UTIL_TEST(ftruncate, 0),
-#ifdef _WIN32
- UTIL_TEST(load_win_lib, 0),
-#endif
-#ifndef _WIN32
- UTIL_TEST(exit_status, 0),
- UTIL_TEST(fgets_eagain, 0),
-#endif
+ UTIL_TEST_WIN_ONLY(load_win_lib, 0),
+ UTIL_TEST_NO_WIN(exit_status, 0),
+ UTIL_TEST_NO_WIN(fgets_eagain, 0),
UTIL_TEST(format_hex_number, 0),
UTIL_TEST(format_dec_number, 0),
UTIL_TEST(join_win_cmdline, 0),
@@ -4473,7 +4742,10 @@ struct testcase_t util_tests[] = {
UTIL_TEST(write_chunks_to_file, 0),
UTIL_TEST(mathlog, 0),
UTIL_TEST(weak_random, 0),
- UTIL_TEST(socket, TT_FORK),
+ { "socket_ipv4", test_util_socket, TT_FORK, &passthrough_setup,
+ (void*)"4" },
+ { "socket_ipv6", test_util_socket, TT_FORK,
+ &passthrough_setup, (void*)"6" },
{ "socketpair", test_util_socketpair, TT_FORK, &passthrough_setup,
(void*)"0" },
{ "socketpair_ersatz", test_util_socketpair, TT_FORK,
@@ -4483,6 +4755,8 @@ struct testcase_t util_tests[] = {
UTIL_TEST(ipv4_validation, 0),
UTIL_TEST(writepid, 0),
UTIL_TEST(get_avail_disk_space, 0),
+ UTIL_TEST(touch_file, 0),
+ UTIL_TEST_NO_WIN(pwdb, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c
new file mode 100644
index 0000000000..45a28ccaac
--- /dev/null
+++ b/src/test/test_util_format.c
@@ -0,0 +1,302 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+
+#define UTIL_FORMAT_PRIVATE
+#include "util_format.h"
+
+#define NS_MODULE util_format
+
+#if !defined(HAVE_HTONLL) && !defined(htonll)
+#ifdef WORDS_BIGENDIAN
+#define htonll(x) (x)
+#else
+static uint64_t
+htonll(uint64_t a)
+{
+ return htonl((uint32_t)(a>>32)) | (((uint64_t)htonl((uint32_t)a))<<32);
+}
+#endif
+#endif
+
+static void
+test_util_format_unaligned_accessors(void *ignored)
+{
+ (void)ignored;
+ char buf[9] = "onionsoup"; // 6f6e696f6e736f7570
+
+ tt_u64_op(get_uint64(buf+1), OP_EQ, htonll(U64_LITERAL(0x6e696f6e736f7570)));
+ tt_uint_op(get_uint32(buf+1), OP_EQ, htonl(0x6e696f6e));
+ tt_uint_op(get_uint16(buf+1), OP_EQ, htons(0x6e69));
+ tt_uint_op(get_uint8(buf+1), OP_EQ, 0x6e);
+
+ set_uint8(buf+7, 0x61);
+ tt_mem_op(buf, OP_EQ, "onionsoap", 9);
+
+ set_uint16(buf+6, htons(0x746f));
+ tt_mem_op(buf, OP_EQ, "onionstop", 9);
+
+ set_uint32(buf+1, htonl(0x78696465));
+ tt_mem_op(buf, OP_EQ, "oxidestop", 9);
+
+ set_uint64(buf+1, htonll(U64_LITERAL(0x6266757363617465)));
+ tt_mem_op(buf, OP_EQ, "obfuscate", 9);
+ done:
+ ;
+}
+
+static void
+test_util_format_base64_encode(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ char *dst;
+
+ src = tor_malloc_zero(256);
+ dst = tor_malloc_zero(1000);
+
+ for (i=0;i<256;i++) {
+ src[i] = (char)i;
+ }
+
+ res = base64_encode(NULL, 1, src, 1, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, 1, NULL, 1, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, 1, src, 10, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, SSIZE_MAX-1, src, 1, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, SSIZE_MAX-1, src, 10, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, 1000, src, 256, 0);
+ tt_int_op(res, OP_EQ, 344);
+ tt_str_op(dst, OP_EQ, "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh"
+ "8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH"
+ "SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3"
+ "BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeY"
+ "mZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wM"
+ "HCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp"
+ "6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==");
+
+ res = base64_encode(dst, 1000, src, 256, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 350);
+ tt_str_op(dst, OP_EQ,
+ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\n"
+ "MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\n"
+ "YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\n"
+ "kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/\n"
+ "wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v\n"
+ "8PHy8/T19vf4+fr7/P3+/w==\n");
+
+ res = base64_encode(dst, 1000, src+1, 255, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 346);
+
+ for (i = 0;i<50;i++) {
+ src[i] = 0;
+ }
+ src[50] = 255;
+ src[51] = 255;
+ src[52] = 255;
+ src[53] = 255;
+
+ res = base64_encode(dst, 1000, src, 54, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 74);
+
+ res = base64_encode(dst, 1000, src+1, 53, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 74);
+
+ res = base64_encode(dst, 1000, src+2, 52, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 74);
+
+ res = base64_encode(dst, 1000, src+3, 51, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 70);
+
+ res = base64_encode(dst, 1000, src+4, 50, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 70);
+
+ res = base64_encode(dst, 1000, src+5, 49, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 70);
+
+ res = base64_encode(dst, 1000, src+6, 48, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 65);
+
+ res = base64_encode(dst, 1000, src+7, 47, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 65);
+
+ res = base64_encode(dst, 1000, src+8, 46, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 65);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+}
+
+static void
+test_util_format_base64_decode_nopad(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 real_src[] = "ZXhhbXBsZQ";
+
+ 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);
+ tt_int_op(res, OP_EQ, -1);
+
+ const char *s = "SGVsbG8gd29ybGQ";
+ res = base64_decode_nopad(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));
+ 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);
+ tt_int_op(res, OP_EQ, 7);
+ tt_mem_op(real_dst, OP_EQ, expected, 7);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+ tor_free(real_dst);
+}
+
+static void
+test_util_format_base64_decode(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ char *dst, *real_dst;
+ uint8_t expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65};
+ char real_src[] = "ZXhhbXBsZQ==";
+
+ 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(dst, 1, src, SIZE_T_CEILING);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_decode(dst, SIZE_T_CEILING+1, src, 10);
+ tt_int_op(res, OP_EQ, -1);
+
+ const char *s = "T3BhIG11bmRv";
+ res = base64_decode(dst, 9, s, strlen(s));
+ tt_int_op(res, OP_EQ, 9);
+ tt_mem_op(dst, OP_EQ, "Opa mundo", 9);
+
+ memset(dst, 0, 1000);
+ res = base64_decode(dst, 100, s, strlen(s));
+ tt_int_op(res, OP_EQ, 9);
+ tt_mem_op(dst, OP_EQ, "Opa mundo", 9);
+
+ s = "SGVsbG8gd29ybGQ=";
+ res = base64_decode(dst, 100, s, strlen(s));
+ tt_int_op(res, OP_EQ, 11);
+ tt_mem_op(dst, OP_EQ, "Hello world", 11);
+
+ res = base64_decode(real_dst, 10, real_src, 10);
+ tt_int_op(res, OP_EQ, 7);
+ tt_mem_op(real_dst, OP_EQ, expected, 7);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+ tor_free(real_dst);
+}
+
+static void
+test_util_format_base16_decode(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ char *dst, *real_dst;
+ char expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65};
+ char real_src[] = "6578616D706C65";
+
+ 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 = base16_decode(dst, 3, src, 3);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, 1, src, 10);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, SIZE_T_CEILING+2, src, 10);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, 1000, "", 0);
+ tt_int_op(res, OP_EQ, 0);
+
+ res = base16_decode(dst, 1000, "aabc", 4);
+ tt_int_op(res, OP_EQ, 0);
+ tt_mem_op(dst, OP_EQ, "\xaa\xbc", 2);
+
+ res = base16_decode(dst, 1000, "aabcd", 6);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, 1000, "axxx", 4);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(real_dst, 10, real_src, 14);
+ tt_int_op(res, OP_EQ, 0);
+ tt_mem_op(real_dst, OP_EQ, expected, 7);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+ tor_free(real_dst);
+}
+
+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,
+ NULL, NULL },
+ { "base64_decode", test_util_format_base64_decode, 0, NULL, NULL },
+ { "base16_decode", test_util_format_base16_decode, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util_process.c b/src/test/test_util_process.c
new file mode 100644
index 0000000000..0a4354c043
--- /dev/null
+++ b/src/test/test_util_process.c
@@ -0,0 +1,82 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define UTIL_PROCESS_PRIVATE
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+
+#include "util_process.h"
+
+#include "log_test_helpers.h"
+
+#ifndef _WIN32
+#define NS_MODULE util_process
+
+static void
+temp_callback(int r, void *s)
+{
+ (void)r;
+ (void)s;
+}
+
+static void
+test_util_process_set_waitpid_callback(void *ignored)
+{
+ (void)ignored;
+ waitpid_callback_t *res1 = NULL, *res2 = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ pid_t pid = (pid_t)42;
+
+ res1 = set_waitpid_callback(pid, temp_callback, NULL);
+ tt_assert(res1);
+
+ res2 = set_waitpid_callback(pid, temp_callback, NULL);
+ tt_assert(res2);
+ expect_log_msg("Replaced a waitpid monitor on pid 42. That should be "
+ "impossible.\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ clear_waitpid_callback(res1);
+ clear_waitpid_callback(res2);
+}
+
+static void
+test_util_process_clear_waitpid_callback(void *ignored)
+{
+ (void)ignored;
+ waitpid_callback_t *res;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ pid_t pid = (pid_t)43;
+
+ clear_waitpid_callback(NULL);
+
+ res = set_waitpid_callback(pid, temp_callback, NULL);
+ clear_waitpid_callback(res);
+ expect_no_log_entry();
+
+#if 0
+ /* No. This is use-after-free. We don't _do_ that. XXXX */
+ clear_waitpid_callback(res);
+ expect_log_msg("Couldn't remove waitpid monitor for pid 43.\n");
+#endif
+
+ done:
+ teardown_capture_of_logs(previous_log);
+}
+#endif /* _WIN32 */
+
+#ifndef _WIN32
+#define TEST(name) { #name, test_util_process_##name, 0, NULL, NULL }
+#else
+#define TEST(name) { #name, NULL, TT_SKIP, NULL, NULL }
+#endif
+
+struct testcase_t util_process_tests[] = {
+ TEST(set_waitpid_callback),
+ TEST(clear_waitpid_callback),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c
index 0d79733cf0..1202f80fa3 100644
--- a/src/test/test_workqueue.c
+++ b/src/test/test_workqueue.c
@@ -390,8 +390,14 @@ main(int argc, char **argv)
init_logging(1);
network_init();
- crypto_global_init(1, NULL, NULL);
- crypto_seed_rng();
+ if (crypto_global_init(1, NULL, NULL) < 0) {
+ printf("Couldn't initialize crypto subsystem; exiting.\n");
+ return 1;
+ }
+ if (crypto_seed_rng() < 0) {
+ printf("Couldn't seed RNG; exiting.\n");
+ return 1;
+ }
rq = replyqueue_new(as_flags);
tor_assert(rq);
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 441024bd7d..da9969d8d9 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -228,6 +228,9 @@ main(int c, const char **v)
int loglevel = LOG_ERR;
int accel_crypto = 0;
+ /* We must initialise logs before we call tor_assert() */
+ init_logging(1);
+
#ifdef USE_DMALLOC
{
int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_);
@@ -238,8 +241,12 @@ main(int c, const char **v)
update_approx_time(time(NULL));
options = options_new();
tor_threads_init();
+
+ struct tor_libevent_cfg cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ tor_libevent_initialize(&cfg);
+
control_initialize_event_queue();
- init_logging(1);
configure_backtrace_handler(get_version());
for (i_out = i = 1; i < c; ++i) {
@@ -272,7 +279,10 @@ main(int c, const char **v)
return 1;
}
crypto_set_tls_dh_prime();
- crypto_seed_rng();
+ if (crypto_seed_rng() < 0) {
+ printf("Couldn't seed RNG; exiting.\n");
+ return 1;
+ }
rep_hist_init();
network_init();
setup_directory();
@@ -294,6 +304,7 @@ main(int c, const char **v)
tor_free_all(0);
dmalloc_log_unfreed();
#endif
+ crypto_global_cleanup();
if (have_failed)
return 1;
diff --git a/src/test/vote_descriptors.inc b/src/test/vote_descriptors.inc
new file mode 100644
index 0000000000..c5ce21f744
--- /dev/null
+++ b/src/test/vote_descriptors.inc
@@ -0,0 +1,94 @@
+const char* VOTE_BODY_V3 =
+"network-status-version 3\n"
+"vote-status vote\n"
+"consensus-methods 13 14 15 16 17 18 19 20 21\n"
+"published 2015-09-02 19:34:15\n"
+"valid-after 2015-09-02 19:50:55\n"
+"fresh-until 2015-09-02 20:07:38\n"
+"valid-until 2015-09-02 20:24:15\n"
+"voting-delay 100 250\n"
+"client-versions 0.1.2.14,0.1.2.17\n"
+"server-versions 0.1.2.10,0.1.2.15,0.1.2.16\n"
+"known-flags Authority Exit Fast Guard MadeOfCheese MadeOfTin Running Stable V2Dir Valid\n"
+"flag-thresholds stable-uptime=0 stable-mtbf=0 fast-speed=0 guard-wfu=0.000% guard-tk=0 guard-bw-inc-exits=0 guard-bw-exc-exits=0 enough-mtbf=0 ignoring-advertised-bws=0\n"
+"params circuitwindow=80 foo=660\n"
+"dir-source Voter3 D867ACF56A9D229B35C25F0090BC9867E906BE69 3.4.5.6 3.4.5.6 80 9000\n"
+"contact voter@example.com\n"
+"legacy-dir-key 4141414141414141414141414141414141414141\n"
+"dir-key-certificate-version 3\n"
+"fingerprint D867ACF56A9D229B35C25F0090BC9867E906BE69\n"
+"dir-key-published 2008-12-12 18:07:24\n"
+"dir-key-expires 2009-12-12 18:07:24\n"
+"dir-identity-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIIBigKCAYEAveMpKlw8oD1YqFqpJchuwSR82BDhutbqgHiez3QO9FmzOctJpV+Y\n"
+"mpTYIJLS/qC+4GBKFF1VK0C4SoBrS3zri0qdXdE+vBGcyrxrjMklpxoqSKRY2011\n"
+"4eqYPghKlo5RzuqteBclGCHyNxWjUJeRKDWgvh+U/gr2uYM6fRm5q0fCzg4aECE7\n"
+"VP6fDGZrMbQI8jHpiMSoC9gkUASNEa6chLInlnP8/H5qUEW4TB9CN/q095pefuwL\n"
+"P+F+1Nz5hnM7fa5XmeMB8iM4RriUmOQlLBZgpQBMpEfWMIPcR9F1Gh3MxERqqUcH\n"
+"tmij+IZdeXg9OkCXykcabaYIhZD3meErn9Tax4oA/THduLfgli9zM0ExwzH1OooN\n"
+"L8rIcJ+2eBo3bQiQUbdYW71sl9w7nSPtircbJUa1mUvWYLPWQxFliPiQSetgJLMj\n"
+"VQqtPmV2hvN2Xk3lLfJO50qMTK7w7Gsaw8UtV4YDM1Hcjp/hQaIB1xfwhXgl+eUU\n"
+"btUa4c+cUTjHAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBALPSUInyuEu6NV3NjozplaniIEBzQXEjv1x9/+mqnwZABpYVmuy9A8nx\n"
+"eoyY3sZFsnYwNW/IZjAgG23pEmevu3F+L4myMjjaa6ORl3MgRYQ4gmuFqpefrGdm\n"
+"ywRCleh2JerkQ4VxOuq10dn/abITzLyaZzMw30KXWp5pxKXOLtxFAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-key-crosscert\n"
+"-----BEGIN ID SIGNATURE-----\n"
+"FTBJNR/Hlt4T53yUMp1r/QCSMCpkHJCbYBT0R0pvYqhqFfYN5qHRSICRXaFFImIF\n"
+"0DGWmwRza6DxPKNzkm5/b7I0de9zJW1jNNdQAQK5xppAtQcAafRdu8cBonnmh9KX\n"
+"k1NrAK/X00FYywju3yl/SxCn1GddVNkHYexEudmJMPM=\n"
+"-----END ID SIGNATURE-----\n"
+"dir-key-certification\n"
+"-----BEGIN SIGNATURE-----\n"
+"pjWguLFBfELZDc6DywL6Do21SCl7LcutfpM92MEn4WYeSNcTXNR6lRX7reOEJk4e\n"
+"NwEaMt+Hl7slgeR5wjnW3OmMmRPZK9bquNWbfD+sAOV9bRFZTpXIdleAQFPlwvMF\n"
+"z/Gzwspzn4i2Yh6hySShrctMmW8YL3OM8LsBXzBhp/rG2uHlsxmIsc13DA6HWt61\n"
+"ffY72uNE6KckDGsQ4wPGP9q69y6g+X+TNio1KPbsILbePv6EjbO+rS8FiS4njPlg\n"
+"SPYry1RaUvxzxTkswIzdE1tjJrUiqpbWlTGxrH9N4OszoLm45Pc784KLULrjKIoi\n"
+"Q+vRsGrcMBAa+kDowWU6H1ryKR7KOhzRTcf2uqLE/W3ezaRwmOG+ETmoVFwbhk2X\n"
+"OlbXEM9fWP+INvFkr6Z93VYL2jGkCjV7e3xXmre/Lb92fUcYi6t5dwzfV8gJnIoG\n"
+"eCHd0K8NrQK0ipVk/7zcPDKOPeo9Y5aj/f6X/pDHtb+Dd5sT+l82G/Tqy4DIYUYR\n"
+"-----END SIGNATURE-----\n"
+"r router2 AwMDAwMDAwMDAwMDAwMDAwMDAwM Tk5OTk5OTk5OTk5OTk5OTk5OTk4 2015-09-02 19:09:15 153.0.136.1 443 8000\n"
+"s Running V2Dir\n"
+"v 0.1.2.14\n"
+"w Bandwidth=30 Measured=30\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa0\n"
+"r router1 BQUFBQUFBQUFBQUFBQUFBQUFBQU TU1NTU1NTU1NTU1NTU1NTU1NTU0 2015-09-02 19:17:35 153.0.153.1 443 0\n"
+"a [1:2:3::4]:4711\n"
+"s Exit Fast Guard Running Stable Valid\n"
+"v 0.2.0.5\n"
+"w Bandwidth=120 Measured=120\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa1\n"
+"r router3 MzMzMzMzMzMzMzMzMzMzMzMzMzM T09PT09PT09PT09PT09PT09PT08 2015-09-02 19:17:35 170.0.153.1 400 9999\n"
+"s Authority Exit Fast Guard Running Stable V2Dir Valid\n"
+"v 0.1.0.3\n"
+"w Bandwidth=120\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 "
+"sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa2\n"
+"r router4 NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ Ly8vLy8vLy8vLy8vLy8vLy8vLy8 2015-09-02 19:17:35 192.0.2.3 500 1999\n"
+"s Running V2Dir\n"
+"v 0.1.6.3\n"
+"w Bandwidth=30\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa3\n"
+"directory-footer\n"
+"directory-signature D867ACF56A9D229B35C25F0090BC9867E906BE69 CBF56A83368A5150F1A9AAADAFB4D77F8C4170E2\n"
+"-----BEGIN SIGNATURE-----\n"
+"AHiWcHe+T3XbnlQqvqSAk6RY3XmEy1+hM2u9Xk6BNi7BpQkEQM1f0vzRpgn5Dnf2\n"
+"TXQWGUq9Z7jdSVnzWT3xqPA4zjw6eZkj+DKUtwq+oEDZGlf8eHTFmr0NAWfwZbk9\n"
+"NAjbMTUXUP37N2XAZwkoCWwFCrrfMwXrL7OhZbj7ifo=\n"
+"-----END SIGNATURE-----\n";
+